From pypy.commits at gmail.com Fri Feb 1 06:09:08 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 01 Feb 2019 03:09:08 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: is this code needed (tests in pypy/interpreter, pypy/objspace pass without it)? Message-ID: <5c5428d4.1c69fb81.91be3.b67e@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95766:5dd0a0074920 Date: 2019-02-01 13:07 +0200 http://bitbucket.org/pypy/pypy/changeset/5dd0a0074920/ Log: is this code needed (tests in pypy/interpreter, pypy/objspace pass without it)? diff --git a/TODO b/TODO --- a/TODO +++ b/TODO @@ -7,7 +7,7 @@ * revisit why runicode import str_decode_utf_8_impl needed instead of runicode import str_decode_utf_8 * revisit all places where we do utf8.decode('utf-8'), they should work - directly with utf8 + directly with utf8 (can be converted via runicode.str_decode_utf_8 as well) - rutf8.utf8_encode_mbcs - unicodehelper.fsencode - unicodehelper.unicode_to_decimal_w diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -838,7 +838,6 @@ # returns a "text" object (ie str in python2 and unicode in python3) if not we_are_translated(): assert type(s) is str - #u = s.decode('utf-8') w_s1 = self.interned_strings.get(s) if w_s1 is None: w_s1 = self.newtext(s) @@ -882,10 +881,6 @@ # interface for marshal_impl if not we_are_translated(): assert type(s) is str - try: - u = s.decode('utf-8') - except UnicodeDecodeError: - return None return self.interned_strings.get(s) # may be None @specialize.arg(1) From pypy.commits at gmail.com Fri Feb 1 06:09:10 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 01 Feb 2019 03:09:10 -0800 (PST) Subject: [pypy-commit] pypy py3.6: typo Message-ID: <5c5428d6.1c69fb81.fc928.bee3@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r95767:c1e462eb3112 Date: 2019-02-01 13:08 +0200 http://bitbucket.org/pypy/pypy/changeset/c1e462eb3112/ Log: typo diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py --- a/pypy/module/sys/vm.py +++ b/pypy/module/sys/vm.py @@ -234,7 +234,7 @@ service_pack_minor = structseqfield(11, "Service Pack minor version number") suite_mask = structseqfield(12, "Bit mask identifying available product suites") product_type = structseqfield(13, "System product type") - _platform_version = structseqfield(14, "Diagnostic version number") + platform_version = structseqfield(14, "Diagnostic version number") ''') From pypy.commits at gmail.com Fri Feb 1 08:53:46 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 01 Feb 2019 05:53:46 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: remove round-tripping str-unicode from errors Message-ID: <5c544f6a.1c69fb81.740c0.30a8@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95768:be2b370c0652 Date: 2019-02-01 15:49 +0200 http://bitbucket.org/pypy/pypy/changeset/be2b370c0652/ Log: remove round-tripping str-unicode from errors diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -472,19 +472,7 @@ assert len(formats) > 0, "unsupported: no % command found" return tuple(parts), tuple(formats) -def _decode_utf8(string): - # when building the error message, don't crash if the byte string - # provided is not valid UTF-8 - if isinstance(string, unicode): - return string - assert isinstance(string, str) - #return string.decode('utf8') - result, consumed = runicode.str_decode_utf_8( - string, len(string), "replace", final=True) - return result - def get_operrcls2(valuefmt): - valuefmt = valuefmt.decode('ascii') strings, formats = decompose_valuefmt(valuefmt) assert len(strings) == len(formats) + 1 try: @@ -503,49 +491,49 @@ self.setup(w_type) def _compute_value(self, space): - # TODO: avoid utf8->unicode->utf8 dance lst = [None] * (len(formats) + len(formats) + 1) + lgt = 0 for i, fmt, attr in entries: lst[i + i] = self.xstrings[i] + lgt += len(self.xstrings[i]) value = getattr(self, attr) if fmt == 'd': - result = str(value).decode('ascii') + result = str(value) + lgt += len(result) elif fmt == 'R': - result = space.realunicode_w(space.repr(value)) + result = space.utf8_w(space.repr(value)) + lgt += len(result) elif fmt == 'S': - result = space.realunicode_w(space.str(value)) + result = space.utf8_w(space.str(value)) + lgt += len(result) elif fmt == 'T': - result = _decode_utf8(space.type(value).name) + result = space.type(value).name + lgt += len(result) elif fmt == 'N': - name = value.getname(space) - if isinstance(name, unicode): - result = name - else: - result = _decode_utf8(name) + result = value.getname(space) + lgt += len(result) elif fmt == '8': # u'str\uxxxx' -> 'str\xXX\xXX' -> u"'str\xXX\xXX'" - if isinstance(value, unicode): - result = runicode.unicode_encode_utf_8(value, - len(value), 'strict', allow_surrogates=True) - else: - from pypy.interpreter import unicodehelper - result = _decode_utf8(unicodehelper.str_decode_utf8( - value, 'replace', True, - unicodehelper.decode_never_raise, True)[0]) + from pypy.interpreter import unicodehelper + result, _lgt, pos = unicodehelper.str_decode_utf8( + value, 'replace', True, + unicodehelper.decode_never_raise, True) + lgt += _lgt + elif isinstance(value, unicode): + # 's' + result = str(value.encode('utf-8')) + lgt += len(value) else: - if isinstance(value, unicode): - result = value - else: - result = _decode_utf8(str(value)) + from rpython.rlib import rutf8 + result = str(value) + # Assumes valid utf-8 + lgt += rutf8.check_utf8(result, True) lst[i + i + 1] = result lst[-1] = self.xstrings[-1] - retval = u''.join(lst) - # We need to annotate both allow_surrogates=True,False - # since this function is used to replace uni.encode('utf8') - # deep in rpython - return runicode.unicode_encode_utf_8(retval, len(retval), - 'strict', allow_surrogates=False), len(retval) - # + lgt += len(self.xstrings[-1]) + retval = ''.join(lst) + return retval, lgt + _fmtcache2[formats] = OpErrFmt return OpErrFmt, strings From pypy.commits at gmail.com Fri Feb 1 08:53:48 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 01 Feb 2019 05:53:48 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: rework to avoid uni.decode Message-ID: <5c544f6c.1c69fb81.8121f.4569@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95769:fcbf4dcf5b74 Date: 2019-02-01 15:50 +0200 http://bitbucket.org/pypy/pypy/changeset/fcbf4dcf5b74/ Log: rework to avoid uni.decode diff --git a/pypy/interpreter/pyparser/pytokenizer.py b/pypy/interpreter/pyparser/pytokenizer.py --- a/pypy/interpreter/pyparser/pytokenizer.py +++ b/pypy/interpreter/pyparser/pytokenizer.py @@ -6,6 +6,7 @@ from pypy.interpreter.pyparser.pytokenize import tabsize, alttabsize, whiteSpaceDFA, \ triple_quoted, endDFAs, single_quoted, pseudoDFA from pypy.interpreter.astcompiler import consts +from rpython.rlib import rutf8 NAMECHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_' NUMCHARS = '0123456789' @@ -46,14 +47,9 @@ def verify_utf8(token): - for c in token: - if ord(c) >= 0x80: - break - else: - return True try: - u = token.decode('utf-8') - except UnicodeDecodeError: + rutf8.check_utf8(token, False) + except ruf8.CheckError: return False return True From pypy.commits at gmail.com Fri Feb 1 08:53:50 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 01 Feb 2019 05:53:50 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: merge py3.5 into branch Message-ID: <5c544f6e.1c69fb81.74f09.07ce@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95770:0c7880912bd4 Date: 2019-02-01 15:52 +0200 http://bitbucket.org/pypy/pypy/changeset/0c7880912bd4/ Log: merge py3.5 into branch diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -40,16 +40,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -59,8 +59,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -70,10 +70,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -114,12 +114,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -134,8 +134,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -144,10 +145,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -163,6 +164,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -176,6 +178,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -187,7 +190,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -198,7 +200,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -210,6 +211,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -217,12 +219,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -242,7 +246,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -270,12 +273,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -290,10 +296,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -301,28 +309,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -332,6 +338,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -340,6 +347,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -349,8 +357,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -364,7 +373,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -384,12 +392,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz diff --git a/extra_tests/cffi_tests/cffi0/test_function.py b/extra_tests/cffi_tests/cffi0/test_function.py --- a/extra_tests/cffi_tests/cffi0/test_function.py +++ b/extra_tests/cffi_tests/cffi0/test_function.py @@ -46,14 +46,15 @@ assert x != math.sin(1.23) # rounding effects assert abs(x - math.sin(1.23)) < 1E-6 - def test_lround_no_return_value(self): + def test_getenv_no_return_value(self): # check that 'void'-returning functions work too ffi = FFI(backend=self.Backend()) ffi.cdef(""" - void lround(double x); + void getenv(char *); """) - m = ffi.dlopen(lib_m) - x = m.lround(1.23) + needs_dlopen_none() + m = ffi.dlopen(None) + x = m.getenv(b"FOO") assert x is None def test_dlopen_filename(self): diff --git a/extra_tests/cffi_tests/cffi1/test_pkgconfig.py b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py new file mode 100644 --- /dev/null +++ b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py @@ -0,0 +1,95 @@ +# Generated by pypy/tool/import_cffi.py +import sys +import subprocess +import py +import cffi.pkgconfig as pkgconfig +from cffi import PkgConfigError + + +def mock_call(libname, flag): + assert libname=="foobarbaz" + flags = { + "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", + "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", + } + return flags[flag] + + +def test_merge_flags(): + d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} + d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} + + pkgconfig.merge_flags(d1, d2) + assert d1 == { + "ham": [1, 2, 3], + "spam" : ["a", "b", "c", "spam", "spam", "spam"], + "bar" : ["b", "a", "z"], + "foo" : []} + + +def test_pkgconfig(): + assert pkgconfig.flags_from_pkgconfig([]) == {} + + saved = pkgconfig.call + try: + pkgconfig.call = mock_call + flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) + finally: + pkgconfig.call = saved + assert flags == { + 'include_dirs': ['/usr/include/python3.6m'], + 'library_dirs': ['/usr/lib64'], + 'libraries': ['python3.6'], + 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], + 'extra_compile_args': ['-O42'], + 'extra_link_args': ['-shared'] + } + +class mock_subprocess: + PIPE = Ellipsis + class Popen: + def __init__(self, cmd, stdout, stderr): + if mock_subprocess.RESULT is None: + raise OSError("oops can't run") + assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] + def communicate(self): + bout, berr, rc = mock_subprocess.RESULT + self.returncode = rc + return bout, berr + +def test_call(): + saved = pkgconfig.subprocess + try: + pkgconfig.subprocess = mock_subprocess + + mock_subprocess.RESULT = None + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "cannot run pkg-config: oops can't run" + + mock_subprocess.RESULT = b"", "Foo error!\n", 1 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "Foo error!" + + mock_subprocess.RESULT = b"abc\\def\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value).startswith("pkg-config --cflags libfoo returned an " + "unsupported backslash-escaped output:") + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + if sys.version_info >= (3,): + mock_subprocess.RESULT = b"\xff\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, + "libfoo", "--cflags", encoding="utf-8") + assert str(e.value) == ( + "pkg-config --cflags libfoo returned bytes that cannot be " + "decoded with encoding 'utf-8':\nb'\\xff\\n'") + + finally: + pkgconfig.subprocess = saved diff --git a/lib-python/3/distutils/sysconfig_pypy.py b/lib-python/3/distutils/sysconfig_pypy.py --- a/lib-python/3/distutils/sysconfig_pypy.py +++ b/lib-python/3/distutils/sysconfig_pypy.py @@ -10,7 +10,7 @@ import sys import os -import imp, _imp +import _imp from distutils.errors import DistutilsPlatformError diff --git a/lib-python/3/test/test_dictviews.py b/lib-python/3/test/test_dictviews.py --- a/lib-python/3/test/test_dictviews.py +++ b/lib-python/3/test/test_dictviews.py @@ -1,9 +1,11 @@ +from test import support import copy import pickle import unittest class DictSetTest(unittest.TestCase): + @support.cpython_only def test_constructors_not_callable(self): kt = type({}.keys()) self.assertRaises(TypeError, kt, {}) diff --git a/lib_pypy/_collections.py b/lib_pypy/_collections.py --- a/lib_pypy/_collections.py +++ b/lib_pypy/_collections.py @@ -390,7 +390,7 @@ class defaultdict(dict): __slots__ = ["default_factory"] - + def __init__(self, *args, **kwds): if len(args) > 0: default_factory = args[0] @@ -401,10 +401,10 @@ default_factory = None self.default_factory = default_factory super(defaultdict, self).__init__(*args, **kwds) - + def __missing__(self, key): # from defaultdict docs - if self.default_factory is None: + if self.default_factory is None: raise KeyError(key) self[key] = value = self.default_factory() return value @@ -420,7 +420,7 @@ def copy(self): return type(self)(self.default_factory, self) - + def __copy__(self): return self.copy() @@ -438,9 +438,3 @@ """ return (type(self), (self.default_factory,), None, None, iter(self.items())) - - -try: - from _pypy_collections import OrderedDict -except ImportError: - pass 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 @@ -3,6 +3,7 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing +from .error import PkgConfigError __version__ = "1.12.0" __version_info__ = (1, 12, 0) 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 @@ -643,6 +643,16 @@ self._assigned_source = (str(module_name), source, source_extension, kwds) + def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, + source_extension='.c', **kwds): + from . import pkgconfig + if not isinstance(pkgconfig_libs, list): + raise TypeError("the pkgconfig_libs argument must be a list " + "of package names") + kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) + pkgconfig.merge_flags(kwds, kwds2) + self.set_source(module_name, source, source_extension, **kwds) + def distutils_extension(self, tmpdir='build', verbose=True): from distutils.dir_util import mkpath from .recompiler import recompile diff --git a/lib_pypy/cffi/error.py b/lib_pypy/cffi/error.py --- a/lib_pypy/cffi/error.py +++ b/lib_pypy/cffi/error.py @@ -1,8 +1,9 @@ class FFIError(Exception): - pass + __module__ = 'cffi' class CDefError(Exception): + __module__ = 'cffi' def __str__(self): try: current_decl = self.args[1] @@ -16,8 +17,15 @@ class VerificationError(Exception): """ An error raised when verification fails """ + __module__ = 'cffi' class VerificationMissing(Exception): """ An error raised when incomplete structures are passed into cdef, but no verification has been done """ + __module__ = 'cffi' + +class PkgConfigError(Exception): + """ An error raised for missing modules in pkg-config + """ + __module__ = 'cffi' diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst --- a/pypy/doc/build.rst +++ b/pypy/doc/build.rst @@ -220,11 +220,12 @@ Making a debug build of PyPy ---------------------------- -If the Makefile is rerun with the lldebug or lldebug0 target, appropriate -compilation flags are added to add debug info and reduce compiler optimizations -to ``-O0`` respectively. If you stop in a debugger, you will see the -very wordy machine-generated C code from the rpython translation step, which -takes a little bit of reading to relate back to the rpython code. +Rerun the ``Makefile`` with the ``make lldebug`` or ``make lldebug0`` target, +which will build in a way that running under a debugger makes sense. +Appropriate compilation flags are added to add debug info, and for ``lldebug0`` +compiler optimizations are fully disabled. If you stop in a debugger, you will +see the very wordy machine-generated C code from the rpython translation step, +which takes a little bit of reading to relate back to the rpython code. Build cffi import libraries for the stdlib ------------------------------------------ diff --git a/pypy/doc/contributor.rst b/pypy/doc/contributor.rst --- a/pypy/doc/contributor.rst +++ b/pypy/doc/contributor.rst @@ -7,16 +7,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -26,8 +26,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -37,10 +37,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -81,12 +81,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -101,8 +101,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -111,10 +112,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -130,6 +131,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -143,6 +145,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -154,7 +157,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -165,7 +167,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -177,6 +178,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -184,12 +186,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -209,7 +213,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -237,12 +240,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -257,10 +263,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -268,28 +276,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -299,6 +305,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -307,6 +314,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -316,8 +324,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -331,7 +340,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -351,12 +359,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz diff --git a/pypy/doc/how-to-release.rst b/pypy/doc/how-to-release.rst --- a/pypy/doc/how-to-release.rst +++ b/pypy/doc/how-to-release.rst @@ -16,9 +16,6 @@ How to Create a PyPy Release ++++++++++++++++++++++++++++ -Overview --------- - As a meta rule setting up issues in the tracker for items here may help not forgetting things. A set of todo files may also work. @@ -28,17 +25,54 @@ Release Steps -------------- +++++++++++++++ -* If needed, make a release branch -* Bump the - pypy version number in module/sys/version.py and in - module/cpyext/include/patchlevel.h and in doc/conf.py. The branch - will capture the revision number of this change for the release. +Make the release branch +------------------------ - Some of the next updates may be done before or after branching; make - sure things are ported back to the trunk and to the branch as - necessary. +This is needed only in case you are doing a new major version; if not, you can +probably reuse the existing release branch. + +We want to be able to freely merge default into the branch and vice-versa; +thus we need to do a complicate dance to avoid to patch the version number +when we do a merge:: + + $ hg up -r default + $ # edit the version to e.g. 7.0.0-final + $ hg ci + $ hg branch release-pypy2.7-7.x && hg ci + $ hg up -r default + $ # edit the version to 7.1.0-alpha0 + $ hg ci + $ hg up -r release-pypy2.7-7.x + $ hg merge default + $ # edit the version to AGAIN 7.0.0-final + $ hg ci + +Then, we need to do the same for the 3.x branch:: + + $ hg up -r py3.5 + $ hg merge default # this brings the version fo 7.1.0-alpha0 + $ hg branch release-pypy3.5-7.x + $ # edit the version to 7.0.0-final + $ hg ci + $ hg up -r py3.5 + $ hg merge release-pypy3.5-7.x + $ # edit the version to 7.1.0-alpha0 + $ hg ci + +To change the version, you need to edit three files: + + - ``module/sys/version.py`` + + - ``module/cpyext/include/patchlevel.h`` + + - ``doc/conf.py`` + + +Other steps +----------- + * Make sure the RPython builds on the buildbot pass with no failures 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-v7.0.0.rst release-v6.0.0.rst release-v5.10.1.rst release-v5.10.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-7.0.0.rst whatsnew-pypy2-6.0.0.rst whatsnew-pypy2-5.10.0.rst whatsnew-pypy2-5.10.0.rst @@ -41,6 +42,7 @@ .. toctree:: whatsnew-pypy3-head.rst + whatsnew-pypy3-7.0.0.rst whatsnew-pypy3-5.9.0.rst whatsnew-pypy3-5.8.0.rst whatsnew-pypy3-5.7.0.rst diff --git a/pypy/doc/interpreter.rst b/pypy/doc/interpreter.rst --- a/pypy/doc/interpreter.rst +++ b/pypy/doc/interpreter.rst @@ -156,7 +156,7 @@ environment found in `Frames`. Frames and Functions have references to a code object. Here is a list of Code attributes: -* ``co_flags`` flags if this code object has nested scopes/generators +* ``co_flags`` flags if this code object has nested scopes/generators/etc. * ``co_stacksize`` the maximum depth the stack can reach while executing the code * ``co_code`` the actual bytecode string diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-v7.0.0.rst @@ -0,0 +1,151 @@ +====================================================== +PyPy v7.0.0: triple release of 2.7, 3.5 and 3.6-alpha +====================================================== + +The PyPy team is proud to release the version 7.0.0 of PyPy, which includes +three different interpreters: + + - PyPy2.7, which is an interpreter supporting the syntax and the features of + Python 2.7 + + - PyPy3.5, which supports Python 3.5 + + - PyPy3.6-alpha: this is the first official release of PyPy to support 3.6 + features, although it is still considered alpha quality. + +All the interpreters are based on much the same codebase, thus the triple +release. + +Until we can work with downstream providers to distribute builds with PyPy, we +have made packages for some common packages `available as wheels`_. + +The GC `hooks`_ , which can be used to gain more insights into its +performance, has been improved and it is now possible to manually manage the +GC by using a combination of ``gc.disable`` and ``gc.collect_step``. See the +`GC blog post`_. + + +We updated the `cffi`_ module included in PyPy to version 1.12, and the +`cppyy`_ backend to 1.4. Please use these to wrap your C and C++ code, +respectively, for a JIT friendly experience. + +As always, this release is 100% compatible with the previous one and fixed +several issues and bugs raised by the growing community of PyPy users. +We strongly recommend updating. + +The PyPy3.6 release and the Windows PyPy3.5 release are still not production +quality so your mileage may vary. There are open issues with incomplete +compatibility and c-extension support. + +The utf8 branch that changes internal representation of unicode to utf8 did not +make it into the release, so there is still more goodness coming. +You can download the v6.0 releases here: + + http://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project. If PyPy is not quite good enough for your needs, we are available for +direct consulting work. + +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. + +.. _`PyPy`: index.html +.. _`RPython`: https://rpython.readthedocs.org +.. _`help`: project-ideas.html +.. _`cffi`: http://cffi.readthedocs.io +.. _`cppyy`: https://cppyy.readthedocs.io +.. _`available as wheels`: https://github.com/antocuni/pypy-wheels +.. _`GC blog post`: https://morepypy.blogspot.com/2019/01/pypy-for-low-latency-systems.html + + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7, 3.5 and 3.6. 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 release supports: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS X 64 bits, Windows 32 bits, OpenBSD, FreeBSD) + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +Unfortunately at the moment of writing our ARM buildbots are out of service, +so for now we are **not** releasing any binary for the ARM architecture. + +.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://rpython.readthedocs.io/en/latest/examples.html + + +Changelog +========= + +If not specified, the changes are shared across versions + +* Support ``__set_name__``, ``__init_subclass__`` (Py3.6) +* Support ``cppyy`` in Py3.5 and Py3.6 +* Use implementation-specific site directories in ``sysconfig`` (Py3.5, Py3.6) +* Adding detection of gcc to ``sysconfig`` (Py3.5, Py3.6) +* Fix multiprocessing regression on newer glibcs +* Make sure 'blocking-ness' of socket is set along with default timeout +* Include ``crypt.h`` for ``crypt()`` on Linux +* Improve and re-organize the contributing_ documentation +* Make the ``__module__`` attribute writable, fixing an incompatibility with + NumPy 1.16 +* Implement ``Py_ReprEnter``, ``Py_ReprLeave(), ``PyMarshal_ReadObjectFromString``, + ``PyMarshal_WriteObjectToString``, ``PyObject_DelItemString``, + ``PyMapping_DelItem``, ``PyMapping_DelItemString``, ``PyEval_GetFrame``, + ``PyOS_InputHook``, ``PyErr_FormatFromCause`` (Py3.6), +* Implement new wordcode instruction encoding (Py3.6) +* Log additional gc-minor and gc-collect-step info in the PYPYLOG +* The ``reverse-debugger`` (revdb) branch has been merged to the default + branch, so it should always be up-to-date. You still need a special pypy + build, but you can compile it from the same source as the one we distribute + for the v7.0.0 release. For more information, see + https://bitbucket.org/pypy/revdb +* Support underscores in numerical literals like ``'4_2'`` (Py3.6) +* Pre-emptively raise MemoryError if the size of dequeue in ``_collections.deque`` + is too large (Py3.5) +* Fix multithreading issues in calls to ``os.setenv`` +* Add missing defines and typedefs for numpy and pandas on MSVC +* Add CPython macros like ``Py_NAN`` to header files +* Rename the ``MethodType`` to ``instancemethod``, like CPython +* Better support for `async with` in generators (Py3.5, Py3.6) +* Improve the performance of ``pow(a, b, c)`` if ``c`` is a large integer +* Now ``vmprof`` works on FreeBSD +* Support GNU Hurd, fixes for FreeBSD +* Add deprecation warning if type of result of ``__float__`` is float inherited + class (Py3.6) +* Fix async generator bug when yielding a ``StopIteration`` (Py3.6) +* Speed up ``max(list-of-int)`` from non-jitted code +* Fix Windows ``os.listdir()`` for some cases (see CPython #32539) +* Add ``select.PIPE_BUF`` +* Use ``subprocess`` to avoid shell injection in ``shutil`` module - backport + of https://bugs.python.org/issue34540 +* Rename ``_Py_ZeroStruct`` to ``_Py_FalseStruct`` (Py3.5, Py3.6) +* Remove some cpyext names for Py3.5, Py3.6 +* Enable use of unicode file names in ``dlopen`` +* Backport CPython fix for ``thread.RLock`` +* Make GC hooks measure time in seconds (as opposed to an opaque unit) +* Refactor and reorganize tests in ``test_lib_pypy`` +* Check error values in ``socket.setblocking`` (Py3.6) +* Add support for FsPath to os.unlink() (Py3.6) +* Fix freezing builtin modules at translation +* Tweak ``W_UnicodeDictionaryStrategy`` which speeds up dictionaries with only + unicode keys + +We also refactored many parts of the JIT bridge optimizations, as well as cpyext +internals, and together with new contributors fixed issues, added new +documentation, and cleaned up the codebase. + +.. _contributing: http://doc.pypy.org/en/latest/contributing.html 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,44 +1,9 @@ ========================== -What's new in PyPy2.7 6.0+ +What's new in PyPy2.7 7.0+ ========================== -.. this is a revision shortly after release-pypy-6.0.0 -.. startrev: e50e11af23f1 - -.. branch: cppyy-packaging - -Main items: vastly better template resolution and improved performance. In -detail: upgrade to backend 1.4, improved handling of templated methods and -functions (in particular automatic deduction of types), improved pythonization -interface, range of compatibility fixes for Python3, free functions now take -fast libffi path when possible, moves for strings (incl. from Python str), -easier/faster handling of std::vector by numpy, improved and faster object -identity preservation - -.. branch: socket_default_timeout_blockingness - -Make sure 'blocking-ness' of socket is set along with default timeout - -.. branch: crypt_h - -Include crypt.h for crypt() on Linux - -.. branch: gc-more-logging - -Log additional gc-minor and gc-collect-step info in the PYPYLOG - -.. branch: reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb - -.. branch: pyparser-improvements-3 - -Small refactorings in the Python parser. - -.. branch: fix-readme-typo - -.. branch: avoid_shell_injection_in_shutil +.. this is a revision shortly after release-pypy-7.0.0 +.. startrev: 481c69f7d81f .. branch: unicode-utf8-re @@ -49,32 +14,6 @@ .. branch: pyparser-improvements-3 Small refactorings in the Python parser. -Backport CPython fix for possible shell injection issue in `distutils.spawn`, -https://bugs.python.org/issue34540 - -.. branch: cffi_dlopen_unicode - -Enable use of unicode file names in `dlopen` - -.. branch: rlock-in-rpython - -Backport CPython fix for `thread.RLock` - - -.. branch: expose-gc-time - -Make GC hooks measure time in seconds (as opposed to an opaque unit). - -.. branch: cleanup-test_lib_pypy - -Update most test_lib_pypy/ tests and move them to extra_tests/. - -.. branch: gc-disable - -Make it possible to manually manage the GC by using a combination of -gc.disable() and gc.collect_step(). Make sure to write a proper release -announcement in which we explain that existing programs could leak memory if -they run for too much time between a gc.disable()/gc.enable() .. branch: unicode-utf8 diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-pypy2-7.0.0.rst copy from pypy/doc/whatsnew-head.rst copy to pypy/doc/whatsnew-pypy2-7.0.0.rst diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-7.0.0.rst copy from pypy/doc/whatsnew-pypy3-head.rst copy to pypy/doc/whatsnew-pypy3-7.0.0.rst 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,14 +1,7 @@ -======================== -What's new in PyPy3 6.0+ -======================== - -.. this is the revision after release-pypy3.5-v6.0 -.. startrev: 580e3e26cd32 - -.. branch: unicode-utf8 - -Use utf-8 internally to represent unicode strings - -.. branch: unicode-utf8-py3 - -Use utf-8 internally to represent unicode strings +======================== +What's new in PyPy3 7.0+ +======================== + +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c + diff --git a/pypy/interpreter/app_main.py b/pypy/interpreter/app_main.py --- a/pypy/interpreter/app_main.py +++ b/pypy/interpreter/app_main.py @@ -3,7 +3,6 @@ # See test/test_app_main. # Missing vs CPython: -b, -d, -x -from __future__ import print_function, unicode_literals USAGE1 = __doc__ = """\ Options and arguments (and corresponding environment variables): -B : don't write .py[co] files on import; also PYTHONDONTWRITEBYTECODE=x @@ -334,7 +333,7 @@ del encerr def create_stdio(fd, writing, name, encoding, errors, unbuffered): - import io + import _io # stdin is always opened in buffered mode, first because it # shouldn't make a difference in common use cases, second because # TextIOWrapper depends on the presence of a read1() method which @@ -342,7 +341,7 @@ buffering = 0 if unbuffered and writing else -1 mode = 'w' if writing else 'r' try: - buf = io.open(fd, mode + 'b', buffering, closefd=False) + buf = _io.open(fd, mode + 'b', buffering, closefd=False) except OSError as e: if e.errno != errno.EBADF: raise @@ -352,7 +351,7 @@ raw.name = name # translate \r\n to \n for sys.stdin on Windows newline = None if sys.platform == 'win32' and not writing else '\n' - stream = io.TextIOWrapper(buf, encoding, errors, newline=newline, + stream = _io.TextIOWrapper(buf, encoding, errors, newline=newline, line_buffering=unbuffered or raw.isatty()) stream.mode = mode return stream @@ -549,12 +548,6 @@ return options -# this indirection is needed to be able to import this module on python2, else -# we have a SyntaxError: unqualified exec in a nested function - at hidden_applevel -def exec_(src, dic): - exec(src, dic) - @hidden_applevel def run_command_line(interactive, inspect, @@ -663,7 +656,7 @@ else: if not isolated: sys.path.insert(0, '') - success = run_toplevel(exec_, bytes, mainmodule.__dict__) + success = run_toplevel(exec, bytes, mainmodule.__dict__) elif run_module != 0: # handle the "-m" command # '' on sys.path is required also here @@ -703,7 +696,7 @@ python_startup, 'exec', PyCF_ACCEPT_NULL_BYTES) - exec_(co_python_startup, mainmodule.__dict__) + exec(co_python_startup, mainmodule.__dict__) mainmodule.__file__ = python_startup mainmodule.__cached__ = None run_toplevel(run_it) @@ -721,7 +714,7 @@ def run_it(): co_stdin = compile(sys.stdin.read(), '', 'exec', PyCF_ACCEPT_NULL_BYTES) - exec_(co_stdin, mainmodule.__dict__) + exec(co_stdin, mainmodule.__dict__) mainmodule.__file__ = '' mainmodule.__cached__ = None success = run_toplevel(run_it) @@ -763,7 +756,7 @@ co = marshal.load(f) if type(co) is not type((lambda:0).__code__): raise RuntimeError("Bad code object in .pyc file") - exec_(co, namespace) + exec(co, namespace) args = (execfile, filename, mainmodule.__dict__) else: filename = sys.argv[0] @@ -791,7 +784,7 @@ code = f.read() co = compile(code, filename, 'exec', PyCF_ACCEPT_NULL_BYTES) - exec_(co, namespace) + exec(co, namespace) args = (execfile, filename, mainmodule.__dict__) success = run_toplevel(*args) 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 @@ -1043,36 +1043,6 @@ assert data.startswith("15\\u20ac ('strict', 'backslashreplace')") -class TestAppMain: - def test_print_info(self): - from pypy.interpreter import app_main - import sys, cStringIO - prev_so = sys.stdout - prev_ti = getattr(sys, 'pypy_translation_info', 'missing') - sys.pypy_translation_info = { - 'translation.foo': True, - 'translation.bar': 42, - 'translation.egg.something': None, - 'objspace.x': 'hello', - } - try: - sys.stdout = f = cStringIO.StringIO() - py.test.raises(SystemExit, app_main.print_info) - finally: - sys.stdout = prev_so - if prev_ti == 'missing': - del sys.pypy_translation_info - else: - sys.pypy_translation_info = prev_ti - assert f.getvalue() == ("[objspace]\n" - " x = 'hello'\n" - "[translation]\n" - " bar = 42\n" - " [egg]\n" - " something = None\n" - " foo = True\n") - - @py.test.mark.skipif('config.getoption("runappdirect")') class AppTestAppMain: def setup_class(self): diff --git a/pypy/module/__builtin__/state.py b/pypy/module/__builtin__/state.py --- a/pypy/module/__builtin__/state.py +++ b/pypy/module/__builtin__/state.py @@ -2,8 +2,8 @@ class State: def __init__(self, space): self.w_open = space.appexec([], """(): - import io - return io.open""") - + import _io + return _io.open""") + def get(space): return space.fromcache(State) diff --git a/pypy/module/_collections/__init__.py b/pypy/module/_collections/__init__.py --- a/pypy/module/_collections/__init__.py +++ b/pypy/module/_collections/__init__.py @@ -8,6 +8,7 @@ appleveldefs = { 'defaultdict': 'app_defaultdict.defaultdict', + 'OrderedDict': 'app_odict.OrderedDict', } interpleveldefs = { @@ -25,15 +26,3 @@ space = self.space space.getattr(self, space.newtext('defaultdict')) # force importing space.delattr(self, space.newtext('__missing__')) - - def startup(self, space): - # OrderedDict is normally present, but in some cases the line - # "from __pypy__ import reversed_dict, move_to_end" from - # _pypy_collections.py raises - space.appexec([self], """(mod): - try: - from _pypy_collections import OrderedDict - mod.OrderedDict = OrderedDict - except ImportError: - pass - """) diff --git a/lib_pypy/_pypy_collections.py b/pypy/module/_collections/app_odict.py rename from lib_pypy/_pypy_collections.py rename to pypy/module/_collections/app_odict.py --- a/lib_pypy/_pypy_collections.py +++ b/pypy/module/_collections/app_odict.py @@ -1,6 +1,5 @@ from __pypy__ import reversed_dict, move_to_end, objects_in_repr from _operator import eq as _eq -import _collections_abc class OrderedDict(dict): @@ -29,7 +28,33 @@ raise TypeError('expected at most 1 arguments, got %d' % len(args)) self.__update(*args, **kwds) - update = __update = _collections_abc.MutableMapping.update + def update(*args, **kwds): + ''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F. + If E present and has a .keys() method, does: for k in E: D[k] = E[k] + If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v + In either case, this is followed by: for k, v in F.items(): D[k] = v + ''' + if not args: + raise TypeError("descriptor 'update' of 'OrderedDict' object " + "needs an argument") + self, *args = args + if len(args) > 1: + raise TypeError('update expected at most 1 arguments, got %d' % + len(args)) + if args: + other = args[0] + if hasattr(other, 'items'): + for key, value in other.items(): + self[key] = value + elif hasattr(other, "keys"): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value + for key, value in kwds.items(): + self[key] = value + __update = update def __reversed__(self): return reversed_dict(self) @@ -106,17 +131,20 @@ "D.values() -> an object providing a view on D's values" return _OrderedDictValuesView(self) +dict_keys = type({}.keys()) +dict_values = type({}.values()) +dict_items = type({}.items()) -class _OrderedDictKeysView(_collections_abc.KeysView): +class _OrderedDictKeysView(dict_keys): def __reversed__(self): - yield from reversed_dict(self._mapping) + yield from reversed_dict(self._dict) -class _OrderedDictItemsView(_collections_abc.ItemsView): +class _OrderedDictItemsView(dict_items): def __reversed__(self): - for key in reversed_dict(self._mapping): - yield (key, self._mapping[key]) + for key in reversed_dict(self._dict): + yield (key, self._dict[key]) -class _OrderedDictValuesView(_collections_abc.ValuesView): +class _OrderedDictValuesView(dict_values): def __reversed__(self): - for key in reversed_dict(self._mapping): - yield self._mapping[key] + for key in reversed_dict(self._dict): + yield self._dict[key] diff --git a/pypy/module/_collections/test/test_ordereddict.py b/pypy/module/_collections/test/test_ordereddict.py --- a/pypy/module/_collections/test/test_ordereddict.py +++ b/pypy/module/_collections/test/test_ordereddict.py @@ -22,3 +22,17 @@ assert d['x'] == 42 d.update({'y': 2}) assert d['y'] == 42 + + def test_reversed(self): + import sys + from _collections import OrderedDict + + pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] + od = OrderedDict(pairs) + if '__pypy__' in sys.builtin_module_names: + # dict ordering is wrong when testing interpreted on top of CPython + pairs = list(dict(od).items()) + assert list(reversed(od)) == [t[0] for t in reversed(pairs)] + assert list(reversed(od.keys())) == [t[0] for t in reversed(pairs)] + assert list(reversed(od.values())) == [t[1] for t in reversed(pairs)] + assert list(reversed(od.items())) == list(reversed(pairs)) 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 @@ -512,6 +512,7 @@ header = DEFAULT_HEADER if func.__name__ in FUNCTIONS_BY_HEADER[header]: raise ValueError("%s already registered" % func.__name__) + func._revdb_c_only_ = True # hack for revdb api_function = COnlyApiFunction(argtypes, restype, func) FUNCTIONS_BY_HEADER[header][func.__name__] = api_function return api_function 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 @@ -32,7 +32,7 @@ * module/sys/version.py * doc/conf.py */ -#define PYPY_VERSION "7.1.0" +#define PYPY_VERSION "7.1.0-alpha0" #define PYPY_VERSION_NUM 0x07010000 /* 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/sys/version.py b/pypy/module/sys/version.py --- a/pypy/module/sys/version.py +++ b/pypy/module/sys/version.py @@ -13,7 +13,7 @@ # make sure to keep PYPY_VERSION in sync with: # module/cpyext/include/patchlevel.h # doc/conf.py -PYPY_VERSION = (7, 1, 0, "alpha0", 0) +PYPY_VERSION = (7, 1, 0, "alpha", 0) import pypy diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py --- a/pypy/objspace/std/dictmultiobject.py +++ b/pypy/objspace/std/dictmultiobject.py @@ -11,7 +11,7 @@ WrappedDefault, applevel, interp2app, unwrap_spec) from pypy.interpreter.mixedmodule import MixedModule from pypy.interpreter.signature import Signature -from pypy.interpreter.typedef import TypeDef +from pypy.interpreter.typedef import TypeDef, interp_attrproperty_w from pypy.interpreter.unicodehelper import decode_utf8sp from pypy.objspace.std.util import negate @@ -1519,6 +1519,12 @@ descr_or, descr_ror = _as_set_op('or', 'update') descr_xor, descr_rxor = _as_set_op('xor', 'symmetric_difference_update') +def new_dict_items(space, w_type, w_dict): + w_dict = space.interp_w(W_DictMultiObject, w_dict) + w_obj = space.allocate_instance(W_DictViewItemsObject, w_type) + W_DictViewObject.__init__(w_obj, space, w_dict) + return w_obj + class W_DictViewItemsObject(W_DictViewObject, SetLikeDictView): def descr_iter(self, space): return W_DictMultiIterItemsObject(space, self.w_dict.iteritems()) @@ -1538,18 +1544,32 @@ return space.w_False return space.newbool(space.eq_w(w_value, w_found)) +def new_dict_keys(space, w_type, w_dict): + w_dict = space.interp_w(W_DictMultiObject, w_dict) + w_obj = space.allocate_instance(W_DictViewKeysObject, w_type) + W_DictViewObject.__init__(w_obj, space, w_dict) + return w_obj + class W_DictViewKeysObject(W_DictViewObject, SetLikeDictView): def descr_iter(self, space): return W_DictMultiIterKeysObject(space, self.w_dict.iterkeys()) + def descr_contains(self, space, w_key): return self.w_dict.descr_contains(space, w_key) +def new_dict_values(space, w_type, w_dict): + w_dict = space.interp_w(W_DictMultiObject, w_dict) + w_obj = space.allocate_instance(W_DictViewValuesObject, w_type) + W_DictViewObject.__init__(w_obj, space, w_dict) + return w_obj + class W_DictViewValuesObject(W_DictViewObject): def descr_iter(self, space): return W_DictMultiIterValuesObject(space, self.w_dict.itervalues()) W_DictViewItemsObject.typedef = TypeDef( "dict_items", + __new__ = interp2app(new_dict_items), __repr__ = interp2app(W_DictViewItemsObject.descr_repr), __len__ = interp2app(W_DictViewItemsObject.descr_len), __iter__ = interp2app(W_DictViewItemsObject.descr_iter), @@ -1571,10 +1591,12 @@ __xor__ = interp2app(W_DictViewItemsObject.descr_xor), __rxor__ = interp2app(W_DictViewItemsObject.descr_rxor), isdisjoint = interp2app(W_DictViewItemsObject.descr_isdisjoint), + _dict = interp_attrproperty_w('w_dict', cls=W_DictViewItemsObject), ) W_DictViewKeysObject.typedef = TypeDef( "dict_keys", + __new__ = interp2app(new_dict_keys), __repr__ = interp2app(W_DictViewKeysObject.descr_repr), __len__ = interp2app(W_DictViewKeysObject.descr_len), __iter__ = interp2app(W_DictViewKeysObject.descr_iter), @@ -1596,11 +1618,14 @@ __xor__ = interp2app(W_DictViewKeysObject.descr_xor), __rxor__ = interp2app(W_DictViewKeysObject.descr_rxor), isdisjoint = interp2app(W_DictViewKeysObject.descr_isdisjoint), + _dict = interp_attrproperty_w('w_dict', cls=W_DictViewKeysObject), ) W_DictViewValuesObject.typedef = TypeDef( "dict_values", + __new__ = interp2app(new_dict_values), __repr__ = interp2app(W_DictViewValuesObject.descr_repr), __len__ = interp2app(W_DictViewValuesObject.descr_len), __iter__ = interp2app(W_DictViewValuesObject.descr_iter), + _dict = interp_attrproperty_w('w_dict', cls=W_DictViewValuesObject), ) diff --git a/pypy/objspace/std/test/test_dictmultiobject.py b/pypy/objspace/std/test/test_dictmultiobject.py --- a/pypy/objspace/std/test/test_dictmultiobject.py +++ b/pypy/objspace/std/test/test_dictmultiobject.py @@ -790,17 +790,6 @@ assert len(d.items()) == 2 assert len(d.values()) == 2 - def test_constructors_not_callable(self): - kt = type({}.keys()) - raises(TypeError, kt, {}) - raises(TypeError, kt) - it = type({}.items()) - raises(TypeError, it, {}) - raises(TypeError, it) - vt = type({}.values()) - raises(TypeError, vt, {}) - raises(TypeError, vt) - def test_dict_keys(self): d = {1: 10, "a": "ABC"} keys = d.keys() diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py --- a/rpython/rlib/rgc.py +++ b/rpython/rlib/rgc.py @@ -67,7 +67,7 @@ """Returns True if we have a "split GC address space", i.e. if we are translating with an option that doesn't support taking raw addresses inside GC objects and "hacking" at them. This is - notably the case with --reversedb.""" + notably the case with --revdb.""" return False # for test purposes we allow objects to be pinned and use diff --git a/rpython/rlib/src/boehm-rawrefcount.c b/rpython/rlib/src/boehm-rawrefcount.c --- a/rpython/rlib/src/boehm-rawrefcount.c +++ b/rpython/rlib/src/boehm-rawrefcount.c @@ -191,6 +191,7 @@ #endif assert(result->ob_refcnt == REFCNT_FROM_PYPY); result->ob_refcnt = 1; + result->ob_pypy_link = 0; p->pyobj = NULL; *pp = p->next_in_bucket; p->next_in_bucket = hash_free_list; diff --git a/rpython/tool/setuptools_msvc.py b/rpython/tool/setuptools_msvc.py --- a/rpython/tool/setuptools_msvc.py +++ b/rpython/tool/setuptools_msvc.py @@ -27,7 +27,6 @@ import platform import itertools import distutils.errors -from pkg_resources.extern.packaging.version import LegacyVersion from setuptools.extern.six.moves import filterfalse @@ -201,6 +200,7 @@ """ if "numpy.distutils" in sys.modules: import numpy as np + from pkg_resources.extern.packaging.version import LegacyVersion if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs) diff --git a/rpython/translator/platform/windows.py b/rpython/translator/platform/windows.py --- a/rpython/translator/platform/windows.py +++ b/rpython/translator/platform/windows.py @@ -56,7 +56,12 @@ # use setuptools from python3 to find tools try: vcdict = _find_vcvarsall(vsver, x64flag) + except ImportError as e: + if 'setuptools' in str(e): + log.error('is setuptools installed (perhaps try %s -mensurepip)?' % sys.executable) + log.error('looking for compiler %s raised exception "%s' % (vsver, str(e))) except Exception as e: + log.error('looking for compiler %s raised exception "%s' % (vsver, str(e))) return None else: if x64flag: diff --git a/rpython/translator/revdb/gencsupp.py b/rpython/translator/revdb/gencsupp.py --- a/rpython/translator/revdb/gencsupp.py +++ b/rpython/translator/revdb/gencsupp.py @@ -51,6 +51,10 @@ ## return False def prepare_function(funcgen): + if getattr(getattr(funcgen.graph, 'func', None), '_revdb_c_only_', False): + extra_enter_text = 'RPY_REVDB_C_ONLY_ENTER' + extra_return_text = 'RPY_REVDB_C_ONLY_LEAVE' + return extra_enter_text, extra_return_text stack_bottom = False for block in funcgen.graph.iterblocks(): for op in block.operations: diff --git a/rpython/translator/revdb/src-revdb/revdb.c b/rpython/translator/revdb/src-revdb/revdb.c --- a/rpython/translator/revdb/src-revdb/revdb.c +++ b/rpython/translator/revdb/src-revdb/revdb.c @@ -253,7 +253,10 @@ "(use REVDB=logfile)\n", (int)getpid()); } - rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); + if (rpy_rev_fileno >= 0) + rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); + else + rpy_revdb.buf_p = NULL; rpy_revdb.buf_limit = rpy_rev_buffer + sizeof(rpy_rev_buffer) - 32; rpy_revdb.unique_id_seen = 1; @@ -269,17 +272,23 @@ ssize_t full_size; assert(rpy_revdb.lock); + if (rpy_revdb.buf_p == NULL) + return; + assert(rpy_rev_fileno >= 0); + /* write the current buffer content to the OS */ full_size = rpy_revdb.buf_p - rpy_rev_buffer; rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); - if (rpy_rev_fileno >= 0) - write_all(rpy_rev_buffer, full_size); + write_all(rpy_rev_buffer, full_size); } static ssize_t current_packet_size(void) { /* must be called with the lock held */ - return rpy_revdb.buf_p - (rpy_rev_buffer + sizeof(int16_t)); + if (rpy_revdb.buf_p != NULL) + return rpy_revdb.buf_p - (rpy_rev_buffer + sizeof(int16_t)); + else + return 0; } RPY_EXTERN @@ -327,6 +336,11 @@ rpy_reverse_db_flush(); assert(current_packet_size() == 0); + if (rpy_rev_fileno < 0) + return; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + *(int16_t *)p = async_code; memcpy(rpy_revdb.buf_p, &content, sizeof(uint64_t)); rpy_revdb.buf_p += sizeof(uint64_t); @@ -472,6 +486,9 @@ if (rpy_rev_fileno < 0) return 1; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + base_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR); if (base_offset < 0) { perror("lseek"); @@ -488,6 +505,9 @@ if (rpy_rev_fileno < 0) return; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + base_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR); if (base_offset < 0) { perror("lseek"); @@ -1033,9 +1053,9 @@ " echo 0 | sudo tee /proc/sys/kernel/randomize_va_space\n" "\n" "It has been reported that on Linux kernel 4.12.4-1-ARCH,\n" - "ASLR cannot be disabled at all for libpypy-c.so. For now\n" - "there is no good solution. Either you downgrade the\n" - "kernel, or you translate with --no-shared (and you loose\n" + "ASLR cannot be disabled at all for libpypy-c.so. It works\n" + "again in kernel 4.19 (and maybe sooner). Either change\n" + "kernels, or translate with --no-shared (but then you loose\n" "PyPy's cpyext ability).\n" "\n", argv[0]); exit(1); diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h b/rpython/translator/revdb/src-revdb/revdb_include.h --- a/rpython/translator/revdb/src-revdb/revdb_include.h +++ b/rpython/translator/revdb/src-revdb/revdb_include.h @@ -16,7 +16,8 @@ #endif bool_t watch_enabled; int lock; - char *buf_p, *buf_limit, *buf_readend; + char *buf_p; /* NULL during recording if recording is actually disabled */ + char *buf_limit, *buf_readend; uint64_t stop_point_seen, stop_point_break; uint64_t unique_id_seen, unique_id_break; } rpy_revdb_t; @@ -85,9 +86,13 @@ { \ decl_e = variable; \ _RPY_REVDB_PRINT("[ wr ]", _e); \ - memcpy(rpy_revdb.buf_p, &_e, sizeof(_e)); \ - if ((rpy_revdb.buf_p += sizeof(_e)) > rpy_revdb.buf_limit) \ - rpy_reverse_db_flush(); \ + char *_dst = rpy_revdb.buf_p; \ + if (_dst) { \ + memcpy(_dst, &_e, sizeof(_e)); \ + if ((rpy_revdb.buf_p = _dst + sizeof(_e)) \ + > rpy_revdb.buf_limit) \ + rpy_reverse_db_flush(); \ + } \ } #define _RPY_REVDB_EMIT_REPLAY(decl_e, variable) \ @@ -179,6 +184,13 @@ rpy_reverse_db_bad_acquire_gil("release"); \ } +#define RPY_REVDB_C_ONLY_ENTER \ + char *saved_bufp = rpy_revdb.buf_p; \ + rpy_revdb.buf_p = NULL; + +#define RPY_REVDB_C_ONLY_LEAVE \ + rpy_revdb.buf_p = saved_bufp; + #define RPY_REVDB_CALLBACKLOC(locnum) \ rpy_reverse_db_callback_loc(locnum) From pypy.commits at gmail.com Fri Feb 1 12:27:16 2019 From: pypy.commits at gmail.com (antocuni) Date: Fri, 01 Feb 2019 09:27:16 -0800 (PST) Subject: [pypy-commit] pypy release-pypy2.7-7.x: hg merge default Message-ID: <5c548174.1c69fb81.9913.6cab@mx.google.com> Author: Antonio Cuni Branch: release-pypy2.7-7.x Changeset: r95771:6017dbfef6af Date: 2019-02-01 18:25 +0100 http://bitbucket.org/pypy/pypy/changeset/6017dbfef6af/ Log: hg merge default diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -40,16 +40,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -59,8 +59,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -70,10 +70,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -114,12 +114,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -134,8 +134,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -144,10 +145,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -163,6 +164,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -176,6 +178,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -187,7 +190,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -198,7 +200,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -210,6 +211,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -217,12 +219,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -242,7 +246,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -270,12 +273,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -290,10 +296,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -301,28 +309,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -332,6 +338,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -340,6 +347,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -349,8 +357,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -364,7 +373,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -384,12 +392,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz diff --git a/extra_tests/cffi_tests/cffi0/test_function.py b/extra_tests/cffi_tests/cffi0/test_function.py --- a/extra_tests/cffi_tests/cffi0/test_function.py +++ b/extra_tests/cffi_tests/cffi0/test_function.py @@ -46,14 +46,15 @@ assert x != math.sin(1.23) # rounding effects assert abs(x - math.sin(1.23)) < 1E-6 - def test_lround_no_return_value(self): + def test_getenv_no_return_value(self): # check that 'void'-returning functions work too ffi = FFI(backend=self.Backend()) ffi.cdef(""" - void lround(double x); + void getenv(char *); """) - m = ffi.dlopen(lib_m) - x = m.lround(1.23) + needs_dlopen_none() + m = ffi.dlopen(None) + x = m.getenv(b"FOO") assert x is None def test_dlopen_filename(self): diff --git a/extra_tests/cffi_tests/cffi1/test_pkgconfig.py b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py new file mode 100644 --- /dev/null +++ b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py @@ -0,0 +1,95 @@ +# Generated by pypy/tool/import_cffi.py +import sys +import subprocess +import py +import cffi.pkgconfig as pkgconfig +from cffi import PkgConfigError + + +def mock_call(libname, flag): + assert libname=="foobarbaz" + flags = { + "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", + "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", + } + return flags[flag] + + +def test_merge_flags(): + d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} + d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} + + pkgconfig.merge_flags(d1, d2) + assert d1 == { + "ham": [1, 2, 3], + "spam" : ["a", "b", "c", "spam", "spam", "spam"], + "bar" : ["b", "a", "z"], + "foo" : []} + + +def test_pkgconfig(): + assert pkgconfig.flags_from_pkgconfig([]) == {} + + saved = pkgconfig.call + try: + pkgconfig.call = mock_call + flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) + finally: + pkgconfig.call = saved + assert flags == { + 'include_dirs': ['/usr/include/python3.6m'], + 'library_dirs': ['/usr/lib64'], + 'libraries': ['python3.6'], + 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], + 'extra_compile_args': ['-O42'], + 'extra_link_args': ['-shared'] + } + +class mock_subprocess: + PIPE = Ellipsis + class Popen: + def __init__(self, cmd, stdout, stderr): + if mock_subprocess.RESULT is None: + raise OSError("oops can't run") + assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] + def communicate(self): + bout, berr, rc = mock_subprocess.RESULT + self.returncode = rc + return bout, berr + +def test_call(): + saved = pkgconfig.subprocess + try: + pkgconfig.subprocess = mock_subprocess + + mock_subprocess.RESULT = None + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "cannot run pkg-config: oops can't run" + + mock_subprocess.RESULT = b"", "Foo error!\n", 1 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "Foo error!" + + mock_subprocess.RESULT = b"abc\\def\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value).startswith("pkg-config --cflags libfoo returned an " + "unsupported backslash-escaped output:") + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + if sys.version_info >= (3,): + mock_subprocess.RESULT = b"\xff\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, + "libfoo", "--cflags", encoding="utf-8") + assert str(e.value) == ( + "pkg-config --cflags libfoo returned bytes that cannot be " + "decoded with encoding 'utf-8':\nb'\\xff\\n'") + + finally: + pkgconfig.subprocess = saved 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 @@ -3,6 +3,7 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing +from .error import PkgConfigError __version__ = "1.12.0" __version_info__ = (1, 12, 0) 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 @@ -592,7 +592,7 @@ if sys.platform == "win32": # we need 'libpypy-c.lib'. Current distributions of # pypy (>= 4.1) contain it as 'libs/python27.lib'. - pythonlib = "python27" + pythonlib = "python{0[0]}{0[1]}".format(sys.version_info) if hasattr(sys, 'prefix'): ensure('library_dirs', os.path.join(sys.prefix, 'libs')) else: @@ -643,6 +643,16 @@ self._assigned_source = (str(module_name), source, source_extension, kwds) + def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, + source_extension='.c', **kwds): + from . import pkgconfig + if not isinstance(pkgconfig_libs, list): + raise TypeError("the pkgconfig_libs argument must be a list " + "of package names") + kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) + pkgconfig.merge_flags(kwds, kwds2) + self.set_source(module_name, source, source_extension, **kwds) + def distutils_extension(self, tmpdir='build', verbose=True): from distutils.dir_util import mkpath from .recompiler import recompile diff --git a/lib_pypy/cffi/error.py b/lib_pypy/cffi/error.py --- a/lib_pypy/cffi/error.py +++ b/lib_pypy/cffi/error.py @@ -1,8 +1,9 @@ class FFIError(Exception): - pass + __module__ = 'cffi' class CDefError(Exception): + __module__ = 'cffi' def __str__(self): try: current_decl = self.args[1] @@ -16,8 +17,15 @@ class VerificationError(Exception): """ An error raised when verification fails """ + __module__ = 'cffi' class VerificationMissing(Exception): """ An error raised when incomplete structures are passed into cdef, but no verification has been done """ + __module__ = 'cffi' + +class PkgConfigError(Exception): + """ An error raised for missing modules in pkg-config + """ + __module__ = 'cffi' diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst --- a/pypy/doc/build.rst +++ b/pypy/doc/build.rst @@ -220,11 +220,12 @@ Making a debug build of PyPy ---------------------------- -If the Makefile is rerun with the lldebug or lldebug0 target, appropriate -compilation flags are added to add debug info and reduce compiler optimizations -to ``-O0`` respectively. If you stop in a debugger, you will see the -very wordy machine-generated C code from the rpython translation step, which -takes a little bit of reading to relate back to the rpython code. +Rerun the ``Makefile`` with the ``make lldebug`` or ``make lldebug0`` target, +which will build in a way that running under a debugger makes sense. +Appropriate compilation flags are added to add debug info, and for ``lldebug0`` +compiler optimizations are fully disabled. If you stop in a debugger, you will +see the very wordy machine-generated C code from the rpython translation step, +which takes a little bit of reading to relate back to the rpython code. Build cffi import libraries for the stdlib ------------------------------------------ diff --git a/pypy/doc/contributor.rst b/pypy/doc/contributor.rst --- a/pypy/doc/contributor.rst +++ b/pypy/doc/contributor.rst @@ -7,16 +7,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -26,8 +26,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -37,10 +37,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -81,12 +81,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -101,8 +101,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -111,10 +112,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -130,6 +131,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -143,6 +145,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -154,7 +157,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -165,7 +167,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -177,6 +178,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -184,12 +186,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -209,7 +213,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -237,12 +240,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -257,10 +263,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -268,28 +276,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -299,6 +305,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -307,6 +314,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -316,8 +324,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -331,7 +340,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -351,12 +359,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz diff --git a/pypy/doc/how-to-release.rst b/pypy/doc/how-to-release.rst --- a/pypy/doc/how-to-release.rst +++ b/pypy/doc/how-to-release.rst @@ -16,9 +16,6 @@ How to Create a PyPy Release ++++++++++++++++++++++++++++ -Overview --------- - As a meta rule setting up issues in the tracker for items here may help not forgetting things. A set of todo files may also work. @@ -28,17 +25,54 @@ Release Steps -------------- +++++++++++++++ -* If needed, make a release branch -* Bump the - pypy version number in module/sys/version.py and in - module/cpyext/include/patchlevel.h and in doc/conf.py. The branch - will capture the revision number of this change for the release. +Make the release branch +------------------------ - Some of the next updates may be done before or after branching; make - sure things are ported back to the trunk and to the branch as - necessary. +This is needed only in case you are doing a new major version; if not, you can +probably reuse the existing release branch. + +We want to be able to freely merge default into the branch and vice-versa; +thus we need to do a complicate dance to avoid to patch the version number +when we do a merge:: + + $ hg up -r default + $ # edit the version to e.g. 7.0.0-final + $ hg ci + $ hg branch release-pypy2.7-7.x && hg ci + $ hg up -r default + $ # edit the version to 7.1.0-alpha0 + $ hg ci + $ hg up -r release-pypy2.7-7.x + $ hg merge default + $ # edit the version to AGAIN 7.0.0-final + $ hg ci + +Then, we need to do the same for the 3.x branch:: + + $ hg up -r py3.5 + $ hg merge default # this brings the version fo 7.1.0-alpha0 + $ hg branch release-pypy3.5-7.x + $ # edit the version to 7.0.0-final + $ hg ci + $ hg up -r py3.5 + $ hg merge release-pypy3.5-7.x + $ # edit the version to 7.1.0-alpha0 + $ hg ci + +To change the version, you need to edit three files: + + - ``module/sys/version.py`` + + - ``module/cpyext/include/patchlevel.h`` + + - ``doc/conf.py`` + + +Other steps +----------- + * Make sure the RPython builds on the buildbot pass with no failures 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-v7.0.0.rst release-v6.0.0.rst release-v5.10.1.rst release-v5.10.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-7.0.0.rst whatsnew-pypy2-6.0.0.rst whatsnew-pypy2-5.10.0.rst whatsnew-pypy2-5.10.0.rst @@ -41,6 +42,7 @@ .. toctree:: whatsnew-pypy3-head.rst + whatsnew-pypy3-7.0.0.rst whatsnew-pypy3-5.9.0.rst whatsnew-pypy3-5.8.0.rst whatsnew-pypy3-5.7.0.rst diff --git a/pypy/doc/interpreter.rst b/pypy/doc/interpreter.rst --- a/pypy/doc/interpreter.rst +++ b/pypy/doc/interpreter.rst @@ -156,7 +156,7 @@ environment found in `Frames`. Frames and Functions have references to a code object. Here is a list of Code attributes: -* ``co_flags`` flags if this code object has nested scopes/generators +* ``co_flags`` flags if this code object has nested scopes/generators/etc. * ``co_stacksize`` the maximum depth the stack can reach while executing the code * ``co_code`` the actual bytecode string diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-v7.0.0.rst @@ -0,0 +1,151 @@ +====================================================== +PyPy v7.0.0: triple release of 2.7, 3.5 and 3.6-alpha +====================================================== + +The PyPy team is proud to release the version 7.0.0 of PyPy, which includes +three different interpreters: + + - PyPy2.7, which is an interpreter supporting the syntax and the features of + Python 2.7 + + - PyPy3.5, which supports Python 3.5 + + - PyPy3.6-alpha: this is the first official release of PyPy to support 3.6 + features, although it is still considered alpha quality. + +All the interpreters are based on much the same codebase, thus the triple +release. + +Until we can work with downstream providers to distribute builds with PyPy, we +have made packages for some common packages `available as wheels`_. + +The GC `hooks`_ , which can be used to gain more insights into its +performance, has been improved and it is now possible to manually manage the +GC by using a combination of ``gc.disable`` and ``gc.collect_step``. See the +`GC blog post`_. + + +We updated the `cffi`_ module included in PyPy to version 1.12, and the +`cppyy`_ backend to 1.4. Please use these to wrap your C and C++ code, +respectively, for a JIT friendly experience. + +As always, this release is 100% compatible with the previous one and fixed +several issues and bugs raised by the growing community of PyPy users. +We strongly recommend updating. + +The PyPy3.6 release and the Windows PyPy3.5 release are still not production +quality so your mileage may vary. There are open issues with incomplete +compatibility and c-extension support. + +The utf8 branch that changes internal representation of unicode to utf8 did not +make it into the release, so there is still more goodness coming. +You can download the v6.0 releases here: + + http://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project. If PyPy is not quite good enough for your needs, we are available for +direct consulting work. + +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. + +.. _`PyPy`: index.html +.. _`RPython`: https://rpython.readthedocs.org +.. _`help`: project-ideas.html +.. _`cffi`: http://cffi.readthedocs.io +.. _`cppyy`: https://cppyy.readthedocs.io +.. _`available as wheels`: https://github.com/antocuni/pypy-wheels +.. _`GC blog post`: https://morepypy.blogspot.com/2019/01/pypy-for-low-latency-systems.html + + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7, 3.5 and 3.6. 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 release supports: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS X 64 bits, Windows 32 bits, OpenBSD, FreeBSD) + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +Unfortunately at the moment of writing our ARM buildbots are out of service, +so for now we are **not** releasing any binary for the ARM architecture. + +.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://rpython.readthedocs.io/en/latest/examples.html + + +Changelog +========= + +If not specified, the changes are shared across versions + +* Support ``__set_name__``, ``__init_subclass__`` (Py3.6) +* Support ``cppyy`` in Py3.5 and Py3.6 +* Use implementation-specific site directories in ``sysconfig`` (Py3.5, Py3.6) +* Adding detection of gcc to ``sysconfig`` (Py3.5, Py3.6) +* Fix multiprocessing regression on newer glibcs +* Make sure 'blocking-ness' of socket is set along with default timeout +* Include ``crypt.h`` for ``crypt()`` on Linux +* Improve and re-organize the contributing_ documentation +* Make the ``__module__`` attribute writable, fixing an incompatibility with + NumPy 1.16 +* Implement ``Py_ReprEnter``, ``Py_ReprLeave(), ``PyMarshal_ReadObjectFromString``, + ``PyMarshal_WriteObjectToString``, ``PyObject_DelItemString``, + ``PyMapping_DelItem``, ``PyMapping_DelItemString``, ``PyEval_GetFrame``, + ``PyOS_InputHook``, ``PyErr_FormatFromCause`` (Py3.6), +* Implement new wordcode instruction encoding (Py3.6) +* Log additional gc-minor and gc-collect-step info in the PYPYLOG +* The ``reverse-debugger`` (revdb) branch has been merged to the default + branch, so it should always be up-to-date. You still need a special pypy + build, but you can compile it from the same source as the one we distribute + for the v7.0.0 release. For more information, see + https://bitbucket.org/pypy/revdb +* Support underscores in numerical literals like ``'4_2'`` (Py3.6) +* Pre-emptively raise MemoryError if the size of dequeue in ``_collections.deque`` + is too large (Py3.5) +* Fix multithreading issues in calls to ``os.setenv`` +* Add missing defines and typedefs for numpy and pandas on MSVC +* Add CPython macros like ``Py_NAN`` to header files +* Rename the ``MethodType`` to ``instancemethod``, like CPython +* Better support for `async with` in generators (Py3.5, Py3.6) +* Improve the performance of ``pow(a, b, c)`` if ``c`` is a large integer +* Now ``vmprof`` works on FreeBSD +* Support GNU Hurd, fixes for FreeBSD +* Add deprecation warning if type of result of ``__float__`` is float inherited + class (Py3.6) +* Fix async generator bug when yielding a ``StopIteration`` (Py3.6) +* Speed up ``max(list-of-int)`` from non-jitted code +* Fix Windows ``os.listdir()`` for some cases (see CPython #32539) +* Add ``select.PIPE_BUF`` +* Use ``subprocess`` to avoid shell injection in ``shutil`` module - backport + of https://bugs.python.org/issue34540 +* Rename ``_Py_ZeroStruct`` to ``_Py_FalseStruct`` (Py3.5, Py3.6) +* Remove some cpyext names for Py3.5, Py3.6 +* Enable use of unicode file names in ``dlopen`` +* Backport CPython fix for ``thread.RLock`` +* Make GC hooks measure time in seconds (as opposed to an opaque unit) +* Refactor and reorganize tests in ``test_lib_pypy`` +* Check error values in ``socket.setblocking`` (Py3.6) +* Add support for FsPath to os.unlink() (Py3.6) +* Fix freezing builtin modules at translation +* Tweak ``W_UnicodeDictionaryStrategy`` which speeds up dictionaries with only + unicode keys + +We also refactored many parts of the JIT bridge optimizations, as well as cpyext +internals, and together with new contributors fixed issues, added new +documentation, and cleaned up the codebase. + +.. _contributing: http://doc.pypy.org/en/latest/contributing.html 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,69 +1,7 @@ ========================== -What's new in PyPy2.7 6.0+ +What's new in PyPy2.7 7.0+ ========================== -.. this is a revision shortly after release-pypy-6.0.0 -.. startrev: e50e11af23f1 +.. this is a revision shortly after release-pypy-7.0.0 +.. startrev: 481c69f7d81f -.. branch: cppyy-packaging - -Main items: vastly better template resolution and improved performance. In -detail: upgrade to backend 1.4, improved handling of templated methods and -functions (in particular automatic deduction of types), improved pythonization -interface, range of compatibility fixes for Python3, free functions now take -fast libffi path when possible, moves for strings (incl. from Python str), -easier/faster handling of std::vector by numpy, improved and faster object -identity preservation - -.. branch: socket_default_timeout_blockingness - -Make sure 'blocking-ness' of socket is set along with default timeout - -.. branch: crypt_h - -Include crypt.h for crypt() on Linux - -.. branch: gc-more-logging - -Log additional gc-minor and gc-collect-step info in the PYPYLOG - -.. branch: reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb - - -.. branch: pyparser-improvements-3 - -Small refactorings in the Python parser. - -.. branch: fix-readme-typo - -.. branch: avoid_shell_injection_in_shutil - -Backport CPython fix for possible shell injection issue in `distutils.spawn`, -https://bugs.python.org/issue34540 - -.. branch: cffi_dlopen_unicode - -Enable use of unicode file names in `dlopen` - -.. branch: rlock-in-rpython - -Backport CPython fix for `thread.RLock` - - -.. branch: expose-gc-time - -Make GC hooks measure time in seconds (as opposed to an opaque unit). - -.. branch: cleanup-test_lib_pypy - -Update most test_lib_pypy/ tests and move them to extra_tests/. - -.. branch: gc-disable - -Make it possible to manually manage the GC by using a combination of -gc.disable() and gc.collect_step(). Make sure to write a proper release -announcement in which we explain that existing programs could leak memory if -they run for too much time between a gc.disable()/gc.enable() diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-pypy2-7.0.0.rst copy from pypy/doc/whatsnew-head.rst copy to pypy/doc/whatsnew-pypy2-7.0.0.rst diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-7.0.0.rst copy from pypy/doc/whatsnew-pypy3-head.rst copy to pypy/doc/whatsnew-pypy3-7.0.0.rst --- a/pypy/doc/whatsnew-pypy3-head.rst +++ b/pypy/doc/whatsnew-pypy3-7.0.0.rst @@ -1,7 +1,19 @@ -========================= -What's new in PyPy3 5.9+ -========================= - -.. this is the revision after release-pypy3.5-5.9 -.. startrev: be41e3ac0a29 - +======================== +What's new in PyPy3 6.0+ +======================== + +.. this is the revision after release-pypy3.5-v6.0 +.. startrev: 580e3e26cd32 + +.. branch: hroncok/fix-multiprocessing-regression-on-newer--1524656522151 + +Fix multiprocessing regression on newer glibcs + +.. branch: py3.5-user-site-impl + +Use implementation-specific site directories in sysconfig like in Python2 + +.. branch: py3.5-reverse-debugger + +The reverse-debugger branch has been merged. For more information, see +https://bitbucket.org/pypy/revdb 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,7 +1,7 @@ -========================= -What's new in PyPy3 5.9+ -========================= - -.. this is the revision after release-pypy3.5-5.9 -.. startrev: be41e3ac0a29 - +======================== +What's new in PyPy3 7.0+ +======================== + +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c + 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 @@ -507,6 +507,7 @@ header = DEFAULT_HEADER if func.__name__ in FUNCTIONS_BY_HEADER[header]: raise ValueError("%s already registered" % func.__name__) + func._revdb_c_only_ = True # hack for revdb api_function = COnlyApiFunction(argtypes, restype, func) FUNCTIONS_BY_HEADER[header][func.__name__] = api_function return api_function 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 @@ -32,9 +32,14 @@ * module/sys/version.py * doc/conf.py */ +<<<<<<< working copy #define PYPY_VERSION "7.0.0" #define PYPY_VERSION_NUM 0x07000000 +======= +#define PYPY_VERSION "7.1.0-alpha0" +#define PYPY_VERSION_NUM 0x07010000 +>>>>>>> merge rev /* Defined to mean a PyPy where cpyext holds more regular references to PyObjects, e.g. staying alive as long as the internal PyPy object stays alive. */ diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py --- a/rpython/rlib/objectmodel.py +++ b/rpython/rlib/objectmodel.py @@ -991,7 +991,9 @@ items = d.items() d.clear() d[key] = value - d.update(items) + # r_dict.update does not support list of tuples, do it manually + for key, value in items: + d[key] = value @specialize.call_location() def move_to_end(d, key, last=True): diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py --- a/rpython/rlib/rgc.py +++ b/rpython/rlib/rgc.py @@ -67,7 +67,7 @@ """Returns True if we have a "split GC address space", i.e. if we are translating with an option that doesn't support taking raw addresses inside GC objects and "hacking" at them. This is - notably the case with --reversedb.""" + notably the case with --revdb.""" return False # for test purposes we allow objects to be pinned and use diff --git a/rpython/rlib/src/boehm-rawrefcount.c b/rpython/rlib/src/boehm-rawrefcount.c --- a/rpython/rlib/src/boehm-rawrefcount.c +++ b/rpython/rlib/src/boehm-rawrefcount.c @@ -191,6 +191,7 @@ #endif assert(result->ob_refcnt == REFCNT_FROM_PYPY); result->ob_refcnt = 1; + result->ob_pypy_link = 0; p->pyobj = NULL; *pp = p->next_in_bucket; p->next_in_bucket = hash_free_list; diff --git a/rpython/rlib/test/test_objectmodel.py b/rpython/rlib/test/test_objectmodel.py --- a/rpython/rlib/test/test_objectmodel.py +++ b/rpython/rlib/test/test_objectmodel.py @@ -708,6 +708,15 @@ move_to_end(d, 'key1', last=False) assert d.items() == [('key1', 'val1'), ('key2', 'val2'), ('key3', 'val3')] +def test_r_dict_move_to_end(): + d = r_dict(strange_key_eq, strange_key_hash) + d['1key'] = 'val1' + d['2key'] = 'val2' + d['3key'] = 'val3' + # does not crash, we can't check that it actually moves to end on CPython + move_to_end(d, '1key') + move_to_end(d, '1key', last=False) + def test_import_from_mixin(): class M: # old-style def f(self): diff --git a/rpython/tool/setuptools_msvc.py b/rpython/tool/setuptools_msvc.py --- a/rpython/tool/setuptools_msvc.py +++ b/rpython/tool/setuptools_msvc.py @@ -27,7 +27,6 @@ import platform import itertools import distutils.errors -from pkg_resources.extern.packaging.version import LegacyVersion from setuptools.extern.six.moves import filterfalse @@ -201,6 +200,7 @@ """ if "numpy.distutils" in sys.modules: import numpy as np + from pkg_resources.extern.packaging.version import LegacyVersion if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs) diff --git a/rpython/translator/platform/windows.py b/rpython/translator/platform/windows.py --- a/rpython/translator/platform/windows.py +++ b/rpython/translator/platform/windows.py @@ -56,7 +56,12 @@ # use setuptools from python3 to find tools try: vcdict = _find_vcvarsall(vsver, x64flag) + except ImportError as e: + if 'setuptools' in str(e): + log.error('is setuptools installed (perhaps try %s -mensurepip)?' % sys.executable) + log.error('looking for compiler %s raised exception "%s' % (vsver, str(e))) except Exception as e: + log.error('looking for compiler %s raised exception "%s' % (vsver, str(e))) return None else: if x64flag: diff --git a/rpython/translator/revdb/gencsupp.py b/rpython/translator/revdb/gencsupp.py --- a/rpython/translator/revdb/gencsupp.py +++ b/rpython/translator/revdb/gencsupp.py @@ -51,6 +51,10 @@ ## return False def prepare_function(funcgen): + if getattr(getattr(funcgen.graph, 'func', None), '_revdb_c_only_', False): + extra_enter_text = 'RPY_REVDB_C_ONLY_ENTER' + extra_return_text = 'RPY_REVDB_C_ONLY_LEAVE' + return extra_enter_text, extra_return_text stack_bottom = False for block in funcgen.graph.iterblocks(): for op in block.operations: diff --git a/rpython/translator/revdb/src-revdb/revdb.c b/rpython/translator/revdb/src-revdb/revdb.c --- a/rpython/translator/revdb/src-revdb/revdb.c +++ b/rpython/translator/revdb/src-revdb/revdb.c @@ -253,7 +253,10 @@ "(use REVDB=logfile)\n", (int)getpid()); } - rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); + if (rpy_rev_fileno >= 0) + rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); + else + rpy_revdb.buf_p = NULL; rpy_revdb.buf_limit = rpy_rev_buffer + sizeof(rpy_rev_buffer) - 32; rpy_revdb.unique_id_seen = 1; @@ -269,17 +272,23 @@ ssize_t full_size; assert(rpy_revdb.lock); + if (rpy_revdb.buf_p == NULL) + return; + assert(rpy_rev_fileno >= 0); + /* write the current buffer content to the OS */ full_size = rpy_revdb.buf_p - rpy_rev_buffer; rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); - if (rpy_rev_fileno >= 0) - write_all(rpy_rev_buffer, full_size); + write_all(rpy_rev_buffer, full_size); } static ssize_t current_packet_size(void) { /* must be called with the lock held */ - return rpy_revdb.buf_p - (rpy_rev_buffer + sizeof(int16_t)); + if (rpy_revdb.buf_p != NULL) + return rpy_revdb.buf_p - (rpy_rev_buffer + sizeof(int16_t)); + else + return 0; } RPY_EXTERN @@ -327,6 +336,11 @@ rpy_reverse_db_flush(); assert(current_packet_size() == 0); + if (rpy_rev_fileno < 0) + return; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + *(int16_t *)p = async_code; memcpy(rpy_revdb.buf_p, &content, sizeof(uint64_t)); rpy_revdb.buf_p += sizeof(uint64_t); @@ -472,6 +486,9 @@ if (rpy_rev_fileno < 0) return 1; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + base_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR); if (base_offset < 0) { perror("lseek"); @@ -488,6 +505,9 @@ if (rpy_rev_fileno < 0) return; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + base_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR); if (base_offset < 0) { perror("lseek"); @@ -1033,9 +1053,9 @@ " echo 0 | sudo tee /proc/sys/kernel/randomize_va_space\n" "\n" "It has been reported that on Linux kernel 4.12.4-1-ARCH,\n" - "ASLR cannot be disabled at all for libpypy-c.so. For now\n" - "there is no good solution. Either you downgrade the\n" - "kernel, or you translate with --no-shared (and you loose\n" + "ASLR cannot be disabled at all for libpypy-c.so. It works\n" + "again in kernel 4.19 (and maybe sooner). Either change\n" + "kernels, or translate with --no-shared (but then you loose\n" "PyPy's cpyext ability).\n" "\n", argv[0]); exit(1); diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h b/rpython/translator/revdb/src-revdb/revdb_include.h --- a/rpython/translator/revdb/src-revdb/revdb_include.h +++ b/rpython/translator/revdb/src-revdb/revdb_include.h @@ -16,7 +16,8 @@ #endif bool_t watch_enabled; int lock; - char *buf_p, *buf_limit, *buf_readend; + char *buf_p; /* NULL during recording if recording is actually disabled */ + char *buf_limit, *buf_readend; uint64_t stop_point_seen, stop_point_break; uint64_t unique_id_seen, unique_id_break; } rpy_revdb_t; @@ -85,9 +86,13 @@ { \ decl_e = variable; \ _RPY_REVDB_PRINT("[ wr ]", _e); \ - memcpy(rpy_revdb.buf_p, &_e, sizeof(_e)); \ - if ((rpy_revdb.buf_p += sizeof(_e)) > rpy_revdb.buf_limit) \ - rpy_reverse_db_flush(); \ + char *_dst = rpy_revdb.buf_p; \ + if (_dst) { \ + memcpy(_dst, &_e, sizeof(_e)); \ + if ((rpy_revdb.buf_p = _dst + sizeof(_e)) \ + > rpy_revdb.buf_limit) \ + rpy_reverse_db_flush(); \ + } \ } #define _RPY_REVDB_EMIT_REPLAY(decl_e, variable) \ @@ -179,6 +184,13 @@ rpy_reverse_db_bad_acquire_gil("release"); \ } +#define RPY_REVDB_C_ONLY_ENTER \ + char *saved_bufp = rpy_revdb.buf_p; \ + rpy_revdb.buf_p = NULL; + +#define RPY_REVDB_C_ONLY_LEAVE \ + rpy_revdb.buf_p = saved_bufp; + #define RPY_REVDB_CALLBACKLOC(locnum) \ rpy_reverse_db_callback_loc(locnum) From pypy.commits at gmail.com Fri Feb 1 12:27:19 2019 From: pypy.commits at gmail.com (antocuni) Date: Fri, 01 Feb 2019 09:27:19 -0800 (PST) Subject: [pypy-commit] pypy release-pypy3.5-7.x: hg merge py3.5, to include the latest cffi updates Message-ID: <5c548177.1c69fb81.a94b6.28e0@mx.google.com> Author: Antonio Cuni Branch: release-pypy3.5-7.x Changeset: r95772:0ed340cb2a4f Date: 2019-02-01 18:26 +0100 http://bitbucket.org/pypy/pypy/changeset/0ed340cb2a4f/ Log: hg merge py3.5, to include the latest cffi updates diff --git a/extra_tests/cffi_tests/cffi1/test_pkgconfig.py b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py new file mode 100644 --- /dev/null +++ b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py @@ -0,0 +1,95 @@ +# Generated by pypy/tool/import_cffi.py +import sys +import subprocess +import py +import cffi.pkgconfig as pkgconfig +from cffi import PkgConfigError + + +def mock_call(libname, flag): + assert libname=="foobarbaz" + flags = { + "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", + "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", + } + return flags[flag] + + +def test_merge_flags(): + d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} + d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} + + pkgconfig.merge_flags(d1, d2) + assert d1 == { + "ham": [1, 2, 3], + "spam" : ["a", "b", "c", "spam", "spam", "spam"], + "bar" : ["b", "a", "z"], + "foo" : []} + + +def test_pkgconfig(): + assert pkgconfig.flags_from_pkgconfig([]) == {} + + saved = pkgconfig.call + try: + pkgconfig.call = mock_call + flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) + finally: + pkgconfig.call = saved + assert flags == { + 'include_dirs': ['/usr/include/python3.6m'], + 'library_dirs': ['/usr/lib64'], + 'libraries': ['python3.6'], + 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], + 'extra_compile_args': ['-O42'], + 'extra_link_args': ['-shared'] + } + +class mock_subprocess: + PIPE = Ellipsis + class Popen: + def __init__(self, cmd, stdout, stderr): + if mock_subprocess.RESULT is None: + raise OSError("oops can't run") + assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] + def communicate(self): + bout, berr, rc = mock_subprocess.RESULT + self.returncode = rc + return bout, berr + +def test_call(): + saved = pkgconfig.subprocess + try: + pkgconfig.subprocess = mock_subprocess + + mock_subprocess.RESULT = None + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "cannot run pkg-config: oops can't run" + + mock_subprocess.RESULT = b"", "Foo error!\n", 1 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "Foo error!" + + mock_subprocess.RESULT = b"abc\\def\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value).startswith("pkg-config --cflags libfoo returned an " + "unsupported backslash-escaped output:") + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + if sys.version_info >= (3,): + mock_subprocess.RESULT = b"\xff\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, + "libfoo", "--cflags", encoding="utf-8") + assert str(e.value) == ( + "pkg-config --cflags libfoo returned bytes that cannot be " + "decoded with encoding 'utf-8':\nb'\\xff\\n'") + + finally: + pkgconfig.subprocess = saved 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 @@ -3,6 +3,7 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing +from .error import PkgConfigError __version__ = "1.12.0" __version_info__ = (1, 12, 0) 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 @@ -643,6 +643,16 @@ self._assigned_source = (str(module_name), source, source_extension, kwds) + def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, + source_extension='.c', **kwds): + from . import pkgconfig + if not isinstance(pkgconfig_libs, list): + raise TypeError("the pkgconfig_libs argument must be a list " + "of package names") + kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) + pkgconfig.merge_flags(kwds, kwds2) + self.set_source(module_name, source, source_extension, **kwds) + def distutils_extension(self, tmpdir='build', verbose=True): from distutils.dir_util import mkpath from .recompiler import recompile diff --git a/lib_pypy/cffi/error.py b/lib_pypy/cffi/error.py --- a/lib_pypy/cffi/error.py +++ b/lib_pypy/cffi/error.py @@ -1,8 +1,9 @@ class FFIError(Exception): - pass + __module__ = 'cffi' class CDefError(Exception): + __module__ = 'cffi' def __str__(self): try: current_decl = self.args[1] @@ -16,8 +17,15 @@ class VerificationError(Exception): """ An error raised when verification fails """ + __module__ = 'cffi' class VerificationMissing(Exception): """ An error raised when incomplete structures are passed into cdef, but no verification has been done """ + __module__ = 'cffi' + +class PkgConfigError(Exception): + """ An error raised for missing modules in pkg-config + """ + __module__ = 'cffi' diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst --- a/pypy/doc/build.rst +++ b/pypy/doc/build.rst @@ -220,11 +220,12 @@ Making a debug build of PyPy ---------------------------- -If the Makefile is rerun with the lldebug or lldebug0 target, appropriate -compilation flags are added to add debug info and reduce compiler optimizations -to ``-O0`` respectively. If you stop in a debugger, you will see the -very wordy machine-generated C code from the rpython translation step, which -takes a little bit of reading to relate back to the rpython code. +Rerun the ``Makefile`` with the ``make lldebug`` or ``make lldebug0`` target, +which will build in a way that running under a debugger makes sense. +Appropriate compilation flags are added to add debug info, and for ``lldebug0`` +compiler optimizations are fully disabled. If you stop in a debugger, you will +see the very wordy machine-generated C code from the rpython translation step, +which takes a little bit of reading to relate back to the rpython code. Build cffi import libraries for the stdlib ------------------------------------------ diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst --- a/pypy/doc/release-v7.0.0.rst +++ b/pypy/doc/release-v7.0.0.rst @@ -19,9 +19,10 @@ Until we can work with downstream providers to distribute builds with PyPy, we have made packages for some common packages `available as wheels`_. -The GC now has `hooks`_ to gain more insights into its performance, and it is -now possible to manually manage the GC by using a combination of -``gc.disable`` and ``gc.collect_step``. See the `GC blog post`_. +The GC `hooks`_ , which can be used to gain more insights into its +performance, has been improved and it is now possible to manually manage the +GC by using a combination of ``gc.disable`` and ``gc.collect_step``. See the +`GC blog post`_. We updated the `cffi`_ module included in PyPy to version 1.12, and the From pypy.commits at gmail.com Fri Feb 1 13:00:37 2019 From: pypy.commits at gmail.com (antocuni) Date: Fri, 01 Feb 2019 10:00:37 -0800 (PST) Subject: [pypy-commit] pypy py3.6: hg merge py3.5 Message-ID: <5c548945.1c69fb81.5b0ca.d855@mx.google.com> Author: Antonio Cuni Branch: py3.6 Changeset: r95773:b1506a30f188 Date: 2019-02-01 18:59 +0100 http://bitbucket.org/pypy/pypy/changeset/b1506a30f188/ Log: hg merge py3.5 diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -40,16 +40,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -59,8 +59,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -70,10 +70,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -114,12 +114,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -134,8 +134,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -144,10 +145,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -163,6 +164,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -176,6 +178,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -187,7 +190,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -198,7 +200,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -210,6 +211,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -217,12 +219,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -242,7 +246,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -270,12 +273,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -290,10 +296,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -301,28 +309,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -332,6 +338,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -340,6 +347,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -349,8 +357,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -364,7 +373,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -384,12 +392,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz diff --git a/extra_tests/cffi_tests/cffi0/test_function.py b/extra_tests/cffi_tests/cffi0/test_function.py --- a/extra_tests/cffi_tests/cffi0/test_function.py +++ b/extra_tests/cffi_tests/cffi0/test_function.py @@ -46,14 +46,15 @@ assert x != math.sin(1.23) # rounding effects assert abs(x - math.sin(1.23)) < 1E-6 - def test_lround_no_return_value(self): + def test_getenv_no_return_value(self): # check that 'void'-returning functions work too ffi = FFI(backend=self.Backend()) ffi.cdef(""" - void lround(double x); + void getenv(char *); """) - m = ffi.dlopen(lib_m) - x = m.lround(1.23) + needs_dlopen_none() + m = ffi.dlopen(None) + x = m.getenv(b"FOO") assert x is None def test_dlopen_filename(self): diff --git a/extra_tests/cffi_tests/cffi1/test_pkgconfig.py b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py new file mode 100644 --- /dev/null +++ b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py @@ -0,0 +1,95 @@ +# Generated by pypy/tool/import_cffi.py +import sys +import subprocess +import py +import cffi.pkgconfig as pkgconfig +from cffi import PkgConfigError + + +def mock_call(libname, flag): + assert libname=="foobarbaz" + flags = { + "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", + "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", + } + return flags[flag] + + +def test_merge_flags(): + d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} + d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} + + pkgconfig.merge_flags(d1, d2) + assert d1 == { + "ham": [1, 2, 3], + "spam" : ["a", "b", "c", "spam", "spam", "spam"], + "bar" : ["b", "a", "z"], + "foo" : []} + + +def test_pkgconfig(): + assert pkgconfig.flags_from_pkgconfig([]) == {} + + saved = pkgconfig.call + try: + pkgconfig.call = mock_call + flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) + finally: + pkgconfig.call = saved + assert flags == { + 'include_dirs': ['/usr/include/python3.6m'], + 'library_dirs': ['/usr/lib64'], + 'libraries': ['python3.6'], + 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], + 'extra_compile_args': ['-O42'], + 'extra_link_args': ['-shared'] + } + +class mock_subprocess: + PIPE = Ellipsis + class Popen: + def __init__(self, cmd, stdout, stderr): + if mock_subprocess.RESULT is None: + raise OSError("oops can't run") + assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] + def communicate(self): + bout, berr, rc = mock_subprocess.RESULT + self.returncode = rc + return bout, berr + +def test_call(): + saved = pkgconfig.subprocess + try: + pkgconfig.subprocess = mock_subprocess + + mock_subprocess.RESULT = None + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "cannot run pkg-config: oops can't run" + + mock_subprocess.RESULT = b"", "Foo error!\n", 1 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "Foo error!" + + mock_subprocess.RESULT = b"abc\\def\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value).startswith("pkg-config --cflags libfoo returned an " + "unsupported backslash-escaped output:") + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + if sys.version_info >= (3,): + mock_subprocess.RESULT = b"\xff\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, + "libfoo", "--cflags", encoding="utf-8") + assert str(e.value) == ( + "pkg-config --cflags libfoo returned bytes that cannot be " + "decoded with encoding 'utf-8':\nb'\\xff\\n'") + + finally: + pkgconfig.subprocess = saved 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 @@ -3,6 +3,7 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing +from .error import PkgConfigError __version__ = "1.12.0" __version_info__ = (1, 12, 0) 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 @@ -643,6 +643,16 @@ self._assigned_source = (str(module_name), source, source_extension, kwds) + def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, + source_extension='.c', **kwds): + from . import pkgconfig + if not isinstance(pkgconfig_libs, list): + raise TypeError("the pkgconfig_libs argument must be a list " + "of package names") + kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) + pkgconfig.merge_flags(kwds, kwds2) + self.set_source(module_name, source, source_extension, **kwds) + def distutils_extension(self, tmpdir='build', verbose=True): from distutils.dir_util import mkpath from .recompiler import recompile diff --git a/lib_pypy/cffi/error.py b/lib_pypy/cffi/error.py --- a/lib_pypy/cffi/error.py +++ b/lib_pypy/cffi/error.py @@ -1,8 +1,9 @@ class FFIError(Exception): - pass + __module__ = 'cffi' class CDefError(Exception): + __module__ = 'cffi' def __str__(self): try: current_decl = self.args[1] @@ -16,8 +17,15 @@ class VerificationError(Exception): """ An error raised when verification fails """ + __module__ = 'cffi' class VerificationMissing(Exception): """ An error raised when incomplete structures are passed into cdef, but no verification has been done """ + __module__ = 'cffi' + +class PkgConfigError(Exception): + """ An error raised for missing modules in pkg-config + """ + __module__ = 'cffi' diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst --- a/pypy/doc/build.rst +++ b/pypy/doc/build.rst @@ -220,11 +220,12 @@ Making a debug build of PyPy ---------------------------- -If the Makefile is rerun with the lldebug or lldebug0 target, appropriate -compilation flags are added to add debug info and reduce compiler optimizations -to ``-O0`` respectively. If you stop in a debugger, you will see the -very wordy machine-generated C code from the rpython translation step, which -takes a little bit of reading to relate back to the rpython code. +Rerun the ``Makefile`` with the ``make lldebug`` or ``make lldebug0`` target, +which will build in a way that running under a debugger makes sense. +Appropriate compilation flags are added to add debug info, and for ``lldebug0`` +compiler optimizations are fully disabled. If you stop in a debugger, you will +see the very wordy machine-generated C code from the rpython translation step, +which takes a little bit of reading to relate back to the rpython code. Build cffi import libraries for the stdlib ------------------------------------------ diff --git a/pypy/doc/contributor.rst b/pypy/doc/contributor.rst --- a/pypy/doc/contributor.rst +++ b/pypy/doc/contributor.rst @@ -7,16 +7,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -26,8 +26,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -37,10 +37,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -81,12 +81,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -101,8 +101,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -111,10 +112,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -130,6 +131,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -143,6 +145,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -154,7 +157,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -165,7 +167,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -177,6 +178,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -184,12 +186,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -209,7 +213,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -237,12 +240,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -257,10 +263,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -268,28 +276,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -299,6 +305,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -307,6 +314,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -316,8 +324,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -331,7 +340,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -351,12 +359,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz 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-v7.0.0.rst release-v6.0.0.rst release-v5.10.1.rst release-v5.10.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-7.0.0.rst whatsnew-pypy2-6.0.0.rst whatsnew-pypy2-5.10.0.rst whatsnew-pypy2-5.10.0.rst @@ -41,6 +42,7 @@ .. toctree:: whatsnew-pypy3-head.rst + whatsnew-pypy3-7.0.0.rst whatsnew-pypy3-5.9.0.rst whatsnew-pypy3-5.8.0.rst whatsnew-pypy3-5.7.0.rst diff --git a/pypy/doc/interpreter.rst b/pypy/doc/interpreter.rst --- a/pypy/doc/interpreter.rst +++ b/pypy/doc/interpreter.rst @@ -156,7 +156,7 @@ environment found in `Frames`. Frames and Functions have references to a code object. Here is a list of Code attributes: -* ``co_flags`` flags if this code object has nested scopes/generators +* ``co_flags`` flags if this code object has nested scopes/generators/etc. * ``co_stacksize`` the maximum depth the stack can reach while executing the code * ``co_code`` the actual bytecode string diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-v7.0.0.rst @@ -0,0 +1,151 @@ +====================================================== +PyPy v7.0.0: triple release of 2.7, 3.5 and 3.6-alpha +====================================================== + +The PyPy team is proud to release the version 7.0.0 of PyPy, which includes +three different interpreters: + + - PyPy2.7, which is an interpreter supporting the syntax and the features of + Python 2.7 + + - PyPy3.5, which supports Python 3.5 + + - PyPy3.6-alpha: this is the first official release of PyPy to support 3.6 + features, although it is still considered alpha quality. + +All the interpreters are based on much the same codebase, thus the triple +release. + +Until we can work with downstream providers to distribute builds with PyPy, we +have made packages for some common packages `available as wheels`_. + +The GC `hooks`_ , which can be used to gain more insights into its +performance, has been improved and it is now possible to manually manage the +GC by using a combination of ``gc.disable`` and ``gc.collect_step``. See the +`GC blog post`_. + + +We updated the `cffi`_ module included in PyPy to version 1.12, and the +`cppyy`_ backend to 1.4. Please use these to wrap your C and C++ code, +respectively, for a JIT friendly experience. + +As always, this release is 100% compatible with the previous one and fixed +several issues and bugs raised by the growing community of PyPy users. +We strongly recommend updating. + +The PyPy3.6 release and the Windows PyPy3.5 release are still not production +quality so your mileage may vary. There are open issues with incomplete +compatibility and c-extension support. + +The utf8 branch that changes internal representation of unicode to utf8 did not +make it into the release, so there is still more goodness coming. +You can download the v6.0 releases here: + + http://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project. If PyPy is not quite good enough for your needs, we are available for +direct consulting work. + +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. + +.. _`PyPy`: index.html +.. _`RPython`: https://rpython.readthedocs.org +.. _`help`: project-ideas.html +.. _`cffi`: http://cffi.readthedocs.io +.. _`cppyy`: https://cppyy.readthedocs.io +.. _`available as wheels`: https://github.com/antocuni/pypy-wheels +.. _`GC blog post`: https://morepypy.blogspot.com/2019/01/pypy-for-low-latency-systems.html + + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7, 3.5 and 3.6. 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 release supports: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS X 64 bits, Windows 32 bits, OpenBSD, FreeBSD) + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +Unfortunately at the moment of writing our ARM buildbots are out of service, +so for now we are **not** releasing any binary for the ARM architecture. + +.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://rpython.readthedocs.io/en/latest/examples.html + + +Changelog +========= + +If not specified, the changes are shared across versions + +* Support ``__set_name__``, ``__init_subclass__`` (Py3.6) +* Support ``cppyy`` in Py3.5 and Py3.6 +* Use implementation-specific site directories in ``sysconfig`` (Py3.5, Py3.6) +* Adding detection of gcc to ``sysconfig`` (Py3.5, Py3.6) +* Fix multiprocessing regression on newer glibcs +* Make sure 'blocking-ness' of socket is set along with default timeout +* Include ``crypt.h`` for ``crypt()`` on Linux +* Improve and re-organize the contributing_ documentation +* Make the ``__module__`` attribute writable, fixing an incompatibility with + NumPy 1.16 +* Implement ``Py_ReprEnter``, ``Py_ReprLeave(), ``PyMarshal_ReadObjectFromString``, + ``PyMarshal_WriteObjectToString``, ``PyObject_DelItemString``, + ``PyMapping_DelItem``, ``PyMapping_DelItemString``, ``PyEval_GetFrame``, + ``PyOS_InputHook``, ``PyErr_FormatFromCause`` (Py3.6), +* Implement new wordcode instruction encoding (Py3.6) +* Log additional gc-minor and gc-collect-step info in the PYPYLOG +* The ``reverse-debugger`` (revdb) branch has been merged to the default + branch, so it should always be up-to-date. You still need a special pypy + build, but you can compile it from the same source as the one we distribute + for the v7.0.0 release. For more information, see + https://bitbucket.org/pypy/revdb +* Support underscores in numerical literals like ``'4_2'`` (Py3.6) +* Pre-emptively raise MemoryError if the size of dequeue in ``_collections.deque`` + is too large (Py3.5) +* Fix multithreading issues in calls to ``os.setenv`` +* Add missing defines and typedefs for numpy and pandas on MSVC +* Add CPython macros like ``Py_NAN`` to header files +* Rename the ``MethodType`` to ``instancemethod``, like CPython +* Better support for `async with` in generators (Py3.5, Py3.6) +* Improve the performance of ``pow(a, b, c)`` if ``c`` is a large integer +* Now ``vmprof`` works on FreeBSD +* Support GNU Hurd, fixes for FreeBSD +* Add deprecation warning if type of result of ``__float__`` is float inherited + class (Py3.6) +* Fix async generator bug when yielding a ``StopIteration`` (Py3.6) +* Speed up ``max(list-of-int)`` from non-jitted code +* Fix Windows ``os.listdir()`` for some cases (see CPython #32539) +* Add ``select.PIPE_BUF`` +* Use ``subprocess`` to avoid shell injection in ``shutil`` module - backport + of https://bugs.python.org/issue34540 +* Rename ``_Py_ZeroStruct`` to ``_Py_FalseStruct`` (Py3.5, Py3.6) +* Remove some cpyext names for Py3.5, Py3.6 +* Enable use of unicode file names in ``dlopen`` +* Backport CPython fix for ``thread.RLock`` +* Make GC hooks measure time in seconds (as opposed to an opaque unit) +* Refactor and reorganize tests in ``test_lib_pypy`` +* Check error values in ``socket.setblocking`` (Py3.6) +* Add support for FsPath to os.unlink() (Py3.6) +* Fix freezing builtin modules at translation +* Tweak ``W_UnicodeDictionaryStrategy`` which speeds up dictionaries with only + unicode keys + +We also refactored many parts of the JIT bridge optimizations, as well as cpyext +internals, and together with new contributors fixed issues, added new +documentation, and cleaned up the codebase. + +.. _contributing: http://doc.pypy.org/en/latest/contributing.html 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,73 +1,6 @@ ========================== -What's new in PyPy2.7 6.0+ +What's new in PyPy2.7 7.0+ ========================== -.. this is a revision shortly after release-pypy-6.0.0 -.. startrev: e50e11af23f1 - -.. branch: cppyy-packaging - -Main items: vastly better template resolution and improved performance. In -detail: upgrade to backend 1.4, improved handling of templated methods and -functions (in particular automatic deduction of types), improved pythonization -interface, range of compatibility fixes for Python3, free functions now take -fast libffi path when possible, moves for strings (incl. from Python str), -easier/faster handling of std::vector by numpy, improved and faster object -identity preservation - -.. branch: socket_default_timeout_blockingness - -Make sure 'blocking-ness' of socket is set along with default timeout - -.. branch: crypt_h - -Include crypt.h for crypt() on Linux - -.. branch: gc-more-logging - -Log additional gc-minor and gc-collect-step info in the PYPYLOG - -.. branch: reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb - - -.. branch: pyparser-improvements-3 - -Small refactorings in the Python parser. - -.. branch: fix-readme-typo - -.. branch: py3.6-wordcode - -implement new wordcode instruction encoding on the 3.6 branch - -.. branch: socket_default_timeout_blockingness - -Backport CPython fix for possible shell injection issue in `distutils.spawn`, -https://bugs.python.org/issue34540 - -.. branch: cffi_dlopen_unicode - -Enable use of unicode file names in `dlopen` - -.. branch: rlock-in-rpython - -Backport CPython fix for `thread.RLock` - - -.. branch: expose-gc-time - -Make GC hooks measure time in seconds (as opposed to an opaque unit). - -.. branch: cleanup-test_lib_pypy - -Update most test_lib_pypy/ tests and move them to extra_tests/. - -.. branch: gc-disable - -Make it possible to manually manage the GC by using a combination of -gc.disable() and gc.collect_step(). Make sure to write a proper release -announcement in which we explain that existing programs could leak memory if -they run for too much time between a gc.disable()/gc.enable() +.. this is a revision shortly after release-pypy-7.0.0 +.. startrev: 481c69f7d81f diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-pypy2-7.0.0.rst copy from pypy/doc/whatsnew-head.rst copy to pypy/doc/whatsnew-pypy2-7.0.0.rst diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-7.0.0.rst copy from pypy/doc/whatsnew-pypy3-head.rst copy to pypy/doc/whatsnew-pypy3-7.0.0.rst 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,25 +1,6 @@ -======================== -What's new in PyPy3 6.0+ -======================== - -.. this is the revision after release-pypy3.5-v6.0 -.. startrev: 580e3e26cd32 - -.. branch: hroncok/fix-multiprocessing-regression-on-newer--1524656522151 - -Fix multiprocessing regression on newer glibcs - -.. branch: py3.5-user-site-impl - -Use implementation-specific site directories in sysconfig like in Python2 - - -.. branch: alex_gaynor/remove-an-unneeded-call-into-openssl-th-1526429141011 - -Remove an unneeded call into OpenSSL, from cpython https://github.com/python/cpython/pull/6887 - - -.. branch: py3.5-reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb +======================== +What's new in PyPy3 7.0+ +======================== + +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c 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 @@ -512,6 +512,7 @@ header = DEFAULT_HEADER if func.__name__ in FUNCTIONS_BY_HEADER[header]: raise ValueError("%s already registered" % func.__name__) + func._revdb_c_only_ = True # hack for revdb api_function = COnlyApiFunction(argtypes, restype, func) FUNCTIONS_BY_HEADER[header][func.__name__] = api_function return api_function diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py --- a/rpython/rlib/rgc.py +++ b/rpython/rlib/rgc.py @@ -67,7 +67,7 @@ """Returns True if we have a "split GC address space", i.e. if we are translating with an option that doesn't support taking raw addresses inside GC objects and "hacking" at them. This is - notably the case with --reversedb.""" + notably the case with --revdb.""" return False # for test purposes we allow objects to be pinned and use diff --git a/rpython/rlib/src/boehm-rawrefcount.c b/rpython/rlib/src/boehm-rawrefcount.c --- a/rpython/rlib/src/boehm-rawrefcount.c +++ b/rpython/rlib/src/boehm-rawrefcount.c @@ -191,6 +191,7 @@ #endif assert(result->ob_refcnt == REFCNT_FROM_PYPY); result->ob_refcnt = 1; + result->ob_pypy_link = 0; p->pyobj = NULL; *pp = p->next_in_bucket; p->next_in_bucket = hash_free_list; diff --git a/rpython/tool/setuptools_msvc.py b/rpython/tool/setuptools_msvc.py --- a/rpython/tool/setuptools_msvc.py +++ b/rpython/tool/setuptools_msvc.py @@ -27,7 +27,6 @@ import platform import itertools import distutils.errors -from pkg_resources.extern.packaging.version import LegacyVersion from setuptools.extern.six.moves import filterfalse @@ -201,6 +200,7 @@ """ if "numpy.distutils" in sys.modules: import numpy as np + from pkg_resources.extern.packaging.version import LegacyVersion if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs) diff --git a/rpython/translator/platform/windows.py b/rpython/translator/platform/windows.py --- a/rpython/translator/platform/windows.py +++ b/rpython/translator/platform/windows.py @@ -56,7 +56,12 @@ # use setuptools from python3 to find tools try: vcdict = _find_vcvarsall(vsver, x64flag) + except ImportError as e: + if 'setuptools' in str(e): + log.error('is setuptools installed (perhaps try %s -mensurepip)?' % sys.executable) + log.error('looking for compiler %s raised exception "%s' % (vsver, str(e))) except Exception as e: + log.error('looking for compiler %s raised exception "%s' % (vsver, str(e))) return None else: if x64flag: diff --git a/rpython/translator/revdb/gencsupp.py b/rpython/translator/revdb/gencsupp.py --- a/rpython/translator/revdb/gencsupp.py +++ b/rpython/translator/revdb/gencsupp.py @@ -51,6 +51,10 @@ ## return False def prepare_function(funcgen): + if getattr(getattr(funcgen.graph, 'func', None), '_revdb_c_only_', False): + extra_enter_text = 'RPY_REVDB_C_ONLY_ENTER' + extra_return_text = 'RPY_REVDB_C_ONLY_LEAVE' + return extra_enter_text, extra_return_text stack_bottom = False for block in funcgen.graph.iterblocks(): for op in block.operations: diff --git a/rpython/translator/revdb/src-revdb/revdb.c b/rpython/translator/revdb/src-revdb/revdb.c --- a/rpython/translator/revdb/src-revdb/revdb.c +++ b/rpython/translator/revdb/src-revdb/revdb.c @@ -253,7 +253,10 @@ "(use REVDB=logfile)\n", (int)getpid()); } - rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); + if (rpy_rev_fileno >= 0) + rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); + else + rpy_revdb.buf_p = NULL; rpy_revdb.buf_limit = rpy_rev_buffer + sizeof(rpy_rev_buffer) - 32; rpy_revdb.unique_id_seen = 1; @@ -269,17 +272,23 @@ ssize_t full_size; assert(rpy_revdb.lock); + if (rpy_revdb.buf_p == NULL) + return; + assert(rpy_rev_fileno >= 0); + /* write the current buffer content to the OS */ full_size = rpy_revdb.buf_p - rpy_rev_buffer; rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); - if (rpy_rev_fileno >= 0) - write_all(rpy_rev_buffer, full_size); + write_all(rpy_rev_buffer, full_size); } static ssize_t current_packet_size(void) { /* must be called with the lock held */ - return rpy_revdb.buf_p - (rpy_rev_buffer + sizeof(int16_t)); + if (rpy_revdb.buf_p != NULL) + return rpy_revdb.buf_p - (rpy_rev_buffer + sizeof(int16_t)); + else + return 0; } RPY_EXTERN @@ -327,6 +336,11 @@ rpy_reverse_db_flush(); assert(current_packet_size() == 0); + if (rpy_rev_fileno < 0) + return; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + *(int16_t *)p = async_code; memcpy(rpy_revdb.buf_p, &content, sizeof(uint64_t)); rpy_revdb.buf_p += sizeof(uint64_t); @@ -472,6 +486,9 @@ if (rpy_rev_fileno < 0) return 1; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + base_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR); if (base_offset < 0) { perror("lseek"); @@ -488,6 +505,9 @@ if (rpy_rev_fileno < 0) return; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + base_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR); if (base_offset < 0) { perror("lseek"); @@ -1033,9 +1053,9 @@ " echo 0 | sudo tee /proc/sys/kernel/randomize_va_space\n" "\n" "It has been reported that on Linux kernel 4.12.4-1-ARCH,\n" - "ASLR cannot be disabled at all for libpypy-c.so. For now\n" - "there is no good solution. Either you downgrade the\n" - "kernel, or you translate with --no-shared (and you loose\n" + "ASLR cannot be disabled at all for libpypy-c.so. It works\n" + "again in kernel 4.19 (and maybe sooner). Either change\n" + "kernels, or translate with --no-shared (but then you loose\n" "PyPy's cpyext ability).\n" "\n", argv[0]); exit(1); diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h b/rpython/translator/revdb/src-revdb/revdb_include.h --- a/rpython/translator/revdb/src-revdb/revdb_include.h +++ b/rpython/translator/revdb/src-revdb/revdb_include.h @@ -16,7 +16,8 @@ #endif bool_t watch_enabled; int lock; - char *buf_p, *buf_limit, *buf_readend; + char *buf_p; /* NULL during recording if recording is actually disabled */ + char *buf_limit, *buf_readend; uint64_t stop_point_seen, stop_point_break; uint64_t unique_id_seen, unique_id_break; } rpy_revdb_t; @@ -85,9 +86,13 @@ { \ decl_e = variable; \ _RPY_REVDB_PRINT("[ wr ]", _e); \ - memcpy(rpy_revdb.buf_p, &_e, sizeof(_e)); \ - if ((rpy_revdb.buf_p += sizeof(_e)) > rpy_revdb.buf_limit) \ - rpy_reverse_db_flush(); \ + char *_dst = rpy_revdb.buf_p; \ + if (_dst) { \ + memcpy(_dst, &_e, sizeof(_e)); \ + if ((rpy_revdb.buf_p = _dst + sizeof(_e)) \ + > rpy_revdb.buf_limit) \ + rpy_reverse_db_flush(); \ + } \ } #define _RPY_REVDB_EMIT_REPLAY(decl_e, variable) \ @@ -179,6 +184,13 @@ rpy_reverse_db_bad_acquire_gil("release"); \ } +#define RPY_REVDB_C_ONLY_ENTER \ + char *saved_bufp = rpy_revdb.buf_p; \ + rpy_revdb.buf_p = NULL; + +#define RPY_REVDB_C_ONLY_LEAVE \ + rpy_revdb.buf_p = saved_bufp; + #define RPY_REVDB_CALLBACKLOC(locnum) \ rpy_reverse_db_callback_loc(locnum) From pypy.commits at gmail.com Fri Feb 1 13:00:39 2019 From: pypy.commits at gmail.com (antocuni) Date: Fri, 01 Feb 2019 10:00:39 -0800 (PST) Subject: [pypy-commit] pypy release-pypy3.6-7.x: hg merge 3.6 Message-ID: <5c548947.1c69fb81.c2a3b.29c3@mx.google.com> Author: Antonio Cuni Branch: release-pypy3.6-7.x Changeset: r95774:4286ef6d6e83 Date: 2019-02-01 18:59 +0100 http://bitbucket.org/pypy/pypy/changeset/4286ef6d6e83/ Log: hg merge 3.6 diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -40,16 +40,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -59,8 +59,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -70,10 +70,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -114,12 +114,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -134,8 +134,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -144,10 +145,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -163,6 +164,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -176,6 +178,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -187,7 +190,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -198,7 +200,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -210,6 +211,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -217,12 +219,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -242,7 +246,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -270,12 +273,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -290,10 +296,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -301,28 +309,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -332,6 +338,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -340,6 +347,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -349,8 +357,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -364,7 +373,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -384,12 +392,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz diff --git a/extra_tests/cffi_tests/cffi0/test_function.py b/extra_tests/cffi_tests/cffi0/test_function.py --- a/extra_tests/cffi_tests/cffi0/test_function.py +++ b/extra_tests/cffi_tests/cffi0/test_function.py @@ -46,14 +46,15 @@ assert x != math.sin(1.23) # rounding effects assert abs(x - math.sin(1.23)) < 1E-6 - def test_lround_no_return_value(self): + def test_getenv_no_return_value(self): # check that 'void'-returning functions work too ffi = FFI(backend=self.Backend()) ffi.cdef(""" - void lround(double x); + void getenv(char *); """) - m = ffi.dlopen(lib_m) - x = m.lround(1.23) + needs_dlopen_none() + m = ffi.dlopen(None) + x = m.getenv(b"FOO") assert x is None def test_dlopen_filename(self): diff --git a/extra_tests/cffi_tests/cffi1/test_pkgconfig.py b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py new file mode 100644 --- /dev/null +++ b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py @@ -0,0 +1,95 @@ +# Generated by pypy/tool/import_cffi.py +import sys +import subprocess +import py +import cffi.pkgconfig as pkgconfig +from cffi import PkgConfigError + + +def mock_call(libname, flag): + assert libname=="foobarbaz" + flags = { + "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", + "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", + } + return flags[flag] + + +def test_merge_flags(): + d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} + d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} + + pkgconfig.merge_flags(d1, d2) + assert d1 == { + "ham": [1, 2, 3], + "spam" : ["a", "b", "c", "spam", "spam", "spam"], + "bar" : ["b", "a", "z"], + "foo" : []} + + +def test_pkgconfig(): + assert pkgconfig.flags_from_pkgconfig([]) == {} + + saved = pkgconfig.call + try: + pkgconfig.call = mock_call + flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) + finally: + pkgconfig.call = saved + assert flags == { + 'include_dirs': ['/usr/include/python3.6m'], + 'library_dirs': ['/usr/lib64'], + 'libraries': ['python3.6'], + 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], + 'extra_compile_args': ['-O42'], + 'extra_link_args': ['-shared'] + } + +class mock_subprocess: + PIPE = Ellipsis + class Popen: + def __init__(self, cmd, stdout, stderr): + if mock_subprocess.RESULT is None: + raise OSError("oops can't run") + assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] + def communicate(self): + bout, berr, rc = mock_subprocess.RESULT + self.returncode = rc + return bout, berr + +def test_call(): + saved = pkgconfig.subprocess + try: + pkgconfig.subprocess = mock_subprocess + + mock_subprocess.RESULT = None + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "cannot run pkg-config: oops can't run" + + mock_subprocess.RESULT = b"", "Foo error!\n", 1 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "Foo error!" + + mock_subprocess.RESULT = b"abc\\def\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value).startswith("pkg-config --cflags libfoo returned an " + "unsupported backslash-escaped output:") + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + if sys.version_info >= (3,): + mock_subprocess.RESULT = b"\xff\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, + "libfoo", "--cflags", encoding="utf-8") + assert str(e.value) == ( + "pkg-config --cflags libfoo returned bytes that cannot be " + "decoded with encoding 'utf-8':\nb'\\xff\\n'") + + finally: + pkgconfig.subprocess = saved diff --git a/lib_pypy/_collections.py b/lib_pypy/_collections.py --- a/lib_pypy/_collections.py +++ b/lib_pypy/_collections.py @@ -390,7 +390,7 @@ class defaultdict(dict): __slots__ = ["default_factory"] - + def __init__(self, *args, **kwds): if len(args) > 0: default_factory = args[0] @@ -401,10 +401,10 @@ default_factory = None self.default_factory = default_factory super(defaultdict, self).__init__(*args, **kwds) - + def __missing__(self, key): # from defaultdict docs - if self.default_factory is None: + if self.default_factory is None: raise KeyError(key) self[key] = value = self.default_factory() return value @@ -420,7 +420,7 @@ def copy(self): return type(self)(self.default_factory, self) - + def __copy__(self): return self.copy() @@ -438,9 +438,3 @@ """ return (type(self), (self.default_factory,), None, None, iter(self.items())) - - -try: - from _pypy_collections import OrderedDict -except ImportError: - pass 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 @@ -3,6 +3,7 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing +from .error import PkgConfigError __version__ = "1.12.0" __version_info__ = (1, 12, 0) 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 @@ -643,6 +643,16 @@ self._assigned_source = (str(module_name), source, source_extension, kwds) + def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, + source_extension='.c', **kwds): + from . import pkgconfig + if not isinstance(pkgconfig_libs, list): + raise TypeError("the pkgconfig_libs argument must be a list " + "of package names") + kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) + pkgconfig.merge_flags(kwds, kwds2) + self.set_source(module_name, source, source_extension, **kwds) + def distutils_extension(self, tmpdir='build', verbose=True): from distutils.dir_util import mkpath from .recompiler import recompile diff --git a/lib_pypy/cffi/error.py b/lib_pypy/cffi/error.py --- a/lib_pypy/cffi/error.py +++ b/lib_pypy/cffi/error.py @@ -1,8 +1,9 @@ class FFIError(Exception): - pass + __module__ = 'cffi' class CDefError(Exception): + __module__ = 'cffi' def __str__(self): try: current_decl = self.args[1] @@ -16,8 +17,15 @@ class VerificationError(Exception): """ An error raised when verification fails """ + __module__ = 'cffi' class VerificationMissing(Exception): """ An error raised when incomplete structures are passed into cdef, but no verification has been done """ + __module__ = 'cffi' + +class PkgConfigError(Exception): + """ An error raised for missing modules in pkg-config + """ + __module__ = 'cffi' diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst --- a/pypy/doc/build.rst +++ b/pypy/doc/build.rst @@ -220,11 +220,12 @@ Making a debug build of PyPy ---------------------------- -If the Makefile is rerun with the lldebug or lldebug0 target, appropriate -compilation flags are added to add debug info and reduce compiler optimizations -to ``-O0`` respectively. If you stop in a debugger, you will see the -very wordy machine-generated C code from the rpython translation step, which -takes a little bit of reading to relate back to the rpython code. +Rerun the ``Makefile`` with the ``make lldebug`` or ``make lldebug0`` target, +which will build in a way that running under a debugger makes sense. +Appropriate compilation flags are added to add debug info, and for ``lldebug0`` +compiler optimizations are fully disabled. If you stop in a debugger, you will +see the very wordy machine-generated C code from the rpython translation step, +which takes a little bit of reading to relate back to the rpython code. Build cffi import libraries for the stdlib ------------------------------------------ diff --git a/pypy/doc/contributor.rst b/pypy/doc/contributor.rst --- a/pypy/doc/contributor.rst +++ b/pypy/doc/contributor.rst @@ -7,16 +7,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -26,8 +26,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -37,10 +37,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -81,12 +81,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -101,8 +101,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -111,10 +112,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -130,6 +131,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -143,6 +145,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -154,7 +157,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -165,7 +167,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -177,6 +178,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -184,12 +186,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -209,7 +213,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -237,12 +240,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -257,10 +263,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -268,28 +276,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -299,6 +305,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -307,6 +314,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -316,8 +324,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -331,7 +340,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -351,12 +359,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz 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-v7.0.0.rst release-v6.0.0.rst release-v5.10.1.rst release-v5.10.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-7.0.0.rst whatsnew-pypy2-6.0.0.rst whatsnew-pypy2-5.10.0.rst whatsnew-pypy2-5.10.0.rst @@ -41,6 +42,7 @@ .. toctree:: whatsnew-pypy3-head.rst + whatsnew-pypy3-7.0.0.rst whatsnew-pypy3-5.9.0.rst whatsnew-pypy3-5.8.0.rst whatsnew-pypy3-5.7.0.rst diff --git a/pypy/doc/interpreter.rst b/pypy/doc/interpreter.rst --- a/pypy/doc/interpreter.rst +++ b/pypy/doc/interpreter.rst @@ -156,7 +156,7 @@ environment found in `Frames`. Frames and Functions have references to a code object. Here is a list of Code attributes: -* ``co_flags`` flags if this code object has nested scopes/generators +* ``co_flags`` flags if this code object has nested scopes/generators/etc. * ``co_stacksize`` the maximum depth the stack can reach while executing the code * ``co_code`` the actual bytecode string diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-v7.0.0.rst @@ -0,0 +1,151 @@ +====================================================== +PyPy v7.0.0: triple release of 2.7, 3.5 and 3.6-alpha +====================================================== + +The PyPy team is proud to release the version 7.0.0 of PyPy, which includes +three different interpreters: + + - PyPy2.7, which is an interpreter supporting the syntax and the features of + Python 2.7 + + - PyPy3.5, which supports Python 3.5 + + - PyPy3.6-alpha: this is the first official release of PyPy to support 3.6 + features, although it is still considered alpha quality. + +All the interpreters are based on much the same codebase, thus the triple +release. + +Until we can work with downstream providers to distribute builds with PyPy, we +have made packages for some common packages `available as wheels`_. + +The GC `hooks`_ , which can be used to gain more insights into its +performance, has been improved and it is now possible to manually manage the +GC by using a combination of ``gc.disable`` and ``gc.collect_step``. See the +`GC blog post`_. + + +We updated the `cffi`_ module included in PyPy to version 1.12, and the +`cppyy`_ backend to 1.4. Please use these to wrap your C and C++ code, +respectively, for a JIT friendly experience. + +As always, this release is 100% compatible with the previous one and fixed +several issues and bugs raised by the growing community of PyPy users. +We strongly recommend updating. + +The PyPy3.6 release and the Windows PyPy3.5 release are still not production +quality so your mileage may vary. There are open issues with incomplete +compatibility and c-extension support. + +The utf8 branch that changes internal representation of unicode to utf8 did not +make it into the release, so there is still more goodness coming. +You can download the v6.0 releases here: + + http://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project. If PyPy is not quite good enough for your needs, we are available for +direct consulting work. + +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. + +.. _`PyPy`: index.html +.. _`RPython`: https://rpython.readthedocs.org +.. _`help`: project-ideas.html +.. _`cffi`: http://cffi.readthedocs.io +.. _`cppyy`: https://cppyy.readthedocs.io +.. _`available as wheels`: https://github.com/antocuni/pypy-wheels +.. _`GC blog post`: https://morepypy.blogspot.com/2019/01/pypy-for-low-latency-systems.html + + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7, 3.5 and 3.6. 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 release supports: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS X 64 bits, Windows 32 bits, OpenBSD, FreeBSD) + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +Unfortunately at the moment of writing our ARM buildbots are out of service, +so for now we are **not** releasing any binary for the ARM architecture. + +.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://rpython.readthedocs.io/en/latest/examples.html + + +Changelog +========= + +If not specified, the changes are shared across versions + +* Support ``__set_name__``, ``__init_subclass__`` (Py3.6) +* Support ``cppyy`` in Py3.5 and Py3.6 +* Use implementation-specific site directories in ``sysconfig`` (Py3.5, Py3.6) +* Adding detection of gcc to ``sysconfig`` (Py3.5, Py3.6) +* Fix multiprocessing regression on newer glibcs +* Make sure 'blocking-ness' of socket is set along with default timeout +* Include ``crypt.h`` for ``crypt()`` on Linux +* Improve and re-organize the contributing_ documentation +* Make the ``__module__`` attribute writable, fixing an incompatibility with + NumPy 1.16 +* Implement ``Py_ReprEnter``, ``Py_ReprLeave(), ``PyMarshal_ReadObjectFromString``, + ``PyMarshal_WriteObjectToString``, ``PyObject_DelItemString``, + ``PyMapping_DelItem``, ``PyMapping_DelItemString``, ``PyEval_GetFrame``, + ``PyOS_InputHook``, ``PyErr_FormatFromCause`` (Py3.6), +* Implement new wordcode instruction encoding (Py3.6) +* Log additional gc-minor and gc-collect-step info in the PYPYLOG +* The ``reverse-debugger`` (revdb) branch has been merged to the default + branch, so it should always be up-to-date. You still need a special pypy + build, but you can compile it from the same source as the one we distribute + for the v7.0.0 release. For more information, see + https://bitbucket.org/pypy/revdb +* Support underscores in numerical literals like ``'4_2'`` (Py3.6) +* Pre-emptively raise MemoryError if the size of dequeue in ``_collections.deque`` + is too large (Py3.5) +* Fix multithreading issues in calls to ``os.setenv`` +* Add missing defines and typedefs for numpy and pandas on MSVC +* Add CPython macros like ``Py_NAN`` to header files +* Rename the ``MethodType`` to ``instancemethod``, like CPython +* Better support for `async with` in generators (Py3.5, Py3.6) +* Improve the performance of ``pow(a, b, c)`` if ``c`` is a large integer +* Now ``vmprof`` works on FreeBSD +* Support GNU Hurd, fixes for FreeBSD +* Add deprecation warning if type of result of ``__float__`` is float inherited + class (Py3.6) +* Fix async generator bug when yielding a ``StopIteration`` (Py3.6) +* Speed up ``max(list-of-int)`` from non-jitted code +* Fix Windows ``os.listdir()`` for some cases (see CPython #32539) +* Add ``select.PIPE_BUF`` +* Use ``subprocess`` to avoid shell injection in ``shutil`` module - backport + of https://bugs.python.org/issue34540 +* Rename ``_Py_ZeroStruct`` to ``_Py_FalseStruct`` (Py3.5, Py3.6) +* Remove some cpyext names for Py3.5, Py3.6 +* Enable use of unicode file names in ``dlopen`` +* Backport CPython fix for ``thread.RLock`` +* Make GC hooks measure time in seconds (as opposed to an opaque unit) +* Refactor and reorganize tests in ``test_lib_pypy`` +* Check error values in ``socket.setblocking`` (Py3.6) +* Add support for FsPath to os.unlink() (Py3.6) +* Fix freezing builtin modules at translation +* Tweak ``W_UnicodeDictionaryStrategy`` which speeds up dictionaries with only + unicode keys + +We also refactored many parts of the JIT bridge optimizations, as well as cpyext +internals, and together with new contributors fixed issues, added new +documentation, and cleaned up the codebase. + +.. _contributing: http://doc.pypy.org/en/latest/contributing.html 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,73 +1,6 @@ ========================== -What's new in PyPy2.7 6.0+ +What's new in PyPy2.7 7.0+ ========================== -.. this is a revision shortly after release-pypy-6.0.0 -.. startrev: e50e11af23f1 - -.. branch: cppyy-packaging - -Main items: vastly better template resolution and improved performance. In -detail: upgrade to backend 1.4, improved handling of templated methods and -functions (in particular automatic deduction of types), improved pythonization -interface, range of compatibility fixes for Python3, free functions now take -fast libffi path when possible, moves for strings (incl. from Python str), -easier/faster handling of std::vector by numpy, improved and faster object -identity preservation - -.. branch: socket_default_timeout_blockingness - -Make sure 'blocking-ness' of socket is set along with default timeout - -.. branch: crypt_h - -Include crypt.h for crypt() on Linux - -.. branch: gc-more-logging - -Log additional gc-minor and gc-collect-step info in the PYPYLOG - -.. branch: reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb - - -.. branch: pyparser-improvements-3 - -Small refactorings in the Python parser. - -.. branch: fix-readme-typo - -.. branch: py3.6-wordcode - -implement new wordcode instruction encoding on the 3.6 branch - -.. branch: socket_default_timeout_blockingness - -Backport CPython fix for possible shell injection issue in `distutils.spawn`, -https://bugs.python.org/issue34540 - -.. branch: cffi_dlopen_unicode - -Enable use of unicode file names in `dlopen` - -.. branch: rlock-in-rpython - -Backport CPython fix for `thread.RLock` - - -.. branch: expose-gc-time - -Make GC hooks measure time in seconds (as opposed to an opaque unit). - -.. branch: cleanup-test_lib_pypy - -Update most test_lib_pypy/ tests and move them to extra_tests/. - -.. branch: gc-disable - -Make it possible to manually manage the GC by using a combination of -gc.disable() and gc.collect_step(). Make sure to write a proper release -announcement in which we explain that existing programs could leak memory if -they run for too much time between a gc.disable()/gc.enable() +.. this is a revision shortly after release-pypy-7.0.0 +.. startrev: 481c69f7d81f diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-pypy2-7.0.0.rst copy from pypy/doc/whatsnew-head.rst copy to pypy/doc/whatsnew-pypy2-7.0.0.rst diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-7.0.0.rst copy from pypy/doc/whatsnew-pypy3-head.rst copy to pypy/doc/whatsnew-pypy3-7.0.0.rst 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,25 +1,6 @@ -======================== -What's new in PyPy3 6.0+ -======================== - -.. this is the revision after release-pypy3.5-v6.0 -.. startrev: 580e3e26cd32 - -.. branch: hroncok/fix-multiprocessing-regression-on-newer--1524656522151 - -Fix multiprocessing regression on newer glibcs - -.. branch: py3.5-user-site-impl - -Use implementation-specific site directories in sysconfig like in Python2 - - -.. branch: alex_gaynor/remove-an-unneeded-call-into-openssl-th-1526429141011 - -Remove an unneeded call into OpenSSL, from cpython https://github.com/python/cpython/pull/6887 - - -.. branch: py3.5-reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb +======================== +What's new in PyPy3 7.0+ +======================== + +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c diff --git a/pypy/module/_collections/__init__.py b/pypy/module/_collections/__init__.py --- a/pypy/module/_collections/__init__.py +++ b/pypy/module/_collections/__init__.py @@ -8,6 +8,7 @@ appleveldefs = { 'defaultdict': 'app_defaultdict.defaultdict', + 'OrderedDict': 'app_odict.OrderedDict', } interpleveldefs = { @@ -25,15 +26,3 @@ space = self.space space.getattr(self, space.newtext('defaultdict')) # force importing space.delattr(self, space.newtext('__missing__')) - - def startup(self, space): - # OrderedDict is normally present, but in some cases the line - # "from __pypy__ import reversed_dict, move_to_end" from - # _pypy_collections.py raises - space.appexec([self], """(mod): - try: - from _pypy_collections import OrderedDict - mod.OrderedDict = OrderedDict - except ImportError: - pass - """) diff --git a/lib_pypy/_pypy_collections.py b/pypy/module/_collections/app_odict.py rename from lib_pypy/_pypy_collections.py rename to pypy/module/_collections/app_odict.py 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 @@ -512,6 +512,7 @@ header = DEFAULT_HEADER if func.__name__ in FUNCTIONS_BY_HEADER[header]: raise ValueError("%s already registered" % func.__name__) + func._revdb_c_only_ = True # hack for revdb api_function = COnlyApiFunction(argtypes, restype, func) FUNCTIONS_BY_HEADER[header][func.__name__] = api_function return api_function diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py --- a/pypy/module/sys/vm.py +++ b/pypy/module/sys/vm.py @@ -234,7 +234,7 @@ service_pack_minor = structseqfield(11, "Service Pack minor version number") suite_mask = structseqfield(12, "Bit mask identifying available product suites") product_type = structseqfield(13, "System product type") - _platform_version = structseqfield(14, "Diagnostic version number") + platform_version = structseqfield(14, "Diagnostic version number") ''') diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py --- a/rpython/rlib/rgc.py +++ b/rpython/rlib/rgc.py @@ -67,7 +67,7 @@ """Returns True if we have a "split GC address space", i.e. if we are translating with an option that doesn't support taking raw addresses inside GC objects and "hacking" at them. This is - notably the case with --reversedb.""" + notably the case with --revdb.""" return False # for test purposes we allow objects to be pinned and use diff --git a/rpython/rlib/src/boehm-rawrefcount.c b/rpython/rlib/src/boehm-rawrefcount.c --- a/rpython/rlib/src/boehm-rawrefcount.c +++ b/rpython/rlib/src/boehm-rawrefcount.c @@ -191,6 +191,7 @@ #endif assert(result->ob_refcnt == REFCNT_FROM_PYPY); result->ob_refcnt = 1; + result->ob_pypy_link = 0; p->pyobj = NULL; *pp = p->next_in_bucket; p->next_in_bucket = hash_free_list; diff --git a/rpython/tool/setuptools_msvc.py b/rpython/tool/setuptools_msvc.py --- a/rpython/tool/setuptools_msvc.py +++ b/rpython/tool/setuptools_msvc.py @@ -27,7 +27,6 @@ import platform import itertools import distutils.errors -from pkg_resources.extern.packaging.version import LegacyVersion from setuptools.extern.six.moves import filterfalse @@ -201,6 +200,7 @@ """ if "numpy.distutils" in sys.modules: import numpy as np + from pkg_resources.extern.packaging.version import LegacyVersion if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs) diff --git a/rpython/translator/platform/windows.py b/rpython/translator/platform/windows.py --- a/rpython/translator/platform/windows.py +++ b/rpython/translator/platform/windows.py @@ -56,7 +56,12 @@ # use setuptools from python3 to find tools try: vcdict = _find_vcvarsall(vsver, x64flag) + except ImportError as e: + if 'setuptools' in str(e): + log.error('is setuptools installed (perhaps try %s -mensurepip)?' % sys.executable) + log.error('looking for compiler %s raised exception "%s' % (vsver, str(e))) except Exception as e: + log.error('looking for compiler %s raised exception "%s' % (vsver, str(e))) return None else: if x64flag: diff --git a/rpython/translator/revdb/gencsupp.py b/rpython/translator/revdb/gencsupp.py --- a/rpython/translator/revdb/gencsupp.py +++ b/rpython/translator/revdb/gencsupp.py @@ -51,6 +51,10 @@ ## return False def prepare_function(funcgen): + if getattr(getattr(funcgen.graph, 'func', None), '_revdb_c_only_', False): + extra_enter_text = 'RPY_REVDB_C_ONLY_ENTER' + extra_return_text = 'RPY_REVDB_C_ONLY_LEAVE' + return extra_enter_text, extra_return_text stack_bottom = False for block in funcgen.graph.iterblocks(): for op in block.operations: diff --git a/rpython/translator/revdb/src-revdb/revdb.c b/rpython/translator/revdb/src-revdb/revdb.c --- a/rpython/translator/revdb/src-revdb/revdb.c +++ b/rpython/translator/revdb/src-revdb/revdb.c @@ -253,7 +253,10 @@ "(use REVDB=logfile)\n", (int)getpid()); } - rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); + if (rpy_rev_fileno >= 0) + rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); + else + rpy_revdb.buf_p = NULL; rpy_revdb.buf_limit = rpy_rev_buffer + sizeof(rpy_rev_buffer) - 32; rpy_revdb.unique_id_seen = 1; @@ -269,17 +272,23 @@ ssize_t full_size; assert(rpy_revdb.lock); + if (rpy_revdb.buf_p == NULL) + return; + assert(rpy_rev_fileno >= 0); + /* write the current buffer content to the OS */ full_size = rpy_revdb.buf_p - rpy_rev_buffer; rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t); - if (rpy_rev_fileno >= 0) - write_all(rpy_rev_buffer, full_size); + write_all(rpy_rev_buffer, full_size); } static ssize_t current_packet_size(void) { /* must be called with the lock held */ - return rpy_revdb.buf_p - (rpy_rev_buffer + sizeof(int16_t)); + if (rpy_revdb.buf_p != NULL) + return rpy_revdb.buf_p - (rpy_rev_buffer + sizeof(int16_t)); + else + return 0; } RPY_EXTERN @@ -327,6 +336,11 @@ rpy_reverse_db_flush(); assert(current_packet_size() == 0); + if (rpy_rev_fileno < 0) + return; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + *(int16_t *)p = async_code; memcpy(rpy_revdb.buf_p, &content, sizeof(uint64_t)); rpy_revdb.buf_p += sizeof(uint64_t); @@ -472,6 +486,9 @@ if (rpy_rev_fileno < 0) return 1; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + base_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR); if (base_offset < 0) { perror("lseek"); @@ -488,6 +505,9 @@ if (rpy_rev_fileno < 0) return; + /* should not be here from the middle of a @c_only function */ + assert(rpy_revdb.buf_p != NULL); + base_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR); if (base_offset < 0) { perror("lseek"); @@ -1033,9 +1053,9 @@ " echo 0 | sudo tee /proc/sys/kernel/randomize_va_space\n" "\n" "It has been reported that on Linux kernel 4.12.4-1-ARCH,\n" - "ASLR cannot be disabled at all for libpypy-c.so. For now\n" - "there is no good solution. Either you downgrade the\n" - "kernel, or you translate with --no-shared (and you loose\n" + "ASLR cannot be disabled at all for libpypy-c.so. It works\n" + "again in kernel 4.19 (and maybe sooner). Either change\n" + "kernels, or translate with --no-shared (but then you loose\n" "PyPy's cpyext ability).\n" "\n", argv[0]); exit(1); diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h b/rpython/translator/revdb/src-revdb/revdb_include.h --- a/rpython/translator/revdb/src-revdb/revdb_include.h +++ b/rpython/translator/revdb/src-revdb/revdb_include.h @@ -16,7 +16,8 @@ #endif bool_t watch_enabled; int lock; - char *buf_p, *buf_limit, *buf_readend; + char *buf_p; /* NULL during recording if recording is actually disabled */ + char *buf_limit, *buf_readend; uint64_t stop_point_seen, stop_point_break; uint64_t unique_id_seen, unique_id_break; } rpy_revdb_t; @@ -85,9 +86,13 @@ { \ decl_e = variable; \ _RPY_REVDB_PRINT("[ wr ]", _e); \ - memcpy(rpy_revdb.buf_p, &_e, sizeof(_e)); \ - if ((rpy_revdb.buf_p += sizeof(_e)) > rpy_revdb.buf_limit) \ - rpy_reverse_db_flush(); \ + char *_dst = rpy_revdb.buf_p; \ + if (_dst) { \ + memcpy(_dst, &_e, sizeof(_e)); \ + if ((rpy_revdb.buf_p = _dst + sizeof(_e)) \ + > rpy_revdb.buf_limit) \ + rpy_reverse_db_flush(); \ + } \ } #define _RPY_REVDB_EMIT_REPLAY(decl_e, variable) \ @@ -179,6 +184,13 @@ rpy_reverse_db_bad_acquire_gil("release"); \ } +#define RPY_REVDB_C_ONLY_ENTER \ + char *saved_bufp = rpy_revdb.buf_p; \ + rpy_revdb.buf_p = NULL; + +#define RPY_REVDB_C_ONLY_LEAVE \ + rpy_revdb.buf_p = saved_bufp; + #define RPY_REVDB_CALLBACKLOC(locnum) \ rpy_reverse_db_callback_loc(locnum) From pypy.commits at gmail.com Sat Feb 2 03:02:16 2019 From: pypy.commits at gmail.com (mattip) Date: Sat, 02 Feb 2019 00:02:16 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: typo Message-ID: <5c554e88.1c69fb81.406ec.f0cb@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95775:5994ada451a7 Date: 2019-02-02 10:01 +0200 http://bitbucket.org/pypy/pypy/changeset/5994ada451a7/ Log: typo diff --git a/pypy/interpreter/pyparser/pytokenizer.py b/pypy/interpreter/pyparser/pytokenizer.py --- a/pypy/interpreter/pyparser/pytokenizer.py +++ b/pypy/interpreter/pyparser/pytokenizer.py @@ -49,7 +49,7 @@ def verify_utf8(token): try: rutf8.check_utf8(token, False) - except ruf8.CheckError: + except rutf8.CheckError: return False return True From pypy.commits at gmail.com Sat Feb 2 12:37:18 2019 From: pypy.commits at gmail.com (mattip) Date: Sat, 02 Feb 2019 09:37:18 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: invalid utf8 in error message should not crash the interpreter Message-ID: <5c55d54e.1c69fb81.dc16f.e4a9@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95776:64938735299b Date: 2019-02-02 17:29 +0200 http://bitbucket.org/pypy/pypy/changeset/64938735299b/ Log: invalid utf8 in error message should not crash the interpreter diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -526,8 +526,10 @@ else: from rpython.rlib import rutf8 result = str(value) - # Assumes valid utf-8 - lgt += rutf8.check_utf8(result, True) + try: + lgt += rutf8.check_utf8(result, True) + except rutf8.CheckError as e: + lgt -= e.pos lst[i + i + 1] = result lst[-1] = self.xstrings[-1] lgt += len(self.xstrings[-1]) From pypy.commits at gmail.com Sat Feb 2 12:37:20 2019 From: pypy.commits at gmail.com (mattip) Date: Sat, 02 Feb 2019 09:37:20 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: remove more bytes.decode, assert on unused case in module.struct Message-ID: <5c55d550.1c69fb81.da02e.dae9@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95777:f165c244dfb4 Date: 2019-02-02 19:06 +0200 http://bitbucket.org/pypy/pypy/changeset/f165c244dfb4/ Log: remove more bytes.decode, assert on unused case in module.struct diff --git a/TODO b/TODO --- a/TODO +++ b/TODO @@ -11,6 +11,8 @@ - rutf8.utf8_encode_mbcs - unicodehelper.fsencode - unicodehelper.unicode_to_decimal_w + - _winreg.inerp_winreg +* remove 'assert not isinstance(*, unicode) * remove asserts from _WIN32 paths in rlib.rposix.re{name,place} * convert all realunicode_w to unicode_w after we flush out all old uses of unicode_w diff --git a/pypy/interpreter/pyparser/pytokenizer.py b/pypy/interpreter/pyparser/pytokenizer.py --- a/pypy/interpreter/pyparser/pytokenizer.py +++ b/pypy/interpreter/pyparser/pytokenizer.py @@ -65,14 +65,9 @@ def verify_identifier(token): # 1=ok; 0=not an identifier; -1=bad utf-8 - for c in token: - if ord(c) >= 0x80: - break - else: - return 1 try: - u = token.decode('utf-8') - except UnicodeDecodeError: + rutf8.check_utf8(token, False) + except rutf8.CheckError: return -1 from pypy.objspace.std.unicodeobject import _isidentifier return _isidentifier(token) 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,7 +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.rlib import jit, rutf8 from rpython.translator import cdir from rpython.translator.tool.cbuild import ExternalCompilationInfo from rpython.translator.gensupp import NameManager @@ -1722,17 +1722,14 @@ raise_import_error(space, space.newtext(msg), w_name, w_path) def get_init_name(space, w_name): - name_u = space.utf8_w(w_name).decode('utf8') - basename_u = name_u.split(u'.')[-1] - try: - basename = basename_u.encode('ascii') + name = space.utf8_w(w_name) + basename = name.split('.')[-1] + if rutf8.first_non_ascii_char(basename) == -1: return 'PyInit_%s' % (basename,) - except UnicodeEncodeError: - basename = space.bytes_w(encode_object( - space, space.newtext(basename_u), 'punycode', None)) - basename = basename.replace('-', '_') - return 'PyInitU_%s' % (basename,) - + basename = space.bytes_w(encode_object( + space, space.newtext(basename), 'punycode', None)) + basename = basename.replace('-', '_') + return 'PyInitU_%s' % (basename,) initfunctype = lltype.Ptr(lltype.FuncType([], PyObject)) diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py --- a/pypy/module/cpyext/state.py +++ b/pypy/module/cpyext/state.py @@ -143,10 +143,12 @@ argv = space.sys.get('argv') if space.len_w(argv): argv0 = space.getitem(argv, space.newint(0)) - progname = space.utf8_w(argv0).decode('utf8') + progname = space.utf8_w(argv0) + lgt = space.len_w(argv0) else: - progname = u"pypy3" - self.programname = rffi.unicode2wcharp(progname) + progname = "pypy3" + lgt = len(progname) + self.programname = rffi.utf82wcharp(progname, lgt) lltype.render_immortal(self.programname) return self.programname diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -358,9 +358,10 @@ if not get_wbuffer(ref): # Copy unicode buffer w_unicode = from_ref(space, rffi.cast(PyObject, ref)) - u = space.utf8_w(w_unicode).decode('utf8') - set_wbuffer(ref, rffi.unicode2wcharp(u)) - set_wsize(ref, len(u)) + u = space.utf8_w(w_unicode) + lgt = space.len_w(w_unicode) + set_wbuffer(ref, rffi.utf82wcharp(u, lgt)) + set_wsize(ref, lgt) if psize: psize[0] = get_wsize(ref) return get_wbuffer(ref) @@ -950,19 +951,21 @@ than, equal, and greater than, respectively. It is best to pass only ASCII-encoded strings, but the function interprets the input string as ISO-8859-1 if it contains non-ASCII characters.""" - uni = space.utf8_w(w_uni).decode('utf8') + utf8 = space.utf8_w(w_uni) + lgt = space.len_w(w_uni) i = 0 # Compare Unicode string and source character set string - while i < len(uni) and string[i] != '\0': - u = ord(uni[i]) + for ch in rutf8.Utf8StringIterator(utf8): + if string[i] == '\0': + break s = ord(string[i]) - if u != s: - if u < s: + if ch != s: + if ch < s: return -1 else: return 1 i += 1 - if i < len(uni): + if i < lgt: return 1 # uni is longer if string[i] != '\0': return -1 # str is longer @@ -1061,14 +1064,6 @@ @cpython_api([PyObject, Py_ssize_t, Py_ssize_t], PyObject) def PyUnicode_Substring(space, w_str, start, end): - usrc = space.utf8_w(w_str).decode('utf8') - length = len(usrc) - if start < 0 or end < 0: - raise oefmt(space.w_IndexError, "string index out of range") - if start >= length or end < start: - result = u'' - else: - if end > length: - end = length - result = usrc[start:end] - return space.newtext(result) + return space.call_method(w_str, '__getitem__', + space.newslice(space.newint(start), space.newint(end), + space.newint(1))) diff --git a/pypy/module/struct/formatiterator.py b/pypy/module/struct/formatiterator.py --- a/pypy/module/struct/formatiterator.py +++ b/pypy/module/struct/formatiterator.py @@ -164,6 +164,7 @@ elif isinstance(value, str): w_value = self.space.newbytes(value) elif isinstance(value, unicode): + assert not isinstance(value, unicode) w_value = self.space.newutf8(value.decode('utf-8'), len(value)) elif isinstance(value, bool): w_value = self.space.newbool(value) From pypy.commits at gmail.com Sun Feb 3 04:33:45 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 03 Feb 2019 01:33:45 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: make tests pass on win32 Message-ID: <5c56b579.1c69fb81.ee7a3.6847@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95778:8084394bc1a7 Date: 2019-02-02 22:20 +0200 http://bitbucket.org/pypy/pypy/changeset/8084394bc1a7/ Log: make tests pass on win32 diff --git a/pypy/interpreter/test/test_unicodehelper.py b/pypy/interpreter/test/test_unicodehelper.py --- a/pypy/interpreter/test/test_unicodehelper.py +++ b/pypy/interpreter/test/test_unicodehelper.py @@ -1,5 +1,6 @@ import py import pytest +from pypy.conftest import option try: from hypothesis import given, strategies HAS_HYPOTHESIS = True @@ -35,10 +36,12 @@ assert encode_utf8(space, u"\u1234") == "\xe1\x88\xb4" py.test.raises(Hit, encode_utf8, space, u"\ud800") py.test.raises(Hit, encode_utf8, space, u"\udc00") - # for the following test, go to lengths to avoid CPython's optimizer - # and .pyc file storage, which collapse the two surrogates into one - c = u"\udc00" - py.test.raises(Hit, encode_utf8, space, u"\ud800" + c) + if option.runappdirect or sys.maxunicode > 0xFFFF: + # for the following test, go to lengths to avoid CPython's + # optimizer and .pyc file storage, which collapse the two + # surrogates into one + c = u"\udc00" + py.test.raises(Hit, encode_utf8, space, u"\ud800" + c) def test_encode_utf8_allow_surrogates(): sp = FakeSpace() @@ -63,11 +66,7 @@ py.test.raises(Hit, decode_utf8, "\xed\xb0\x80") py.test.raises(Hit, decode_utf8, "\xed\xa0\x80\xed\xb0\x80") got = decode_utf8("\xf0\x90\x80\x80") - if sys.maxunicode > 65535: - assert got == ("\xf0\x90\x80\x80", 1, 4) - else: - # never reached - assert map(ord, got) == [55296, 56320] + assert got == ("\xf0\x90\x80\x80", 1, 4) def test_utf8_encode_ascii(): assert utf8_encode_ascii("abc", "??", "??") == "abc" From pypy.commits at gmail.com Sun Feb 3 04:33:47 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 03 Feb 2019 01:33:47 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: implemented via PyUnicode_EncodeXXX Message-ID: <5c56b57b.1c69fb81.a94b6.5a5f@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95779:07bd199c16df Date: 2019-02-02 22:20 +0200 http://bitbucket.org/pypy/pypy/changeset/07bd199c16df/ Log: implemented via PyUnicode_EncodeXXX diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py --- a/pypy/module/cpyext/stubs.py +++ b/pypy/module/cpyext/stubs.py @@ -1556,13 +1556,6 @@ in consumed.""" raise NotImplementedError - at cpython_api([rffi.CArrayPtr(Py_UNICODE), Py_ssize_t, rffi.CCHARP], PyObject) -def PyUnicode_EncodeMBCS(space, s, size, errors): - """Encode the Py_UNICODE buffer of the given size using MBCS and return - a Python bytes object. Return NULL if an exception was raised by the - codec.""" - raise NotImplementedError - @cpython_api([PyObject], PyObject) def PyUnicode_AsMBCSString(space, unicode): """Encode a Unicode object using MBCS and return the result as Python bytes From pypy.commits at gmail.com Sun Feb 3 04:33:48 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 03 Feb 2019 01:33:48 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: skip test on windows Message-ID: <5c56b57c.1c69fb81.c2a3b.8d75@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95780:7909694b7ee1 Date: 2019-02-03 08:01 +0200 http://bitbucket.org/pypy/pypy/changeset/7909694b7ee1/ Log: skip test on windows diff --git a/pypy/module/cpyext/test/_widechar.c b/pypy/module/cpyext/test/_widechar.c --- a/pypy/module/cpyext/test/_widechar.c +++ b/pypy/module/cpyext/test/_widechar.c @@ -19,8 +19,15 @@ return wide; } +static PyObject * +get_sizeof_wchar(PyObject *self) +{ + return PyLong_FromLong(sizeof(wchar_t)); +} + static PyMethodDef TestMethods[] = { - {"test_widechar", (PyCFunction)test_widechar, METH_NOARGS}, + {"test_widechar", (PyCFunction)test_widechar, METH_NOARGS}, + {"get_sizeof_wchar", (PyCFunction)get_sizeof_wchar,METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/pypy/module/cpyext/test/test_unicodeobject.py b/pypy/module/cpyext/test/test_unicodeobject.py --- a/pypy/module/cpyext/test/test_unicodeobject.py +++ b/pypy/module/cpyext/test/test_unicodeobject.py @@ -371,6 +371,8 @@ def test_invalid(self): m = self.import_module('_widechar') + if m.get_sizeof_wchar() != 4: + pytest.skip('only for sizeof(wchar)==4') raises(ValueError, m.test_widechar) def test_AsUTFNString(self): @@ -719,7 +721,7 @@ w_bytes = PyUnicode_EncodeMBCS(space, wbuf, 4, None) rffi.free_wcharp(wbuf) assert space.type(w_bytes) is space.w_bytes - assert space.text_w(w_bytes) == "abc?" + assert space.utf8_w(w_bytes) == "abc?" def test_escape(self, space): def test(ustr): From pypy.commits at gmail.com Sun Feb 3 04:33:50 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 03 Feb 2019 01:33:50 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: add passing test Message-ID: <5c56b57e.1c69fb81.3d324.73b5@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95781:c0c8a1eba246 Date: 2019-02-03 09:57 +0200 http://bitbucket.org/pypy/pypy/changeset/c0c8a1eba246/ Log: add passing test diff --git a/pypy/interpreter/test/test_unicodehelper.py b/pypy/interpreter/test/test_unicodehelper.py --- a/pypy/interpreter/test/test_unicodehelper.py +++ b/pypy/interpreter/test/test_unicodehelper.py @@ -181,3 +181,12 @@ u'12\u1234'.encode('utf8'), 'xmlcharrefreplace', handler) assert result == '12ሴ' + at pytest.mark.skipif(sys.platform != 'win32', reason='Windows only test') +def test_encode_mbcs(space): + u = u"abc" + u"-\u5171\u0141\u2661\u0363\uDC80" + utf8 = u.encode('utf8') + with pytest.raises(UnicodeEncodeError): + def eh(errors, enc, msg, b, startingpos, endingpos): + u = b.decode('utf-8') + raise UnicodeEncodeError(enc, u, startingpos, endingpos, msg) + uh.utf8_encode_mbcs(utf8, 'strict', eh) From pypy.commits at gmail.com Sun Feb 3 17:06:57 2019 From: pypy.commits at gmail.com (stevie_92) Date: Sun, 03 Feb 2019 14:06:57 -0800 (PST) Subject: [pypy-commit] pypy cpyext-gc-cycle: Fixed some bugs, tests and minor issues Message-ID: <5c576601.1c69fb81.a671c.f47c@mx.google.com> Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95782:b59793e59182 Date: 2019-02-03 23:06 +0100 http://bitbucket.org/pypy/pypy/changeset/b59793e59182/ Log: Fixed some bugs, tests and minor issues Added new test which currently fails and should be addressed Implemented some mocks for the tests, still WIP Removed some debug messages 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 @@ -977,7 +977,7 @@ # we hope that malloc removal removes the newtuple() that is # inserted exactly here by the varargs specializer - print "start to pypy" + # print "start to pypy" # see "Handling of the GIL" above (careful, we don't have the GIL here) tid = rthread.get_or_make_ident() @@ -1091,7 +1091,7 @@ _restore_gil_state(pygilstate_release, gilstate, gil_release, _gil_auto, tid) - print "end to pypy" + # print "end to pypy" return retval diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c --- a/pypy/module/cpyext/src/object.c +++ b/pypy/module/cpyext/src/object.c @@ -54,7 +54,11 @@ PyGC_Head * _PyPy_pyobj_as_gc(GCHdr_PyObject *obj) { - return AS_GC(obj); + if (PyType_IS_GC(((PyObject *)obj)->ob_type)) { + return AS_GC(obj); + } else { + return NULL; + } } void diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py --- a/pypy/module/cpyext/state.py +++ b/pypy/module/cpyext/state.py @@ -60,7 +60,8 @@ space = self.space if not self.space.config.translating: def dealloc_trigger(): - from pypy.module.cpyext.pyobject import PyObject, decref, cts + from pypy.module.cpyext.pyobject import PyObject, decref, \ + incref, cts print 'dealloc_trigger...' while True: ob = rawrefcount.next_dead(PyObject) @@ -70,11 +71,47 @@ name = rffi.charp2str(cts.cast('char*', pto.c_tp_name)) print 'deallocating PyObject', ob, 'of type', name decref(space, ob) + while True: + ob = rawrefcount.cyclic_garbage_head(PyObject) + if not ob: + break + pto = ob.c_ob_type + name = rffi.charp2str(cts.cast('char*', pto.c_tp_name)) + print 'clearing PyObject', ob, 'of type', name + + pyobj = rffi.cast(PyObject, ob) + adr_int = llmemory.cast_adr_to_int( + llmemory.cast_ptr_to_adr(pyobj)) + if pyobj.c_ob_type and pyobj.c_ob_type.c_tp_clear: + incref(space, ob) + pyobj.c_ob_type.c_tp_clear(pyobj) + decref(space, ob) + + head = rawrefcount.cyclic_garbage_head(PyObject) + if adr_int == llmemory.cast_adr_to_int( + llmemory.cast_ptr_to_adr(head)): + rawrefcount.cyclic_garbage_remove() print 'dealloc_trigger DONE' return "RETRY" - def tp_traverse(obj_addr, callback, args): - # TODO: implement - pass + def tp_traverse(pyobj_ptr, callback, args): + from pypy.module.cpyext.api import PyObject + from pypy.module.cpyext.typeobjectdefs import visitproc + from pypy.module.cpyext.pyobject import cts + # convert to pointers with correct types (PyObject) + callback_addr = llmemory.cast_ptr_to_adr(callback) + callback_ptr = llmemory.cast_adr_to_ptr(callback_addr, + visitproc) + pyobj_addr = llmemory.cast_ptr_to_adr(pyobj_ptr) + pyobj = llmemory.cast_adr_to_ptr(pyobj_addr, PyObject) + + pto = pyobj.c_ob_type + name = rffi.charp2str(cts.cast('char*', pto.c_tp_name)) + print 'traverse PyObject', pyobj, 'of type', name + + # now call tp_traverse (if possible) + if pyobj.c_ob_type and pyobj.c_ob_type.c_tp_traverse: + pyobj.c_ob_type.c_tp_traverse(pyobj, callback_ptr, + args) rawrefcount.init(dealloc_trigger, tp_traverse) else: if space.config.translation.gc == "boehm": @@ -227,12 +264,14 @@ break pyobj = rffi.cast(PyObject, py_obj) - if pyobj.c_ob_type and pyobj.c_ob_type.c_tp_clear: + adr_int = llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(pyobj)) + if pyobj.c_ob_type.c_tp_clear: incref(space, py_obj) pyobj.c_ob_type.c_tp_clear(pyobj) decref(space, py_obj) - if py_obj == rawrefcount.cyclic_garbage_head(PyObject): + head = rawrefcount.cyclic_garbage_head(PyObject) + if adr_int == llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(head)): rawrefcount.cyclic_garbage_remove() class PyObjDeallocAction(executioncontext.AsyncAction): diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -1040,3 +1040,210 @@ module.untrack(f) result = self.in_pygclist(pygchead) assert not result + + def test_gc_collect_simple(self): + """ + Test if a simple collect is working + TODO: make more precise + """ + + if self.runappdirect: + skip('cannot import module with undefined functions') + + init = """ + if (Py_IsInitialized()) { + PyObject* m; + if (PyType_Ready(&CycleType) < 0) + return; + m = Py_InitModule("Cycle", module_methods); + if (m == NULL) + return; + Py_INCREF(&CycleType); + PyModule_AddObject(m, "Cycle", (PyObject *)&CycleType); + } + """ + + body = """ + #include + #include "structmember.h" + #include + #include + typedef struct { + PyObject_HEAD + PyObject *next; + PyObject *val; + } Cycle; + static PyTypeObject CycleType; + static int Cycle_traverse(Cycle *self, visitproc visit, void *arg) + { + printf("traverse begin!\\n"); + int vret; + if (self->next) { + vret = visit(self->next, arg); + if (vret != 0) + return vret; + } + if (self->val) { + vret = visit(self->val, arg); + if (vret != 0) + return vret; + } + printf("traverse end!\\n"); + return 0; + } + static int Cycle_clear(Cycle *self) + { + printf("clear!\\n"); + PyObject *tmp; + tmp = self->next; + self->next = NULL; + Py_XDECREF(tmp); + tmp = self->val; + self->val = NULL; + Py_XDECREF(tmp); + return 0; + } + static void Cycle_dealloc(Cycle* self) + { + printf("dealloc!\\n"); + PyObject_GC_UnTrack(self); + Py_TYPE(self)->tp_free((PyObject*)self); + } + static PyObject* Cycle_new(PyTypeObject *type, PyObject *args, + PyObject *kwds) + { + printf("\\nCycle begin new\\n"); + fflush(stdout); + Cycle *self; + self = PyObject_GC_New(Cycle, type); + if (self != NULL) { + //self->next = PyString_FromString(""); + //if (self->next == NULL) { + // Py_DECREF(self); + // return NULL; + //} + PyObject_GC_Track(self); + printf("\\nCycle tracked: %lx\\n", (Py_ssize_t)self); + printf("\\nCycle refcnt: %lx\\n", (Py_ssize_t)self->ob_refcnt); + printf("\\nCycle pypy_link: %lx\\n", (Py_ssize_t)self->ob_pypy_link); + raise(SIGINT); + } else { + printf("\\nCycle new null\\n"); + } + fflush(stdout); + return (PyObject *)self; + } + static int Cycle_init(Cycle *self, PyObject *args, PyObject *kwds) + { + PyObject *next=NULL, *tmp; + static char *kwlist[] = {"next", NULL}; + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, + &next)) + return -1; + if (next) { + tmp = self->next; + Py_INCREF(next); + self->next = next; + Py_XDECREF(tmp); + } + return 0; + } + static PyMemberDef Cycle_members[] = { + {"next", T_OBJECT_EX, offsetof(Cycle, next), 0, "next"}, + {"val", T_OBJECT_EX, offsetof(Cycle, val), 0, "val"}, + {NULL} /* Sentinel */ + }; + static PyMethodDef Cycle_methods[] = { + {NULL} /* Sentinel */ + }; + static PyTypeObject CycleType = { + PyVarObject_HEAD_INIT(NULL, 0) + "Cycle.Cycle", /* tp_name */ + sizeof(Cycle), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Cycle_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC, /* tp_flags */ + "Cycle objects", /* tp_doc */ + (traverseproc)Cycle_traverse, /* tp_traverse */ + (inquiry)Cycle_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Cycle_methods, /* tp_methods */ + Cycle_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Cycle_init, /* tp_init */ + 0, /* tp_alloc */ + Cycle_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ + }; + + static Cycle *c; + static PyObject * Cycle_cc(Cycle *self, PyObject *val) + { + c = PyObject_GC_New(Cycle, &CycleType); + if (c == NULL) + return NULL; + PyObject_GC_Track(c); + Py_INCREF(val); + c->val = val; // set value + Py_INCREF(c); + c->next = (PyObject *)c; // create self reference + Py_INCREF(Py_None); + return Py_None; + } + static PyObject * Cycle_cd(Cycle *self) + { + Py_DECREF(c); // throw cycle away + Py_INCREF(Py_None); + return Py_None; + } + static PyMethodDef module_methods[] = { + {"createCycle", (PyCFunction)Cycle_cc, METH_OLDARGS, ""}, + {"discardCycle", (PyCFunction)Cycle_cd, METH_NOARGS, ""}, + {NULL} /* Sentinel */ + }; + """ + + module = self.import_module(name='Cycle', init=init, body=body) + + # TODO: The code below will fail as soon as the host GC kicks in the + # test uses the rawrefcount module for object <-> pyobject linking, + # which currently sets an invalid pointer to the object in the + # pyobject's header, which in turn causes the GC to crash (because + # it currently assumes any non-null pointer is a valid pointer and + # tries to follow it). Even with debug_collect. + # + # Solutions - A: set a valid pointer in rawrefcount (best) + # - B: set a special pointer in rawrefcount, + # which will be detected as such in the GC and + # 1) ... handled correctly + # 2) ... always be kept -> floating garbage + # + # Note: As we use the GC of the host, that is running the tests, + # running it on CPython or any other version of PyPy might lead to + # different results. + module.Cycle() + self.debug_collect() \ No newline at end of file diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py --- a/rpython/memory/gc/incminimark.py +++ b/rpython/memory/gc/incminimark.py @@ -3033,6 +3033,8 @@ self.rrc_pyobj_list = self._pygchdr(pyobj_list) self.rrc_pyobj_old_list = \ lltype.malloc(self.PYOBJ_GC_HDR, flavor='raw', immortal=True) + self.rrc_pyobj_old_list.c_gc_next = self.rrc_pyobj_old_list + self.rrc_pyobj_old_list.c_gc_prev = self.rrc_pyobj_old_list self.rrc_pyobj_garbage_list = \ lltype.malloc(self.PYOBJ_GC_HDR, flavor='raw', immortal=True) self.rrc_pyobj_garbage_list.c_gc_next = self.rrc_pyobj_garbage_list @@ -3124,7 +3126,9 @@ next.c_gc_prev = gchdr def rrc_invoke_callback(self): - if self.rrc_enabled and self.rrc_dealloc_pending.non_empty(): + if self.rrc_enabled and (self.rrc_dealloc_pending.non_empty() or + self.rrc_pyobj_garbage_list.c_gc_next <> + self.rrc_pyobj_garbage_list): self.rrc_dealloc_trigger_callback() def rrc_minor_collection_trace(self): @@ -3203,8 +3207,8 @@ if rc >= REFCNT_FROM_PYPY_LIGHT: rc -= REFCNT_FROM_PYPY_LIGHT if rc == 0: - if major: # remove from old list - pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject)) + pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject)) + if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR): next = pygchdr.c_gc_next next.c_gc_prev = pygchdr.c_gc_prev pygchdr.c_gc_prev.c_gc_next = next @@ -3237,6 +3241,7 @@ self._rrc_collect_rawrefcount_roots() self._rrc_mark_rawrefcount() self.rrc_p_list_old.foreach(self._rrc_major_trace, None) + self.rrc_o_list_old.foreach(self._rrc_major_trace, None) # TODO: for all unreachable objects, which are marked potentially # TODO: uncollectable, move them to the set of uncollectable objs @@ -3263,9 +3268,14 @@ # TODO: pypy objects def _rrc_major_trace(self, pyobject, ignore): - rc = self.rrc_pyobj_as_gc(self._pyobj(pyobject)).c_gc_refs + pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject)) + if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR): + rc = pygchdr.c_gc_refs + else: + rc = self._pyobj(pyobject).c_ob_refcnt + if rc == 0: - pass # the corresponding object may die + pass # the corresponding object may die else: # force the corresponding object to be alive intobj = self._pyobj(pyobject).c_ob_pypy_link @@ -3336,6 +3346,7 @@ # For every object in this set, if it is marked, add 1 as a real # refcount self.rrc_p_list_old.foreach(self._rrc_obj_fix_refcnt, None) + self.rrc_o_list_old.foreach(self._rrc_obj_fix_refcnt, None) # Subtract all internal refcounts from the cyclic refcount # of rawrefcounted objects @@ -3351,10 +3362,10 @@ def _rrc_obj_fix_refcnt(self, pyobject, ignore): intobj = self._pyobj(pyobject).c_ob_pypy_link obj = llmemory.cast_int_to_adr(intobj) - # TODO: only if Py_TPFLAGS_HAVE_GC is set gchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject)) - if self.header(obj).tid & (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS): - gchdr.c_gc_refs += 1 + if gchdr <> lltype.nullptr(self.PYOBJ_GC_HDR): + if self.header(obj).tid & (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS): + gchdr.c_gc_refs += 1 def _rrc_mark_rawrefcount(self): if self.rrc_pyobj_list.c_gc_next == self.rrc_pyobj_list: @@ -3421,9 +3432,9 @@ return rffi.cast(rffi.INT_real, 0) def _rrc_visit_action(self, pyobj, ignore): - # TODO: only if Py_TPFLAGS_HAVE_GC is set pygchdr = self.rrc_pyobj_as_gc(pyobj) - pygchdr.c_gc_refs += self.rrc_refcnt_add + if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR): + pygchdr.c_gc_refs += self.rrc_refcnt_add def _rrc_traverse(self, pyobj, refcnt_add): from rpython.rlib.objectmodel import we_are_translated diff --git a/rpython/memory/gc/test/dot/keep_cross_simple.dot b/rpython/memory/gc/test/dot/keep_cross_simple.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/keep_cross_simple.dot @@ -0,0 +1,5 @@ +digraph G { + "a" [type=B, alive=y, rooted=y]; + "b" [type=C, alive=y]; + "a" -> "b"; +} diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py --- a/rpython/memory/gc/test/test_rawrefcount.py +++ b/rpython/memory/gc/test/test_rawrefcount.py @@ -57,7 +57,8 @@ count1 = len(self.trigger) self.gc.rrc_invoke_callback() count2 = len(self.trigger) - assert count2 - count1 == expected_trigger + # TODO: fix assertion + # assert count2 - count1 == expected_trigger def _rawrefcount_addref(self, pyobj_from, pyobj_to): refs = self.pyobj_refs[self.pyobjs.index(pyobj_from)] @@ -208,7 +209,8 @@ py.test.raises(RuntimeError, "r1.c_ob_refcnt") # dead py.test.raises(RuntimeError, "p1.x") # dead self.gc.check_no_more_rawrefcount_state() - assert self.trigger == [] + # TODO: fix assertion + # assert self.trigger == [] assert self.gc.rawrefcount_next_dead() == llmemory.NULL def test_rawrefcount_dies_quickly(self, old=False): @@ -364,7 +366,8 @@ self._collect(major=False) check_alive(0) assert p1.x == 42 - assert self.trigger == [] + # TODO: fix assertion + # assert self.trigger == [] self._collect(major=True, expected_trigger=1) py.test.raises(RuntimeError, "p1.x") # dead assert r1.c_ob_refcnt == 1 @@ -490,46 +493,49 @@ # do collection self.gc.collect() - def decref_children(pyobj): - self.gc.rrc_tp_traverse(pyobj, decref, None) - def decref(pyobj, ignore): - pyobj.c_ob_refcnt -= 1 - if pyobj.c_ob_refcnt == 0: - gchdr = self.gc.rrc_pyobj_as_gc(pyobj) - next = gchdr.c_gc_next - next.c_gc_prev = gchdr.c_gc_prev - gchdr.c_gc_prev.c_gc_next = next - decref_children(pyobj) - self.pyobjs[self.pyobjs.index(pyobj)] = \ - lltype.nullptr(PYOBJ_HDR_PTR.TO) - lltype.free(pyobj, flavor='raw') + self.gc.rrc_invoke_callback() + if self.trigger <> []: + # do cleanup after collection (clear all dead pyobjects) + def decref_children(pyobj): + self.gc.rrc_tp_traverse(pyobj, decref, None) - next_dead = self.gc.rawrefcount_next_dead() - while next_dead <> llmemory.NULL: - pyobj = llmemory.cast_adr_to_ptr(next_dead, self.gc.PYOBJ_HDR_PTR) - decref(pyobj, None) + def decref(pyobj, ignore): + pyobj.c_ob_refcnt -= 1 + if pyobj.c_ob_refcnt == 0: + gchdr = self.gc.rrc_pyobj_as_gc(pyobj) + next = gchdr.c_gc_next + next.c_gc_prev = gchdr.c_gc_prev + gchdr.c_gc_prev.c_gc_next = next + decref_children(pyobj) + self.pyobjs[self.pyobjs.index(pyobj)] = \ + lltype.nullptr(PYOBJ_HDR_PTR.TO) + lltype.free(pyobj, flavor='raw') + next_dead = self.gc.rawrefcount_next_dead() + while next_dead <> llmemory.NULL: + pyobj = llmemory.cast_adr_to_ptr(next_dead, self.gc.PYOBJ_HDR_PTR) + decref(pyobj, None) + next_dead = self.gc.rawrefcount_next_dead() - # free cyclic structures - next_dead = self.gc.rawrefcount_cyclic_garbage_head() - while next_dead <> llmemory.NULL: - pyobj = llmemory.cast_adr_to_ptr(next_dead, self.gc.PYOBJ_HDR_PTR) - pyobj.c_ob_refcnt += 1 + next_dead = self.gc.rawrefcount_cyclic_garbage_head() + while next_dead <> llmemory.NULL: + pyobj = llmemory.cast_adr_to_ptr(next_dead, self.gc.PYOBJ_HDR_PTR) + pyobj.c_ob_refcnt += 1 - def free(pyobj_to, pyobj_from): - refs = self.pyobj_refs[self.pyobjs.index(pyobj_from)] - refs.remove(pyobj_to) - decref(pyobj_to, None) - self.gc.rrc_tp_traverse(pyobj, free, pyobj) + def clear(pyobj_to, pyobj_from): + refs = self.pyobj_refs[self.pyobjs.index(pyobj_from)] + refs.remove(pyobj_to) + decref(pyobj_to, None) + self.gc.rrc_tp_traverse(pyobj, clear, pyobj) - decref(pyobj, None) + decref(pyobj, None) - curr = llmemory.cast_adr_to_int(next_dead) - next_dead = self.gc.rawrefcount_cyclic_garbage_head() + curr = llmemory.cast_adr_to_int(next_dead) + next_dead = self.gc.rawrefcount_cyclic_garbage_head() - if llmemory.cast_adr_to_int(next_dead) == curr: - self.gc.rawrefcount_cyclic_garbage_remove() - next_dead = self.gc.rawrefcount_cyclic_garbage_head() + if llmemory.cast_adr_to_int(next_dead) == curr: + self.gc.rawrefcount_cyclic_garbage_remove() + next_dead = self.gc.rawrefcount_cyclic_garbage_head() # check livelihood of objects, according to graph for name in nodes: diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py --- a/rpython/rlib/rawrefcount.py +++ b/rpython/rlib/rawrefcount.py @@ -132,13 +132,14 @@ @not_rpython def cyclic_garbage_head(OB_PTR_TYPE): - # TODO - return lltype.nullptr(OB_PTR_TYPE.TO) + if len(old_pyobj_list) > 0: + return old_pyobj_list[0] + else: + return lltype.nullptr(OB_PTR_TYPE.TO) @not_rpython def cyclic_garbage_remove(): - # TODO - pass + old_pyobj_list.remove(old_pyobj_list[0]) @not_rpython def _collect(track_allocation=True): @@ -175,6 +176,13 @@ detach(ob, wr_o_list) _o_list = Ellipsis + global _pyobj_list, old_pyobj_list + old_pyobj_list = [] + pyobj_hdr = _pyobj_list.c_gc_next + while pyobj_hdr != _pyobj_list: + old_pyobj_list.append(pyobj_hdr) + pyobj_hdr = pyobj_hdr.c_gc_next + rgc.collect() # forces the cycles to be resolved and the weakrefs to die rgc.collect() rgc.collect() @@ -193,6 +201,7 @@ ob.c_ob_refcnt -= REFCNT_FROM_PYPY_LIGHT ob.c_ob_pypy_link = 0 if ob.c_ob_refcnt == 0: + # TODO: remove from list?! lltype.free(ob, flavor='raw', track_allocation=track_allocation) else: @@ -218,7 +227,13 @@ for ob, wr in wr_o_list: attach(ob, wr, _o_list) - if _d_list: + pyobj_hdr = _pyobj_list.c_gc_next + while pyobj_hdr != _pyobj_list: + if pyobj_hdr in old_pyobj_list: + old_pyobj_list.remove(pyobj_hdr) + pyobj_hdr = pyobj_hdr.c_gc_next + + if _d_list or len(old_pyobj_list) > 0: res = _dealloc_trigger_callback() if res == "RETRY": _collect(track_allocation=track_allocation) diff --git a/rpython/rlib/test/test_rawrefcount.py b/rpython/rlib/test/test_rawrefcount.py --- a/rpython/rlib/test/test_rawrefcount.py +++ b/rpython/rlib/test/test_rawrefcount.py @@ -265,7 +265,7 @@ print "NEXT_DEAD != OB" return 1 if ob.c_ob_refcnt != 1: - print "next_dead().ob_refcnt != 1" + print "next_dead().c_ob_refcnt != 1" return 1 if rawrefcount.next_dead(PyObject) != lltype.nullptr(PyObjectS): print "NEXT_DEAD second time != NULL" diff --git a/rpython/rlib/test/test_rawrefcount_boehm.py b/rpython/rlib/test/test_rawrefcount_boehm.py --- a/rpython/rlib/test/test_rawrefcount_boehm.py +++ b/rpython/rlib/test/test_rawrefcount_boehm.py @@ -283,7 +283,7 @@ ob = rawrefcount.next_dead(PyObject) if not ob: break if ob.c_ob_refcnt != 1: - print "next_dead().ob_refcnt != 1" + print "next_dead().c_ob_refcnt != 1" return 1 deadlist.append(ob) if len(deadlist) == 0: From pypy.commits at gmail.com Mon Feb 4 04:53:58 2019 From: pypy.commits at gmail.com (JonnyBoy) Date: Mon, 04 Feb 2019 01:53:58 -0800 (PST) Subject: [pypy-commit] extradoc extradoc: people.txt edited online with Bitbucket Message-ID: <5c580bb6.1c69fb81.95000.3c62@mx.google.com> Author: John Witulski Branch: extradoc Changeset: r5943:ae84ef77dc4c Date: 2019-02-04 09:53 +0000 http://bitbucket.org/pypy/extradoc/changeset/ae84ef77dc4c/ Log: people.txt edited online with Bitbucket diff --git a/sprintinfo/ddorf2019/people.txt b/sprintinfo/ddorf2019/people.txt --- a/sprintinfo/ddorf2019/people.txt +++ b/sprintinfo/ddorf2019/people.txt @@ -18,7 +18,7 @@ Maciej Fijałkowski Feb 3 - 9 airbnb Łukasz Langa Feb 3 - 9 airbnb Armin Rigo Feb 3 - 9 airbnb -John Witulski ? lives there +John Witulski Feb 3,5,9 lives there Semih Demir ? lives there Stefan Troost ? lives there Ronan Lamy Feb 3 - 8 Hotel Diana From pypy.commits at gmail.com Mon Feb 4 05:18:38 2019 From: pypy.commits at gmail.com (Alexander Schremmer) Date: Mon, 04 Feb 2019 02:18:38 -0800 (PST) Subject: [pypy-commit] pypy math-improvements: Merge default into math-improvements. Message-ID: <5c58117e.1c69fb81.68e44.bf48@mx.google.com> Author: Alexander Schremmer Branch: math-improvements Changeset: r95783:b17fec9f72db Date: 2019-02-04 11:13 +0100 http://bitbucket.org/pypy/pypy/changeset/b17fec9f72db/ Log: Merge default into math-improvements. diff too long, truncating to 2000 out of 13722 lines diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -30,7 +30,7 @@ DEALINGS IN THE SOFTWARE. -PyPy Copyright holders 2003-2018 +PyPy Copyright holders 2003-2019 -------------------------------- Except when otherwise stated (look for LICENSE files or information at @@ -40,16 +40,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -59,8 +59,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -70,10 +70,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -114,12 +114,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -134,8 +134,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -144,10 +145,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -163,6 +164,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -176,6 +178,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -187,7 +190,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -198,7 +200,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -210,6 +211,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -217,12 +219,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -242,7 +246,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -270,12 +273,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -290,10 +296,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -301,28 +309,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -332,6 +338,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -340,6 +347,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -349,8 +357,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -364,7 +373,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -384,12 +392,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz diff --git a/extra_tests/cffi_tests/cffi0/test_ffi_backend.py b/extra_tests/cffi_tests/cffi0/test_ffi_backend.py --- a/extra_tests/cffi_tests/cffi0/test_ffi_backend.py +++ b/extra_tests/cffi_tests/cffi0/test_ffi_backend.py @@ -325,8 +325,31 @@ a = array.array('H', [10000, 20000, 30000]) c = ffi.from_buffer(a) assert ffi.typeof(c) is ffi.typeof("char[]") + assert len(c) == 6 ffi.cast("unsigned short *", c)[1] += 500 assert list(a) == [10000, 20500, 30000] + assert c == ffi.from_buffer("char[]", a, True) + assert c == ffi.from_buffer(a, require_writable=True) + # + c = ffi.from_buffer("unsigned short[]", a) + assert len(c) == 3 + assert c[1] == 20500 + # + p = ffi.from_buffer(b"abcd") + assert p[2] == b"c" + # + assert p == ffi.from_buffer(b"abcd", require_writable=False) + py.test.raises((TypeError, BufferError), ffi.from_buffer, + "char[]", b"abcd", True) + py.test.raises((TypeError, BufferError), ffi.from_buffer, b"abcd", + require_writable=True) + + def test_release(self): + ffi = FFI() + p = ffi.new("int[]", 123) + ffi.release(p) + # here, reading p[0] might give garbage or segfault... + ffi.release(p) # no effect def test_memmove(self): ffi = FFI() diff --git a/extra_tests/cffi_tests/cffi0/test_function.py b/extra_tests/cffi_tests/cffi0/test_function.py --- a/extra_tests/cffi_tests/cffi0/test_function.py +++ b/extra_tests/cffi_tests/cffi0/test_function.py @@ -46,14 +46,15 @@ assert x != math.sin(1.23) # rounding effects assert abs(x - math.sin(1.23)) < 1E-6 - def test_lround_no_return_value(self): + def test_getenv_no_return_value(self): # check that 'void'-returning functions work too ffi = FFI(backend=self.Backend()) ffi.cdef(""" - void lround(double x); + void getenv(char *); """) - m = ffi.dlopen(lib_m) - x = m.lround(1.23) + needs_dlopen_none() + m = ffi.dlopen(None) + x = m.getenv(b"FOO") assert x is None def test_dlopen_filename(self): diff --git a/extra_tests/cffi_tests/cffi1/test_ffi_obj.py b/extra_tests/cffi_tests/cffi1/test_ffi_obj.py --- a/extra_tests/cffi_tests/cffi1/test_ffi_obj.py +++ b/extra_tests/cffi_tests/cffi1/test_ffi_obj.py @@ -239,11 +239,33 @@ def test_ffi_from_buffer(): import array ffi = _cffi1_backend.FFI() - a = array.array('H', [10000, 20000, 30000]) + a = array.array('H', [10000, 20000, 30000, 40000]) c = ffi.from_buffer(a) assert ffi.typeof(c) is ffi.typeof("char[]") + assert len(c) == 8 ffi.cast("unsigned short *", c)[1] += 500 - assert list(a) == [10000, 20500, 30000] + assert list(a) == [10000, 20500, 30000, 40000] + py.test.raises(TypeError, ffi.from_buffer, a, True) + assert c == ffi.from_buffer("char[]", a, True) + assert c == ffi.from_buffer(a, require_writable=True) + # + c = ffi.from_buffer("unsigned short[]", a) + assert len(c) == 4 + assert c[1] == 20500 + # + c = ffi.from_buffer("unsigned short[2][2]", a) + assert len(c) == 2 + assert len(c[0]) == 2 + assert c[0][1] == 20500 + # + p = ffi.from_buffer(b"abcd") + assert p[2] == b"c" + # + assert p == ffi.from_buffer(b"abcd", require_writable=False) + py.test.raises((TypeError, BufferError), ffi.from_buffer, + "char[]", b"abcd", True) + py.test.raises((TypeError, BufferError), ffi.from_buffer, b"abcd", + require_writable=True) def test_memmove(): ffi = _cffi1_backend.FFI() diff --git a/extra_tests/cffi_tests/cffi1/test_new_ffi_1.py b/extra_tests/cffi_tests/cffi1/test_new_ffi_1.py --- a/extra_tests/cffi_tests/cffi1/test_new_ffi_1.py +++ b/extra_tests/cffi_tests/cffi1/test_new_ffi_1.py @@ -1457,6 +1457,35 @@ import gc; gc.collect(); gc.collect(); gc.collect() assert seen == [3] + def test_release(self): + p = ffi.new("int[]", 123) + ffi.release(p) + # here, reading p[0] might give garbage or segfault... + ffi.release(p) # no effect + + def test_release_new_allocator(self): + seen = [] + def myalloc(size): + seen.append(size) + return ffi.new("char[]", b"X" * size) + def myfree(raw): + seen.append(raw) + alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree) + p = alloc2("int[]", 15) + assert seen == [15 * 4] + ffi.release(p) + assert seen == [15 * 4, p] + ffi.release(p) # no effect + assert seen == [15 * 4, p] + # + del seen[:] + p = alloc2("struct ab *") + assert seen == [2 * 4] + ffi.release(p) + assert seen == [2 * 4, p] + ffi.release(p) # no effect + assert seen == [2 * 4, p] + def test_CData_CType(self): assert isinstance(ffi.cast("int", 0), ffi.CData) assert isinstance(ffi.new("int *"), ffi.CData) @@ -1647,14 +1676,6 @@ py.test.raises(TypeError, len, q.a) py.test.raises(TypeError, list, q.a) - def test_from_buffer(self): - import array - a = array.array('H', [10000, 20000, 30000]) - c = ffi.from_buffer(a) - assert ffi.typeof(c) is ffi.typeof("char[]") - ffi.cast("unsigned short *", c)[1] += 500 - assert list(a) == [10000, 20500, 30000] - def test_all_primitives(self): assert set(PRIMITIVE_TO_INDEX) == set([ "char", diff --git a/extra_tests/cffi_tests/cffi1/test_pkgconfig.py b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py new file mode 100644 --- /dev/null +++ b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py @@ -0,0 +1,95 @@ +# Generated by pypy/tool/import_cffi.py +import sys +import subprocess +import py +import cffi.pkgconfig as pkgconfig +from cffi import PkgConfigError + + +def mock_call(libname, flag): + assert libname=="foobarbaz" + flags = { + "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", + "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", + } + return flags[flag] + + +def test_merge_flags(): + d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} + d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} + + pkgconfig.merge_flags(d1, d2) + assert d1 == { + "ham": [1, 2, 3], + "spam" : ["a", "b", "c", "spam", "spam", "spam"], + "bar" : ["b", "a", "z"], + "foo" : []} + + +def test_pkgconfig(): + assert pkgconfig.flags_from_pkgconfig([]) == {} + + saved = pkgconfig.call + try: + pkgconfig.call = mock_call + flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) + finally: + pkgconfig.call = saved + assert flags == { + 'include_dirs': ['/usr/include/python3.6m'], + 'library_dirs': ['/usr/lib64'], + 'libraries': ['python3.6'], + 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], + 'extra_compile_args': ['-O42'], + 'extra_link_args': ['-shared'] + } + +class mock_subprocess: + PIPE = Ellipsis + class Popen: + def __init__(self, cmd, stdout, stderr): + if mock_subprocess.RESULT is None: + raise OSError("oops can't run") + assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] + def communicate(self): + bout, berr, rc = mock_subprocess.RESULT + self.returncode = rc + return bout, berr + +def test_call(): + saved = pkgconfig.subprocess + try: + pkgconfig.subprocess = mock_subprocess + + mock_subprocess.RESULT = None + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "cannot run pkg-config: oops can't run" + + mock_subprocess.RESULT = b"", "Foo error!\n", 1 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "Foo error!" + + mock_subprocess.RESULT = b"abc\\def\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value).startswith("pkg-config --cflags libfoo returned an " + "unsupported backslash-escaped output:") + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + if sys.version_info >= (3,): + mock_subprocess.RESULT = b"\xff\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, + "libfoo", "--cflags", encoding="utf-8") + assert str(e.value) == ( + "pkg-config --cflags libfoo returned bytes that cannot be " + "decoded with encoding 'utf-8':\nb'\\xff\\n'") + + finally: + pkgconfig.subprocess = saved diff --git a/extra_tests/cffi_tests/cffi1/test_recompiler.py b/extra_tests/cffi_tests/cffi1/test_recompiler.py --- a/extra_tests/cffi_tests/cffi1/test_recompiler.py +++ b/extra_tests/cffi_tests/cffi1/test_recompiler.py @@ -5,7 +5,7 @@ from cffi import recompiler from extra_tests.cffi_tests.udir import udir from extra_tests.cffi_tests.support import u, long -from extra_tests.cffi_tests.support import FdWriteCapture, StdErrCapture +from extra_tests.cffi_tests.support import FdWriteCapture, StdErrCapture, _verify try: import importlib @@ -36,7 +36,7 @@ # add '-Werror' to the existing 'extra_compile_args' flags kwds['extra_compile_args'] = (kwds.get('extra_compile_args', []) + ['-Werror']) - return recompiler._verify(ffi, module_name, source, *args, **kwds) + return _verify(ffi, module_name, source, *args, **kwds) def test_set_source_no_slashes(): ffi = FFI() @@ -1539,15 +1539,18 @@ assert (pt.x, pt.y) == (99*500*999, -99*500*999) def test_extern_python_1(): + import warnings ffi = FFI() - ffi.cdef(""" + with warnings.catch_warnings(record=True) as log: + ffi.cdef(""" extern "Python" { int bar(int, int); void baz(int, int); int bok(void); void boz(void); } - """) + """) + assert len(log) == 0, "got a warning: %r" % (log,) lib = verify(ffi, 'test_extern_python_1', """ static void baz(int, int); /* forward */ """) diff --git a/extra_tests/cffi_tests/cffi1/test_verify1.py b/extra_tests/cffi_tests/cffi1/test_verify1.py --- a/extra_tests/cffi_tests/cffi1/test_verify1.py +++ b/extra_tests/cffi_tests/cffi1/test_verify1.py @@ -4,6 +4,7 @@ from cffi import CDefError from cffi import recompiler from extra_tests.cffi_tests.support import * +from extra_tests.cffi_tests.support import _verify import _cffi_backend lib_m = ['m'] @@ -38,9 +39,8 @@ except AttributeError: pass self.set_source(module_name, preamble) - return recompiler._verify(self, module_name, preamble, *args, - extra_compile_args=self._extra_compile_args, - **kwds) + return _verify(self, module_name, preamble, *args, + extra_compile_args=self._extra_compile_args, **kwds) class FFI_warnings_not_error(FFI): _extra_compile_args = [] diff --git a/extra_tests/cffi_tests/support.py b/extra_tests/cffi_tests/support.py --- a/extra_tests/cffi_tests/support.py +++ b/extra_tests/cffi_tests/support.py @@ -62,3 +62,28 @@ def getvalue(self): return self._value + +def _verify(ffi, module_name, preamble, *args, **kwds): + import imp + from cffi.recompiler import recompile + from .udir import udir + assert module_name not in sys.modules, "module name conflict: %r" % ( + module_name,) + kwds.setdefault('tmpdir', str(udir)) + outputfilename = recompile(ffi, module_name, preamble, *args, **kwds) + module = imp.load_dynamic(module_name, outputfilename) + # + # hack hack hack: copy all *bound methods* from module.ffi back to the + # ffi instance. Then calls like ffi.new() will invoke module.ffi.new(). + for name in dir(module.ffi): + if not name.startswith('_'): + attr = getattr(module.ffi, name) + if attr is not getattr(ffi, name, object()): + setattr(ffi, name, attr) + def typeof_disabled(*args, **kwds): + raise NotImplementedError + ffi._typeof = typeof_disabled + for name in dir(ffi): + if not name.startswith('_') and not hasattr(module.ffi, name): + setattr(ffi, name, NotImplemented) + return module.lib diff --git a/pypy/module/test_lib_pypy/ctypes_tests/__init__.py b/extra_tests/ctypes_tests/__init__.py rename from pypy/module/test_lib_pypy/ctypes_tests/__init__.py rename to extra_tests/ctypes_tests/__init__.py diff --git a/pypy/module/test_lib_pypy/ctypes_tests/_ctypes_test.c b/extra_tests/ctypes_tests/_ctypes_test.c rename from pypy/module/test_lib_pypy/ctypes_tests/_ctypes_test.c rename to extra_tests/ctypes_tests/_ctypes_test.c diff --git a/pypy/module/test_lib_pypy/ctypes_tests/conftest.py b/extra_tests/ctypes_tests/conftest.py rename from pypy/module/test_lib_pypy/ctypes_tests/conftest.py rename to extra_tests/ctypes_tests/conftest.py --- a/pypy/module/test_lib_pypy/ctypes_tests/conftest.py +++ b/extra_tests/ctypes_tests/conftest.py @@ -3,10 +3,6 @@ import sys import os -def pytest_ignore_collect(path): - if '__pypy__' not in sys.builtin_module_names: - return True - # XXX: copied from pypy/tool/cpyext/extbuild.py if os.name != 'nt': so_ext = 'so' @@ -85,8 +81,7 @@ return outputfilename # end copy -def compile_so_file(): - udir = pytest.ensuretemp('_ctypes_test') +def compile_so_file(udir): cfile = py.path.local(__file__).dirpath().join("_ctypes_test.c") if sys.platform == 'win32': @@ -96,8 +91,12 @@ return c_compile([cfile], str(udir / '_ctypes_test'), libraries=libraries) -# we need to run after the "tmpdir" plugin which installs pytest.ensuretemp - at pytest.mark.trylast -def pytest_configure(config): - global sofile - sofile = compile_so_file() + at pytest.fixture(scope='session') +def sofile(tmpdir_factory): + udir = tmpdir_factory.mktemp('_ctypes_test') + return str(compile_so_file(udir)) + + at pytest.fixture +def dll(sofile): + from ctypes import CDLL + return CDLL(str(sofile)) diff --git a/pypy/module/test_lib_pypy/ctypes_tests/support.py b/extra_tests/ctypes_tests/support.py rename from pypy/module/test_lib_pypy/ctypes_tests/support.py rename to extra_tests/ctypes_tests/support.py diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_anon.py b/extra_tests/ctypes_tests/test_anon.py rename from pypy/module/test_lib_pypy/ctypes_tests/test_anon.py rename to extra_tests/ctypes_tests/test_anon.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_anon.py +++ b/extra_tests/ctypes_tests/test_anon.py @@ -1,86 +1,55 @@ import pytest from ctypes import * -from .support import BaseCTypesTestChecker -class TestAnon(BaseCTypesTestChecker): + at pytest.mark.pypy_only +def test_nested(): + class ANON_S(Structure): + _fields_ = [("a", c_int)] - def test_anon(self): - class ANON(Union): - _fields_ = [("a", c_int), - ("b", c_int)] + class ANON_U(Union): + _fields_ = [("_", ANON_S), + ("b", c_int)] + _anonymous_ = ["_"] - class Y(Structure): - _fields_ = [("x", c_int), - ("_", ANON), - ("y", c_int)] - _anonymous_ = ["_"] + class Y(Structure): + _fields_ = [("x", c_int), + ("_", ANON_U), + ("y", c_int)] + _anonymous_ = ["_"] - assert Y.a.offset == sizeof(c_int) - assert Y.b.offset == sizeof(c_int) + assert Y.x.offset == 0 + assert Y.a.offset == sizeof(c_int) + assert Y.b.offset == sizeof(c_int) + assert Y._.offset == sizeof(c_int) + assert Y.y.offset == sizeof(c_int) * 2 - assert ANON.a.offset == 0 - assert ANON.b.offset == 0 + assert Y._names_ == ['x', 'a', 'b', 'y'] - def test_anon_nonseq(self): - # TypeError: _anonymous_ must be a sequence - with pytest.raises(TypeError): - type(Structure)( - "Name", (Structure,), {"_fields_": [], "_anonymous_": 42}) +def test_anonymous_fields_on_instance(): + # this is about the *instance-level* access of anonymous fields, + # which you'd guess is the most common, but used not to work + # (issue #2230) - def test_anon_nonmember(self): - # AttributeError: type object 'Name' has no attribute 'x' - with pytest.raises(AttributeError): - type(Structure)( - "Name", (Structure,), {"_fields_": [], "_anonymous_": ["x"]}) + class B(Structure): + _fields_ = [("x", c_int), ("y", c_int), ("z", c_int)] + class A(Structure): + _anonymous_ = ["b"] + _fields_ = [("b", B)] - def test_nested(self): - class ANON_S(Structure): - _fields_ = [("a", c_int)] + a = A() + a.x = 5 + assert a.x == 5 + assert a.b.x == 5 + a.b.x += 1 + assert a.x == 6 - class ANON_U(Union): - _fields_ = [("_", ANON_S), - ("b", c_int)] - _anonymous_ = ["_"] + class C(Structure): + _anonymous_ = ["a"] + _fields_ = [("v", c_int), ("a", A)] - class Y(Structure): - _fields_ = [("x", c_int), - ("_", ANON_U), - ("y", c_int)] - _anonymous_ = ["_"] - - assert Y.x.offset == 0 - assert Y.a.offset == sizeof(c_int) - assert Y.b.offset == sizeof(c_int) - assert Y._.offset == sizeof(c_int) - assert Y.y.offset == sizeof(c_int) * 2 - - assert Y._names_ == ['x', 'a', 'b', 'y'] - - def test_anonymous_fields_on_instance(self): - # this is about the *instance-level* access of anonymous fields, - # which you'd guess is the most common, but used not to work - # (issue #2230) - - class B(Structure): - _fields_ = [("x", c_int), ("y", c_int), ("z", c_int)] - class A(Structure): - _anonymous_ = ["b"] - _fields_ = [("b", B)] - - a = A() - a.x = 5 - assert a.x == 5 - assert a.b.x == 5 - a.b.x += 1 - assert a.x == 6 - - class C(Structure): - _anonymous_ = ["a"] - _fields_ = [("v", c_int), ("a", A)] - - c = C() - c.v = 3 - c.y = -8 - assert c.v == 3 - assert c.y == c.a.y == c.a.b.y == -8 - assert not hasattr(c, 'b') + c = C() + c.v = 3 + c.y = -8 + assert c.v == 3 + assert c.y == c.a.y == c.a.b.y == -8 + assert not hasattr(c, 'b') diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_array.py b/extra_tests/ctypes_tests/test_array.py rename from pypy/module/test_lib_pypy/ctypes_tests/test_array.py rename to extra_tests/ctypes_tests/test_array.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_array.py +++ b/extra_tests/ctypes_tests/test_array.py @@ -1,177 +1,64 @@ import pytest from ctypes import * -from .support import BaseCTypesTestChecker -formats = "bBhHiIlLqQfd" +def test_slice(): + values = list(range(5)) + numarray = c_int * 5 -formats = c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, \ - c_long, c_ulonglong, c_float, c_double + na = numarray(*(c_int(x) for x in values)) -class TestArray(BaseCTypesTestChecker): - def test_simple(self): - # create classes holding simple numeric types, and check - # various properties. + assert list(na[0:0]) == [] + assert list(na[:]) == values + assert list(na[:10]) == values - init = range(15, 25) +def test_init_again(): + sz = (c_char * 3)() + addr1 = addressof(sz) + sz.__init__(*b"foo") + addr2 = addressof(sz) + assert addr1 == addr2 - for fmt in formats: - alen = len(init) - int_array = ARRAY(fmt, alen) +def test_array_of_structures(): + class X(Structure): + _fields_ = [('x', c_int), ('y', c_int)] - ia = int_array(*init) - # length of instance ok? - assert len(ia) == alen + Y = X * 2 + y = Y() + x = X() + x.y = 3 + y[1] = x + assert y[1].y == 3 - # slot values ok? - values = [ia[i] for i in range(len(init))] - assert values == init +def test_output_simple(): + A = c_char * 10 + TP = POINTER(A) + x = TP(A()) + assert x[0] != b'' - # change the items - from operator import setitem - new_values = range(42, 42+alen) - [setitem(ia, n, new_values[n]) for n in range(alen)] - values = [ia[i] for i in range(len(init))] - assert values == new_values + A = c_wchar * 10 + TP = POINTER(A) + x = TP(A()) + assert x[0] != b'' - # are the items initialized to 0? - ia = int_array() - values = [ia[i] for i in range(len(init))] - assert values == [0] * len(init) +def test_output_simple_array(): + A = c_char * 10 + AA = A * 10 + aa = AA() + assert aa[0] != b'' - # Too many in itializers should be caught - with pytest.raises(IndexError): - int_array(*range(alen*2)) +def test_output_complex_test(): + class Car(Structure): + _fields_ = [("brand", c_char * 10), + ("speed", c_float), + ("owner", c_char * 10)] - CharArray = ARRAY(c_char, 3) + assert isinstance(Car(b"abcdefghi", 42.0, b"12345").brand, bytes) + assert Car(b"abcdefghi", 42.0, b"12345").brand == b"abcdefghi" + assert Car(b"abcdefghio", 42.0, b"12345").brand == b"abcdefghio" + with pytest.raises(ValueError): + Car(b"abcdefghiop", 42.0, b"12345") - ca = CharArray("a", "b", "c") - - # Should this work? It doesn't: - # CharArray("abc") - with pytest.raises(TypeError): - CharArray("abc") - - assert ca[0] == "a" - assert ca[1] == "b" - assert ca[2] == "c" - assert ca[-3] == "a" - assert ca[-2] == "b" - assert ca[-1] == "c" - - assert len(ca) == 3 - - # slicing is now supported, but not extended slicing (3-argument)! - from operator import getslice, delitem - with pytest.raises(TypeError): - getslice(ca, 0, 1, -1) - - # cannot delete items - with pytest.raises(TypeError): - delitem(ca, 0) - - def test_numeric_arrays(self): - - alen = 5 - - numarray = ARRAY(c_int, alen) - - na = numarray() - values = [na[i] for i in range(alen)] - assert values == [0] * alen - - na = numarray(*[c_int()] * alen) - values = [na[i] for i in range(alen)] - assert values == [0]*alen - - na = numarray(1, 2, 3, 4, 5) - values = [i for i in na] - assert values == [1, 2, 3, 4, 5] - - na = numarray(*map(c_int, (1, 2, 3, 4, 5))) - values = [i for i in na] - assert values == [1, 2, 3, 4, 5] - - def test_slice(self): - values = range(5) - numarray = c_int * 5 - - na = numarray(*(c_int(x) for x in values)) - - assert list(na[0:0]) == [] - assert list(na[:]) == values - assert list(na[:10]) == values - - def test_classcache(self): - assert not ARRAY(c_int, 3) is ARRAY(c_int, 4) - assert ARRAY(c_int, 3) is ARRAY(c_int, 3) - - def test_from_address(self): - # Failed with 0.9.8, reported by JUrner - p = create_string_buffer("foo") - sz = (c_char * 3).from_address(addressof(p)) - assert sz[:] == "foo" - assert sz.value == "foo" - - def test_init_again(self): - sz = (c_char * 3)() - addr1 = addressof(sz) - sz.__init__(*"foo") - addr2 = addressof(sz) - assert addr1 == addr2 - - try: - create_unicode_buffer - except NameError: - pass - else: - def test_from_addressW(self): - p = create_unicode_buffer("foo") - sz = (c_wchar * 3).from_address(addressof(p)) - assert sz[:] == "foo" - assert sz.value == "foo" - -class TestSophisticatedThings(BaseCTypesTestChecker): - def test_array_of_structures(self): - class X(Structure): - _fields_ = [('x', c_int), ('y', c_int)] - - Y = X * 2 - y = Y() - x = X() - x.y = 3 - y[1] = x - assert y[1].y == 3 - - def test_output_simple(self): - A = c_char * 10 - TP = POINTER(A) - x = TP(A()) - assert x[0] != '' - - A = c_wchar * 10 - TP = POINTER(A) - x = TP(A()) - assert x[0] != '' - - def test_output_simple_array(self): - A = c_char * 10 - AA = A * 10 - aa = AA() - assert aa[0] != '' - - def test_output_complex_test(self): - class Car(Structure): - _fields_ = [("brand", c_char * 10), - ("speed", c_float), - ("owner", c_char * 10)] - - assert isinstance(Car("abcdefghi", 42.0, "12345").brand, bytes) - assert Car("abcdefghi", 42.0, "12345").brand == "abcdefghi" - assert Car("abcdefghio", 42.0, "12345").brand == "abcdefghio" - with pytest.raises(ValueError): - Car("abcdefghiop", 42.0, "12345") - - A = Car._fields_[2][1] - TP = POINTER(A) - x = TP(A()) - assert x[0] != '' + A = Car._fields_[2][1] + TP = POINTER(A) + x = TP(A()) + assert x[0] != b'' diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_base.py b/extra_tests/ctypes_tests/test_base.py rename from pypy/module/test_lib_pypy/ctypes_tests/test_base.py rename to extra_tests/ctypes_tests/test_base.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_base.py +++ b/extra_tests/ctypes_tests/test_base.py @@ -1,26 +1,24 @@ -from .support import WhiteBoxTests - +import pytest from ctypes import * -# WhiteBoxTests +pytestmark = pytest.mark.pypy_only -class TestCTypesBase(WhiteBoxTests): - def test_pointer(self): - p = pointer(pointer(c_int(2))) - x = p[0] - assert x._base is p +def test_pointer(): + p = pointer(pointer(c_int(2))) + x = p[0] + assert x._base is p - def test_structure(self): - class X(Structure): - _fields_ = [('x', POINTER(c_int)), - ('y', POINTER(c_int))] +def test_structure(): + class X(Structure): + _fields_ = [('x', POINTER(c_int)), + ('y', POINTER(c_int))] - x = X() - assert x.y._base is x - assert x.y._index == 1 + x = X() + assert x.y._base is x + assert x.y._index == 1 - def test_array(self): - X = POINTER(c_int) * 24 - x = X() - assert x[16]._base is x - assert x[16]._index == 16 +def test_array(): + X = POINTER(c_int) * 24 + x = X() + assert x[16]._base is x + assert x[16]._index == 16 diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_bitfields.py b/extra_tests/ctypes_tests/test_bitfields.py rename from pypy/module/test_lib_pypy/ctypes_tests/test_bitfields.py rename to extra_tests/ctypes_tests/test_bitfields.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_bitfields.py +++ b/extra_tests/ctypes_tests/test_bitfields.py @@ -1,249 +1,19 @@ import pytest from ctypes import * -from .support import BaseCTypesTestChecker -import os -import ctypes -signed_int_types = (c_byte, c_short, c_int, c_long, c_longlong) -unsigned_int_types = (c_ubyte, c_ushort, c_uint, c_ulong, c_ulonglong) -int_types = unsigned_int_types + signed_int_types +def test_set_fields_attr(): + class A(Structure): + pass + A._fields_ = [("a", c_byte), ("b", c_ubyte)] +def test_set_fields_attr_bitfields(): + class A(Structure): + pass + A._fields_ = [("a", POINTER(A)), ("b", c_ubyte, 4)] -def setup_module(mod): - import conftest - _ctypes_test = str(conftest.sofile) - func = CDLL(_ctypes_test).unpack_bitfields - func.argtypes = POINTER(BITS), c_char - mod.func = func - - -class BITS(Structure): - _fields_ = [("A", c_int, 1), - ("B", c_int, 2), - ("C", c_int, 3), - ("D", c_int, 4), - ("E", c_int, 5), - ("F", c_int, 6), - ("G", c_int, 7), - ("H", c_int, 8), - ("I", c_int, 9), - - ("M", c_short, 1), - ("N", c_short, 2), - ("O", c_short, 3), - ("P", c_short, 4), - ("Q", c_short, 5), - ("R", c_short, 6), - ("S", c_short, 7)] - - -class TestC: - def test_ints(self): - for i in range(512): - for name in "ABCDEFGHI": - b = BITS() - setattr(b, name, i) - assert (name, i, getattr(b, name)) == (name, i, func(byref(b), name)) - - def test_shorts(self): - for i in range(256): - for name in "MNOPQRS": - b = BITS() - setattr(b, name, i) - assert (name, i, getattr(b, name)) == (name, i, func(byref(b), name)) - - -class TestBitField: - def test_longlong(self): - class X(Structure): - _fields_ = [("a", c_longlong, 1), - ("b", c_longlong, 62), - ("c", c_longlong, 1)] - - assert sizeof(X) == sizeof(c_longlong) - x = X() - x.a, x.b, x.c = -1, 7, -1 - assert (x.a, x.b, x.c) == (-1, 7, -1) - - x = X() - x.a, x.b, x.c = -1, -7, -1 - assert (x.a, x.b, x.c) == (-1, -7, -1) - - def test_ulonglong(self): - class X(Structure): - _fields_ = [("a", c_ulonglong, 1), - ("b", c_ulonglong, 62), - ("c", c_ulonglong, 1)] - - assert sizeof(X) == sizeof(c_longlong) - x = X() - assert (x.a, x.b, x.c) == (0, 0, 0) - x.a, x.b, x.c = 7, 2305843009213693953, 7 - assert (x.a, x.b, x.c) == (1, 2305843009213693953, 1) - - def test_signed(self): - for c_typ in signed_int_types: - class X(Structure): - _fields_ = [("dummy", c_typ), - ("a", c_typ, 3), - ("b", c_typ, 3), - ("c", c_typ, 1)] - assert sizeof(X) == sizeof(c_typ)*2 - - x = X() - assert (c_typ, x.a, x.b, x.c) == (c_typ, 0, 0, 0) - x.a = -1 - assert (c_typ, x.a, x.b, x.c) == (c_typ, -1, 0, 0) - x.a, x.b = 0, -1 - assert (c_typ, x.a, x.b, x.c) == (c_typ, 0, -1, 0) - - def test_unsigned(self): - for c_typ in unsigned_int_types: - class X(Structure): - _fields_ = [("a", c_typ, 3), - ("b", c_typ, 3), - ("c", c_typ, 1)] - assert sizeof(X) == sizeof(c_typ) - - x = X() - assert (c_typ, x.a, x.b, x.c) == (c_typ, 0, 0, 0) - x.a = -1 - assert (c_typ, x.a, x.b, x.c) == (c_typ, 7, 0, 0) - x.a, x.b = 0, -1 - assert (c_typ, x.a, x.b, x.c) == (c_typ, 0, 7, 0) - - def fail_fields(self, *fields): - return self.get_except(type(Structure), "X", (), - {"_fields_": fields}) - - def test_nonint_types(self): - # bit fields are not allowed on non-integer types. - result = self.fail_fields(("a", c_char_p, 1)) - assert result == (TypeError, 'bit fields not allowed for type c_char_p') - - result = self.fail_fields(("a", c_void_p, 1)) - assert result == (TypeError, 'bit fields not allowed for type c_void_p') - - if c_int != c_long: - result = self.fail_fields(("a", POINTER(c_int), 1)) - assert result == (TypeError, 'bit fields not allowed for type LP_c_int') - - result = self.fail_fields(("a", c_char, 1)) - assert result == (TypeError, 'bit fields not allowed for type c_char') - - try: - c_wchar - except NameError: - pass - else: - result = self.fail_fields(("a", c_wchar, 1)) - assert result == (TypeError, 'bit fields not allowed for type c_wchar') - - class Dummy(Structure): - _fields_ = [] - - result = self.fail_fields(("a", Dummy, 1)) - assert result == (TypeError, 'bit fields not allowed for type Dummy') - - def test_single_bitfield_size(self): - for c_typ in int_types: - result = self.fail_fields(("a", c_typ, -1)) - assert result == (ValueError, 'number of bits invalid for bit field') - - result = self.fail_fields(("a", c_typ, 0)) - assert result == (ValueError, 'number of bits invalid for bit field') - - class X(Structure): - _fields_ = [("a", c_typ, 1)] - assert sizeof(X) == sizeof(c_typ) - - class X(Structure): - _fields_ = [("a", c_typ, sizeof(c_typ)*8)] - assert sizeof(X) == sizeof(c_typ) - - result = self.fail_fields(("a", c_typ, sizeof(c_typ)*8 + 1)) - assert result == (ValueError, 'number of bits invalid for bit field') - - def test_multi_bitfields_size(self): - class X(Structure): - _fields_ = [("a", c_short, 1), - ("b", c_short, 14), - ("c", c_short, 1)] - assert sizeof(X) == sizeof(c_short) - - class X(Structure): - _fields_ = [("a", c_short, 1), - ("a1", c_short), - ("b", c_short, 14), - ("c", c_short, 1)] - assert sizeof(X) == sizeof(c_short)*3 - assert X.a.offset == 0 - assert X.a1.offset == sizeof(c_short) - assert X.b.offset == sizeof(c_short)*2 - assert X.c.offset == sizeof(c_short)*2 - - class X(Structure): - _fields_ = [("a", c_short, 3), - ("b", c_short, 14), - ("c", c_short, 14)] - assert sizeof(X) == sizeof(c_short)*3 - assert X.a.offset == sizeof(c_short)*0 - assert X.b.offset == sizeof(c_short)*1 - assert X.c.offset == sizeof(c_short)*2 - - def get_except(self, func, *args, **kw): - try: - func(*args, **kw) - except Exception as detail: - import traceback - traceback.print_exc() - return detail.__class__, str(detail) - - def test_mixed_1(self): - class X(Structure): - _fields_ = [("a", c_byte, 4), - ("b", c_int, 4)] - if os.name in ("nt", "ce"): - assert sizeof(X) == sizeof(c_int)*2 - else: - assert sizeof(X) == sizeof(c_int) - - def test_mixed_2(self): - class X(Structure): - _fields_ = [("a", c_byte, 4), - ("b", c_int, 32)] - assert sizeof(X) == sizeof(c_int)*2 - - def test_mixed_3(self): - class X(Structure): - _fields_ = [("a", c_byte, 4), - ("b", c_ubyte, 4)] - assert sizeof(X) == sizeof(c_byte) - - def test_anon_bitfields(self): - # anonymous bit-fields gave a strange error message - class X(Structure): - _fields_ = [("a", c_byte, 4), - ("b", c_ubyte, 4)] - class Y(Structure): - _anonymous_ = ["_"] - _fields_ = [("_", X)] - - def test_set_fields_attr(self): - class A(Structure): - pass - A._fields_ = [("a", c_byte), - ("b", c_ubyte)] - - def test_set_fields_attr_bitfields(self): - class A(Structure): - pass - A._fields_ = [("a", POINTER(A)), - ("b", c_ubyte, 4)] - - def test_set_fields_cycle_fails(self): - class A(Structure): - pass - with pytest.raises(AttributeError): - A._fields_ = [("a", A)] +def test_set_fields_cycle_fails(): + class A(Structure): + pass + with pytest.raises(AttributeError): + A._fields_ = [("a", A)] diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_buffers.py b/extra_tests/ctypes_tests/test_buffers.py rename from pypy/module/test_lib_pypy/ctypes_tests/test_buffers.py rename to extra_tests/ctypes_tests/test_buffers.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_buffers.py +++ b/extra_tests/ctypes_tests/test_buffers.py @@ -1,76 +1,38 @@ from ctypes import * -from .support import BaseCTypesTestChecker -class TestStringBuffer(BaseCTypesTestChecker): +def test_buffer(): + b = create_string_buffer(32) + assert len(b) == 32 + assert sizeof(b) == 32 * sizeof(c_char) + assert type(b[0]) is str - def test_buffer(self): - b = create_string_buffer(32) - assert len(b) == 32 - assert sizeof(b) == 32 * sizeof(c_char) - assert type(b[0]) is str + b = create_string_buffer(33L) + assert len(b) == 33 + assert sizeof(b) == 33 * sizeof(c_char) + assert type(b[0]) is str - b = create_string_buffer(33L) - assert len(b) == 33 - assert sizeof(b) == 33 * sizeof(c_char) - assert type(b[0]) is str + b = create_string_buffer(b"abc") + assert len(b) == 4 # trailing nul char + assert sizeof(b) == 4 * sizeof(c_char) + assert type(b[0]) is str + assert b[0] == b"a" + assert b[:] == b"abc\0" - b = create_string_buffer("abc") - assert len(b) == 4 # trailing nul char - assert sizeof(b) == 4 * sizeof(c_char) - assert type(b[0]) is str - assert b[0] == "a" - assert b[:] == "abc\0" +def test_from_buffer(): + b1 = bytearray(b"abcde") + b = (c_char * 5).from_buffer(b1) + assert b[2] == b"c" + # + b1 = bytearray(b"abcd") + b = c_int.from_buffer(b1) + assert b.value in (1684234849, # little endian + 1633837924) # big endian - def test_string_conversion(self): - b = create_string_buffer(u"abc") - assert len(b) == 4 # trailing nul char - assert sizeof(b) == 4 * sizeof(c_char) - assert type(b[0]) is str - assert b[0] == "a" - assert b[:] == "abc\0" - - def test_from_buffer(self): - b1 = bytearray("abcde") - b = (c_char * 5).from_buffer(b1) - assert b[2] == "c" - # - b1 = bytearray("abcd") - b = c_int.from_buffer(b1) - assert b.value in (1684234849, # little endian - 1633837924) # big endian - - def test_from_buffer_keepalive(self): - # Issue #2878 - b1 = bytearray("ab") - array = (c_uint16 * 32)() - array[6] = c_uint16.from_buffer(b1) - # this is also what we get on CPython. I don't think it makes - # sense because the array contains just a copy of the number. - assert array._objects == {'6': b1} - - try: - c_wchar - except NameError: - pass - else: - def test_unicode_buffer(self): - b = create_unicode_buffer(32) - assert len(b) == 32 - assert sizeof(b) == 32 * sizeof(c_wchar) - assert type(b[0]) is unicode - - b = create_unicode_buffer(u"abc") - assert len(b) == 4 # trailing nul char - assert sizeof(b) == 4 * sizeof(c_wchar) - assert type(b[0]) is unicode - assert b[0] == u"a" - assert b[:] == "abc\0" - - def test_unicode_conversion(self): - b = create_unicode_buffer("abc") - assert len(b) == 4 # trailing nul char - assert sizeof(b) == 4 * sizeof(c_wchar) - assert type(b[0]) is unicode - assert b[0] == u"a" - assert b[:] == "abc\0" - +def test_from_buffer_keepalive(): + # Issue #2878 + b1 = bytearray(b"ab") + array = (c_uint16 * 32)() + array[6] = c_uint16.from_buffer(b1) + # this is also what we get on CPython. I don't think it makes + # sense because the array contains just a copy of the number. + assert array._objects == {'6': b1} diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_callback_traceback.py b/extra_tests/ctypes_tests/test_callback_traceback.py rename from pypy/module/test_lib_pypy/ctypes_tests/test_callback_traceback.py rename to extra_tests/ctypes_tests/test_callback_traceback.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_callback_traceback.py +++ b/extra_tests/ctypes_tests/test_callback_traceback.py @@ -1,80 +1,35 @@ # derived from test_random_things.py -import py +import pytest + from ctypes import * -import sys -def callback_func(arg): - 42 / arg - raise ValueError(arg) +_rawffi = pytest.importorskip('_rawffi') -class TestCallbackTraceback: - # When an exception is raised in a ctypes callback function, the C - # code prints a traceback. +# +# This test makes sure the exception types *and* the exception +# value is printed correctly. + + at pytest.mark.skipif("sys.flags.inspect") +def test_SystemExit(monkeypatch, capsys): + """ + When an exception is raised in a ctypes callback function, the C + code prints a traceback. When SystemExit is raised, the interpreter + normally exits immediately. + """ + def callback_func(arg): + raise SystemExit(42) + def custom_exit(value): + raise Exception("<<>>" % (value,)) + monkeypatch.setattr(_rawffi, 'exit', custom_exit) + cb = CFUNCTYPE(c_int, c_int)(callback_func) + cb2 = cast(cast(cb, c_void_p), CFUNCTYPE(c_int, c_int)) + out, err = capsys.readouterr() + assert not err + cb2(0) + out, err = capsys.readouterr() + assert err.splitlines()[-1] == "Exception: <<>>" # - # This test makes sure the exception types *and* the exception - # value is printed correctly. - # - # Changed in 0.9.3: No longer is '(in callback)' prepended to the - # error message - instead a additional frame for the C code is - # created, then a full traceback printed. When SystemExit is - # raised in a callback function, the interpreter exits. - - def capture_stderr(self, func, *args, **kw): - # helper - call function 'func', and return the captured stderr - import StringIO - old_stderr = sys.stderr - logger = sys.stderr = StringIO.StringIO() - try: - func(*args, **kw) - finally: - sys.stderr = old_stderr - return logger.getvalue() - - def test_ValueError(self): - cb = CFUNCTYPE(c_int, c_int)(callback_func) - out = self.capture_stderr(cb, 42) - assert out.splitlines()[-1] == ( - "ValueError: 42") - - def test_IntegerDivisionError(self): - cb = CFUNCTYPE(c_int, c_int)(callback_func) - out = self.capture_stderr(cb, 0) - assert out.splitlines()[-1][:19] == ( - "ZeroDivisionError: ") - - def test_FloatDivisionError(self): - cb = CFUNCTYPE(c_int, c_double)(callback_func) - out = self.capture_stderr(cb, 0.0) - assert out.splitlines()[-1][:19] == ( - "ZeroDivisionError: ") - - def test_TypeErrorDivisionError(self): - cb = CFUNCTYPE(c_int, c_char_p)(callback_func) - out = self.capture_stderr(cb, "spam") - assert out.splitlines()[-1].startswith( - "TypeError: " - "unsupported operand type(s) for") - - def test_SystemExit(self): - import _rawffi - if sys.flags.inspect: - skip("requires sys.flags.inspect == 0") - def callback_func(arg): - raise SystemExit(42) - def custom_exit(value): - raise Exception("<<>>" % (value,)) - original_exit = _rawffi.exit - try: - _rawffi.exit = custom_exit - # - cb = CFUNCTYPE(c_int, c_int)(callback_func) - cb2 = cast(cast(cb, c_void_p), CFUNCTYPE(c_int, c_int)) - out = self.capture_stderr(cb2, 0) - assert out.splitlines()[-1] == "Exception: <<>>" - # - cb = CFUNCTYPE(c_int, c_int)(callback_func) - out = self.capture_stderr(cb, 0) - assert out.splitlines()[-1] == "Exception: <<>>" - # - finally: - _rawffi.exit = original_exit + cb = CFUNCTYPE(c_int, c_int)(callback_func) + cb(0) + out, err = capsys.readouterr() + assert err.splitlines()[-1] == "Exception: <<>>" diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_callbacks.py b/extra_tests/ctypes_tests/test_callbacks.py rename from pypy/module/test_lib_pypy/ctypes_tests/test_callbacks.py rename to extra_tests/ctypes_tests/test_callbacks.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_callbacks.py +++ b/extra_tests/ctypes_tests/test_callbacks.py @@ -1,283 +1,194 @@ +import pytest + +import math from ctypes import * -import pytest from .support import BaseCTypesTestChecker -class TestCallbacks(BaseCTypesTestChecker): - functype = CFUNCTYPE - -## def tearDown(self): -## import gc -## gc.collect() - - def callback(self, *args): - self.got_args = args - return args[-1] - - def check_type(self, typ, arg): - unwrapped_types = { - c_float: (float,), - c_double: (float,), - c_char: (str,), - c_char_p: (str,), - c_uint: (int, long), - c_ulong: (int, long), - } - - PROTO = self.functype.im_func(typ, typ) - cfunc = PROTO(self.callback) - result = cfunc(arg) - if typ == c_float: - assert abs(result - arg) < 0.000001 - else: - assert self.got_args == (arg,) - assert result == arg - - result2 = cfunc(typ(arg)) - assert type(result2) in unwrapped_types.get(typ, (int, long)) - - PROTO = self.functype.im_func(typ, c_byte, typ) - result = PROTO(self.callback)(-3, arg) - if typ == c_float: - assert abs(result - arg) < 0.000001 - else: - assert self.got_args == (-3, arg) - assert result == arg - - ################ - - def test_byte(self): - self.check_type(c_byte, 42) - self.check_type(c_byte, -42) - - def test_ubyte(self): - self.check_type(c_ubyte, 42) - - def test_short(self): - self.check_type(c_short, 42) - self.check_type(c_short, -42) - - def test_ushort(self): - self.check_type(c_ushort, 42) - - def test_int(self): - self.check_type(c_int, 42) - self.check_type(c_int, -42) - - def test_uint(self): - self.check_type(c_uint, 42) - - def test_long(self): - self.check_type(c_long, 42) - self.check_type(c_long, -42) - - def test_ulong(self): - self.check_type(c_ulong, 42) - - def test_longlong(self): - self.check_type(c_longlong, 42) - self.check_type(c_longlong, -42) - - def test_ulonglong(self): - self.check_type(c_ulonglong, 42) - - def test_float(self): - # only almost equal: double -> float -> double - import math - self.check_type(c_float, math.e) - self.check_type(c_float, -math.e) - - def test_double(self): - self.check_type(c_double, 3.14) - self.check_type(c_double, -3.14) - - def test_char(self): - self.check_type(c_char, "x") - self.check_type(c_char, "a") - - # disabled: would now (correctly) raise a RuntimeWarning about - # a memory leak. A callback function cannot return a non-integral - # C type without causing a memory leak. -## def test_char_p(self): -## self.check_type(c_char_p, "abc") -## self.check_type(c_char_p, "def") - - - @pytest.mark.xfail( - reason="we are less strict about callback return type sanity") - def test_unsupported_restype_1(self): - # Only "fundamental" result types are supported for callback - # functions, the type must have a non-NULL stgdict->setfunc. - # POINTER(c_double), for example, is not supported. - - prototype = self.functype.im_func(POINTER(c_double)) - # The type is checked when the prototype is called - with pytest.raises(TypeError): - prototype(lambda: None) - +functypes = [CFUNCTYPE] try: - WINFUNCTYPE + functypes.append(WINFUNCTYPE) except NameError: pass -else: - class TestStdcallCallbacks(TestCallbacks): - functype = WINFUNCTYPE -################################################################ -class TestSampleCallbacks(BaseCTypesTestChecker): +def callback(*args): + callback.got_args = args + return args[-1] - def test_integrate(self): - # Derived from some then non-working code, posted by David Foster - import conftest - _ctypes_test = str(conftest.sofile) - dll = CDLL(_ctypes_test) +unwrapped_types = { + c_float: (float,), + c_double: (float,), + c_char: (str,), + c_char_p: (str,), + c_uint: (int, long), + c_ulong: (int, long), + } - # The function prototype called by 'integrate': double func(double); - CALLBACK = CFUNCTYPE(c_double, c_double) + at pytest.mark.parametrize("typ, arg", [ + (c_byte, 42), + (c_byte, -42), + (c_ubyte, 42), + (c_short, 42), + (c_short, -42), + (c_ushort, 42), + (c_int, 42), + (c_int, -42), + (c_uint, 42), + (c_long, 42), + (c_long, -42), + (c_ulong, 42), + (c_longlong, 42), + (c_longlong, -42), + (c_ulonglong, 42), + (c_float, math.e), # only almost equal: double -> float -> double + (c_float, -math.e), + (c_double, 3.14), + (c_double, -3.14), + (c_char, b"x"), + (c_char, b"a"), +]) + at pytest.mark.parametrize('functype', functypes) +def test_types(typ, arg, functype): + PROTO = functype(typ, typ) + cfunc = PROTO(callback) + result = cfunc(arg) + if typ == c_float: + assert abs(result - arg) < 0.000001 + else: + assert callback.got_args == (arg,) + assert result == arg - # The integrate function itself, exposed from the _ctypes_test dll - integrate = dll.integrate - integrate.argtypes = (c_double, c_double, CALLBACK, c_long) - integrate.restype = c_double + result2 = cfunc(typ(arg)) + assert type(result2) in unwrapped_types.get(typ, (int, long)) - def func(x): - print 'calculating x**2 of',x - return x**2 + PROTO = functype(typ, c_byte, typ) + result = PROTO(callback)(-3, arg) + if typ == c_float: + assert abs(result - arg) < 0.000001 + else: + assert callback.got_args == (-3, arg) + assert result == arg - result = integrate(0.0, 1.0, CALLBACK(func), 10) - diff = abs(result - 1./3.) + at pytest.mark.parametrize('functype', functypes) +def test_unsupported_restype_1(functype): + # Only "fundamental" result types are supported for callback + # functions, the type must have a non-NULL stgdict->setfunc. + # POINTER(c_double), for example, is not supported. - assert diff < 0.01, "%s not less than 0.01" % diff + prototype = functype(POINTER(c_double)) + # The type is checked when the prototype is called + with pytest.raises(TypeError): + prototype(lambda: None) -################################################################ -class TestMoreCallbacks(BaseCTypesTestChecker): +def test_callback_with_struct_argument(): + class RECT(Structure): + _fields_ = [("left", c_int), ("top", c_int), + ("right", c_int), ("bottom", c_int)] - def test_callback_with_struct_argument(self): - class RECT(Structure): - _fields_ = [("left", c_int), ("top", c_int), - ("right", c_int), ("bottom", c_int)] + proto = CFUNCTYPE(c_int, RECT) - proto = CFUNCTYPE(c_int, RECT) - def callback(point): - point.left *= -1 - return point.left+point.top+point.right+point.bottom + def callback(point): + point.left *= -1 + return point.left + point.top + point.right + point.bottom - cbp = proto(callback) + cbp = proto(callback) + rect = RECT(-1000, 100, 10, 1) + res = cbp(rect) + assert res == 1111 + assert rect.left == -1000 # must not have been changed! - rect = RECT(-1000,100,10,1) +def test_callback_from_c_with_struct_argument(dll): + class RECT(Structure): + _fields_ = [("left", c_long), ("top", c_long), + ("right", c_long), ("bottom", c_long)] - res = cbp(rect) + proto = CFUNCTYPE(c_int, RECT) - assert res == 1111 - assert rect.left == -1000 # must not have been changed! + def callback(point): + return point.left + point.top + point.right + point.bottom - def test_callback_from_c_with_struct_argument(self): - import conftest - _ctypes_test = str(conftest.sofile) - dll = CDLL(_ctypes_test) + cbp = proto(callback) + rect = RECT(1000, 100, 10, 1) - class RECT(Structure): - _fields_ = [("left", c_long), ("top", c_long), - ("right", c_long), ("bottom", c_long)] + call_callback_with_rect = dll.call_callback_with_rect + call_callback_with_rect.restype = c_int + call_callback_with_rect.argtypes = [proto, RECT] + res = call_callback_with_rect(cbp, rect) + assert res == 1111 - proto = CFUNCTYPE(c_int, RECT) - def callback(point): - return point.left+point.top+point.right+point.bottom +def test_callback_unsupported_return_struct(): + class RECT(Structure): + _fields_ = [("left", c_int), ("top", c_int), + ("right", c_int), ("bottom", c_int)] - cbp = proto(callback) - rect = RECT(1000,100,10,1) + proto = CFUNCTYPE(RECT, c_int) + with pytest.raises(TypeError): + proto(lambda r: 0) - call_callback_with_rect = dll.call_callback_with_rect - call_callback_with_rect.restype = c_int - call_callback_with_rect.argtypes = [proto, RECT] - res = call_callback_with_rect(cbp, rect) - assert res == 1111 - def test_callback_unsupported_return_struct(self): - class RECT(Structure): - _fields_ = [("left", c_int), ("top", c_int), - ("right", c_int), ("bottom", c_int)] +def test_qsort(dll): + PI = POINTER(c_int) + A = c_int*5 + a = A() + for i in range(5): + a[i] = 5-i - proto = CFUNCTYPE(RECT, c_int) - with pytest.raises(TypeError): - proto(lambda r: 0) + assert a[0] == 5 # sanity + def comp(a, b): + a = a.contents.value + b = b.contents.value + return cmp(a,b) + qs = dll.my_qsort + qs.restype = None + CMP = CFUNCTYPE(c_int, PI, PI) + qs.argtypes = (PI, c_size_t, c_size_t, CMP) - def test_qsort(self): - import conftest - _ctypes_test = str(conftest.sofile) - dll = CDLL(_ctypes_test) + qs(cast(a, PI), 5, sizeof(c_int), CMP(comp)) - PI = POINTER(c_int) - A = c_int*5 - a = A() - for i in range(5): - a[i] = 5-i + res = list(a) - assert a[0] == 5 # sanity + assert res == [1,2,3,4,5] - def comp(a, b): - a = a.contents.value - b = b.contents.value - return cmp(a,b) - qs = dll.my_qsort - qs.restype = None - CMP = CFUNCTYPE(c_int, PI, PI) - qs.argtypes = (PI, c_size_t, c_size_t, CMP) +def test_pyobject_as_opaque(dll): + def callback(arg): + return arg() - qs(cast(a, PI), 5, sizeof(c_int), CMP(comp)) + CTP = CFUNCTYPE(c_int, py_object) + cfunc = dll._testfunc_callback_opaque + cfunc.argtypes = [CTP, py_object] + cfunc.restype = c_int + res = cfunc(CTP(callback), lambda : 3) + assert res == 3 - res = list(a) +def test_callback_void(capsys, dll): + def callback(): + pass - assert res == [1,2,3,4,5] + CTP = CFUNCTYPE(None) + cfunc = dll._testfunc_callback_void + cfunc.argtypes = [CTP] + cfunc.restype = int + cfunc(CTP(callback)) + out, err = capsys.readouterr() + assert (out, err) == ("", "") - def test_pyobject_as_opaque(self): - import conftest - _ctypes_test = str(conftest.sofile) - dll = CDLL(_ctypes_test) - def callback(arg): - return arg() +def test_callback_pyobject(): + def callback(obj): + return obj - CTP = CFUNCTYPE(c_int, py_object) - cfunc = dll._testfunc_callback_opaque - cfunc.argtypes = [CTP, py_object] - cfunc.restype = c_int - res = cfunc(CTP(callback), lambda : 3) - assert res == 3 + FUNC = CFUNCTYPE(py_object, py_object) + cfunc = FUNC(callback) + param = c_int(42) + assert cfunc(param) is param - def test_callback_void(self, capsys): - import conftest - _ctypes_test = str(conftest.sofile) - dll = CDLL(_ctypes_test) - - def callback(): - pass - - CTP = CFUNCTYPE(None) - cfunc = dll._testfunc_callback_void - cfunc.argtypes = [CTP] - cfunc.restype = int - cfunc(CTP(callback)) - out, err = capsys.readouterr() - assert (out, err) == ("", "") - - From pypy.commits at gmail.com Mon Feb 4 08:50:07 2019 From: pypy.commits at gmail.com (fijal) Date: Mon, 04 Feb 2019 05:50:07 -0800 (PST) Subject: [pypy-commit] pypy arm64: (arigo, fijal) Start building the scaffolding - passes STP encoding test Message-ID: <5c58430f.1c69fb81.77a67.b293@mx.google.com> Author: Maciej Fijalkowski Branch: arm64 Changeset: r95784:b24d4ad8a3c6 Date: 2019-02-04 13:49 +0000 http://bitbucket.org/pypy/pypy/changeset/b24d4ad8a3c6/ Log: (arigo, fijal) Start building the scaffolding - passes STP encoding test diff --git a/rpython/jit/backend/aarch64/__init__.py b/rpython/jit/backend/aarch64/__init__.py new file mode 100644 diff --git a/rpython/jit/backend/aarch64/arch.py b/rpython/jit/backend/aarch64/arch.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/arch.py @@ -0,0 +1,12 @@ + +WORD = 8 + +# The stack contains the force_index and the, callee saved registers and +# ABI required information +# All the rest of the data is in a GC-managed variable-size "frame". +# This jitframe object's address is always stored in the register FP +# A jitframe is a jit.backend.llsupport.llmodel.jitframe.JITFRAME +# Stack frame fixed area +# Currently only the force_index +JITFRAME_FIXED_SIZE = 12 + 8 +# 12 GPR + 8 VFP Regs diff --git a/rpython/jit/backend/aarch64/assembler.py b/rpython/jit/backend/aarch64/assembler.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/assembler.py @@ -0,0 +1,192 @@ + +from rpython.jit.backend.aarch64.arch import WORD, JITFRAME_FIXED_SIZE +from rpython.jit.backend.aarch64.codebuilder import InstrBuilder +#from rpython.jit.backend.arm.locations import imm, StackLocation, get_fp_offset +#from rpython.jit.backend.arm.helper.regalloc import VMEM_imm_size +from rpython.jit.backend.aarch64.opassembler import ResOpAssembler +from rpython.jit.backend.aarch64.regalloc import Regalloc +# CoreRegisterManager, check_imm_arg, VFPRegisterManager, +# operations as regalloc_operations) +#from rpython.jit.backend.arm import callbuilder +from rpython.jit.backend.aarch64 import registers as r +from rpython.jit.backend.llsupport import jitframe +from rpython.jit.backend.llsupport.assembler import BaseAssembler +from rpython.jit.backend.llsupport.regalloc import get_scale, valid_addressing_size +from rpython.jit.backend.llsupport.asmmemmgr import MachineDataBlockWrapper +from rpython.jit.backend.model import CompiledLoopToken +from rpython.jit.codewriter.effectinfo import EffectInfo +from rpython.jit.metainterp.history import AbstractFailDescr, FLOAT, INT, VOID +from rpython.jit.metainterp.resoperation import rop +from rpython.rlib.debug import debug_print, debug_start, debug_stop +from rpython.rlib.jit import AsmInfo +from rpython.rlib.objectmodel import we_are_translated, specialize, compute_unique_id +from rpython.rlib.rarithmetic import r_uint +from rpython.rtyper.annlowlevel import llhelper, cast_instance_to_gcref +from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rtyper.lltypesystem.lloperation import llop +from rpython.rlib.rjitlog import rjitlog as jl + +class AssemblerARM64(ResOpAssembler): + def assemble_loop(self, jd_id, unique_id, logger, loopname, inputargs, + operations, looptoken, log): + clt = CompiledLoopToken(self.cpu, looptoken.number) + looptoken.compiled_loop_token = clt + + if not we_are_translated(): + # Arguments should be unique + assert len(set(inputargs)) == len(inputargs) + + self.setup(looptoken) + + frame_info = self.datablockwrapper.malloc_aligned( + jitframe.JITFRAMEINFO_SIZE, alignment=WORD) + clt.frame_info = rffi.cast(jitframe.JITFRAMEINFOPTR, frame_info) + clt.frame_info.clear() # for now + + if log: + operations = self._inject_debugging_code(looptoken, operations, + 'e', looptoken.number) + + regalloc = Regalloc(assembler=self) + allgcrefs = [] + operations = regalloc.prepare_loop(inputargs, operations, looptoken, + allgcrefs) + self.reserve_gcref_table(allgcrefs) + functionpos = self.mc.get_relative_pos() + + self._call_header_with_stack_check() + self._check_frame_depth_debug(self.mc) + + loop_head = self.mc.get_relative_pos() + looptoken._ll_loop_code = loop_head + # + frame_depth_no_fixed_size = self._assemble(regalloc, inputargs, operations) + self.update_frame_depth(frame_depth_no_fixed_size + JITFRAME_FIXED_SIZE) + # + size_excluding_failure_stuff = self.mc.get_relative_pos() + + self.write_pending_failure_recoveries() + + full_size = self.mc.get_relative_pos() + rawstart = self.materialize_loop(looptoken) + looptoken._ll_function_addr = rawstart + functionpos + + self.patch_gcref_table(looptoken, rawstart) + self.process_pending_guards(rawstart) + self.fixup_target_tokens(rawstart) + + if log and not we_are_translated(): + self.mc._dump_trace(rawstart, + 'loop.asm') + + ops_offset = self.mc.ops_offset + + if logger: + log = logger.log_trace(jl.MARK_TRACE_ASM, None, self.mc) + log.write(inputargs, operations, ops_offset=ops_offset) + + # legacy + if logger.logger_ops: + logger.logger_ops.log_loop(inputargs, operations, 0, + "rewritten", name=loopname, + ops_offset=ops_offset) + + self.teardown() + + debug_start("jit-backend-addr") + debug_print("Loop %d (%s) has address 0x%x to 0x%x (bootstrap 0x%x)" % ( + looptoken.number, loopname, + r_uint(rawstart + loop_head), + r_uint(rawstart + size_excluding_failure_stuff), + r_uint(rawstart + functionpos))) + debug_print(" gc table: 0x%x" % r_uint(rawstart)) + debug_print(" function: 0x%x" % r_uint(rawstart + functionpos)) + debug_print(" resops: 0x%x" % r_uint(rawstart + loop_head)) + debug_print(" failures: 0x%x" % r_uint(rawstart + + size_excluding_failure_stuff)) + debug_print(" end: 0x%x" % r_uint(rawstart + full_size)) + debug_stop("jit-backend-addr") + + return AsmInfo(ops_offset, rawstart + loop_head, + size_excluding_failure_stuff - loop_head) + + def setup(self, looptoken): + BaseAssembler.setup(self, looptoken) + assert self.memcpy_addr != 0, 'setup_once() not called?' + if we_are_translated(): + self.debug = False + self.current_clt = looptoken.compiled_loop_token + self.mc = InstrBuilder() + self.pending_guards = [] + #assert self.datablockwrapper is None --- but obscure case + # possible, e.g. getting MemoryError and continuing + allblocks = self.get_asmmemmgr_blocks(looptoken) + self.datablockwrapper = MachineDataBlockWrapper(self.cpu.asmmemmgr, + allblocks) + self.mc.datablockwrapper = self.datablockwrapper + self.target_tokens_currently_compiling = {} + self.frame_depth_to_patch = [] + + def _build_failure_recovery(self, exc, withfloats=False): + pass # XXX + + def _build_wb_slowpath(self, withcards, withfloats=False, for_frame=False): + pass # XXX + + def build_frame_realloc_slowpath(self): + pass + + def _build_propagate_exception_path(self): + pass + + def _build_cond_call_slowpath(self, supports_floats, callee_only): + pass + + def _build_stack_check_slowpath(self): + pass + + def reserve_gcref_table(self, allgcrefs): + pass + + def _call_header_with_stack_check(self): + self._call_header() + if self.stack_check_slowpath == 0: + pass # no stack check (e.g. not translated) + else: + endaddr, lengthaddr, _ = self.cpu.insert_stack_check() + # load stack end + self.mc.gen_load_int(r.ip.value, endaddr) # load ip, [end] + self.mc.LDR_ri(r.ip.value, r.ip.value) # LDR ip, ip + # load stack length + self.mc.gen_load_int(r.lr.value, lengthaddr) # load lr, lengh + self.mc.LDR_ri(r.lr.value, r.lr.value) # ldr lr, *lengh + # calculate ofs + self.mc.SUB_rr(r.ip.value, r.ip.value, r.sp.value) # SUB ip, current + # if ofs + self.mc.CMP_rr(r.ip.value, r.lr.value) # CMP ip, lr + self.mc.BL(self.stack_check_slowpath, c=c.HI) # call if ip > lr + + def _call_header(self): + stack_size = WORD #alignment + stack_size += len(r.callee_saved_registers) * WORD + if self.cpu.supports_floats: + stack_size += len(r.callee_saved_vfp_registers) * 2 * WORD + + # push all callee saved registers including lr; and push r1 as + # well, which contains the threadlocal_addr argument. Note that + # we're pushing a total of 10 words, which keeps the stack aligned. + self.mc.PUSH([reg.value for reg in r.callee_saved_registers] + + [r.r1.value]) + self.saved_threadlocal_addr = 0 # at offset 0 from location 'sp' + if self.cpu.supports_floats: + self.mc.VPUSH([reg.value for reg in r.callee_saved_vfp_registers]) + self.saved_threadlocal_addr += ( + len(r.callee_saved_vfp_registers) * 2 * WORD) + assert stack_size % 8 == 0 # ensure we keep alignment + + # set fp to point to the JITFRAME + self.mc.MOV_rr(r.fp.value, r.r0.value) + # + gcrootmap = self.cpu.gc_ll_descr.gcrootmap + if gcrootmap and gcrootmap.is_shadow_stack: + self.gen_shadowstack_header(gcrootmap) diff --git a/rpython/jit/backend/aarch64/codebuilder.py b/rpython/jit/backend/aarch64/codebuilder.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/codebuilder.py @@ -0,0 +1,61 @@ + +from rpython.jit.backend.llsupport.asmmemmgr import BlockBuilderMixin +from rpython.jit.backend.aarch64.locations import RegisterLocation + +class AbstractAarch64Builder(object): + def write32(self, word): + self.writechar(chr(word & 0xFF)) + self.writechar(chr((word >> 8) & 0xFF)) + self.writechar(chr((word >> 16) & 0xFF)) + self.writechar(chr((word >> 24) & 0xFF)) + + def RET_r(self, arg): + self.write32((0b1101011001011111 << 16) | (arg << 5)) + + def STP_rr_preindex(self, reg1, reg2, rn, offset): + base = 0b1010100110 + assert -512 <= offset < 512 + assert offset & 0x7 == 0 + self.write32((base << 22) | ((0x7F & (offset >> 3)) << 15) | + (reg2 << 10) | (rn << 5) | reg1) + +class InstrBuilder(BlockBuilderMixin, AbstractAarch64Builder): + + def __init__(self, arch_version=7): + AbstractAarch64Builder.__init__(self) + self.init_block_builder() + # + # ResOperation --> offset in the assembly. + # ops_offset[None] represents the beginning of the code after the last op + # (i.e., the tail of the loop) + self.ops_offset = {} + + def mark_op(self, op): + pos = self.get_relative_pos() + self.ops_offset[op] = pos + + def _dump_trace(self, addr, name, formatter=-1): + if not we_are_translated(): + if formatter != -1: + name = name % formatter + dir = udir.ensure('asm', dir=True) + f = dir.join(name).open('wb') + data = rffi.cast(rffi.CCHARP, addr) + for i in range(self.currpos()): + f.write(data[i]) + f.close() + + def clear_cache(self, addr): + if we_are_translated(): + startaddr = rffi.cast(llmemory.Address, addr) + endaddr = rffi.cast(llmemory.Address, + addr + self.get_relative_pos()) + clear_cache(startaddr, endaddr) + + def copy_to_raw_memory(self, addr): + self._copy_to_raw_memory(addr) + self.clear_cache(addr) + self._dump(addr, "jit-backend-dump", 'arm') + + def currpos(self): + return self.get_relative_pos() diff --git a/rpython/jit/backend/aarch64/locations.py b/rpython/jit/backend/aarch64/locations.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/locations.py @@ -0,0 +1,116 @@ + +from rpython.jit.backend.aarch64.arch import WORD, JITFRAME_FIXED_SIZE +from rpython.jit.metainterp.history import INT, FLOAT + +class AssemblerLocation(object): + _immutable_ = True + type = INT + + def is_imm(self): + return False + + def is_stack(self): + return False + + def is_raw_sp(self): + return False + + def is_core_reg(self): + return False + + def is_vfp_reg(self): + return False + + def is_imm_float(self): + return False + + def is_float(self): + return False + + def as_key(self): + raise NotImplementedError + + def get_position(self): + raise NotImplementedError # only for stack + +class RegisterLocation(AssemblerLocation): + _immutable_ = True + + def __init__(self, value): + self.value = value + + def __repr__(self): + return 'x%d' % self.value + + def is_core_reg(self): + return True + + def as_key(self): # 0 <= as_key <= 30, 31 being zero register + xxx + return self.value + +class VFPRegisterLocation(RegisterLocation): + _immutable_ = True + type = FLOAT + + def __repr__(self): + return 'vfp(d%d)' % self.value + + def is_core_reg(self): + return False + + def is_vfp_reg(self): + return True + + def as_key(self): # 40 <= as_key <= 71 + xxx + return self.value + 40 + + def is_float(self): + return True + +class StackLocation(AssemblerLocation): + _immutable_ = True + + def __init__(self, position, fp_offset, type=INT): + self.position = position + self.value = fp_offset + self.type = type + + def __repr__(self): + return 'FP(%s)+%d' % (self.type, self.position,) + + def location_code(self): + return 'b' + + def get_position(self): + return self.position + + def assembler(self): + return repr(self) + + def is_stack(self): + return True + + def as_key(self): # an aligned word + 10000 + XXX + return self.position + 10000 + + def is_float(self): + return self.type == FLOAT + + +class ZeroRegister(AssemblerLocation): + _immutable_ = True + + def __init__(self): + self.value = 31 + + def __repr__(self): + return "xzr" + + def as_key(self): + return 31 + +def get_fp_offset(base_ofs, position): + return base_ofs + WORD * (position + JITFRAME_FIXED_SIZE) diff --git a/rpython/jit/backend/aarch64/opassembler.py b/rpython/jit/backend/aarch64/opassembler.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/opassembler.py @@ -0,0 +1,5 @@ + +from rpython.jit.backend.llsupport.assembler import GuardToken, BaseAssembler + +class ResOpAssembler(BaseAssembler): + pass diff --git a/rpython/jit/backend/aarch64/regalloc.py b/rpython/jit/backend/aarch64/regalloc.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/regalloc.py @@ -0,0 +1,170 @@ + +from rpython.jit.backend.aarch64 import registers as r +from rpython.jit.backend.aarch64 import locations + +from rpython.jit.metainterp.history import (Const, ConstInt, ConstFloat, + ConstPtr, + INT, REF, FLOAT) +from rpython.jit.metainterp.history import TargetToken +from rpython.jit.backend.llsupport.regalloc import FrameManager, \ + RegisterManager, TempVar, compute_vars_longevity, BaseRegalloc, \ + get_scale + +class ARMFrameManager(FrameManager): + + def __init__(self, base_ofs): + FrameManager.__init__(self) + self.base_ofs = base_ofs + + def frame_pos(self, i, box_type): + return locations.StackLocation(i, locations.get_fp_offset(self.base_ofs, i), box_type) + + @staticmethod + def frame_size(type): + return 1 + + @staticmethod + def get_loc_index(loc): + assert loc.is_stack() + return loc.position + +class ARMRegisterManager(RegisterManager): + def return_constant(self, v, forbidden_vars=[], selected_reg=None): + self._check_type(v) + if isinstance(v, Const): + if isinstance(v, ConstPtr): + tp = REF + elif isinstance(v, ConstFloat): + tp = FLOAT + else: + tp = INT + loc = self.get_scratch_reg(tp, + self.temp_boxes + forbidden_vars, + selected_reg=selected_reg) + immvalue = self.convert_to_imm(v) + self.assembler.load(loc, immvalue) + return loc + else: + return RegisterManager.return_constant(self, v, + forbidden_vars, selected_reg) + + +class VFPRegisterManager(ARMRegisterManager): + all_regs = r.all_vfp_regs + box_types = [FLOAT] + save_around_call_regs = r.all_vfp_regs + + def convert_to_imm(self, c): + adr = self.assembler.datablockwrapper.malloc_aligned(8, 8) + x = c.getfloatstorage() + rffi.cast(rffi.CArrayPtr(longlong.FLOATSTORAGE), adr)[0] = x + return locations.ConstFloatLoc(adr) + + def __init__(self, longevity, frame_manager=None, assembler=None): + RegisterManager.__init__(self, longevity, frame_manager, assembler) + + def after_call(self, v): + """ Adjust registers according to the result of the call, + which is in variable v. + """ + self._check_type(v) + reg = self.force_allocate_reg(v, selected_reg=r.d0) + return reg + + def get_scratch_reg(self, type=FLOAT, forbidden_vars=[], selected_reg=None): + assert type == FLOAT # for now + box = TempFloat() + self.temp_boxes.append(box) + reg = self.force_allocate_reg(box, forbidden_vars=forbidden_vars, + selected_reg=selected_reg) + return reg + + +class CoreRegisterManager(ARMRegisterManager): + all_regs = r.all_regs + box_types = None # or a list of acceptable types + no_lower_byte_regs = all_regs + save_around_call_regs = r.caller_resp + frame_reg = r.fp + + def __init__(self, longevity, frame_manager=None, assembler=None): + RegisterManager.__init__(self, longevity, frame_manager, assembler) + + def call_result_location(self, v): + return r.r0 + + def convert_to_imm(self, c): + if isinstance(c, ConstInt): + val = rffi.cast(rffi.INT, c.value) + return locations.ImmLocation(val) + else: + assert isinstance(c, ConstPtr) + return locations.ImmLocation(rffi.cast(lltype.Signed, c.value)) + assert 0 + + def get_scratch_reg(self, type=INT, forbidden_vars=[], selected_reg=None): + assert type == INT or type == REF + box = None + if type == INT: + box = TempInt() + else: + box = TempPtr() + self.temp_boxes.append(box) + reg = self.force_allocate_reg(box, forbidden_vars=forbidden_vars, + selected_reg=selected_reg) + return reg + + def get_free_reg(self): + free_regs = self.free_regs + for i in range(len(free_regs) - 1, -1, -1): + if free_regs[i] in self.save_around_call_regs: + continue + return free_regs[i] + +class Regalloc(BaseRegalloc): + + def __init__(self, assembler): + self.cpu = assembler.cpu + self.assembler = assembler + self.frame_manager = None + self.jump_target_descr = None + self.final_jump_op = None + + def _prepare(self, inputargs, operations, allgcrefs): + cpu = self.cpu + self.fm = ARMFrameManager(cpu.get_baseofs_of_frame_field()) + self.frame_manager = self.fm + operations = cpu.gc_ll_descr.rewrite_assembler(cpu, operations, + allgcrefs) + # compute longevity of variables + longevity, last_real_usage = compute_vars_longevity(inputargs, operations) + self.longevity = longevity + self.last_real_usage = last_real_usage + fm = self.frame_manager + asm = self.assembler + self.vfprm = VFPRegisterManager(longevity, fm, asm) + self.rm = CoreRegisterManager(longevity, fm, asm) + return operations + + def prepare_loop(self, inputargs, operations, looptoken, allgcrefs): + operations = self._prepare(inputargs, operations, allgcrefs) + self._set_initial_bindings(inputargs, looptoken) + self.possibly_free_vars(list(inputargs)) + return operations + + def possibly_free_var(self, var): + if var.type == FLOAT: + self.vfprm.possibly_free_var(var) + else: + self.rm.possibly_free_var(var) + + def possibly_free_vars_for_op(self, op): + for i in range(op.numargs()): + var = op.getarg(i) + if var is not None: # xxx kludgy + self.possibly_free_var(var) + + def possibly_free_vars(self, vars): + for var in vars: + if var is not None: # xxx kludgy + self.possibly_free_var(var) diff --git a/rpython/jit/backend/aarch64/registers.py b/rpython/jit/backend/aarch64/registers.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/registers.py @@ -0,0 +1,25 @@ + +from rpython.jit.backend.aarch64.locations import (RegisterLocation, + ZeroRegister, VFPRegisterLocation) + + +registers = [RegisterLocation(i) for i in range(31)] +sp = wzr = ZeroRegister() +[x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, + x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, + x21, x22, x23, x24, x25, x26, x27, x28, x29, x30] = registers + +vfpregisters = [VFPRegisterLocation(i) for i in range(32)] +all_vfp_regs = vfpregisters[:16] +all_regs = registers[:16] + [x19, x20, x21, x22] + +lr = x30 +fp = x29 +ip1 = x17 +ip0 = x16 + +callee_resp = [x19, x20, x21, x22, fp] + +argument_regs = caller_resp = [x0, x1, x2, x3, x4, x5, x6, x7] + +callee_saved_registers = callee_resp + [lr] diff --git a/rpython/jit/backend/aarch64/runner.py b/rpython/jit/backend/aarch64/runner.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/runner.py @@ -0,0 +1,27 @@ + +from rpython.rtyper.lltypesystem import llmemory, lltype +from rpython.jit.backend.aarch64.assembler import AssemblerARM64 +from rpython.jit.backend.llsupport.llmodel import AbstractLLCPU + +class CPU_ARM64(AbstractLLCPU): + """ARM 64""" + backend_name = "aarch64" + + IS_64_BIT = True + + def __init__(self, rtyper, stats, opts=None, translate_support_code=False, + gcdescr=None): + AbstractLLCPU.__init__(self, rtyper, stats, opts, + translate_support_code, gcdescr) + + def setup(self): + self.assembler = AssemblerARM64(self, self.translate_support_code) + + def setup_once(self): + self.assembler.setup_once() + + def cast_ptr_to_int(x): + adr = llmemory.cast_ptr_to_adr(x) + return CPU_ARM64.cast_adr_to_int(adr) + cast_ptr_to_int._annspecialcase_ = 'specialize:arglltype(0)' + cast_ptr_to_int = staticmethod(cast_ptr_to_int) diff --git a/rpython/jit/backend/aarch64/test/__init__.py b/rpython/jit/backend/aarch64/test/__init__.py new file mode 100644 diff --git a/rpython/jit/backend/aarch64/test/gen.py b/rpython/jit/backend/aarch64/test/gen.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/test/gen.py @@ -0,0 +1,51 @@ +import os +from rpython.tool.udir import udir +import tempfile + +class ASMInstruction(object): + + asm_opts = '-march=armv8-a' + body = """.section .text +_start: .global _start + .global main + b main +main: + .ascii "START " + %s + .ascii "END " +""" + begin_tag = 'START ' + end_tag = 'END ' + base_name = 'test_%d.asm' + index = 0 + + def __init__(self, instr): + self.instr = instr + self.file = udir.join(self.base_name % self.index) + while self.file.check(): + self.index += 1 + self.file = udir.join(self.base_name % self.index) + + def encode(self): + f = open("%s/a.out" % (udir),'rb') + data = f.read() + f.close() + i = data.find(self.begin_tag) + assert i>=0 + j = data.find(self.end_tag, i) + assert j>=0 + as_code = data[i+len(self.begin_tag):j] + return as_code + + def assemble(self, *args): + res = self.body % (self.instr) + self.file.write(res) + os.system("as --fatal-warnings %s %s -o %s/a.out" % (self.asm_opts, self.file, udir)) + + #def __del__(self): + # self.file.close() + +def assemble(instr): + a = ASMInstruction(instr) + a.assemble(instr) + return a.encode() diff --git a/rpython/jit/backend/aarch64/test/test_instr_builder.py b/rpython/jit/backend/aarch64/test/test_instr_builder.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/test/test_instr_builder.py @@ -0,0 +1,34 @@ + +from rpython.jit.backend.aarch64 import registers as r +from rpython.jit.backend.aarch64 import codebuilder +from rpython.jit.backend.aarch64.test.gen import assemble + +class CodeBuilder(codebuilder.InstrBuilder): + def __init__(self, arch_version=7): + self.arch_version = arch_version + self.buffer = [] + + def writechar(self, char): + self.buffer.append(char) + + def hexdump(self): + return ''.join(self.buffer) + +class TestInstrBuilder(object): + def setup_method(self, meth): + self.cb = CodeBuilder() + + def test_ret(self): + self.cb.RET_r(r.x0.value) + res = self.cb.hexdump() + exp = assemble('RET x0') + assert res == exp + self.cb = CodeBuilder() + self.cb.RET_r(r.x0.value) + self.cb.RET_r(r.x5.value) + self.cb.RET_r(r.x3.value) + assert self.cb.hexdump() == assemble('RET x0\nRET x5\n RET x3') + + def test_call_header(self): + self.cb.STP_rr_preindex(r.x29.value, r.x30.value, r.sp.value, -32) + assert self.cb.hexdump() == assemble("STP x29, x30, [sp, -32]!") diff --git a/rpython/jit/backend/aarch64/test/test_runner.py b/rpython/jit/backend/aarch64/test/test_runner.py new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/test/test_runner.py @@ -0,0 +1,46 @@ +import py +from rpython.jit.backend.detect_cpu import getcpuclass +from rpython.jit.backend.test.runner_test import LLtypeBackendTest,\ + boxfloat, constfloat +from rpython.jit.metainterp.history import BasicFailDescr, BasicFinalDescr +from rpython.jit.metainterp.resoperation import (ResOperation, rop, + InputArgInt) +from rpython.jit.tool.oparser import parse +from rpython.rtyper.lltypesystem import lltype, llmemory +from rpython.rtyper import rclass +from rpython.rtyper.annlowlevel import llhelper +from rpython.jit.codewriter.effectinfo import EffectInfo +from rpython.jit.metainterp.history import JitCellToken, TargetToken +from rpython.jit.codewriter import longlong + + +CPU = getcpuclass() + +class FakeStats(object): + pass + + +class TestARM64(LLtypeBackendTest): + + # for the individual tests see + # ====> ../../test/runner_test.py + + #add_loop_instructions = 'ldr; adds; cmp; beq; b;' + #if arch_version == 7: + # bridge_loop_instructions = ('ldr; movw; nop; cmp; bge; ' + # 'push; movw; movt; push; movw; movt; ' + # 'blx; movw; movt; bx;') + #else: + # bridge_loop_instructions = ('ldr; mov; nop; nop; nop; cmp; bge; ' + # 'push; ldr; mov; ' + # '[^;]+; ' # inline constant + # 'push; ldr; mov; ' + # '[^;]+; ' # inline constant + # 'blx; ldr; mov; ' + # '[^;]+; ' # inline constant + # 'bx;') + + def get_cpu(self): + cpu = CPU(rtyper=None, stats=FakeStats()) + cpu.setup_once() + return cpu diff --git a/rpython/jit/backend/arm/test/gen.py b/rpython/jit/backend/arm/test/gen.py --- a/rpython/jit/backend/arm/test/gen.py +++ b/rpython/jit/backend/arm/test/gen.py @@ -2,6 +2,7 @@ from rpython.tool.udir import udir import tempfile from rpython.jit.backend.arm.test.support import AS + class ASMInstruction(object): asm_opts = '-mfpu=neon -mcpu=cortex-a8 -march=armv7-a' diff --git a/rpython/jit/backend/detect_cpu.py b/rpython/jit/backend/detect_cpu.py --- a/rpython/jit/backend/detect_cpu.py +++ b/rpython/jit/backend/detect_cpu.py @@ -14,6 +14,7 @@ MODEL_X86_NO_SSE2 = 'x86-without-sse2' MODEL_X86_64 = 'x86-64' MODEL_ARM = 'arm' +MODEL_ARM64 = 'aarch64' MODEL_PPC_64 = 'ppc-64' MODEL_S390_64 = 's390x' # don't use '_' in the model strings; they are replaced by '-' @@ -25,6 +26,7 @@ mapping = { MODEL_X86_64: ['__amd64__', '__amd64', '__x86_64__', '__x86_64', '_M_X64', '_M_AMD64'], MODEL_ARM: ['__arm__', '__thumb__','_M_ARM_EP'], + MODEL_ARM64: ['__aarch64__'], MODEL_X86: ['i386', '__i386', '__i386__', '__i686__','_M_IX86'], MODEL_PPC_64: ['__powerpc64__'], MODEL_S390_64:['__s390x__'], @@ -68,6 +70,7 @@ 'amd64': MODEL_X86, # freebsd 'AMD64': MODEL_X86, # win64 'armv8l': MODEL_ARM, # 32-bit ARMv8 + 'aarch64': MODEL_ARM64, 'armv7l': MODEL_ARM, 'armv6l': MODEL_ARM, 'arm': MODEL_ARM, # freebsd @@ -119,6 +122,8 @@ return "rpython.jit.backend.x86.runner", "CPU_X86_64" elif backend_name == MODEL_ARM: return "rpython.jit.backend.arm.runner", "CPU_ARM" + elif backend_name == MODEL_ARM64: + return "rpython.jit.backend.aarch64.runner", "CPU_ARM64" elif backend_name == MODEL_PPC_64: return "rpython.jit.backend.ppc.runner", "PPC_CPU" elif backend_name == MODEL_S390_64: From pypy.commits at gmail.com Mon Feb 4 08:52:17 2019 From: pypy.commits at gmail.com (ambv) Date: Mon, 04 Feb 2019 05:52:17 -0800 (PST) Subject: [pypy-commit] pypy default: (cfbolz, ambv) Rename zlib.compress argument for consistency Message-ID: <5c584391.1c69fb81.f6b82.e6d9@mx.google.com> Author: Łukasz Langa Branch: Changeset: r95785:a03772d3a33a Date: 2019-02-04 14:30 +0100 http://bitbucket.org/pypy/pypy/changeset/a03772d3a33a/ Log: (cfbolz,ambv) Rename zlib.compress argument for consistency compressobj.compress() uses the name `data` for the first argument whereas zlib.compress() used `string` before. diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -65,10 +65,10 @@ return OperationError(w_error, space.newtext(msg)) - at unwrap_spec(string='bufferstr', level=int) -def compress(space, string, level=rzlib.Z_DEFAULT_COMPRESSION): + at unwrap_spec(data='bufferstr', level=int) +def compress(space, data, level=rzlib.Z_DEFAULT_COMPRESSION): """ - compress(string[, level]) -- Returned compressed string. + compress(data[, level]) -- Returned compressed string. Optional arg level is the compression level, in 1-9. """ @@ -78,7 +78,7 @@ except ValueError: raise zlib_error(space, "Bad compression level") try: - result = rzlib.compress(stream, string, rzlib.Z_FINISH) + result = rzlib.compress(stream, data, rzlib.Z_FINISH) finally: rzlib.deflateEnd(stream) except rzlib.RZlibError as e: From pypy.commits at gmail.com Mon Feb 4 08:52:19 2019 From: pypy.commits at gmail.com (ambv) Date: Mon, 04 Feb 2019 05:52:19 -0800 (PST) Subject: [pypy-commit] pypy py3.6: Merge default Message-ID: <5c584393.1c69fb81.198c6.d599@mx.google.com> Author: Łukasz Langa Branch: py3.6 Changeset: r95786:241160c766fd Date: 2019-02-04 14:33 +0100 http://bitbucket.org/pypy/pypy/changeset/241160c766fd/ Log: Merge default diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -44,10 +44,10 @@ return OperationError(w_error, space.newtext(msg)) - at unwrap_spec(string='bufferstr', level=int) -def compress(space, string, level=rzlib.Z_DEFAULT_COMPRESSION): + at unwrap_spec(data='bufferstr', level=int) +def compress(space, data, level=rzlib.Z_DEFAULT_COMPRESSION): """ - compress(string[, level]) -- Returned compressed string. + compress(data[, level]) -- Returned compressed string. Optional arg level is the compression level, in 1-9. """ @@ -57,7 +57,7 @@ except ValueError: raise zlib_error(space, "Bad compression level") try: - result = rzlib.compress(stream, string, rzlib.Z_FINISH) + result = rzlib.compress(stream, data, rzlib.Z_FINISH) finally: rzlib.deflateEnd(stream) except rzlib.RZlibError as e: From pypy.commits at gmail.com Mon Feb 4 09:22:01 2019 From: pypy.commits at gmail.com (ambv) Date: Mon, 04 Feb 2019 06:22:01 -0800 (PST) Subject: [pypy-commit] pypy py3.6: (cfbolz, ambv) Skip CPython-only tests in zlib Message-ID: <5c584a89.1c69fb81.190b.3d8c@mx.google.com> Author: Łukasz Langa Branch: py3.6 Changeset: r95787:81bdf2c6ef55 Date: 2019-02-04 15:21 +0100 http://bitbucket.org/pypy/pypy/changeset/81bdf2c6ef55/ Log: (cfbolz,ambv) Skip CPython-only tests in zlib See: https://github.com/python/cpython/pull/11754 diff --git a/lib-python/3/test/test_zlib.py b/lib-python/3/test/test_zlib.py --- a/lib-python/3/test/test_zlib.py +++ b/lib-python/3/test/test_zlib.py @@ -164,6 +164,7 @@ x = zlib.compress(HAMLET_SCENE) self.assertEqual(zlib.decompress(x), HAMLET_SCENE) + @support.cpython_only def test_keywords(self): x = zlib.compress(HAMLET_SCENE, level=3) self.assertEqual(zlib.decompress(x), HAMLET_SCENE) @@ -244,6 +245,7 @@ self.assertIsInstance(dco.unconsumed_tail, bytes) self.assertIsInstance(dco.unused_data, bytes) + @support.cpython_only def test_keywords(self): level = 2 method = zlib.DEFLATED From pypy.commits at gmail.com Mon Feb 4 09:39:41 2019 From: pypy.commits at gmail.com (Alexander Schremmer) Date: Mon, 04 Feb 2019 06:39:41 -0800 (PST) Subject: [pypy-commit] pypy math-improvements: Clean whitespace and one comment. Message-ID: <5c584ead.1c69fb81.25cad.8edd@mx.google.com> Author: Alexander Schremmer Branch: math-improvements Changeset: r95788:fcbdc6c463bf Date: 2019-02-04 15:36 +0100 http://bitbucket.org/pypy/pypy/changeset/fcbdc6c463bf/ Log: Clean whitespace and one comment. diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py --- a/pypy/objspace/std/longobject.py +++ b/pypy/objspace/std/longobject.py @@ -311,7 +311,7 @@ exp_int = 0 exp_bigint = None sign = 0 - + if isinstance(w_exponent, W_AbstractIntObject): exp_int = w_exponent.int_w(space) if exp_int > 0: @@ -323,7 +323,7 @@ else: exp_bigint = w_exponent.asbigint() sign = exp_bigint.sign - + if space.is_none(w_modulus): if sign < 0: self = self.descr_float(space) @@ -333,10 +333,10 @@ return W_LongObject(self.num.int_pow(exp_int)) else: return W_LongObject(self.num.pow(exp_bigint)) - + elif isinstance(w_modulus, W_AbstractIntObject): w_modulus = w_modulus.descr_long(space) - + elif not isinstance(w_modulus, W_AbstractLongObject): return space.w_NotImplemented @@ -400,7 +400,6 @@ @delegate_other def descr_rsub(self, space, w_other): - # XXX should support fast int? return W_LongObject(w_other.asbigint().sub(self.num)) def _make_generic_descr_binop(opname): From pypy.commits at gmail.com Mon Feb 4 09:39:43 2019 From: pypy.commits at gmail.com (Alexander Schremmer) Date: Mon, 04 Feb 2019 06:39:43 -0800 (PST) Subject: [pypy-commit] pypy math-improvements: Clean whitespace. Message-ID: <5c584eaf.1c69fb81.b8c45.9a1a@mx.google.com> Author: Alexander Schremmer Branch: math-improvements Changeset: r95789:cef706f5a301 Date: 2019-02-04 15:36 +0100 http://bitbucket.org/pypy/pypy/changeset/cef706f5a301/ Log: Clean whitespace. diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py --- a/rpython/rlib/rbigint.py +++ b/rpython/rlib/rbigint.py @@ -1254,7 +1254,7 @@ z._normalize() return z rshift._always_inline_ = 'try' # It's so fast that it's always benefitial. - + @jit.elidable def rqshift(self, int_other): wordshift = int_other / SHIFT @@ -2062,13 +2062,13 @@ else: vtop = v.widedigit(j) assert vtop <= wm1 - + vv = (vtop << SHIFT) | v.widedigit(abs(j-1)) - + # Hints to make division just as fast as doing it unsigned. But avoids casting to get correct results. assert vv >= 0 assert wm1 >= 1 - + q = vv / wm1 r = vv % wm1 # This seems to be slightly faster on widen digits than vv - wm1 * q. vj2 = v.digit(abs(j-2)) From pypy.commits at gmail.com Mon Feb 4 09:39:45 2019 From: pypy.commits at gmail.com (Alexander Schremmer) Date: Mon, 04 Feb 2019 06:39:45 -0800 (PST) Subject: [pypy-commit] pypy math-improvements: Clean whitespace. Message-ID: <5c584eb1.1c69fb81.dc807.bb36@mx.google.com> Author: Alexander Schremmer Branch: math-improvements Changeset: r95790:00a7b3f13930 Date: 2019-02-04 15:37 +0100 http://bitbucket.org/pypy/pypy/changeset/00a7b3f13930/ Log: Clean whitespace. diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py --- a/rpython/rtyper/lltypesystem/ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/ll2ctypes.py @@ -181,8 +181,8 @@ @property def value(self): res = self[0] | (self[1] << 64) - return res - + return res + _ctypes_cache[rffi.__INT128_T] = c_int128 _ctypes_cache[rffi.__UINT128_T] = c_uint128 From pypy.commits at gmail.com Mon Feb 4 09:39:46 2019 From: pypy.commits at gmail.com (Alexander Schremmer) Date: Mon, 04 Feb 2019 06:39:46 -0800 (PST) Subject: [pypy-commit] pypy math-improvements: Add more passing tests. Message-ID: <5c584eb2.1c69fb81.213fb.37a4@mx.google.com> Author: Alexander Schremmer Branch: math-improvements Changeset: r95791:5657b4134c73 Date: 2019-02-04 15:38 +0100 http://bitbucket.org/pypy/pypy/changeset/5657b4134c73/ Log: Add more passing tests. diff --git a/rpython/rlib/test/test_rbigint.py b/rpython/rlib/test/test_rbigint.py --- a/rpython/rlib/test/test_rbigint.py +++ b/rpython/rlib/test/test_rbigint.py @@ -129,6 +129,12 @@ assert res2.tolong() == -1L assert res3.tolong() == -1L + def test_floordiv2(self): + n1 = rbigint.fromlong(sys.maxint + 1) + n2 = rbigint.fromlong(-(sys.maxint + 1)) + assert n1.floordiv(n2).tolong() == -1L + assert n2.floordiv(n1).tolong() == -1L + def test_truediv(self): for op1 in gen_signs(long_vals_not_too_big): rl_op1 = rbigint.fromlong(op1) @@ -1149,8 +1155,14 @@ except Exception as e: pytest.raises(type(e), f1.pow, f2, f3) else: - v = f1.pow(f2, f3) - assert v.tolong() == res + v1 = f1.pow(f2, f3) + try: + v2 = f1.int_pow(f2.toint(), f3) + except OverflowError: + pass + else: + assert v2.tolong() == res + assert v1.tolong() == res @given(biglongs, biglongs) @example(510439143470502793407446782273075179618477362188870662225920, @@ -1212,19 +1224,34 @@ assert ra.truediv(rb) == a / b @given(longs, longs) - def test_bitwise(self, x, y): + def test_bitwise_and_mul(self, x, y): lx = rbigint.fromlong(x) ly = rbigint.fromlong(y) - for mod in "xor and_ or_".split(): - res1 = getattr(lx, mod)(ly).tolong() + for mod in "xor and_ or_ mul".split(): + res1a = getattr(lx, mod)(ly).tolong() + res1b = getattr(ly, mod)(lx).tolong() + res2 = getattr(operator, mod)(x, y) + assert res1a == res2 + + @given(longs, ints) + def test_int_bitwise_and_mul(self, x, y): + lx = rbigint.fromlong(x) + for mod in "xor and_ or_ mul".split(): + res1 = getattr(lx, 'int_' + mod)(y).tolong() res2 = getattr(operator, mod)(x, y) assert res1 == res2 @given(longs, ints) - def test_int_bitwise(self, x, y): + def test_int_comparison(self, x, y): lx = rbigint.fromlong(x) - for mod in "xor and_ or_".split(): - res1 = getattr(lx, 'int_' + mod)(y).tolong() - res2 = getattr(operator, mod)(x, y) - assert res1 == res2 + assert lx.int_lt(y) == (x < y) + assert lx.int_eq(y) == (x == y) + assert lx.int_le(y) == (x <= y) + @given(longs, longs) + def test_int_comparison(self, x, y): + lx = rbigint.fromlong(x) + ly = rbigint.fromlong(y) + assert lx.lt(ly) == (x < y) + assert lx.eq(ly) == (x == y) + assert lx.le(ly) == (x <= y) From pypy.commits at gmail.com Mon Feb 4 09:39:48 2019 From: pypy.commits at gmail.com (Alexander Schremmer) Date: Mon, 04 Feb 2019 06:39:48 -0800 (PST) Subject: [pypy-commit] pypy math-improvements: Change interface to ovf2long, found one buggy invocation, wrote test for it. Message-ID: <5c584eb4.1c69fb81.5b59c.34ff@mx.google.com> Author: Alexander Schremmer Branch: math-improvements Changeset: r95792:116eb65b5ec6 Date: 2019-02-04 15:38 +0100 http://bitbucket.org/pypy/pypy/changeset/116eb65b5ec6/ Log: Change interface to ovf2long, found one buggy invocation, wrote test for it. diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py --- a/pypy/objspace/std/intobject.py +++ b/pypy/objspace/std/intobject.py @@ -299,7 +299,7 @@ return ix -def _pow_ovf2long(space, iv, iw, iw_obj, w_modulus): +def _pow_ovf2long(space, iv, w_iv, iw, w_iw, w_modulus): if space.is_none(w_modulus) and _recover_with_smalllong(space): from pypy.objspace.std.smalllongobject import _pow as _pow_small try: @@ -308,32 +308,38 @@ return _pow_small(space, r_longlong(iv), iw, r_longlong(0)) except (OverflowError, ValueError): pass - from pypy.objspace.std.longobject import W_LongObject - w_iv = W_LongObject.fromint(space, iv) + from pypy.objspace.std.longobject import W_LongObject, W_AbstractLongObject + if w_iv is None or not isinstance(w_iv, W_AbstractLongObject): + w_iv = W_LongObject.fromint(space, iv) + if w_iw is None or not isinstance(w_iw, W_AbstractLongObject): + w_iw = W_LongObject.fromint(space, iw) - return w_iv.descr_pow(space, iw_obj, w_modulus) + return w_iv.descr_pow(space, w_iw, w_modulus) def _make_ovf2long(opname, ovf2small=None): op = getattr(operator, opname, None) assert op or ovf2small - def ovf2long(space, x, y, y_obj): + def ovf2long(space, x, w_x, y, w_y): """Handle overflowing to smalllong or long""" if _recover_with_smalllong(space): if ovf2small: return ovf2small(space, x, y) - # Assume a generic operation without an explicit #iovf2small + # Assume a generic operation without an explicit ovf2small # handler from pypy.objspace.std.smalllongobject import W_SmallLongObject a = r_longlong(x) b = r_longlong(y) return W_SmallLongObject(op(a, b)) - from pypy.objspace.std.longobject import W_LongObject - w_x = W_LongObject.fromint(space, x) + from pypy.objspace.std.longobject import W_LongObject, W_AbstractLongObject + if w_x is None or not isinstance(w_x, W_AbstractLongObject): + w_x = W_LongObject.fromint(space, x) + if w_y is None or not isinstance(w_y, W_AbstractLongObject): + w_y = W_LongObject.fromint(space, y) - return getattr(w_x, 'descr_' + opname)(space, y_obj) + return getattr(w_x, 'descr_' + opname)(space, w_y) return ovf2long @@ -496,12 +502,12 @@ # can't return NotImplemented (space.pow doesn't do full # ternary, i.e. w_modulus.__zpow__(self, w_exponent)), so # handle it ourselves - return _pow_ovf2long(space, x, y, w_exponent, w_modulus) + return _pow_ovf2long(space, x, self, y, w_exponent, w_modulus) try: result = _pow(space, x, y, z) except (OverflowError, ValueError): - return _pow_ovf2long(space, x, y, w_exponent, w_modulus) + return _pow_ovf2long(space, x, self, y, w_exponent, w_modulus) return space.newint(result) @unwrap_spec(w_modulus=WrappedDefault(None)) @@ -546,7 +552,7 @@ try: z = ovfcheck(op(x, y)) except OverflowError: - return ovf2long(space, x, y, w_other) + return ovf2long(space, x, self, y, w_other) else: z = op(x, y) return wrapint(space, z) @@ -568,7 +574,7 @@ try: z = ovfcheck(op(y, x)) except OverflowError: - return ovf2long(space, y, x, w_other) + return ovf2long(space, y, w_other, x, self) # XXX write a test else: z = op(y, x) return wrapint(space, z) @@ -599,7 +605,7 @@ try: return func(space, x, y) except OverflowError: - return ovf2long(space, x, y, w_other) + return ovf2long(space, x, self, y, w_other) else: return func(space, x, y) @@ -614,7 +620,7 @@ try: return func(space, y, x) except OverflowError: - return ovf2long(space, y, x, self) + return ovf2long(space, y, w_other, x, self) else: return func(space, y, x) diff --git a/pypy/objspace/std/test/test_intobject.py b/pypy/objspace/std/test/test_intobject.py --- a/pypy/objspace/std/test/test_intobject.py +++ b/pypy/objspace/std/test/test_intobject.py @@ -679,6 +679,11 @@ x = int(321) assert x.__rlshift__(333) == 1422567365923326114875084456308921708325401211889530744784729710809598337369906606315292749899759616L + def test_some_rops(self): + import sys + x = int(-sys.maxint) + assert x.__rsub__(2) == (2 + sys.maxint) + class AppTestIntShortcut(AppTestInt): spaceconfig = {"objspace.std.intshortcut": True} From pypy.commits at gmail.com Mon Feb 4 09:58:18 2019 From: pypy.commits at gmail.com (fijal) Date: Mon, 04 Feb 2019 06:58:18 -0800 (PST) Subject: [pypy-commit] pypy arm64: (fijal, arigo) Message-ID: <5c58530a.1c69fb81.c0aec.a6ce@mx.google.com> Author: Maciej Fijalkowski Branch: arm64 Changeset: r95793:35474a89bf8d Date: 2019-02-04 14:57 +0000 http://bitbucket.org/pypy/pypy/changeset/35474a89bf8d/ Log: (fijal, arigo) Use hypothesis diff --git a/rpython/jit/backend/aarch64/test/test_instr_builder.py b/rpython/jit/backend/aarch64/test/test_instr_builder.py --- a/rpython/jit/backend/aarch64/test/test_instr_builder.py +++ b/rpython/jit/backend/aarch64/test/test_instr_builder.py @@ -1,4 +1,4 @@ - +from hypothesis import given, settings, strategies as st from rpython.jit.backend.aarch64 import registers as r from rpython.jit.backend.aarch64 import codebuilder from rpython.jit.backend.aarch64.test.gen import assemble @@ -14,21 +14,23 @@ def hexdump(self): return ''.join(self.buffer) + class TestInstrBuilder(object): - def setup_method(self, meth): - self.cb = CodeBuilder() - def test_ret(self): - self.cb.RET_r(r.x0.value) - res = self.cb.hexdump() - exp = assemble('RET x0') + @settings(max_examples=20) + @given(r1=st.sampled_from(r.registers)) + def test_ret(self, r1): + cb = CodeBuilder() + cb.RET_r(r1.value) + res = cb.hexdump() + exp = assemble('RET %r' % r1) assert res == exp - self.cb = CodeBuilder() - self.cb.RET_r(r.x0.value) - self.cb.RET_r(r.x5.value) - self.cb.RET_r(r.x3.value) - assert self.cb.hexdump() == assemble('RET x0\nRET x5\n RET x3') - def test_call_header(self): - self.cb.STP_rr_preindex(r.x29.value, r.x30.value, r.sp.value, -32) - assert self.cb.hexdump() == assemble("STP x29, x30, [sp, -32]!") + @settings(max_examples=20) + @given(r1=st.sampled_from(r.registers), + r2=st.sampled_from(r.registers), + offset=st.integers(min_value=-64, max_value=63)) + def test_call_header(self, r1, r2, offset): + cb = CodeBuilder() + cb.STP_rr_preindex(r1.value, r2.value, r.sp.value, offset * 8) + assert cb.hexdump() == assemble("STP %r, %r, [sp, %d]!" % (r1, r2, offset * 8)) From pypy.commits at gmail.com Mon Feb 4 10:37:55 2019 From: pypy.commits at gmail.com (ambv) Date: Mon, 04 Feb 2019 07:37:55 -0800 (PST) Subject: [pypy-commit] pypy py3.6: (cfbolz, ambv) [pickle, object] __getnewargs_ex__() is now used in protocols 2 and 3 Message-ID: <5c585c53.1c69fb81.a94b6.d3d1@mx.google.com> Author: Łukasz Langa Branch: py3.6 Changeset: r95794:bae9ac47e0a5 Date: 2019-02-04 16:37 +0100 http://bitbucket.org/pypy/pypy/changeset/bae9ac47e0a5/ Log: (cfbolz,ambv) [pickle,object] __getnewargs_ex__() is now used in protocols 2 and 3 This is a change in Python 3.6. diff --git a/pypy/objspace/std/objectobject.py b/pypy/objspace/std/objectobject.py --- a/pypy/objspace/std/objectobject.py +++ b/pypy/objspace/std/objectobject.py @@ -59,13 +59,9 @@ if not kwargs: newobj = copyreg.__newobj__ args2 = (cls,) + args - elif proto >= 4: + else: newobj = copyreg.__newobj_ex__ args2 = (cls, args, kwargs) - else: - raise ValueError("must use protocol 4 or greater to copy this " - "object; since __getnewargs_ex__ returned " - "keyword arguments.") state = _getstate(obj) listitems = iter(obj) if isinstance(obj, list) else None dictitems = iter(obj.items()) if isinstance(obj, dict) else None diff --git a/pypy/objspace/std/test/test_obj.py b/pypy/objspace/std/test/test_obj.py --- a/pypy/objspace/std/test/test_obj.py +++ b/pypy/objspace/std/test/test_obj.py @@ -70,10 +70,11 @@ def __getnewargs_ex__(self): return (self._name,), dict(value=int(self)) import copyreg - assert NamedInt("Name", value=42).__reduce__(4) == ( - copyreg.__newobj_ex__, - (NamedInt, ('Name',), dict(value=42)), - dict(_name='Name'), None, None) + for protocol in [2, 3, 4]: + assert NamedInt("Name", value=42).__reduce__(protocol) == ( + copyreg.__newobj_ex__, + (NamedInt, ('Name',), dict(value=42)), + dict(_name='Name'), None, None) def test_reduce_ex_does_getattr(self): seen = [] From pypy.commits at gmail.com Mon Feb 4 10:58:27 2019 From: pypy.commits at gmail.com (fijal) Date: Mon, 04 Feb 2019 07:58:27 -0800 (PST) Subject: [pypy-commit] pypy arm64: (fijal, arigo) Message-ID: <5c586123.1c69fb81.11ed8.45ab@mx.google.com> Author: Maciej Fijalkowski Branch: arm64 Changeset: r95795:35539313ed06 Date: 2019-02-04 15:57 +0000 http://bitbucket.org/pypy/pypy/changeset/35539313ed06/ Log: (fijal, arigo) slow progress diff --git a/rpython/jit/backend/aarch64/assembler.py b/rpython/jit/backend/aarch64/assembler.py --- a/rpython/jit/backend/aarch64/assembler.py +++ b/rpython/jit/backend/aarch64/assembler.py @@ -143,6 +143,9 @@ pass def _build_stack_check_slowpath(self): + self.stack_check_slowpath = 0 #XXX + + def _check_frame_depth_debug(self, mc): pass def reserve_gcref_table(self, allgcrefs): @@ -167,25 +170,24 @@ self.mc.BL(self.stack_check_slowpath, c=c.HI) # call if ip > lr def _call_header(self): - stack_size = WORD #alignment - stack_size += len(r.callee_saved_registers) * WORD + stack_size = (len(r.callee_saved_registers) + 2) * WORD + self.mc.STP_rr_preindex(r.fp.value, r.lr.value, r.sp.value, -stack_size) + for i in range(0, len(r.callee_saved_registers), 2): + self.mc.STP_rri(r.callee_saved_registers[i].value, + r.callee_saved_registers[i + 1].value, + r.sp.value, + (i + 2) * WORD) + + #self.saved_threadlocal_addr = 0 # at offset 0 from location 'sp' + # ^^^XXX save it from register x1 into some place if self.cpu.supports_floats: - stack_size += len(r.callee_saved_vfp_registers) * 2 * WORD - - # push all callee saved registers including lr; and push r1 as - # well, which contains the threadlocal_addr argument. Note that - # we're pushing a total of 10 words, which keeps the stack aligned. - self.mc.PUSH([reg.value for reg in r.callee_saved_registers] + - [r.r1.value]) - self.saved_threadlocal_addr = 0 # at offset 0 from location 'sp' - if self.cpu.supports_floats: + XXX self.mc.VPUSH([reg.value for reg in r.callee_saved_vfp_registers]) self.saved_threadlocal_addr += ( len(r.callee_saved_vfp_registers) * 2 * WORD) - assert stack_size % 8 == 0 # ensure we keep alignment - # set fp to point to the JITFRAME - self.mc.MOV_rr(r.fp.value, r.r0.value) + # set fp to point to the JITFRAME, passed in argument 'x0' + self.mc.MOV_rr(r.fp.value, r.x0.value) # gcrootmap = self.cpu.gc_ll_descr.gcrootmap if gcrootmap and gcrootmap.is_shadow_stack: diff --git a/rpython/jit/backend/aarch64/codebuilder.py b/rpython/jit/backend/aarch64/codebuilder.py --- a/rpython/jit/backend/aarch64/codebuilder.py +++ b/rpython/jit/backend/aarch64/codebuilder.py @@ -1,6 +1,8 @@ from rpython.jit.backend.llsupport.asmmemmgr import BlockBuilderMixin from rpython.jit.backend.aarch64.locations import RegisterLocation +from rpython.jit.backend.aarch64 import registers as r + class AbstractAarch64Builder(object): def write32(self, word): @@ -19,6 +21,27 @@ self.write32((base << 22) | ((0x7F & (offset >> 3)) << 15) | (reg2 << 10) | (rn << 5) | reg1) + def STP_rri(self, reg1, reg2, rn, offset): + base = 0b1010100100 + assert -512 <= offset < 512 + assert offset & 0x7 == 0 + self.write32((base << 22) | ((0x7F & (offset >> 3)) << 15) | + (reg2 << 10) | (rn << 5) | reg1) + + def MOV_rr(self, rd, rn): + self.ORR_rr(rd, r.xzr.value, rn) + + def ORR_rr(self, rd, rn, rm): + base = 0b10101010000 + self.write32((base << 21) | (rm << 16) | + (rn << 5) | rd) + + def ADD_ri(self, rd, rn, constant): + base = 0b1001000100 + assert 0 <= constant < 4096 + self.write32((base << 22) | (constant << 10) | + (rn << 5) | rd) + class InstrBuilder(BlockBuilderMixin, AbstractAarch64Builder): def __init__(self, arch_version=7): diff --git a/rpython/jit/backend/aarch64/registers.py b/rpython/jit/backend/aarch64/registers.py --- a/rpython/jit/backend/aarch64/registers.py +++ b/rpython/jit/backend/aarch64/registers.py @@ -4,7 +4,7 @@ registers = [RegisterLocation(i) for i in range(31)] -sp = wzr = ZeroRegister() +sp = xzr = ZeroRegister() [x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30] = registers @@ -18,8 +18,6 @@ ip1 = x17 ip0 = x16 -callee_resp = [x19, x20, x21, x22, fp] +callee_saved_registers = [x19, x20, x21, x22] argument_regs = caller_resp = [x0, x1, x2, x3, x4, x5, x6, x7] - -callee_saved_registers = callee_resp + [lr] diff --git a/rpython/jit/backend/aarch64/test/test_instr_builder.py b/rpython/jit/backend/aarch64/test/test_instr_builder.py --- a/rpython/jit/backend/aarch64/test/test_instr_builder.py +++ b/rpython/jit/backend/aarch64/test/test_instr_builder.py @@ -30,7 +30,18 @@ @given(r1=st.sampled_from(r.registers), r2=st.sampled_from(r.registers), offset=st.integers(min_value=-64, max_value=63)) - def test_call_header(self, r1, r2, offset): + def test_STP_rr(self, r1, r2, offset): cb = CodeBuilder() cb.STP_rr_preindex(r1.value, r2.value, r.sp.value, offset * 8) assert cb.hexdump() == assemble("STP %r, %r, [sp, %d]!" % (r1, r2, offset * 8)) + cb = CodeBuilder() + cb.STP_rri(r1.value, r2.value, r.sp.value, offset * 8) + assert cb.hexdump() == assemble("STP %r, %r, [sp, %d]" % (r1, r2, offset * 8)) + + @settings(max_examples=20) + @given(r1=st.sampled_from(r.registers), + r2=st.sampled_from(r.registers)) + def test_MOV_rr(self, r1, r2): + cb = CodeBuilder() + cb.MOV_rr(r1.value, r2.value) + assert cb.hexdump() == assemble("MOV %r, %r" % (r1, r2)) From pypy.commits at gmail.com Mon Feb 4 11:03:52 2019 From: pypy.commits at gmail.com (ambv) Date: Mon, 04 Feb 2019 08:03:52 -0800 (PST) Subject: [pypy-commit] pypy default: (cfbolz, ambv) [sre] Make the error message consistent with CPython Message-ID: <5c586268.1c69fb81.2cb39.3112@mx.google.com> Author: Łukasz Langa Branch: Changeset: r95796:5961de08b177 Date: 2019-02-04 17:01 +0100 http://bitbucket.org/pypy/pypy/changeset/5961de08b177/ Log: (cfbolz,ambv) [sre] Make the error message consistent with CPython diff --git a/pypy/module/_sre/interp_sre.py b/pypy/module/_sre/interp_sre.py --- a/pypy/module/_sre/interp_sre.py +++ b/pypy/module/_sre/interp_sre.py @@ -531,7 +531,7 @@ assert idx >= 0 return fmarks[idx], fmarks[idx+1] else: - raise oefmt(space.w_IndexError, "group index out of range") + raise oefmt(space.w_IndexError, "no such group") def _last_index(self): mark = self.ctx.match_marks From pypy.commits at gmail.com Mon Feb 4 11:03:55 2019 From: pypy.commits at gmail.com (ambv) Date: Mon, 04 Feb 2019 08:03:55 -0800 (PST) Subject: [pypy-commit] pypy py3.6: Merge default Message-ID: <5c58626b.1c69fb81.cde92.7919@mx.google.com> Author: Łukasz Langa Branch: py3.6 Changeset: r95797:2c9cbd1a5331 Date: 2019-02-04 17:02 +0100 http://bitbucket.org/pypy/pypy/changeset/2c9cbd1a5331/ Log: Merge default diff --git a/pypy/module/_sre/interp_sre.py b/pypy/module/_sre/interp_sre.py --- a/pypy/module/_sre/interp_sre.py +++ b/pypy/module/_sre/interp_sre.py @@ -650,7 +650,7 @@ assert idx >= 0 return fmarks[idx], fmarks[idx+1] else: - raise oefmt(space.w_IndexError, "group index out of range") + raise oefmt(space.w_IndexError, "no such group") def _last_index(self): mark = self.ctx.match_marks From pypy.commits at gmail.com Tue Feb 5 05:28:34 2019 From: pypy.commits at gmail.com (ambv) Date: Tue, 05 Feb 2019 02:28:34 -0800 (PST) Subject: [pypy-commit] pypy py3.6: [test_re] After bpo-30978 str.format_map() correctly passes through lookup exceptions Message-ID: <5c596552.1c69fb81.b7150.a31a@mx.google.com> Author: Łukasz Langa Branch: py3.6 Changeset: r95798:2d839065d4ea Date: 2019-02-05 11:27 +0100 http://bitbucket.org/pypy/pypy/changeset/2d839065d4ea/ Log: [test_re] After bpo-30978 str.format_map() correctly passes through lookup exceptions It no longer wraps IndexError with the KeyError. This is PyPy behavior, too. The test was not updated. diff --git a/lib-python/3/test/test_re.py b/lib-python/3/test/test_re.py --- a/lib-python/3/test/test_re.py +++ b/lib-python/3/test/test_re.py @@ -473,7 +473,7 @@ m[(0,)] with self.assertRaisesRegex(IndexError, 'no such group'): m[(0, 1)] - with self.assertRaisesRegex(KeyError, 'a2'): + with self.assertRaisesRegex(IndexError, 'no such group'): 'a1={a2}'.format_map(m) m = pat.match('ac') From pypy.commits at gmail.com Tue Feb 5 05:57:55 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:57:55 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: started work on windows installer Message-ID: <5c596c33.1c69fb81.9f6c7.2c52@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95799:beb845313379 Date: 2018-12-22 12:03 +0000 http://bitbucket.org/pypy/pypy/changeset/beb845313379/ Log: started work on windows installer diff --git a/pypy/tool/release/windowsinstaller/buildinstaller.py b/pypy/tool/release/windowsinstaller/buildinstaller.py new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/buildinstaller.py @@ -0,0 +1,25 @@ +# +# Script to build the PyPy Installer +# +import sys +import subprocess + + +candle = "candle.exe" +pypywxs = "pypy.wxs" +light = "light.exe" +wixobj = "pypy.wixobj" +path = "pypy3-v6.0.0-win32" + + +build = subprocess.Popen([candle, pypywxs]) +build.wait() + +if build.returncode != 0: + sys.exit("Failed to run candle") + +build = subprocess.Popen([light ,"-ext", "WixUIExtension" ,"-b" , path , wixobj]) +build.wait() + +if build.returncode != 0: + sys.exit("Failed to run light") diff --git a/pypy/tool/release/windowsinstaller/pypy.wxs b/pypy/tool/release/windowsinstaller/pypy.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/pypy.wxs @@ -0,0 +1,64 @@ + + + + + + Privileged + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From pypy.commits at gmail.com Tue Feb 5 05:57:57 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:57:57 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Started merging cpython scripts Message-ID: <5c596c35.1c69fb81.7c215.affb@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95800:c9a11ed5ae5d Date: 2018-12-24 08:55 +0000 http://bitbucket.org/pypy/pypy/changeset/c9a11ed5ae5d/ Log: Started merging cpython scripts diff --git a/pypy/tool/release/windowsinstaller/README b/pypy/tool/release/windowsinstaller/README new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/README @@ -0,0 +1,8 @@ +Prerequisites: + Visual Studio 2017 + Wix 3.11.1 + Wix VSExtension for VS 2017 + +Steps: + 1. Translate the interpretter from the \pypy\goal folder. + 2. Run buildrelease.bat from the visual studio command line. \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/buildrelease.bat b/pypy/tool/release/windowsinstaller/buildrelease.bat new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/buildrelease.bat @@ -0,0 +1,44 @@ + at setlocal + at echo off + + +set D=%~dp0 +set PCBUILD=%D%..\..\..\..\PCbuild\ +if "%Py_OutDir%"=="" set Py_OutDir=%PCBUILD% +set EXTERNALS=%D%..\..\externals\windows-installer\ + +set BUILDX86= +set BUILDX64= +set TARGET=Rebuild +set TESTTARGETDIR= +set PGO=-m test -q --pgo +set BUILDNUGET=1 +set BUILDZIP=1 + + +:CheckOpts +if "%1" EQU "-h" goto Help +if "%1" EQU "-o" (set OUTDIR=%~2) && shift && shift && goto CheckOpts +if "%1" EQU "--out" (set OUTDIR=%~2) && shift && shift && goto CheckOpts + + +if "%1" NEQ "" echo Invalid option: "%1" && exit /B 1 + +if not defined BUILDX86 if not defined BUILDX64 (set BUILDX86=1) && (set BUILDX64=1) + +call "%PCBUILD%find_msbuild.bat" %MSBUILD% +if ERRORLEVEL 1 (echo Cannot locate MSBuild.exe on PATH or as MSBUILD variable & exit /b 2) + + +%MSBUILD% "%D%bundle\releaselocal.wixproj" /t:Rebuild /p:RebuildAll=true +if errorlevel 1 exit /B + +exit /B 0 + +:Help +echo buildrelease.bat [--out DIR] +echo [-h] +echo. +echo --out (-o) Specify an additional output directory for installers +echo -h Display this help information +echo. diff --git a/pypy/tool/release/windowsinstaller/msi.props b/pypy/tool/release/windowsinstaller/msi.props new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/msi.props @@ -0,0 +1,185 @@ + + + + $(OutputName) + false + false + $(SuppressIces);ICE03;ICE57;ICE61 + 1026 + false + true + Release + x86 + perUser + <_MakeCatCommand Condition="'$(_MakeCatCommand)' == ''">makecat + + + + + + + + $(ComputerName)/$(ArchName)/ + $(ReleaseUri)/ + + + + + + + + WixUtilExtension + WixUtilExtension + + + + + $(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)$(ArchName)_$(Configuration)\msi_$(OutputName) + $(IntermediateOutputPath)_$(OutputSuffix) + $(BuildPath) + $(OutputPath)\ + $(OutputPath) + true + $(ExternalsDir)\windows-installer\redist-1\$(Platform) + $([System.IO.Path]::GetFullPath($(CRTRedist))) + pypy$(MajorVersionNumber)$(MinorVersionNumber)$(MicroVersionNumber)$(ReleaseLevelName).chm + + $(MajorVersionNumber).$(MinorVersionNumber).$(Field3Value).0 + + + + $([System.Math]::Floor($([System.DateTime]::Now.Subtract($([System.DateTime]::new(2001, 1, 1))).TotalDays))) + $(MajorVersionNumber).$(MinorVersionNumber).$(MicroVersionNumber)dev$(RevisionNumber) + $(MajorVersionNumber).$(MinorVersionNumber).$(RevisionNumber).0 + + + + 32-bit + 64-bit + 32bit + 64bit + + $(DefineConstants); + Version=$(InstallerVersion); + ShortVersion=$(MajorVersionNumber).$(MinorVersionNumber); + LongVersion=$(PythonVersion); + MajorVersionNumber=$(MajorVersionNumber); + MinorVersionNumber=$(MinorVersionNumber); + UpgradeMinimumVersion=$(MajorVersionNumber).$(MinorVersionNumber).0.0; + NextMajorVersionNumber=$(MajorVersionNumber).$([msbuild]::Add($(MinorVersionNumber), 1)).0.0; + Bitness=$(Bitness); + PlatformArchitecture=$(PlatformArchitecture); + PyDebugExt=$(PyDebugExt); + PyArchExt=$(PyArchExt); + PyTestExt=$(PyTestExt); + OptionalFeatureName=$(OutputName); + + + $(DefineConstants);CRTRedist=$(CRTRedist); + + + $(DefineConstants);Suffix32=-32;ssltag=-1_1; + + + $(DefineConstants);Suffix32=;ssltag=-1_1-x64; + + + + + + generated_filelist + + + false + + + false + + + + + + + + + + + src + + + tcltk + + + redist + + + build32 + + + build64 + + + + + + + + + <_Uuid Include="CoreUpgradeCode"> + upgradecode + + <_Uuid Include="UpgradeCode"> + upgradecode/$(OutputName) + + <_Uuid Include="InstallDirectoryGuidSeed"> + installdirectoryseed + + <_Uuid Include="PythonExeComponentGuid"> + pypy.exe + + <_Uuid Include="PythonwExeComponentGuid"> + pypyw.exe + + <_Uuid Include="RemoveLib2to3PickleComponentGuid"> + lib2to3/pickles + + <_Uuid Include="CommonPythonRegComponentGuid"> + registry + + <_Uuid Include="PythonRegComponentGuid"> + registry/$(OutputName) + + + + + <_Uuids>@(_Uuid->'("%(Identity)", "$(MajorVersionNumber).$(MinorVersionNumber)/%(Uri)")',',') + <_GenerateCommand>import uuid; print('\n'.join('{}={}'.format(i, uuid.uuid5(uuid.UUID('c8d9733e-a70c-43ff-ab0c-e26456f11083'), '$(ReleaseUri.Replace(`{arch}`, `$(ArchName)`))' + j)) for i,j in [$(_Uuids.Replace(`"`,`'`))])) + + + + + + + + + + $(DefineConstants);@(_UuidValue,';'); + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/msi.targets b/pypy/tool/release/windowsinstaller/msi.targets new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/msi.targets @@ -0,0 +1,93 @@ + + + + + + <_FileListTarget>$(IntermediateOutputPath)$(MSBuildProjectName).g.csv + <_InstallFilesTarget>$(IntermediateOutputPath)$(MSBuildProjectName).g.wxs + + + + + <_Source>%(Source)$([msbuild]::MakeRelative(%(SourceBase), %(FullPath))) + <_Target>%(Target_)$([msbuild]::MakeRelative(%(TargetBase), %(FullPath))) + + + <_CatalogFiles Include="@(InstallFiles)" Condition="%(InstallFiles.IncludeInCat) and ''!=$([System.IO.File]::ReadAllText(%(InstallFiles.FullPath)))" /> + + + + + + + + + + + + + + <_CatFileSourceTarget>$(IntermediateOutputPath)$(MSBuildProjectName).cdf + <_CatFileTarget>$(IntermediateOutputPath)python_$(MSBuildProjectName).cat + <_CatFile>[CatalogHeader] +Name=$([System.IO.Path]::GetFileName($(_CatFileTarget))) +ResultDir=$([System.IO.Path]::GetDirectoryName($(_CatFileTarget))) +PublicVersion=1 +CatalogVersion=2 +HashAlgorithms=SHA256 +PageHashes=false +EncodingType= + +[CatalogFiles] +@(_CatalogFiles->'<HASH>%(Filename)%(Extension)=%(FullPath)',' +') + + + + + + + + + + + + + + + <_Content>$([System.IO.File]::ReadAllText(%(WxlTemplate.FullPath)).Replace(`{{ShortVersion}}`, `$(MajorVersionNumber).$(MinorVersionNumber)$(PyTestExt)`).Replace(`{{LongVersion}}`, `$(PythonVersion)$(PyTestExt)`).Replace(`{{Bitness}}`, `$(Bitness)`)) + <_ExistingContent Condition="Exists('$(IntermediateOutputPath)%(WxlTemplate.Filename).wxl')">$([System.IO.File]::ReadAllText($(IntermediateOutputPath)%(WxlTemplate.Filename).wxl)) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/wix.props b/pypy/tool/release/windowsinstaller/wix.props new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/wix.props @@ -0,0 +1,13 @@ + + + + + + $(MSBuildThisFileDirectory)\Wix\ + $(MSBuildExtensionsPath32)\Microsoft\Wix\v3.x\ + $(ExternalsDir)\windows-installer\wix\ + $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Installer XML\3.11 at InstallRoot) + $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows Installer XML\3.11 at InstallRoot) + $(WixInstallPath)\Wix.targets + + \ No newline at end of file From pypy.commits at gmail.com Tue Feb 5 05:58:01 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:01 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Further hackery. Message-ID: <5c596c39.1c69fb81.e4d29.6c5a@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95802:1e97dbe9c397 Date: 2018-12-24 21:06 +0000 http://bitbucket.org/pypy/pypy/changeset/1e97dbe9c397/ Log: Further hackery. diff --git a/pypy/tool/release/windowsinstaller/bundle/bootstrap/pythonba.vcxproj b/pypy/tool/release/windowsinstaller/bundle/bootstrap/pythonba.vcxproj --- a/pypy/tool/release/windowsinstaller/bundle/bootstrap/pythonba.vcxproj +++ b/pypy/tool/release/windowsinstaller/bundle/bootstrap/pythonba.vcxproj @@ -21,6 +21,7 @@ Release Win32 + v141 v140 v120 {7A09B132-B3EE-499B-A700-A4B2157FEA3D} @@ -40,18 +41,18 @@ _CRT_STDIO_LEGACY_WIDE_SPECIFIERS=1;%(PreprocessorDefinitions) - $(WixInstallPath)sdk\vs2017\inc - $(WixInstallPath)sdk\vs2015\inc - $(WixInstallPath)sdk\vs2013\inc + $(WixRootPath)sdk\vs2017\inc + $(WixRootPath)sdk\vs2015\inc + $(WixRootPath)sdk\vs2013\inc Use pch.h MultiThreaded comctl32.lib;gdiplus.lib;msimg32.lib;shlwapi.lib;wininet.lib;dutil.lib;balutil.lib;version.lib;uxtheme.lib;%(AdditionalDependencies) - $(WixInstallPath)sdk\vs2017\lib\x86 - $(WixInstallPath)sdk\vs2015\lib\x86 - $(WixInstallPath)sdk\vs2013\lib\x86 + $(WixRootPath)sdk\vs2017\lib\x86 + $(WixRootPath)sdk\vs2015\lib\x86 + $(WixRootPath)sdk\vs2013\lib\x86 pythonba.def true diff --git a/pypy/tool/release/windowsinstaller/include/include.wixproj b/pypy/tool/release/windowsinstaller/include/include.wixproj new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/include/include.wixproj @@ -0,0 +1,26 @@ + + + + {93912B72-A2D0-49C4-84DC-5B5B490B60AC} + 2.0 + dev + Package + + + + + + + + + + + $(PySourcePath) + !(bindpath.src) + $(PySourcePath) + + include + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/include/include.wxs b/pypy/tool/release/windowsinstaller/include/include.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/include/include.wxs @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/pypy/tool/release/windowsinstaller/include/include_en-US.wxl b/pypy/tool/release/windowsinstaller/include/include_en-US.wxl new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/include/include_en-US.wxl @@ -0,0 +1,5 @@ + + + Header Files + include + diff --git a/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wixproj b/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wixproj new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wixproj @@ -0,0 +1,17 @@ + + + + {8B49F535-E206-4149-8D88-752929BCEB87} + 2.0 + lib_pypy + Package + + + + + + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wxs b/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wxs @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy_en-US.wxl b/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy_en-US.wxl new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy_en-US.wxl @@ -0,0 +1,5 @@ + + + Header Files + include + diff --git a/pypy/tool/release/windowsinstaller/wix.props b/pypy/tool/release/windowsinstaller/wix.props --- a/pypy/tool/release/windowsinstaller/wix.props +++ b/pypy/tool/release/windowsinstaller/wix.props @@ -5,9 +5,10 @@ $(MSBuildThisFileDirectory)\Wix\ $(MSBuildExtensionsPath32)\Microsoft\Wix\v3.x\ - $(ExternalsDir)\windows-installer\wix\ $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Installer XML\3.11 at InstallRoot) $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows Installer XML\3.11 at InstallRoot) + $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Installer XML\3.11 at InstallFolder) + $(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows Installer XML\3.11 at InstallFolder) $(WixMSBuildPath)\Wix.targets \ No newline at end of file From pypy.commits at gmail.com Tue Feb 5 05:57:59 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:57:59 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Got to the point where bootstrap is being built. Message-ID: <5c596c37.1c69fb81.f01f9.488d@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95801:fb576872b8ca Date: 2018-12-24 19:53 +0000 http://bitbucket.org/pypy/pypy/changeset/fb576872b8ca/ Log: Got to the point where bootstrap is being built. diff too long, truncating to 2000 out of 3893 lines diff --git a/pypy/tool/release/windowsinstaller/bundle/bootstrap/LICENSE.txt b/pypy/tool/release/windowsinstaller/bundle/bootstrap/LICENSE.txt new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/bootstrap/LICENSE.txt @@ -0,0 +1,25 @@ +This license applies to the bootstrapper application that is embedded within the installer. It has no impact on the licensing for the rest of the installer or Python itself, as no code covered by this license exists in any other part of the product. + +--- + +Microsoft Reciprocal License (MS-RL) + +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. + +1. Definitions + The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. + A "contribution" is the original software, or any additions or changes to the software. + A "contributor" is any person that distributes its contribution under this license. + "Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights + (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + (A) Reciprocal Grants- For any file you distribute that contains code from the software (in source code or binary format), you must provide recipients the source code to that file along with a copy of this license, which license will govern that file. You may license other files that are entirely your own work and do not contain code from the software under any terms you choose. + (B) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + (C) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + (D) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + (E) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + (F) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. diff --git a/pypy/tool/release/windowsinstaller/bundle/bootstrap/PythonBootstrapperApplication.cpp b/pypy/tool/release/windowsinstaller/bundle/bootstrap/PythonBootstrapperApplication.cpp new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/bootstrap/PythonBootstrapperApplication.cpp @@ -0,0 +1,3233 @@ +//------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2004, Outercurve Foundation. +// This software is released under Microsoft Reciprocal License (MS-RL). +// The license and further copyright text can be found in the file +// LICENSE.TXT at the root directory of the distribution. +// +//------------------------------------------------------------------------------------------------- + + +#include "pch.h" + +static const LPCWSTR PYBA_WINDOW_CLASS = L"PythonBA"; +static const DWORD PYBA_ACQUIRE_PERCENTAGE = 30; +static const LPCWSTR PYBA_VARIABLE_BUNDLE_FILE_VERSION = L"WixBundleFileVersion"; + +enum PYBA_STATE { + PYBA_STATE_INITIALIZING, + PYBA_STATE_INITIALIZED, + PYBA_STATE_HELP, + PYBA_STATE_DETECTING, + PYBA_STATE_DETECTED, + PYBA_STATE_PLANNING, + PYBA_STATE_PLANNED, + PYBA_STATE_APPLYING, + PYBA_STATE_CACHING, + PYBA_STATE_CACHED, + PYBA_STATE_EXECUTING, + PYBA_STATE_EXECUTED, + PYBA_STATE_APPLIED, + PYBA_STATE_FAILED, +}; + +static const int WM_PYBA_SHOW_HELP = WM_APP + 100; +static const int WM_PYBA_DETECT_PACKAGES = WM_APP + 101; +static const int WM_PYBA_PLAN_PACKAGES = WM_APP + 102; +static const int WM_PYBA_APPLY_PACKAGES = WM_APP + 103; +static const int WM_PYBA_CHANGE_STATE = WM_APP + 104; +static const int WM_PYBA_SHOW_FAILURE = WM_APP + 105; + +// This enum must be kept in the same order as the PAGE_NAMES array. +enum PAGE { + PAGE_LOADING, + PAGE_HELP, + PAGE_INSTALL, + PAGE_UPGRADE, + PAGE_SIMPLE_INSTALL, + PAGE_CUSTOM1, + PAGE_CUSTOM2, + PAGE_MODIFY, + PAGE_PROGRESS, + PAGE_PROGRESS_PASSIVE, + PAGE_SUCCESS, + PAGE_FAILURE, + COUNT_PAGE, +}; + +// This array must be kept in the same order as the PAGE enum. +static LPCWSTR PAGE_NAMES[] = { + L"Loading", + L"Help", + L"Install", + L"Upgrade", + L"SimpleInstall", + L"Custom1", + L"Custom2", + L"Modify", + L"Progress", + L"ProgressPassive", + L"Success", + L"Failure", +}; + +enum CONTROL_ID { + // Non-paged controls + ID_CLOSE_BUTTON = THEME_FIRST_ASSIGN_CONTROL_ID, + ID_MINIMIZE_BUTTON, + + // Welcome page + ID_INSTALL_BUTTON, + ID_INSTALL_CUSTOM_BUTTON, + ID_INSTALL_SIMPLE_BUTTON, + ID_INSTALL_UPGRADE_BUTTON, + ID_INSTALL_UPGRADE_CUSTOM_BUTTON, + ID_INSTALL_CANCEL_BUTTON, + ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, + + // Customize Page + ID_TARGETDIR_EDITBOX, + ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, + ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX, + ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, + ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL, + ID_CUSTOM_COMPILE_ALL_CHECKBOX, + ID_CUSTOM_BROWSE_BUTTON, + ID_CUSTOM_BROWSE_BUTTON_LABEL, + ID_CUSTOM_INSTALL_BUTTON, + ID_CUSTOM_NEXT_BUTTON, + ID_CUSTOM1_BACK_BUTTON, + ID_CUSTOM2_BACK_BUTTON, + ID_CUSTOM1_CANCEL_BUTTON, + ID_CUSTOM2_CANCEL_BUTTON, + + // Modify page + ID_MODIFY_BUTTON, + ID_REPAIR_BUTTON, + ID_UNINSTALL_BUTTON, + ID_MODIFY_CANCEL_BUTTON, + + // Progress page + ID_CACHE_PROGRESS_PACKAGE_TEXT, + ID_CACHE_PROGRESS_BAR, + ID_CACHE_PROGRESS_TEXT, + + ID_EXECUTE_PROGRESS_PACKAGE_TEXT, + ID_EXECUTE_PROGRESS_BAR, + ID_EXECUTE_PROGRESS_TEXT, + ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, + + ID_OVERALL_PROGRESS_PACKAGE_TEXT, + ID_OVERALL_PROGRESS_BAR, + ID_OVERALL_CALCULATED_PROGRESS_BAR, + ID_OVERALL_PROGRESS_TEXT, + + ID_PROGRESS_CANCEL_BUTTON, + + // Success page + ID_SUCCESS_TEXT, + ID_SUCCESS_RESTART_TEXT, + ID_SUCCESS_RESTART_BUTTON, + ID_SUCCESS_CANCEL_BUTTON, + ID_SUCCESS_MAX_PATH_BUTTON, + + // Failure page + ID_FAILURE_LOGFILE_LINK, + ID_FAILURE_MESSAGE_TEXT, + ID_FAILURE_RESTART_TEXT, + ID_FAILURE_RESTART_BUTTON, + ID_FAILURE_CANCEL_BUTTON +}; + +static THEME_ASSIGN_CONTROL_ID CONTROL_ID_NAMES[] = { + { ID_CLOSE_BUTTON, L"CloseButton" }, + { ID_MINIMIZE_BUTTON, L"MinimizeButton" }, + + { ID_INSTALL_BUTTON, L"InstallButton" }, + { ID_INSTALL_CUSTOM_BUTTON, L"InstallCustomButton" }, + { ID_INSTALL_SIMPLE_BUTTON, L"InstallSimpleButton" }, + { ID_INSTALL_UPGRADE_BUTTON, L"InstallUpgradeButton" }, + { ID_INSTALL_UPGRADE_CUSTOM_BUTTON, L"InstallUpgradeCustomButton" }, + { ID_INSTALL_CANCEL_BUTTON, L"InstallCancelButton" }, + { ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, L"InstallLauncherAllUsers" }, + + { ID_TARGETDIR_EDITBOX, L"TargetDir" }, + { ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, L"AssociateFiles" }, + { ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX, L"InstallAllUsers" }, + { ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, L"CustomInstallLauncherAllUsers" }, + { ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL, L"Include_launcherHelp" }, + { ID_CUSTOM_COMPILE_ALL_CHECKBOX, L"CompileAll" }, + { ID_CUSTOM_BROWSE_BUTTON, L"CustomBrowseButton" }, + { ID_CUSTOM_BROWSE_BUTTON_LABEL, L"CustomBrowseButtonLabel" }, + { ID_CUSTOM_INSTALL_BUTTON, L"CustomInstallButton" }, + { ID_CUSTOM_NEXT_BUTTON, L"CustomNextButton" }, + { ID_CUSTOM1_BACK_BUTTON, L"Custom1BackButton" }, + { ID_CUSTOM2_BACK_BUTTON, L"Custom2BackButton" }, + { ID_CUSTOM1_CANCEL_BUTTON, L"Custom1CancelButton" }, + { ID_CUSTOM2_CANCEL_BUTTON, L"Custom2CancelButton" }, + + { ID_MODIFY_BUTTON, L"ModifyButton" }, + { ID_REPAIR_BUTTON, L"RepairButton" }, + { ID_UNINSTALL_BUTTON, L"UninstallButton" }, + { ID_MODIFY_CANCEL_BUTTON, L"ModifyCancelButton" }, + + { ID_CACHE_PROGRESS_PACKAGE_TEXT, L"CacheProgressPackageText" }, + { ID_CACHE_PROGRESS_BAR, L"CacheProgressbar" }, + { ID_CACHE_PROGRESS_TEXT, L"CacheProgressText" }, + { ID_EXECUTE_PROGRESS_PACKAGE_TEXT, L"ExecuteProgressPackageText" }, + { ID_EXECUTE_PROGRESS_BAR, L"ExecuteProgressbar" }, + { ID_EXECUTE_PROGRESS_TEXT, L"ExecuteProgressText" }, + { ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L"ExecuteProgressActionDataText" }, + { ID_OVERALL_PROGRESS_PACKAGE_TEXT, L"OverallProgressPackageText" }, + { ID_OVERALL_PROGRESS_BAR, L"OverallProgressbar" }, + { ID_OVERALL_CALCULATED_PROGRESS_BAR, L"OverallCalculatedProgressbar" }, + { ID_OVERALL_PROGRESS_TEXT, L"OverallProgressText" }, + { ID_PROGRESS_CANCEL_BUTTON, L"ProgressCancelButton" }, + + { ID_SUCCESS_TEXT, L"SuccessText" }, + { ID_SUCCESS_RESTART_TEXT, L"SuccessRestartText" }, + { ID_SUCCESS_RESTART_BUTTON, L"SuccessRestartButton" }, + { ID_SUCCESS_CANCEL_BUTTON, L"SuccessCancelButton" }, + { ID_SUCCESS_MAX_PATH_BUTTON, L"SuccessMaxPathButton" }, + + { ID_FAILURE_LOGFILE_LINK, L"FailureLogFileLink" }, + { ID_FAILURE_MESSAGE_TEXT, L"FailureMessageText" }, + { ID_FAILURE_RESTART_TEXT, L"FailureRestartText" }, + { ID_FAILURE_RESTART_BUTTON, L"FailureRestartButton" }, + { ID_FAILURE_CANCEL_BUTTON, L"FailureCancelButton" }, +}; + +static struct { LPCWSTR regName; LPCWSTR variableName; } OPTIONAL_FEATURES[] = { + { L"core_d", L"Include_debug" }, + { L"core_pdb", L"Include_symbols" }, + { L"dev", L"Include_dev" }, + { L"doc", L"Include_doc" }, + { L"exe", L"Include_exe" }, + { L"lib", L"Include_lib" }, + { L"path", L"PrependPath" }, + { L"pip", L"Include_pip" }, + { L"tcltk", L"Include_tcltk" }, + { L"test", L"Include_test" }, + { L"tools", L"Include_tools" }, + { L"Shortcuts", L"Shortcuts" }, + // Include_launcher and AssociateFiles are handled separately and so do + // not need to be included in this list. + { nullptr, nullptr } +}; + + + +class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication { + void ShowPage(DWORD newPageId) { + // Process each control for special handling in the new page. + ProcessPageControls(ThemeGetPage(_theme, newPageId)); + + // Enable disable controls per-page. + if (_pageIds[PAGE_INSTALL] == newPageId || + _pageIds[PAGE_SIMPLE_INSTALL] == newPageId || + _pageIds[PAGE_UPGRADE] == newPageId) { + InstallPage_Show(); + } else if (_pageIds[PAGE_CUSTOM1] == newPageId) { + Custom1Page_Show(); + } else if (_pageIds[PAGE_CUSTOM2] == newPageId) { + Custom2Page_Show(); + } else if (_pageIds[PAGE_MODIFY] == newPageId) { + ModifyPage_Show(); + } else if (_pageIds[PAGE_SUCCESS] == newPageId) { + SuccessPage_Show(); + } else if (_pageIds[PAGE_FAILURE] == newPageId) { + FailurePage_Show(); + } + + // Prevent repainting while switching page to avoid ugly flickering + _suppressPaint = TRUE; + ThemeShowPage(_theme, newPageId, SW_SHOW); + ThemeShowPage(_theme, _visiblePageId, SW_HIDE); + _suppressPaint = FALSE; + InvalidateRect(_theme->hwndParent, nullptr, TRUE); + _visiblePageId = newPageId; + + // On the install page set the focus to the install button or + // the next enabled control if install is disabled + if (_pageIds[PAGE_INSTALL] == newPageId) { + ThemeSetFocus(_theme, ID_INSTALL_BUTTON); + } else if (_pageIds[PAGE_SIMPLE_INSTALL] == newPageId) { + ThemeSetFocus(_theme, ID_INSTALL_SIMPLE_BUTTON); + } + } + + // + // Handles control clicks + // + void OnCommand(CONTROL_ID id) { + LPWSTR defaultDir = nullptr; + LPWSTR targetDir = nullptr; + LONGLONG elevated, crtInstalled, installAllUsers; + BOOL checked, launcherChecked; + WCHAR wzPath[MAX_PATH] = { }; + BROWSEINFOW browseInfo = { }; + PIDLIST_ABSOLUTE pidl = nullptr; + DWORD pageId; + HRESULT hr = S_OK; + + switch(id) { + case ID_CLOSE_BUTTON: + OnClickCloseButton(); + break; + + // Install commands + case ID_INSTALL_SIMPLE_BUTTON: __fallthrough; + case ID_INSTALL_UPGRADE_BUTTON: __fallthrough; + case ID_INSTALL_BUTTON: + SavePageSettings(); + + hr = BalGetNumericVariable(L"InstallAllUsers", &installAllUsers); + ExitOnFailure(hr, L"Failed to get install scope"); + + hr = _engine->SetVariableNumeric(L"CompileAll", installAllUsers); + ExitOnFailure(hr, L"Failed to update CompileAll"); + + hr = EnsureTargetDir(); + ExitOnFailure(hr, L"Failed to set TargetDir"); + + OnPlan(BOOTSTRAPPER_ACTION_INSTALL); + break; + + case ID_CUSTOM1_BACK_BUTTON: + SavePageSettings(); + if (_modifying) { + GoToPage(PAGE_MODIFY); + } else if (_upgrading) { + GoToPage(PAGE_UPGRADE); + } else { + GoToPage(PAGE_INSTALL); + } + break; + + case ID_INSTALL_CUSTOM_BUTTON: __fallthrough; + case ID_INSTALL_UPGRADE_CUSTOM_BUTTON: __fallthrough; + case ID_CUSTOM2_BACK_BUTTON: + SavePageSettings(); + GoToPage(PAGE_CUSTOM1); + break; + + case ID_CUSTOM_NEXT_BUTTON: + SavePageSettings(); + GoToPage(PAGE_CUSTOM2); + break; + + case ID_CUSTOM_INSTALL_BUTTON: + SavePageSettings(); + + hr = EnsureTargetDir(); + ExitOnFailure(hr, L"Failed to set TargetDir"); + + hr = BalGetStringVariable(L"TargetDir", &targetDir); + if (SUCCEEDED(hr)) { + // TODO: Check whether directory exists and contains another installation + ReleaseStr(targetDir); + } + + OnPlan(_command.action); + break; + + case ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX: + checked = ThemeIsControlChecked(_theme, ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX); + _engine->SetVariableNumeric(L"InstallLauncherAllUsers", checked); + + ThemeControlElevates(_theme, ID_INSTALL_BUTTON, WillElevate()); + break; + + case ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX: + checked = ThemeIsControlChecked(_theme, ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX); + _engine->SetVariableNumeric(L"InstallLauncherAllUsers", checked); + + ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, WillElevate()); + break; + + case ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX: + checked = ThemeIsControlChecked(_theme, ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX); + _engine->SetVariableNumeric(L"InstallAllUsers", checked); + + ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, WillElevate()); + ThemeControlEnable(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, !checked); + if (checked) { + _engine->SetVariableNumeric(L"CompileAll", 1); + ThemeSendControlMessage(_theme, ID_CUSTOM_COMPILE_ALL_CHECKBOX, BM_SETCHECK, BST_CHECKED, 0); + } + ThemeGetTextControl(_theme, ID_TARGETDIR_EDITBOX, &targetDir); + if (targetDir) { + // Check the current value against the default to see + // if we should switch it automatically. + hr = BalGetStringVariable( + checked ? L"DefaultJustForMeTargetDir" : L"DefaultAllUsersTargetDir", + &defaultDir + ); + + if (SUCCEEDED(hr) && defaultDir) { + LPWSTR formatted = nullptr; + if (defaultDir[0] && SUCCEEDED(BalFormatString(defaultDir, &formatted))) { + if (wcscmp(formatted, targetDir) == 0) { + ReleaseStr(defaultDir); + defaultDir = nullptr; + ReleaseStr(formatted); + formatted = nullptr; + + hr = BalGetStringVariable( + checked ? L"DefaultAllUsersTargetDir" : L"DefaultJustForMeTargetDir", + &defaultDir + ); + if (SUCCEEDED(hr) && defaultDir && defaultDir[0] && SUCCEEDED(BalFormatString(defaultDir, &formatted))) { + ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, formatted); + ReleaseStr(formatted); + } + } else { + ReleaseStr(formatted); + } + } + + ReleaseStr(defaultDir); + } + } + break; + + case ID_CUSTOM_BROWSE_BUTTON: + browseInfo.hwndOwner = _hWnd; + browseInfo.pszDisplayName = wzPath; + browseInfo.lpszTitle = _theme->sczCaption; + browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; + pidl = ::SHBrowseForFolderW(&browseInfo); + if (pidl && ::SHGetPathFromIDListW(pidl, wzPath)) { + ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, wzPath); + } + + if (pidl) { + ::CoTaskMemFree(pidl); + } + break; + + // Modify commands + case ID_MODIFY_BUTTON: + // Some variables cannot be modified + _engine->SetVariableString(L"InstallAllUsersState", L"disable"); + _engine->SetVariableString(L"InstallLauncherAllUsersState", L"disable"); + _engine->SetVariableString(L"TargetDirState", L"disable"); + _engine->SetVariableString(L"CustomBrowseButtonState", L"disable"); + _modifying = TRUE; + GoToPage(PAGE_CUSTOM1); + break; + + case ID_REPAIR_BUTTON: + OnPlan(BOOTSTRAPPER_ACTION_REPAIR); + break; + + case ID_UNINSTALL_BUTTON: + OnPlan(BOOTSTRAPPER_ACTION_UNINSTALL); + break; + + case ID_SUCCESS_MAX_PATH_BUTTON: + EnableMaxPathSupport(); + ThemeControlEnable(_theme, ID_SUCCESS_MAX_PATH_BUTTON, FALSE); + break; + } + + LExit: + return; + } + + void InstallPage_Show() { + // Ensure the All Users install button has a UAC shield + BOOL elevated = WillElevate(); + ThemeControlElevates(_theme, ID_INSTALL_BUTTON, elevated); + ThemeControlElevates(_theme, ID_INSTALL_SIMPLE_BUTTON, elevated); + ThemeControlElevates(_theme, ID_INSTALL_UPGRADE_BUTTON, elevated); + } + + void Custom1Page_Show() { + LONGLONG installLauncherAllUsers; + + if (FAILED(BalGetNumericVariable(L"InstallLauncherAllUsers", &installLauncherAllUsers))) { + installLauncherAllUsers = 0; + } + + ThemeSendControlMessage(_theme, ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, BM_SETCHECK, + installLauncherAllUsers ? BST_CHECKED : BST_UNCHECKED, 0); + + LOC_STRING *pLocString = nullptr; + LPCWSTR locKey = L"#(loc.Include_launcherHelp)"; + LONGLONG detectedLauncher; + + if (SUCCEEDED(BalGetNumericVariable(L"DetectedLauncher", &detectedLauncher)) && detectedLauncher) { + locKey = L"#(loc.Include_launcherRemove)"; + } else if (SUCCEEDED(BalGetNumericVariable(L"DetectedOldLauncher", &detectedLauncher)) && detectedLauncher) { + locKey = L"#(loc.Include_launcherUpgrade)"; + } + + if (SUCCEEDED(LocGetString(_wixLoc, locKey, &pLocString)) && pLocString) { + ThemeSetTextControl(_theme, ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL, pLocString->wzText); + } + } + + void Custom2Page_Show() { + HRESULT hr; + LONGLONG installAll, includeLauncher; + + if (FAILED(BalGetNumericVariable(L"InstallAllUsers", &installAll))) { + installAll = 0; + } + + if (WillElevate()) { + ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, TRUE); + ThemeShowControl(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, SW_HIDE); + } else { + ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, FALSE); + ThemeShowControl(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, SW_SHOW); + } + + if (SUCCEEDED(BalGetNumericVariable(L"Include_launcher", &includeLauncher)) && includeLauncher) { + ThemeControlEnable(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, TRUE); + } else { + ThemeSendControlMessage(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, BM_SETCHECK, BST_UNCHECKED, 0); + ThemeControlEnable(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, FALSE); + } + + LPWSTR targetDir = nullptr; + hr = BalGetStringVariable(L"TargetDir", &targetDir); + if (SUCCEEDED(hr) && targetDir && targetDir[0]) { + ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, targetDir); + StrFree(targetDir); + } else if (SUCCEEDED(hr)) { + StrFree(targetDir); + targetDir = nullptr; + + LPWSTR defaultTargetDir = nullptr; + hr = BalGetStringVariable(L"DefaultCustomTargetDir", &defaultTargetDir); + if (SUCCEEDED(hr) && defaultTargetDir && !defaultTargetDir[0]) { + StrFree(defaultTargetDir); + defaultTargetDir = nullptr; + + hr = BalGetStringVariable( + installAll ? L"DefaultAllUsersTargetDir" : L"DefaultJustForMeTargetDir", + &defaultTargetDir + ); + } + if (SUCCEEDED(hr) && defaultTargetDir) { + if (defaultTargetDir[0] && SUCCEEDED(BalFormatString(defaultTargetDir, &targetDir))) { + ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, targetDir); + StrFree(targetDir); + } + StrFree(defaultTargetDir); + } + } + } + + void ModifyPage_Show() { + ThemeControlEnable(_theme, ID_REPAIR_BUTTON, !_suppressRepair); + } + + void SuccessPage_Show() { + // on the "Success" page, check if the restart button should be enabled. + BOOL showRestartButton = FALSE; + LOC_STRING *successText = nullptr; + HRESULT hr = S_OK; + + if (_restartRequired) { + if (BOOTSTRAPPER_RESTART_PROMPT == _command.restart) { + showRestartButton = TRUE; + } + } + + switch (_plannedAction) { + case BOOTSTRAPPER_ACTION_INSTALL: + hr = LocGetString(_wixLoc, L"#(loc.SuccessInstallMessage)", &successText); + break; + case BOOTSTRAPPER_ACTION_MODIFY: + hr = LocGetString(_wixLoc, L"#(loc.SuccessModifyMessage)", &successText); + break; + case BOOTSTRAPPER_ACTION_REPAIR: + hr = LocGetString(_wixLoc, L"#(loc.SuccessRepairMessage)", &successText); + break; + case BOOTSTRAPPER_ACTION_UNINSTALL: + hr = LocGetString(_wixLoc, L"#(loc.SuccessRemoveMessage)", &successText); + break; + } + + if (successText) { + LPWSTR formattedString = nullptr; + BalFormatString(successText->wzText, &formattedString); + if (formattedString) { + ThemeSetTextControl(_theme, ID_SUCCESS_TEXT, formattedString); + StrFree(formattedString); + } + } + + ThemeControlEnable(_theme, ID_SUCCESS_RESTART_TEXT, showRestartButton); + ThemeControlEnable(_theme, ID_SUCCESS_RESTART_BUTTON, showRestartButton); + + if (_command.action != BOOTSTRAPPER_ACTION_INSTALL || + !IsWindowsVersionOrGreater(10, 0, 0)) { + ThemeControlEnable(_theme, ID_SUCCESS_MAX_PATH_BUTTON, FALSE); + } else { + DWORD dataType = 0, buffer = 0, bufferLen = sizeof(buffer); + HKEY hKey; + LRESULT res = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Control\\FileSystem", + 0, + KEY_READ, + &hKey + ); + if (res == ERROR_SUCCESS) { + res = RegQueryValueExW(hKey, L"LongPathsEnabled", nullptr, &dataType, + (LPBYTE)&buffer, &bufferLen); + RegCloseKey(hKey); + } + else { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Failed to open SYSTEM\\CurrentControlSet\\Control\\FileSystem: error code %d", res); + } + if (res == ERROR_SUCCESS && dataType == REG_DWORD && buffer == 0) { + ThemeControlElevates(_theme, ID_SUCCESS_MAX_PATH_BUTTON, TRUE); + } + else { + if (res == ERROR_SUCCESS) + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Failed to read LongPathsEnabled value: error code %d", res); + else + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Hiding MAX_PATH button because it is already enabled"); + ThemeControlEnable(_theme, ID_SUCCESS_MAX_PATH_BUTTON, FALSE); + } + } + } + + void FailurePage_Show() { + // on the "Failure" page, show error message and check if the restart button should be enabled. + + // if there is a log file variable then we'll assume the log file exists. + BOOL showLogLink = (_bundle.sczLogVariable && *_bundle.sczLogVariable); + BOOL showErrorMessage = FALSE; + BOOL showRestartButton = FALSE; + + if (FAILED(_hrFinal)) { + LPWSTR unformattedText = nullptr; + LPWSTR text = nullptr; + + // If we know the failure message, use that. + if (_failedMessage && *_failedMessage) { + StrAllocString(&unformattedText, _failedMessage, 0); + } else { + // try to get the error message from the error code. + StrAllocFromError(&unformattedText, _hrFinal, nullptr); + if (!unformattedText || !*unformattedText) { + StrAllocFromError(&unformattedText, E_FAIL, nullptr); + } + } + + if (E_WIXSTDBA_CONDITION_FAILED == _hrFinal) { + if (unformattedText) { + StrAllocString(&text, unformattedText, 0); + } + } else { + StrAllocFormatted(&text, L"0x%08x - %ls", _hrFinal, unformattedText); + } + + if (text) { + ThemeSetTextControl(_theme, ID_FAILURE_MESSAGE_TEXT, text); + showErrorMessage = TRUE; + } + + ReleaseStr(text); + ReleaseStr(unformattedText); + } + + if (_restartRequired && BOOTSTRAPPER_RESTART_PROMPT == _command.restart) { + showRestartButton = TRUE; + } + + ThemeControlEnable(_theme, ID_FAILURE_LOGFILE_LINK, showLogLink); + ThemeControlEnable(_theme, ID_FAILURE_MESSAGE_TEXT, showErrorMessage); + ThemeControlEnable(_theme, ID_FAILURE_RESTART_TEXT, showRestartButton); + ThemeControlEnable(_theme, ID_FAILURE_RESTART_BUTTON, showRestartButton); + } + + static void EnableMaxPathSupport() { + LPWSTR targetDir = nullptr, defaultDir = nullptr; + HRESULT hr = BalGetStringVariable(L"TargetDir", &targetDir); + if (FAILED(hr) || !targetDir || !targetDir[0]) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to get TargetDir"); + return; + } + + LPWSTR pythonw = nullptr; + StrAllocFormatted(&pythonw, L"%ls\\pythonw.exe", targetDir); + if (!pythonw || !pythonw[0]) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to construct pythonw.exe path"); + return; + } + + LPCWSTR arguments = L"-c \"import winreg; " + "winreg.SetValueEx(" + "winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, " + "r'SYSTEM\\CurrentControlSet\\Control\\FileSystem'), " + "'LongPathsEnabled', " + "None, " + "winreg.REG_DWORD, " + "1" + ")\""; + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Executing %ls %ls", pythonw, arguments); + HINSTANCE res = ShellExecuteW(0, L"runas", pythonw, arguments, NULL, SW_HIDE); + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "return code 0x%08x", res); + } + +public: // IBootstrapperApplication + virtual STDMETHODIMP OnStartup() { + HRESULT hr = S_OK; + DWORD dwUIThreadId = 0; + + // create UI thread + _hUiThread = ::CreateThread(nullptr, 0, UiThreadProc, this, 0, &dwUIThreadId); + if (!_hUiThread) { + ExitWithLastError(hr, "Failed to create UI thread."); + } + + LExit: + return hr; + } + + + virtual STDMETHODIMP_(int) OnShutdown() { + int nResult = IDNOACTION; + + // wait for UI thread to terminate + if (_hUiThread) { + ::WaitForSingleObject(_hUiThread, INFINITE); + ReleaseHandle(_hUiThread); + } + + // If a restart was required. + if (_restartRequired && _allowRestart) { + nResult = IDRESTART; + } + + return nResult; + } + + virtual STDMETHODIMP_(int) OnDetectRelatedMsiPackage( + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR /*wzProductCode*/, + __in BOOL fPerMachine, + __in DWORD64 /*dw64Version*/, + __in BOOTSTRAPPER_RELATED_OPERATION operation + ) { + if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation && + (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1) || + CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_JustForMe", -1))) { + auto hr = LoadAssociateFilesStateFromKey(_engine, fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER); + if (hr == S_OK) { + _engine->SetVariableNumeric(L"AssociateFiles", 1); + } else if (FAILED(hr)) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr); + } + + _engine->SetVariableNumeric(L"Include_launcher", 1); + _engine->SetVariableNumeric(L"DetectedOldLauncher", 1); + _engine->SetVariableNumeric(L"InstallLauncherAllUsers", fPerMachine ? 1 : 0); + } + return CheckCanceled() ? IDCANCEL : IDNOACTION; + } + + virtual STDMETHODIMP_(int) OnDetectRelatedBundle( + __in LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in LPCWSTR /*wzBundleTag*/, + __in BOOL fPerMachine, + __in DWORD64 /*dw64Version*/, + __in BOOTSTRAPPER_RELATED_OPERATION operation + ) { + BalInfoAddRelatedBundleAsPackage(&_bundle.packages, wzBundleId, relationType, fPerMachine); + + // Remember when our bundle would cause a downgrade. + if (BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) { + _downgradingOtherVersion = TRUE; + } else if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Detected previous version - planning upgrade"); + _upgrading = TRUE; + + LoadOptionalFeatureStates(_engine); + } else if (BOOTSTRAPPER_RELATED_OPERATION_NONE == operation) { + if (_command.action == BOOTSTRAPPER_ACTION_INSTALL) { + LOC_STRING *pLocString = nullptr; + if (SUCCEEDED(LocGetString(_wixLoc, L"#(loc.FailureExistingInstall)", &pLocString)) && pLocString) { + BalFormatString(pLocString->wzText, &_failedMessage); + } else { + BalFormatString(L"Cannot install [WixBundleName] because it is already installed.", &_failedMessage); + } + BalLog( + BOOTSTRAPPER_LOG_LEVEL_ERROR, + "Related bundle %ls is preventing install", + wzBundleId + ); + SetState(PYBA_STATE_FAILED, E_WIXSTDBA_CONDITION_FAILED); + } + } + + return CheckCanceled() ? IDCANCEL : IDOK; + } + + + virtual STDMETHODIMP_(void) OnDetectPackageComplete( + __in LPCWSTR wzPackageId, + __in HRESULT hrStatus, + __in BOOTSTRAPPER_PACKAGE_STATE state + ) { + if (FAILED(hrStatus)) { + return; + } + + BOOL detectedLauncher = FALSE; + HKEY hkey = HKEY_LOCAL_MACHINE; + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1)) { + if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == state) { + detectedLauncher = TRUE; + _engine->SetVariableNumeric(L"InstallLauncherAllUsers", 1); + } + } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_JustForMe", -1)) { + if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == state) { + detectedLauncher = TRUE; + _engine->SetVariableNumeric(L"InstallLauncherAllUsers", 0); + } + } + + if (detectedLauncher) { + /* When we detect the current version of the launcher. */ + _engine->SetVariableNumeric(L"Include_launcher", 1); + _engine->SetVariableNumeric(L"DetectedLauncher", 1); + _engine->SetVariableString(L"Include_launcherState", L"disable"); + _engine->SetVariableString(L"InstallLauncherAllUsersState", L"disable"); + + auto hr = LoadAssociateFilesStateFromKey(_engine, hkey); + if (hr == S_OK) { + _engine->SetVariableNumeric(L"AssociateFiles", 1); + } else if (FAILED(hr)) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr); + } + } + } + + + virtual STDMETHODIMP_(void) OnDetectComplete(__in HRESULT hrStatus) { + if (SUCCEEDED(hrStatus) && _baFunction) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running detect complete BA function"); + _baFunction->OnDetectComplete(); + } + + if (SUCCEEDED(hrStatus)) { + hrStatus = EvaluateConditions(); + } + + if (SUCCEEDED(hrStatus)) { + // Ensure the default path has been set + hrStatus = EnsureTargetDir(); + } + + SetState(PYBA_STATE_DETECTED, hrStatus); + + // If we're not interacting with the user or we're doing a layout or we're just after a force restart + // then automatically start planning. + if (BOOTSTRAPPER_DISPLAY_FULL > _command.display || + BOOTSTRAPPER_ACTION_LAYOUT == _command.action || + BOOTSTRAPPER_ACTION_UNINSTALL == _command.action || + BOOTSTRAPPER_RESUME_TYPE_REBOOT == _command.resumeType) { + if (SUCCEEDED(hrStatus)) { + ::PostMessageW(_hWnd, WM_PYBA_PLAN_PACKAGES, 0, _command.action); + } + } + } + + + virtual STDMETHODIMP_(int) OnPlanRelatedBundle( + __in_z LPCWSTR /*wzBundleId*/, + __inout_z BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ) { + return CheckCanceled() ? IDCANCEL : IDOK; + } + + + virtual STDMETHODIMP_(int) OnPlanPackageBegin( + __in_z LPCWSTR wzPackageId, + __inout BOOTSTRAPPER_REQUEST_STATE *pRequestState + ) { + HRESULT hr = S_OK; + BAL_INFO_PACKAGE* pPackage = nullptr; + + if (_nextPackageAfterRestart) { + // After restart we need to finish the dependency registration for our package so allow the package + // to go present. + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, _nextPackageAfterRestart, -1)) { + // Do not allow a repair because that could put us in a perpetual restart loop. + if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == *pRequestState) { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + } + + ReleaseNullStr(_nextPackageAfterRestart); // no more skipping now. + } else { + // not the matching package, so skip it. + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Skipping package: %ls, after restart because it was applied before the restart.", wzPackageId); + + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + } + } else if ((_plannedAction == BOOTSTRAPPER_ACTION_INSTALL || _plannedAction == BOOTSTRAPPER_ACTION_MODIFY) && + SUCCEEDED(BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage))) { + BOOL f = FALSE; + if (SUCCEEDED(_engine->EvaluateCondition(pPackage->sczInstallCondition, &f)) && f) { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + } + } + + return CheckCanceled() ? IDCANCEL : IDOK; + } + + virtual STDMETHODIMP_(int) OnPlanMsiFeature( + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzFeatureId, + __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState + ) { + LONGLONG install; + + if (wcscmp(wzFeatureId, L"AssociateFiles") == 0 || wcscmp(wzFeatureId, L"Shortcuts") == 0) { + if (SUCCEEDED(_engine->GetVariableNumeric(wzFeatureId, &install)) && install) { + *pRequestedState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; + } else { + *pRequestedState = BOOTSTRAPPER_FEATURE_STATE_ABSENT; + } + } else { + *pRequestedState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; + } + return CheckCanceled() ? IDCANCEL : IDNOACTION; + } + + virtual STDMETHODIMP_(void) OnPlanComplete(__in HRESULT hrStatus) { + if (SUCCEEDED(hrStatus) && _baFunction) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running plan complete BA function"); + _baFunction->OnPlanComplete(); + } + + SetState(PYBA_STATE_PLANNED, hrStatus); + + if (SUCCEEDED(hrStatus)) { + ::PostMessageW(_hWnd, WM_PYBA_APPLY_PACKAGES, 0, 0); + } + + _startedExecution = FALSE; + _calculatedCacheProgress = 0; + _calculatedExecuteProgress = 0; + } + + + virtual STDMETHODIMP_(int) OnCachePackageBegin( + __in_z LPCWSTR wzPackageId, + __in DWORD cCachePayloads, + __in DWORD64 dw64PackageCacheSize + ) { + if (wzPackageId && *wzPackageId) { + BAL_INFO_PACKAGE* pPackage = nullptr; + HRESULT hr = BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage); + LPCWSTR wz = (SUCCEEDED(hr) && pPackage->sczDisplayName) ? pPackage->sczDisplayName : wzPackageId; + + ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_PACKAGE_TEXT, wz); + + // If something started executing, leave it in the overall progress text. + if (!_startedExecution) { + ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, wz); + } + } + + return __super::OnCachePackageBegin(wzPackageId, cCachePayloads, dw64PackageCacheSize); + } + + + virtual STDMETHODIMP_(int) OnCacheAcquireProgress( + __in_z LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ) { + WCHAR wzProgress[5] = { }; + +#ifdef DEBUG + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnCacheAcquireProgress() - container/package: %ls, payload: %ls, progress: %I64u, total: %I64u, overall progress: %u%%", wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage); +#endif + + ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallPercentage); + ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_TEXT, wzProgress); + + ThemeSetProgressControl(_theme, ID_CACHE_PROGRESS_BAR, dwOverallPercentage); + + _calculatedCacheProgress = dwOverallPercentage * PYBA_ACQUIRE_PERCENTAGE / 100; + ThemeSetProgressControl(_theme, ID_OVERALL_CALCULATED_PROGRESS_BAR, _calculatedCacheProgress + _calculatedExecuteProgress); + + SetTaskbarButtonProgress(_calculatedCacheProgress + _calculatedExecuteProgress); + + return __super::OnCacheAcquireProgress(wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage); + } + + + virtual STDMETHODIMP_(int) OnCacheAcquireComplete( + __in_z LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __in int nRecommendation + ) { + SetProgressState(hrStatus); + return __super::OnCacheAcquireComplete(wzPackageOrContainerId, wzPayloadId, hrStatus, nRecommendation); + } + + + virtual STDMETHODIMP_(int) OnCacheVerifyComplete( + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __in int nRecommendation + ) { + SetProgressState(hrStatus); + return __super::OnCacheVerifyComplete(wzPackageId, wzPayloadId, hrStatus, nRecommendation); + } + + + virtual STDMETHODIMP_(void) OnCacheComplete(__in HRESULT /*hrStatus*/) { + ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_PACKAGE_TEXT, L""); + SetState(PYBA_STATE_CACHED, S_OK); // we always return success here and let OnApplyComplete() deal with the error. + } + + + virtual STDMETHODIMP_(int) OnError( + __in BOOTSTRAPPER_ERROR_TYPE errorType, + __in LPCWSTR wzPackageId, + __in DWORD dwCode, + __in_z LPCWSTR wzError, + __in DWORD dwUIHint, + __in DWORD /*cData*/, + __in_ecount_z_opt(cData) LPCWSTR* /*rgwzData*/, + __in int nRecommendation + ) { + int nResult = nRecommendation; + LPWSTR sczError = nullptr; + + if (BOOTSTRAPPER_DISPLAY_EMBEDDED == _command.display) { + HRESULT hr = _engine->SendEmbeddedError(dwCode, wzError, dwUIHint, &nResult); + if (FAILED(hr)) { + nResult = IDERROR; + } + } else if (BOOTSTRAPPER_DISPLAY_FULL == _command.display) { + // If this is an authentication failure, let the engine try to handle it for us. + if (BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER == errorType || BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY == errorType) { + nResult = IDTRYAGAIN; + } else // show a generic error message box. + { + BalRetryErrorOccurred(wzPackageId, dwCode); + + if (!_showingInternalUIThisPackage) { + // If no error message was provided, use the error code to try and get an error message. + if (!wzError || !*wzError || BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER != errorType) { + HRESULT hr = StrAllocFromError(&sczError, dwCode, nullptr); + if (FAILED(hr) || !sczError || !*sczError) { + StrAllocFormatted(&sczError, L"0x%x", dwCode); + } + } + + nResult = ::MessageBoxW(_hWnd, sczError ? sczError : wzError, _theme->sczCaption, dwUIHint); + } + } + + SetProgressState(HRESULT_FROM_WIN32(dwCode)); + } else { + // just take note of the error code and let things continue. + BalRetryErrorOccurred(wzPackageId, dwCode); + } + + ReleaseStr(sczError); + return nResult; + } + + + virtual STDMETHODIMP_(int) OnExecuteMsiMessage( + __in_z LPCWSTR wzPackageId, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in DWORD cData, + __in_ecount_z_opt(cData) LPCWSTR* rgwzData, + __in int nRecommendation + ) { +#ifdef DEBUG + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnExecuteMsiMessage() - package: %ls, message: %ls", wzPackageId, wzMessage); +#endif + if (BOOTSTRAPPER_DISPLAY_FULL == _command.display && (INSTALLMESSAGE_WARNING == mt || INSTALLMESSAGE_USER == mt)) { + int nResult = ::MessageBoxW(_hWnd, wzMessage, _theme->sczCaption, uiFlags); + return nResult; + } + + if (INSTALLMESSAGE_ACTIONSTART == mt) { + ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, wzMessage); + } + + return __super::OnExecuteMsiMessage(wzPackageId, mt, uiFlags, wzMessage, cData, rgwzData, nRecommendation); + } + + + virtual STDMETHODIMP_(int) OnProgress(__in DWORD dwProgressPercentage, __in DWORD dwOverallProgressPercentage) { + WCHAR wzProgress[5] = { }; + +#ifdef DEBUG + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnProgress() - progress: %u%%, overall progress: %u%%", dwProgressPercentage, dwOverallProgressPercentage); +#endif + + ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage); + ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_TEXT, wzProgress); + + ThemeSetProgressControl(_theme, ID_OVERALL_PROGRESS_BAR, dwOverallProgressPercentage); + SetTaskbarButtonProgress(dwOverallProgressPercentage); + + return __super::OnProgress(dwProgressPercentage, dwOverallProgressPercentage); + } + + + virtual STDMETHODIMP_(int) OnExecutePackageBegin(__in_z LPCWSTR wzPackageId, __in BOOL fExecute) { + LPWSTR sczFormattedString = nullptr; + + _startedExecution = TRUE; + + if (wzPackageId && *wzPackageId) { + BAL_INFO_PACKAGE* pPackage = nullptr; + BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage); + + LPCWSTR wz = wzPackageId; + if (pPackage) { + LOC_STRING* pLocString = nullptr; + + switch (pPackage->type) { + case BAL_INFO_PACKAGE_TYPE_BUNDLE_ADDON: + LocGetString(_wixLoc, L"#(loc.ExecuteAddonRelatedBundleMessage)", &pLocString); + break; + + case BAL_INFO_PACKAGE_TYPE_BUNDLE_PATCH: + LocGetString(_wixLoc, L"#(loc.ExecutePatchRelatedBundleMessage)", &pLocString); + break; + + case BAL_INFO_PACKAGE_TYPE_BUNDLE_UPGRADE: + LocGetString(_wixLoc, L"#(loc.ExecuteUpgradeRelatedBundleMessage)", &pLocString); + break; + } + + if (pLocString) { + // If the wix developer is showing a hidden variable in the UI, then obviously they don't care about keeping it safe + // so don't go down the rabbit hole of making sure that this is securely freed. + BalFormatString(pLocString->wzText, &sczFormattedString); + } + + wz = sczFormattedString ? sczFormattedString : pPackage->sczDisplayName ? pPackage->sczDisplayName : wzPackageId; + } + + _showingInternalUIThisPackage = pPackage && pPackage->fDisplayInternalUI; + + ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_PACKAGE_TEXT, wz); + ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, wz); + } else { + _showingInternalUIThisPackage = FALSE; + } + + ReleaseStr(sczFormattedString); + return __super::OnExecutePackageBegin(wzPackageId, fExecute); + } + + + virtual int __stdcall OnExecuteProgress( + __in_z LPCWSTR wzPackageId, + __in DWORD dwProgressPercentage, + __in DWORD dwOverallProgressPercentage + ) { + WCHAR wzProgress[8] = { }; + +#ifdef DEBUG + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnExecuteProgress() - package: %ls, progress: %u%%, overall progress: %u%%", wzPackageId, dwProgressPercentage, dwOverallProgressPercentage); +#endif + + ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage); + ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_TEXT, wzProgress); + + ThemeSetProgressControl(_theme, ID_EXECUTE_PROGRESS_BAR, dwOverallProgressPercentage); + + _calculatedExecuteProgress = dwOverallProgressPercentage * (100 - PYBA_ACQUIRE_PERCENTAGE) / 100; + ThemeSetProgressControl(_theme, ID_OVERALL_CALCULATED_PROGRESS_BAR, _calculatedCacheProgress + _calculatedExecuteProgress); + + SetTaskbarButtonProgress(_calculatedCacheProgress + _calculatedExecuteProgress); + + return __super::OnExecuteProgress(wzPackageId, dwProgressPercentage, dwOverallProgressPercentage); + } + + + virtual STDMETHODIMP_(int) OnExecutePackageComplete( + __in_z LPCWSTR wzPackageId, + __in HRESULT hrExitCode, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __in int nRecommendation + ) { + SetProgressState(hrExitCode); + + if (_wcsnicmp(wzPackageId, L"path_", 5) == 0 && SUCCEEDED(hrExitCode)) { + SendMessageTimeoutW( + HWND_BROADCAST, + WM_SETTINGCHANGE, + 0, + reinterpret_cast(L"Environment"), + SMTO_ABORTIFHUNG, + 1000, + nullptr + ); + } + + int nResult = __super::OnExecutePackageComplete(wzPackageId, hrExitCode, restart, nRecommendation); + + return nResult; + } + + + virtual STDMETHODIMP_(void) OnExecuteComplete(__in HRESULT hrStatus) { + ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_PACKAGE_TEXT, L""); + ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L""); + ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, L""); + ThemeControlEnable(_theme, ID_PROGRESS_CANCEL_BUTTON, FALSE); // no more cancel. + + SetState(PYBA_STATE_EXECUTED, S_OK); // we always return success here and let OnApplyComplete() deal with the error. + SetProgressState(hrStatus); + } + + + virtual STDMETHODIMP_(int) OnResolveSource( + __in_z LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource + ) { + int nResult = IDERROR; // assume we won't resolve source and that is unexpected. + + if (BOOTSTRAPPER_DISPLAY_FULL == _command.display) { + if (wzDownloadSource) { + nResult = IDDOWNLOAD; + } else { + // prompt to change the source location. + OPENFILENAMEW ofn = { }; + WCHAR wzFile[MAX_PATH] = { }; + + ::StringCchCopyW(wzFile, countof(wzFile), wzLocalSource); + + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = _hWnd; + ofn.lpstrFile = wzFile; + ofn.nMaxFile = countof(wzFile); + ofn.lpstrFilter = L"All Files\0*.*\0"; + ofn.nFilterIndex = 1; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + ofn.lpstrTitle = _theme->sczCaption; + + if (::GetOpenFileNameW(&ofn)) { + HRESULT hr = _engine->SetLocalSource(wzPackageOrContainerId, wzPayloadId, ofn.lpstrFile); + nResult = SUCCEEDED(hr) ? IDRETRY : IDERROR; + } else { + nResult = IDCANCEL; + } + } + } else if (wzDownloadSource) { + // If doing a non-interactive install and download source is available, let's try downloading the package silently + nResult = IDDOWNLOAD; + } + // else there's nothing more we can do in non-interactive mode + + return CheckCanceled() ? IDCANCEL : nResult; + } + + + virtual STDMETHODIMP_(int) OnApplyComplete(__in HRESULT hrStatus, __in BOOTSTRAPPER_APPLY_RESTART restart) { + _restartResult = restart; // remember the restart result so we return the correct error code no matter what the user chooses to do in the UI. + + // If a restart was encountered and we are not suppressing restarts, then restart is required. + _restartRequired = (BOOTSTRAPPER_APPLY_RESTART_NONE != restart && BOOTSTRAPPER_RESTART_NEVER < _command.restart); + // If a restart is required and we're not displaying a UI or we are not supposed to prompt for restart then allow the restart. + _allowRestart = _restartRequired && (BOOTSTRAPPER_DISPLAY_FULL > _command.display || BOOTSTRAPPER_RESTART_PROMPT < _command.restart); + + // If we are showing UI, wait a beat before moving to the final screen. + if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) { + ::Sleep(250); + } + + SetState(PYBA_STATE_APPLIED, hrStatus); + SetTaskbarButtonProgress(100); // show full progress bar, green, yellow, or red + + return IDNOACTION; + } + + virtual STDMETHODIMP_(void) OnLaunchApprovedExeComplete(__in HRESULT hrStatus, __in DWORD /*processId*/) { + } + + +private: + // + // UiThreadProc - entrypoint for UI thread. + // + static DWORD WINAPI UiThreadProc(__in LPVOID pvContext) { + HRESULT hr = S_OK; + PythonBootstrapperApplication* pThis = (PythonBootstrapperApplication*)pvContext; + BOOL comInitialized = FALSE; + BOOL ret = FALSE; + MSG msg = { }; + + // Initialize COM and theme. + hr = ::CoInitialize(nullptr); + BalExitOnFailure(hr, "Failed to initialize COM."); + comInitialized = TRUE; + + hr = ThemeInitialize(pThis->_hModule); + BalExitOnFailure(hr, "Failed to initialize theme manager."); + + hr = pThis->InitializeData(); + BalExitOnFailure(hr, "Failed to initialize data in bootstrapper application."); + + // Create main window. + pThis->InitializeTaskbarButton(); + hr = pThis->CreateMainWindow(); + BalExitOnFailure(hr, "Failed to create main window."); + + pThis->ValidateOperatingSystem(); + + if (FAILED(pThis->_hrFinal)) { + pThis->SetState(PYBA_STATE_FAILED, hr); + ::PostMessageW(pThis->_hWnd, WM_PYBA_SHOW_FAILURE, 0, 0); + } else { + // Okay, we're ready for packages now. + pThis->SetState(PYBA_STATE_INITIALIZED, hr); + ::PostMessageW(pThis->_hWnd, BOOTSTRAPPER_ACTION_HELP == pThis->_command.action ? WM_PYBA_SHOW_HELP : WM_PYBA_DETECT_PACKAGES, 0, 0); + } + + // message pump + while (0 != (ret = ::GetMessageW(&msg, nullptr, 0, 0))) { + if (-1 == ret) { + hr = E_UNEXPECTED; + BalExitOnFailure(hr, "Unexpected return value from message pump."); + } else if (!ThemeHandleKeyboardMessage(pThis->_theme, msg.hwnd, &msg)) { + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + } + } + + // Succeeded thus far, check to see if anything went wrong while actually + // executing changes. + if (FAILED(pThis->_hrFinal)) { + hr = pThis->_hrFinal; + } else if (pThis->CheckCanceled()) { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + + LExit: + // destroy main window + pThis->DestroyMainWindow(); + + // initiate engine shutdown + DWORD dwQuit = HRESULT_CODE(hr); + if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == pThis->_restartResult) { + dwQuit = ERROR_SUCCESS_REBOOT_INITIATED; + } else if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == pThis->_restartResult) { + dwQuit = ERROR_SUCCESS_REBOOT_REQUIRED; + } + pThis->_engine->Quit(dwQuit); + + ReleaseTheme(pThis->_theme); + ThemeUninitialize(); + + // uninitialize COM + if (comInitialized) { + ::CoUninitialize(); + } + + return hr; + } + + // + // ParseVariablesFromUnattendXml - reads options from unattend.xml if it + // exists + // + HRESULT ParseVariablesFromUnattendXml() { + HRESULT hr = S_OK; + LPWSTR sczUnattendXmlPath = nullptr; + IXMLDOMDocument *pixdUnattend = nullptr; + IXMLDOMNodeList *pNodes = nullptr; + IXMLDOMNode *pNode = nullptr; + long cNodes; + DWORD dwAttr; + LPWSTR scz = nullptr; + BOOL bValue; + int iValue; + BOOL tryConvert; + BSTR bstrValue = nullptr; + + hr = BalFormatString(L"[WixBundleOriginalSourceFolder]unattend.xml", &sczUnattendXmlPath); + BalExitOnFailure(hr, "Failed to calculate path to unattend.xml"); + + if (!FileExistsEx(sczUnattendXmlPath, &dwAttr)) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_VERBOSE, "Did not find %ls", sczUnattendXmlPath); + hr = S_FALSE; + goto LExit; + } + + hr = XmlLoadDocumentFromFile(sczUnattendXmlPath, &pixdUnattend); + BalExitOnFailure1(hr, "Failed to read %ls", sczUnattendXmlPath); + + // get the list of variables users have overridden + hr = XmlSelectNodes(pixdUnattend, L"/Options/Option", &pNodes); + if (S_FALSE == hr) { + ExitFunction1(hr = S_OK); + } + BalExitOnFailure(hr, "Failed to select option nodes."); + + hr = pNodes->get_length((long*)&cNodes); + BalExitOnFailure(hr, "Failed to get option node count."); + + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Reading settings from %ls", sczUnattendXmlPath); + + for (DWORD i = 0; i < cNodes; ++i) { + hr = XmlNextElement(pNodes, &pNode, nullptr); + BalExitOnFailure(hr, "Failed to get next node."); + + // @Name + hr = XmlGetAttributeEx(pNode, L"Name", &scz); + BalExitOnFailure(hr, "Failed to get @Name."); + + tryConvert = TRUE; + hr = XmlGetAttribute(pNode, L"Value", &bstrValue); + if (FAILED(hr) || !bstrValue || !*bstrValue) { + hr = XmlGetText(pNode, &bstrValue); + tryConvert = FALSE; + } + BalExitOnFailure(hr, "Failed to get @Value."); + + if (tryConvert && + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, bstrValue, -1, L"yes", -1)) { + _engine->SetVariableNumeric(scz, 1); + } else if (tryConvert && + CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, bstrValue, -1, L"no", -1)) { + _engine->SetVariableNumeric(scz, 0); + } else if (tryConvert && ::StrToIntExW(bstrValue, STIF_DEFAULT, &iValue)) { + _engine->SetVariableNumeric(scz, iValue); + } else { + _engine->SetVariableString(scz, bstrValue); + } + + ReleaseNullBSTR(bstrValue); + ReleaseNullStr(scz); + ReleaseNullObject(pNode); + } + + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Finished reading from %ls", sczUnattendXmlPath); + + LExit: + ReleaseObject(pNode); + ReleaseObject(pNodes); + ReleaseObject(pixdUnattend); + ReleaseStr(sczUnattendXmlPath); + + return hr; + } + + + // + // InitializeData - initializes all the package information. + // + HRESULT InitializeData() { + HRESULT hr = S_OK; + LPWSTR sczModulePath = nullptr; + IXMLDOMDocument *pixdManifest = nullptr; + + hr = BalManifestLoad(_hModule, &pixdManifest); + BalExitOnFailure(hr, "Failed to load bootstrapper application manifest."); + + hr = ParseOverridableVariablesFromXml(pixdManifest); + BalExitOnFailure(hr, "Failed to read overridable variables."); + + hr = ParseVariablesFromUnattendXml(); + ExitOnFailure(hr, "Failed to read unattend.ini file."); + + hr = ProcessCommandLine(&_language); + ExitOnFailure(hr, "Unknown commandline parameters."); + + hr = PathRelativeToModule(&sczModulePath, nullptr, _hModule); + BalExitOnFailure(hr, "Failed to get module path."); + + hr = LoadLocalization(sczModulePath, _language); + ExitOnFailure(hr, "Failed to load localization."); + + hr = LoadTheme(sczModulePath, _language); + ExitOnFailure(hr, "Failed to load theme."); + + hr = BalInfoParseFromXml(&_bundle, pixdManifest); + BalExitOnFailure(hr, "Failed to load bundle information."); + + hr = BalConditionsParseFromXml(&_conditions, pixdManifest, _wixLoc); + BalExitOnFailure(hr, "Failed to load conditions from XML."); + + hr = LoadBootstrapperBAFunctions(); + BalExitOnFailure(hr, "Failed to load bootstrapper functions."); + + hr = UpdateUIStrings(_command.action); + BalExitOnFailure(hr, "Failed to load UI strings."); + + if (_command.action == BOOTSTRAPPER_ACTION_MODIFY) { + LoadOptionalFeatureStates(_engine); + } + + GetBundleFileVersion(); + // don't fail if we couldn't get the version info; best-effort only + LExit: + ReleaseObject(pixdManifest); + ReleaseStr(sczModulePath); + + return hr; + } + + + // + // ProcessCommandLine - process the provided command line arguments. + // + HRESULT ProcessCommandLine(__inout LPWSTR* psczLanguage) { + HRESULT hr = S_OK; + int argc = 0; + LPWSTR* argv = nullptr; + LPWSTR sczVariableName = nullptr; + LPWSTR sczVariableValue = nullptr; + + if (_command.wzCommandLine && *_command.wzCommandLine) { + argv = ::CommandLineToArgvW(_command.wzCommandLine, &argc); + ExitOnNullWithLastError(argv, hr, "Failed to get command line."); + + for (int i = 0; i < argc; ++i) { + if (argv[i][0] == L'-' || argv[i][0] == L'/') { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1)) { + if (i + 1 >= argc) { + hr = E_INVALIDARG; + BalExitOnFailure(hr, "Must specify a language."); + } + + ++i; + + hr = StrAllocString(psczLanguage, &argv[i][0], 0); + BalExitOnFailure(hr, "Failed to copy language."); + } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"simple", -1)) { + _engine->SetVariableNumeric(L"SimpleInstall", 1); + } + } else if (_overridableVariables) { + int value; + const wchar_t* pwc = wcschr(argv[i], L'='); + if (pwc) { + hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]); + BalExitOnFailure(hr, "Failed to copy variable name."); + + hr = DictKeyExists(_overridableVariables, sczVariableName); + if (E_NOTFOUND == hr) { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Ignoring attempt to set non-overridable variable: '%ls'.", sczVariableName); + hr = S_OK; + continue; + } + ExitOnFailure(hr, "Failed to check the dictionary of overridable variables."); + + hr = StrAllocString(&sczVariableValue, ++pwc, 0); + BalExitOnFailure(hr, "Failed to copy variable value."); + + if (::StrToIntEx(sczVariableValue, STIF_DEFAULT, &value)) { + hr = _engine->SetVariableNumeric(sczVariableName, value); + } else { + hr = _engine->SetVariableString(sczVariableName, sczVariableValue); + } + BalExitOnFailure(hr, "Failed to set variable."); + } else { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]); + } + } + } + } + + LExit: + if (argv) { + ::LocalFree(argv); + } + + ReleaseStr(sczVariableName); + ReleaseStr(sczVariableValue); + + return hr; + } + + HRESULT LoadLocalization(__in_z LPCWSTR wzModulePath, __in_z_opt LPCWSTR wzLanguage) { + HRESULT hr = S_OK; + LPWSTR sczLocPath = nullptr; + LPCWSTR wzLocFileName = L"Default.wxl"; + + hr = LocProbeForFile(wzModulePath, wzLocFileName, wzLanguage, &sczLocPath); + BalExitOnFailure2(hr, "Failed to probe for loc file: %ls in path: %ls", wzLocFileName, wzModulePath); + + hr = LocLoadFromFile(sczLocPath, &_wixLoc); + BalExitOnFailure1(hr, "Failed to load loc file from path: %ls", sczLocPath); + + if (WIX_LOCALIZATION_LANGUAGE_NOT_SET != _wixLoc->dwLangId) { + ::SetThreadLocale(_wixLoc->dwLangId); + } + + hr = StrAllocString(&_confirmCloseMessage, L"#(loc.ConfirmCancelMessage)", 0); + ExitOnFailure(hr, "Failed to initialize confirm message loc identifier."); + + hr = LocLocalizeString(_wixLoc, &_confirmCloseMessage); + BalExitOnFailure1(hr, "Failed to localize confirm close message: %ls", _confirmCloseMessage); + + LExit: + ReleaseStr(sczLocPath); + + return hr; + } + + + HRESULT LoadTheme(__in_z LPCWSTR wzModulePath, __in_z_opt LPCWSTR wzLanguage) { + HRESULT hr = S_OK; + LPWSTR sczThemePath = nullptr; + LPCWSTR wzThemeFileName = L"Default.thm"; + LPWSTR sczCaption = nullptr; + + hr = LocProbeForFile(wzModulePath, wzThemeFileName, wzLanguage, &sczThemePath); + BalExitOnFailure2(hr, "Failed to probe for theme file: %ls in path: %ls", wzThemeFileName, wzModulePath); + + hr = ThemeLoadFromFile(sczThemePath, &_theme); + BalExitOnFailure1(hr, "Failed to load theme from path: %ls", sczThemePath); + + hr = ThemeLocalize(_theme, _wixLoc); + BalExitOnFailure1(hr, "Failed to localize theme: %ls", sczThemePath); + + // Update the caption if there are any formatted strings in it. + // If the wix developer is showing a hidden variable in the UI, then + // obviously they don't care about keeping it safe so don't go down the + // rabbit hole of making sure that this is securely freed. + hr = BalFormatString(_theme->sczCaption, &sczCaption); + if (SUCCEEDED(hr)) { + ThemeUpdateCaption(_theme, sczCaption); + } + + LExit: + ReleaseStr(sczCaption); + ReleaseStr(sczThemePath); + + return hr; + } + + + HRESULT ParseOverridableVariablesFromXml(__in IXMLDOMDocument* pixdManifest) { + HRESULT hr = S_OK; + IXMLDOMNode* pNode = nullptr; + IXMLDOMNodeList* pNodes = nullptr; + DWORD cNodes = 0; + LPWSTR scz = nullptr; + BOOL hidden = FALSE; + + // get the list of variables users can override on the command line + hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/WixStdbaOverridableVariable", &pNodes); + if (S_FALSE == hr) { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to select overridable variable nodes."); + + hr = pNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get overridable variable node count."); + + if (cNodes) { + hr = DictCreateStringList(&_overridableVariables, 32, DICT_FLAG_NONE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + for (DWORD i = 0; i < cNodes; ++i) { + hr = XmlNextElement(pNodes, &pNode, nullptr); + ExitOnFailure(hr, "Failed to get next node."); + + // @Name + hr = XmlGetAttributeEx(pNode, L"Name", &scz); + ExitOnFailure(hr, "Failed to get @Name."); + + hr = XmlGetYesNoAttribute(pNode, L"Hidden", &hidden); + + if (!hidden) { + hr = DictAddKey(_overridableVariables, scz); + ExitOnFailure1(hr, "Failed to add \"%ls\" to the string dictionary.", scz); + } + + // prepare next iteration + ReleaseNullObject(pNode); + } + } + + LExit: + ReleaseObject(pNode); + ReleaseObject(pNodes); + ReleaseStr(scz); + return hr; + } + + + // + // Get the file version of the bootstrapper and record in bootstrapper log file + // + HRESULT GetBundleFileVersion() { + HRESULT hr = S_OK; + ULARGE_INTEGER uliVersion = { }; + LPWSTR sczCurrentPath = nullptr; + + hr = PathForCurrentProcess(&sczCurrentPath, nullptr); + BalExitOnFailure(hr, "Failed to get bundle path."); + + hr = FileVersion(sczCurrentPath, &uliVersion.HighPart, &uliVersion.LowPart); + BalExitOnFailure(hr, "Failed to get bundle file version."); + + hr = _engine->SetVariableVersion(PYBA_VARIABLE_BUNDLE_FILE_VERSION, uliVersion.QuadPart); + BalExitOnFailure(hr, "Failed to set WixBundleFileVersion variable."); + + LExit: + ReleaseStr(sczCurrentPath); + + return hr; + } + + + // + // CreateMainWindow - creates the main install window. + // + HRESULT CreateMainWindow() { + HRESULT hr = S_OK; + HICON hIcon = reinterpret_cast(_theme->hIcon); + WNDCLASSW wc = { }; + DWORD dwWindowStyle = 0; + int x = CW_USEDEFAULT; + int y = CW_USEDEFAULT; + POINT ptCursor = { }; + HMONITOR hMonitor = nullptr; + MONITORINFO mi = { }; + COLORREF fg, bg; + HBRUSH bgBrush; + + // If the theme did not provide an icon, try using the icon from the bundle engine. + if (!hIcon) { + HMODULE hBootstrapperEngine = ::GetModuleHandleW(nullptr); + if (hBootstrapperEngine) { + hIcon = ::LoadIconW(hBootstrapperEngine, MAKEINTRESOURCEW(1)); + } + } + + fg = RGB(0, 0, 0); + bg = RGB(255, 255, 255); + bgBrush = (HBRUSH)(COLOR_WINDOW+1); + if (_theme->dwFontId < _theme->cFonts) { + THEME_FONT *font = &_theme->rgFonts[_theme->dwFontId]; + fg = font->crForeground; + bg = font->crBackground; + bgBrush = font->hBackground; + RemapColor(&fg, &bg, &bgBrush); + } + + // Register the window class and create the window. + wc.lpfnWndProc = PythonBootstrapperApplication::WndProc; + wc.hInstance = _hModule; + wc.hIcon = hIcon; + wc.hCursor = ::LoadCursorW(nullptr, (LPCWSTR)IDC_ARROW); + wc.hbrBackground = bgBrush; + wc.lpszMenuName = nullptr; + wc.lpszClassName = PYBA_WINDOW_CLASS; + if (!::RegisterClassW(&wc)) { + ExitWithLastError(hr, "Failed to register window."); + } + + _registered = TRUE; + + // Calculate the window style based on the theme style and command display value. + dwWindowStyle = _theme->dwStyle; + if (BOOTSTRAPPER_DISPLAY_NONE >= _command.display) { + dwWindowStyle &= ~WS_VISIBLE; + } + + // Don't show the window if there is a splash screen (it will be made visible when the splash screen is hidden) + if (::IsWindow(_command.hwndSplashScreen)) { + dwWindowStyle &= ~WS_VISIBLE; + } + + // Center the window on the monitor with the mouse. + if (::GetCursorPos(&ptCursor)) { + hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST); + if (hMonitor) { + mi.cbSize = sizeof(mi); + if (::GetMonitorInfoW(hMonitor, &mi)) { + x = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - _theme->nWidth) / 2; + y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - _theme->nHeight) / 2; + } + } + } + + _hWnd = ::CreateWindowExW( + 0, + wc.lpszClassName, + _theme->sczCaption, + dwWindowStyle, + x, + y, + _theme->nWidth, + _theme->nHeight, + HWND_DESKTOP, + nullptr, + _hModule, + this + ); + ExitOnNullWithLastError(_hWnd, hr, "Failed to create window."); + + hr = S_OK; + + LExit: + return hr; + } + + + // + // InitializeTaskbarButton - initializes taskbar button for progress. + // + void InitializeTaskbarButton() { + HRESULT hr = S_OK; + + hr = ::CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, __uuidof(ITaskbarList3), reinterpret_cast(&_taskbarList)); + if (REGDB_E_CLASSNOTREG == hr) { + // not supported before Windows 7 + ExitFunction1(hr = S_OK); + } + BalExitOnFailure(hr, "Failed to create ITaskbarList3. Continuing."); + + _taskbarButtonCreatedMessage = ::RegisterWindowMessageW(L"TaskbarButtonCreated"); + BalExitOnNullWithLastError(_taskbarButtonCreatedMessage, hr, "Failed to get TaskbarButtonCreated message. Continuing."); + + LExit: + return; + } + + // + // DestroyMainWindow - clean up all the window registration. + // + void DestroyMainWindow() { + if (::IsWindow(_hWnd)) { + ::DestroyWindow(_hWnd); + _hWnd = nullptr; + _taskbarButtonOK = FALSE; + } + + if (_registered) { + ::UnregisterClassW(PYBA_WINDOW_CLASS, _hModule); + _registered = FALSE; + } + } + + + // + // WndProc - standard windows message handler. + // + static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) { +#pragma warning(suppress:4312) + auto pBA = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); + + switch (uMsg) { + case WM_NCCREATE: { + LPCREATESTRUCT lpcs = reinterpret_cast(lParam); + pBA = reinterpret_cast(lpcs->lpCreateParams); +#pragma warning(suppress:4244) + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pBA)); + break; + } + + case WM_NCDESTROY: { + LRESULT lres = ThemeDefWindowProc(pBA ? pBA->_theme : nullptr, hWnd, uMsg, wParam, lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + return lres; + } + + case WM_CREATE: + if (!pBA->OnCreate(hWnd)) { + return -1; + } + break; + + case WM_QUERYENDSESSION: + return IDCANCEL != pBA->OnSystemShutdown(static_cast(lParam), IDCANCEL); + + case WM_CLOSE: + // If the user chose not to close, do *not* let the default window proc handle the message. + if (!pBA->OnClose()) { + return 0; + } + break; + + case WM_DESTROY: + ::PostQuitMessage(0); + break; + + case WM_PAINT: __fallthrough; + case WM_ERASEBKGND: + if (pBA && pBA->_suppressPaint) { + return TRUE; + } + break; + + case WM_PYBA_SHOW_HELP: + pBA->OnShowHelp(); + return 0; + + case WM_PYBA_DETECT_PACKAGES: + pBA->OnDetect(); + return 0; + + case WM_PYBA_PLAN_PACKAGES: + pBA->OnPlan(static_cast(lParam)); + return 0; + + case WM_PYBA_APPLY_PACKAGES: + pBA->OnApply(); + return 0; + + case WM_PYBA_CHANGE_STATE: + pBA->OnChangeState(static_cast(lParam)); + return 0; + + case WM_PYBA_SHOW_FAILURE: + pBA->OnShowFailure(); + return 0; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + // Customize commands + // Success/failure commands + case ID_SUCCESS_RESTART_BUTTON: __fallthrough; + case ID_FAILURE_RESTART_BUTTON: + pBA->OnClickRestartButton(); + return 0; + + case IDCANCEL: __fallthrough; + case ID_INSTALL_CANCEL_BUTTON: __fallthrough; + case ID_CUSTOM1_CANCEL_BUTTON: __fallthrough; + case ID_CUSTOM2_CANCEL_BUTTON: __fallthrough; + case ID_MODIFY_CANCEL_BUTTON: __fallthrough; + case ID_PROGRESS_CANCEL_BUTTON: __fallthrough; + case ID_SUCCESS_CANCEL_BUTTON: __fallthrough; + case ID_FAILURE_CANCEL_BUTTON: __fallthrough; + case ID_CLOSE_BUTTON: + pBA->OnCommand(ID_CLOSE_BUTTON); + return 0; + + default: + pBA->OnCommand((CONTROL_ID)LOWORD(wParam)); + } + break; + + case WM_NOTIFY: + if (lParam) { + LPNMHDR pnmhdr = reinterpret_cast(lParam); + switch (pnmhdr->code) { + case NM_CLICK: __fallthrough; + case NM_RETURN: + switch (static_cast(pnmhdr->idFrom)) { + case ID_FAILURE_LOGFILE_LINK: + pBA->OnClickLogFileLink(); + return 1; + } + } + } + break; + + case WM_CTLCOLORSTATIC: + case WM_CTLCOLORBTN: + if (pBA) { + HBRUSH brush = nullptr; + if (pBA->SetControlColor((HWND)lParam, (HDC)wParam, &brush)) { + return (LRESULT)brush; + } + } + break; + } + + if (pBA && pBA->_taskbarList && uMsg == pBA->_taskbarButtonCreatedMessage) { + pBA->_taskbarButtonOK = TRUE; + return 0; + } From pypy.commits at gmail.com Tue Feb 5 05:58:03 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:03 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Additional files from cpython Message-ID: <5c596c3b.1c69fb81.6599d.437e@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95803:536b0f301622 Date: 2018-12-24 21:07 +0000 http://bitbucket.org/pypy/pypy/changeset/536b0f301622/ Log: Additional files from cpython diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.targets b/pypy/tool/release/windowsinstaller/bundle/bundle.targets new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.targets @@ -0,0 +1,98 @@ + + + + 2.0 + Bundle + + Release + 1132;1135;1140 + $(OutputName)-$(PythonVersion) + $(OutputName)-$(MajorVersionNumber).$(MinorVersionNumber).$(MicroVersionNumber).$(RevisionNumber) + $(OutputName)-amd64 + $(OutputName)-$(OutputSuffix) + $(OutputName)-d + $(OutputName) + + $(OutputPath)en-us\ + $(OutputPath) + + + $(DownloadUrlBase.TrimEnd(`/`))/{version}/{arch}{releasename}/{msi} + $(DefineConstants);DownloadUrl=$(DownloadUrl.Replace(`{version}`, `$(MajorVersionNumber).$(MinorVersionNumber).$(MicroVersionNumber)`).Replace(`{arch}`, `$(ArchName)`).Replace(`{releasename}`, `$(ReleaseLevelName)`).Replace(`{msi}`, `{2}`)) + $(DefineConstants);DownloadUrl={2} + + + + + WixUtilExtension + WixUtilExtension + + + WixDependencyExtension + WixDependencyExtension + + + WixBalExtension + WixBalExtension + + + + + + + + + + + + + + + + + + + + + + + + BuildForRelease=$(BuildForRelease) + + + + + + + + + Build + + + + + Rebuild + + + + + + + + + + + + + + + + $(DefineConstants);BootstrapApp=$(BootstrapAppPath) + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.wxl b/pypy/tool/release/windowsinstaller/bundle/bundle.wxl new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.wxl @@ -0,0 +1,7 @@ + + + C Runtime Update (KB2999226) + Precompiling standard library + Precompiling standard library (-O) + Precompiling standard library (-OO) + diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pypy/tool/release/windowsinstaller/bundle/packagegroups/include.wxs b/pypy/tool/release/windowsinstaller/bundle/packagegroups/include.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/packagegroups/include.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/bundle/packagegroups/lib_pypy.wxs b/pypy/tool/release/windowsinstaller/bundle/packagegroups/lib_pypy.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/packagegroups/lib_pypy.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/bundle/releaselocal.wixproj b/pypy/tool/release/windowsinstaller/bundle/releaselocal.wixproj new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/releaselocal.wixproj @@ -0,0 +1,21 @@ + + + + {5E95D46C-119F-449E-BE0E-AE92B78570C8} + pypy + + + + + + + + $(DefineConstants); + CompressMSI=yes; + CompressPDB=no; + CompressMSI_D=no + + + + + \ No newline at end of file From pypy.commits at gmail.com Tue Feb 5 05:58:05 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:05 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Got windows msi installer to build. It is not currently in a usable state. Message-ID: <5c596c3d.1c69fb81.4ee7b.b8d0@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95804:000052a98664 Date: 2018-12-25 19:20 +0000 http://bitbucket.org/pypy/pypy/changeset/000052a98664/ Log: Got windows msi installer to build. It is not currently in a usable state. diff --git a/PC/icons/setup.ico b/PC/icons/setup.ico new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e54364b3af11d3173c8b48d8ee32bce18f85f2ec GIT binary patch [cut] diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props new file mode 100644 --- /dev/null +++ b/PCbuild/pyproject.props @@ -0,0 +1,197 @@ + + + + <_ProjectFileVersion>10.0.30319.1 + 10.0 + $(BuildPath) + $(OutDir)\ + $(MSBuildThisFileDirectory)obj\ + $(Py_IntDir)\$(MajorVersionNumber)$(MinorVersionNumber)$(ArchName)_$(Configuration)\$(ProjectName)\ + $(IntDir.Replace(`\\`, `\`)) + $(ProjectName) + $(TargetName)$(PyDebugExt) + false + false + true + true + false + false + + + + <_DebugPreprocessorDefinition>NDEBUG; + <_DebugPreprocessorDefinition Condition="$(Configuration) == 'Debug'">_DEBUG; + <_PlatformPreprocessorDefinition>_WIN32; + <_PlatformPreprocessorDefinition Condition="$(Platform) == 'x64'">_WIN64;_M_X64; + <_PydPreprocessorDefinition Condition="$(TargetExt) == '.pyd'">Py_BUILD_CORE_MODULE; + + + + $(PySourcePath)Include;$(PySourcePath)Include\internal;$(PySourcePath)PC;$(IntDir);%(AdditionalIncludeDirectories) + WIN32;$(_PlatformPreprocessorDefinition)$(_DebugPreprocessorDefinition)$(_PydPreprocessorDefinition)%(PreprocessorDefinitions) + + MaxSpeed + true + true + + MultiThreadedDLL + true + Level3 + ProgramDatabase + Default + true + true + NoExtensions + OnlyExplicitInline + OnlyExplicitInline + + + Disabled + false + MultiThreadedDebugDLL + + + Strict + + + $(OutDir);%(AdditionalLibraryDirectories) + true + $(OutDir)$(TargetName).pdb + Windows + true + true + true + LIBC;%(IgnoreSpecificDefaultLibraries) + MachineX86 + MachineX64 + $(OutDir)$(TargetName).pgd + UseLinkTimeCodeGeneration + PGInstrument + PGUpdate + + + true + true + true + + + $(PySourcePath)PC;$(PySourcePath)Include;$(IntDir);%(AdditionalIncludeDirectories) + $(_DebugPreprocessorDefinition)%(PreprocessorDefinitions) + 0x0409 + + + $(_DebugPreprocessorDefinition)%(PreprocessorDefinitions) + true + true + Win32 + X64 + $(IntDir) + $(MSBuildProjectName)_i.c + $(MSBuildProjectName)_p.c + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_PGCFiles Include="$(OutDir)instrumented\$(TargetName)!*.pgc" /> + <_PGDFile Include="$(OutDir)instrumented\$(TargetName).pgd" /> + <_CopyFiles Include="@(_PGCFiles);@(_PGDFile)" Condition="Exists(%(FullPath))" /> + + + + + + + + $(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots at KitsRoot10)\bin\$(DefaultWindowsSDKVersion)\x86 + $(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots at KitsRoot10)\bin\x86 + $(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots at KitsRoot81)\bin\x86 + $(registry:HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Kits\Installed Roots at KitsRoot)\bin\x86 + $(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A at InstallationFolder)\Bin\ + <_SignCommand Condition="Exists($(SdkBinPath)) and '$(SigningCertificate)' != '' and $(SupportSigning)">"$(SdkBinPath)\signtool.exe" sign /q /a /n "$(SigningCertificate)" /fd sha256 /t http://timestamp.verisign.com/scripts/timestamp.dll /d "Python $(PythonVersion)" + <_MakeCatCommand Condition="Exists($(SdkBinPath))">"$(SdkBinPath)\makecat.exe" + + + + + + + diff --git a/PCbuild/python.props b/PCbuild/python.props new file mode 100644 --- /dev/null +++ b/PCbuild/python.props @@ -0,0 +1,193 @@ + + + + Win32 + Release + + v141 + v140 + v120 + v110 + v100 + + $(BasePlatformToolset) + false + true + + + amd64 + win32 + + + $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)\..\)) + $(PySourcePath)\ + + + $(PySourcePath)pypy\goal\ + + + $(PySourcePath)PCbuild\win32\ + $(Py_OutDir)\win32\ + $(BuildPath32) + $(BuildPath)\ + $(BuildPath)instrumented\ + + + $(EXTERNALS_DIR) + $([System.IO.Path]::GetFullPath(`$(PySourcePath)externals`)) + $(ExternalsDir)\ + $(ExternalsDir)sqlite-3.21.0.0\ + $(ExternalsDir)bzip2-1.0.6\ + $(ExternalsDir)xz-5.2.2\ + $(ExternalsDir)openssl-1.1.0i\ + $(ExternalsDir)openssl-bin-1.1.0i\$(ArchName)\ + $(opensslOutDir)include + $(ExternalsDir)\nasm-2.11.06\ + $(ExternalsDir)\zlib-1.2.11\ + + + _d + + + -test + + + -32 + + + $(InterpreterPath)pypy3-c$(PyDebugExt).exe + + + + + <_RegistryVersion>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) + <_RegistryVersion Condition="$(_RegistryVersion) == ''">$(Registry:HKEY_LOCAL_MACHINE\WOW6432Node\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0 at ProductVersion) + 10.0.17134.0 + 10.0.16299.0 + 10.0.15063.0 + 10.0.14393.0 + 10.0.10586.0 + 10.0.10240.0 + + + + + <_PatchLevelContent>$([System.IO.File]::ReadAllText(`$(PySourcePath)Include\patchlevel.h`)) + $([System.Text.RegularExpressions.Regex]::Match($(_PatchLevelContent), `define\s+PY_MAJOR_VERSION\s+(\d+)`).Groups[1].Value) + $([System.Text.RegularExpressions.Regex]::Match($(_PatchLevelContent), `define\s+PY_MINOR_VERSION\s+(\d+)`).Groups[1].Value) + $([System.Text.RegularExpressions.Regex]::Match($(_PatchLevelContent), `define\s+PY_MICRO_VERSION\s+(\d+)`).Groups[1].Value) + <_ReleaseLevel>$([System.Text.RegularExpressions.Regex]::Match($(_PatchLevelContent), `define\s+PY_RELEASE_LEVEL\s+PY_RELEASE_LEVEL_(\w+)`).Groups[1].Value) + $([System.Text.RegularExpressions.Regex]::Match($(_PatchLevelContent), `define\s+PY_RELEASE_SERIAL\s+(\d+)`).Groups[1].Value) + 15 + 10 + 11 + 12 + a$(ReleaseSerial) + b$(ReleaseSerial) + rc$(ReleaseSerial) + + + + + $([System.Text.RegularExpressions.Regex]::Match($(OverrideVersion), `(\d+)\.(\d+)\.(\d+)((a|b|rc)(\d))?`).Groups[1].Value) + $([System.Text.RegularExpressions.Regex]::Match($(OverrideVersion), `(\d+)\.(\d+)\.(\d+)((a|b|rc)(\d))?`).Groups[2].Value) + $([System.Text.RegularExpressions.Regex]::Match($(OverrideVersion), `(\d+)\.(\d+)\.(\d+)((a|b|rc)(\d))?`).Groups[3].Value) + $([System.Text.RegularExpressions.Regex]::Match($(OverrideVersion), `(\d+)\.(\d+)\.(\d+)((a|b|rc)(\d))?`).Groups[4].Value) + <_ReleaseLevel>$([System.Text.RegularExpressions.Regex]::Match($(OverrideVersion), `(\d+)\.(\d+)\.(\d+)((a|b|rc)(\d))?`).Groups[5].Value) + $([System.Text.RegularExpressions.Regex]::Match($(OverrideVersion), `(\d+)\.(\d+)\.(\d+)((a|b|rc)(\d))?`).Groups[6].Value) + 0 + 15 + 10 + 11 + 12 + + + + $(MajorVersionNumber).$(MinorVersionNumber).$(MicroVersionNumber) + $(MajorVersionNumber).$(MinorVersionNumber).$(MicroVersionNumber)$(ReleaseLevelName) + $([msbuild]::BitwiseOr( + $([msbuild]::Multiply($(MajorVersionNumber), 16777216)), + $([msbuild]::BitwiseOr( + $([msbuild]::Multiply($(MinorVersionNumber), 65536)), + $([msbuild]::BitwiseOr( + $([msbuild]::Multiply($(MicroVersionNumber), 256)), + $([msbuild]::BitwiseOr( + $([msbuild]::Multiply($(ReleaseLevelNumber), 16)), + $(ReleaseSerial) + )) + )) + )) + )) + $([msbuild]::Add( + $(ReleaseSerial), + $([msbuild]::Add( + $([msbuild]::Multiply($(ReleaseLevelNumber), 10)), + $([msbuild]::Multiply($(MicroVersionNumber), 1000)) + )) + )) + $([msbuild]::Add($(Field3Value), 9000)) + + + python$(MajorVersionNumber)$(MinorVersionNumber)$(PyDebugExt) + + + .cp$(MajorVersionNumber)$(MinorVersionNumber)-win32 + .cp$(MajorVersionNumber)$(MinorVersionNumber)-win_amd64 + + + $(MajorVersionNumber).$(MinorVersionNumber)$(PyArchExt)$(PyTestExt) + + + + + + + + + + + + diff --git a/pypy/tool/release/windowsinstaller/include/include.wixproj b/pypy/tool/release/windowsinstaller/include/include.wixproj --- a/pypy/tool/release/windowsinstaller/include/include.wixproj +++ b/pypy/tool/release/windowsinstaller/include/include.wixproj @@ -3,7 +3,7 @@ {93912B72-A2D0-49C4-84DC-5B5B490B60AC} 2.0 - dev + include Package From pypy.commits at gmail.com Tue Feb 5 05:58:07 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:07 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: First icon drawn Message-ID: <5c596c3f.1c69fb81.44675.9894@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95805:647ac737bf55 Date: 2018-12-26 12:55 +0000 http://bitbucket.org/pypy/pypy/changeset/647ac737bf55/ Log: First icon drawn diff --git a/PC/icons/setup.ico b/PC/icons/setup.ico index e54364b3af11d3173c8b48d8ee32bce18f85f2ec..351b306272b000141ec1342ce6b243d34f5a5127 GIT binary patch [cut] From pypy.commits at gmail.com Tue Feb 5 05:58:09 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:09 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Cleaned up 64x64 icon Message-ID: <5c596c41.1c69fb81.9d9c6.5bb7@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95806:ca3f2c17865d Date: 2018-12-26 13:23 +0000 http://bitbucket.org/pypy/pypy/changeset/ca3f2c17865d/ Log: Cleaned up 64x64 icon diff --git a/PC/icons/setup.ico b/PC/icons/setup.ico index 351b306272b000141ec1342ce6b243d34f5a5127..210aa45604f6d85938829750732e67dd213e26db GIT binary patch [cut] From pypy.commits at gmail.com Tue Feb 5 05:58:10 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:10 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Finished creating pypy setup icons. Message-ID: <5c596c42.1c69fb81.9daa0.c553@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95807:ff86926a903e Date: 2018-12-26 15:18 +0000 http://bitbucket.org/pypy/pypy/changeset/ff86926a903e/ Log: Finished creating pypy setup icons. diff --git a/PC/icons/setup.ico b/PC/icons/setup.ico index 210aa45604f6d85938829750732e67dd213e26db..ab52102a8533bf956aadd8ae6bab13ecde1395b1 GIT binary patch [cut] From pypy.commits at gmail.com Tue Feb 5 05:58:12 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:12 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Corrected a large number of cpython references. Message-ID: <5c596c44.1c69fb81.3ceb0.5b42@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95808:8acc3489da28 Date: 2018-12-26 17:47 +0000 http://bitbucket.org/pypy/pypy/changeset/8acc3489da28/ Log: Corrected a large number of cpython references. diff --git a/pypy/tool/release/windowsinstaller/bundle/SideBar.png b/pypy/tool/release/windowsinstaller/bundle/SideBar.png new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..037da0d8515b469ff9e6ef1ad0e9b393b6f1f154 GIT binary patch [cut] diff --git a/pypy/tool/release/windowsinstaller/bundle/SideBar.xcf b/pypy/tool/release/windowsinstaller/bundle/SideBar.xcf new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..19f731cb5a7d8be36ef5f14d5dfd677058ffeb75 GIT binary patch [cut] diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs --- a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs @@ -34,15 +34,15 @@ - + - + - - - + + + - - - - - + + + + + + + + + + + diff --git a/pypy/tool/release/windowsinstaller/msi.props b/pypy/tool/release/windowsinstaller/msi.props --- a/pypy/tool/release/windowsinstaller/msi.props +++ b/pypy/tool/release/windowsinstaller/msi.props @@ -117,6 +117,9 @@ + + goal + src diff --git a/pypy/tool/release/windowsinstaller/pypy.wxs b/pypy/tool/release/windowsinstaller/pypy.wxs deleted file mode 100644 --- a/pypy/tool/release/windowsinstaller/pypy.wxs +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - Privileged - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From pypy.commits at gmail.com Tue Feb 5 05:58:16 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:16 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Fixed missing file ids. Message-ID: <5c596c48.1c69fb81.a9bca.30e4@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95810:8d97265b8c16 Date: 2018-12-27 18:09 +0000 http://bitbucket.org/pypy/pypy/changeset/8d97265b8c16/ Log: Fixed missing file ids. diff --git a/pypy/tool/release/windowsinstaller/exe/exe.wxs b/pypy/tool/release/windowsinstaller/exe/exe.wxs --- a/pypy/tool/release/windowsinstaller/exe/exe.wxs +++ b/pypy/tool/release/windowsinstaller/exe/exe.wxs @@ -17,10 +17,10 @@ - + - diff --git a/pypy/tool/release/windowsinstaller/exe/exe_files.wxs b/pypy/tool/release/windowsinstaller/exe/exe_files.wxs --- a/pypy/tool/release/windowsinstaller/exe/exe_files.wxs +++ b/pypy/tool/release/windowsinstaller/exe/exe_files.wxs @@ -15,16 +15,16 @@ - + - + - + - + From pypy.commits at gmail.com Tue Feb 5 05:58:17 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:17 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: lib-python builds Message-ID: <5c596c49.1c69fb81.d03bc.6c66@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95811:0a8a958e20ed Date: 2018-12-29 20:40 +0000 http://bitbucket.org/pypy/pypy/changeset/0a8a958e20ed/ Log: lib-python builds diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.targets b/pypy/tool/release/windowsinstaller/bundle/bundle.targets --- a/pypy/tool/release/windowsinstaller/bundle/bundle.targets +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.targets @@ -64,8 +64,8 @@ - + diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs --- a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs @@ -97,6 +97,8 @@ - + + + diff --git a/pypy/tool/release/windowsinstaller/common.wxs b/pypy/tool/release/windowsinstaller/common.wxs --- a/pypy/tool/release/windowsinstaller/common.wxs +++ b/pypy/tool/release/windowsinstaller/common.wxs @@ -80,6 +80,11 @@ + + + + + diff --git a/pypy/tool/release/windowsinstaller/lib_python/lib_python.wixproj b/pypy/tool/release/windowsinstaller/lib_python/lib_python.wixproj new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/lib_python/lib_python.wixproj @@ -0,0 +1,31 @@ + + + + {221C8034-F90F-42E4-951F-7752B54F029D} + 2.0 + lib_python + Package + + + + + + + + + + + $(PySourcePath) + !(bindpath.src) + $(PySourcePath) + + lib_python + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/lib_python/lib_python.wxs b/pypy/tool/release/windowsinstaller/lib_python/lib_python.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/lib_python/lib_python.wxs @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/pypy/tool/release/windowsinstaller/lib_python/lib_python_en-US.wxl b/pypy/tool/release/windowsinstaller/lib_python/lib_python_en-US.wxl new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/lib_python/lib_python_en-US.wxl @@ -0,0 +1,5 @@ + + + Python Library + lib python + From pypy.commits at gmail.com Tue Feb 5 05:58:19 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:19 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Added libs folder Message-ID: <5c596c4b.1c69fb81.a9bca.30e8@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95812:8c7fe370d197 Date: 2018-12-29 21:34 +0000 http://bitbucket.org/pypy/pypy/changeset/8c7fe370d197/ Log: Added libs folder diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs --- a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs @@ -66,7 +66,7 @@ - + @@ -99,6 +99,7 @@ + diff --git a/pypy/tool/release/windowsinstaller/bundle/packagegroups/lib_python.wxs b/pypy/tool/release/windowsinstaller/bundle/packagegroups/lib_python.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/packagegroups/lib_python.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/bundle/packagegroups/libs.wxs b/pypy/tool/release/windowsinstaller/bundle/packagegroups/libs.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/packagegroups/libs.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/libs/libs.wixproj b/pypy/tool/release/windowsinstaller/libs/libs.wixproj new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/libs/libs.wixproj @@ -0,0 +1,18 @@ + + + + {6EE63EEF-D6C5-4E96-840B-BFDE73B9068F} + 2.0 + libs + Package + + + + + + + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/libs/libs.wxs b/pypy/tool/release/windowsinstaller/libs/libs.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/libs/libs.wxs @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/pypy/tool/release/windowsinstaller/libs/libs_en-US.wxl b/pypy/tool/release/windowsinstaller/libs/libs_en-US.wxl new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/libs/libs_en-US.wxl @@ -0,0 +1,5 @@ + + + Libraries + libs + diff --git a/pypy/tool/release/windowsinstaller/libs/libs_files.wxs b/pypy/tool/release/windowsinstaller/libs/libs_files.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/libs/libs_files.wxs @@ -0,0 +1,10 @@ + + + + + + + + + + From pypy.commits at gmail.com Tue Feb 5 05:58:21 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:21 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Added external and site packages. Message-ID: <5c596c4d.1c69fb81.931e1.73d9@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95813:e0469a100160 Date: 2018-12-30 22:08 +0000 http://bitbucket.org/pypy/pypy/changeset/e0469a100160/ Log: Added external and site packages. diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.targets b/pypy/tool/release/windowsinstaller/bundle/bundle.targets --- a/pypy/tool/release/windowsinstaller/bundle/bundle.targets +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.targets @@ -63,11 +63,11 @@ + - - + diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs --- a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs @@ -100,6 +100,8 @@ - + + + diff --git a/pypy/tool/release/windowsinstaller/bundle/packagegroups/external.wxs b/pypy/tool/release/windowsinstaller/bundle/packagegroups/external.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/packagegroups/external.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/bundle/packagegroups/site-packages.wxs b/pypy/tool/release/windowsinstaller/bundle/packagegroups/site-packages.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/packagegroups/site-packages.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/external/external.wixproj b/pypy/tool/release/windowsinstaller/external/external.wixproj new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/external/external.wixproj @@ -0,0 +1,35 @@ + + + + {97B17A60-4BE4-40CE-B187-E9A9A47319BB} + 2.0 + external + Package + + + + + + + + + + + + + $(PySourcePath) + !(bindpath.src) + $(PySourcePath) + + tcl + + + $(PySourcePath) + !(bindpath.src) + $(PySourcePath) + + tcl + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/external/external.wxs b/pypy/tool/release/windowsinstaller/external/external.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/external/external.wxs @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/pypy/tool/release/windowsinstaller/external/external_en-US.wxl_template b/pypy/tool/release/windowsinstaller/external/external_en-US.wxl_template new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/external/external_en-US.wxl_template @@ -0,0 +1,5 @@ + + + External Libraries + external + diff --git a/pypy/tool/release/windowsinstaller/external/external_files.wxs b/pypy/tool/release/windowsinstaller/external/external_files.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/external/external_files.wxs @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/pypy/tool/release/windowsinstaller/site-packages/site-packages.wixproj b/pypy/tool/release/windowsinstaller/site-packages/site-packages.wixproj new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/site-packages/site-packages.wixproj @@ -0,0 +1,26 @@ + + + + {CB35E339-9711-49F0-A231-5FB2B1211BDB} + 2.0 + site-packages + Package + + + + + + + + + + + $(PySourcePath) + !(bindpath.src) + $(PySourcePath) + + site-packages + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/site-packages/site-packages.wxs b/pypy/tool/release/windowsinstaller/site-packages/site-packages.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/site-packages/site-packages.wxs @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/pypy/tool/release/windowsinstaller/site-packages/site-packages_en-US.wxl b/pypy/tool/release/windowsinstaller/site-packages/site-packages_en-US.wxl new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/site-packages/site-packages_en-US.wxl @@ -0,0 +1,5 @@ + + + Site Packages + site packages + From pypy.commits at gmail.com Tue Feb 5 05:58:23 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:23 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: renamed folder to reduce problems Message-ID: <5c596c4f.1c69fb81.980a1.30ef@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95814:04de7b50bbee Date: 2018-12-31 18:01 +0000 http://bitbucket.org/pypy/pypy/changeset/04de7b50bbee/ Log: renamed folder to reduce problems diff --git a/pypy/tool/release/windowsinstaller/site-packages/site-packages.wixproj b/pypy/tool/release/windowsinstaller/sitepackages/site-packages.wixproj rename from pypy/tool/release/windowsinstaller/site-packages/site-packages.wixproj rename to pypy/tool/release/windowsinstaller/sitepackages/site-packages.wixproj diff --git a/pypy/tool/release/windowsinstaller/site-packages/site-packages.wxs b/pypy/tool/release/windowsinstaller/sitepackages/site-packages.wxs rename from pypy/tool/release/windowsinstaller/site-packages/site-packages.wxs rename to pypy/tool/release/windowsinstaller/sitepackages/site-packages.wxs diff --git a/pypy/tool/release/windowsinstaller/site-packages/site-packages_en-US.wxl b/pypy/tool/release/windowsinstaller/sitepackages/site-packages_en-US.wxl rename from pypy/tool/release/windowsinstaller/site-packages/site-packages_en-US.wxl rename to pypy/tool/release/windowsinstaller/sitepackages/site-packages_en-US.wxl From pypy.commits at gmail.com Tue Feb 5 05:58:24 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:24 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Further renaming Message-ID: <5c596c50.1c69fb81.720a.dbc7@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95815:14976c9f5538 Date: 2018-12-31 19:39 +0000 http://bitbucket.org/pypy/pypy/changeset/14976c9f5538/ Log: Further renaming diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.targets b/pypy/tool/release/windowsinstaller/bundle/bundle.targets --- a/pypy/tool/release/windowsinstaller/bundle/bundle.targets +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.targets @@ -67,7 +67,7 @@ - + diff --git a/pypy/tool/release/windowsinstaller/bundle/packagegroups/site-packages.wxs b/pypy/tool/release/windowsinstaller/bundle/packagegroups/sitepackages.wxs rename from pypy/tool/release/windowsinstaller/bundle/packagegroups/site-packages.wxs rename to pypy/tool/release/windowsinstaller/bundle/packagegroups/sitepackages.wxs --- a/pypy/tool/release/windowsinstaller/bundle/packagegroups/site-packages.wxs +++ b/pypy/tool/release/windowsinstaller/bundle/packagegroups/sitepackages.wxs @@ -1,22 +1,22 @@ - - + + InstallCondition="InstallAllUsers and Include_site_packages and not LauncherOnly"> - + InstallCondition="not InstallAllUsers and Include_site_packages and not LauncherOnly"> diff --git a/pypy/tool/release/windowsinstaller/sitepackages/site-packages.wixproj b/pypy/tool/release/windowsinstaller/sitepackages/sitepackages.wixproj rename from pypy/tool/release/windowsinstaller/sitepackages/site-packages.wixproj rename to pypy/tool/release/windowsinstaller/sitepackages/sitepackages.wixproj --- a/pypy/tool/release/windowsinstaller/sitepackages/site-packages.wixproj +++ b/pypy/tool/release/windowsinstaller/sitepackages/sitepackages.wixproj @@ -3,23 +3,25 @@ {CB35E339-9711-49F0-A231-5FB2B1211BDB} 2.0 - site-packages + site_packages Package - + - + $(PySourcePath) !(bindpath.src) $(PySourcePath) - site-packages + site_packages diff --git a/pypy/tool/release/windowsinstaller/sitepackages/site-packages.wxs b/pypy/tool/release/windowsinstaller/sitepackages/sitepackages.wxs rename from pypy/tool/release/windowsinstaller/sitepackages/site-packages.wxs rename to pypy/tool/release/windowsinstaller/sitepackages/sitepackages.wxs --- a/pypy/tool/release/windowsinstaller/sitepackages/site-packages.wxs +++ b/pypy/tool/release/windowsinstaller/sitepackages/sitepackages.wxs @@ -7,7 +7,7 @@ - + diff --git a/pypy/tool/release/windowsinstaller/sitepackages/site-packages_en-US.wxl b/pypy/tool/release/windowsinstaller/sitepackages/sitepackages_en-US.wxl rename from pypy/tool/release/windowsinstaller/sitepackages/site-packages_en-US.wxl rename to pypy/tool/release/windowsinstaller/sitepackages/sitepackages_en-US.wxl From pypy.commits at gmail.com Tue Feb 5 05:58:26 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:26 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Further renaming Message-ID: <5c596c52.1c69fb81.3da66.3ac3@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95816:c38660f18fad Date: 2018-12-31 19:39 +0000 http://bitbucket.org/pypy/pypy/changeset/c38660f18fad/ Log: Further renaming diff --git a/pypy/tool/release/windowsinstaller/sitepackages/sitepackages.wixproj b/pypy/tool/release/windowsinstaller/site_packages/sitepackages.wixproj rename from pypy/tool/release/windowsinstaller/sitepackages/sitepackages.wixproj rename to pypy/tool/release/windowsinstaller/site_packages/sitepackages.wixproj diff --git a/pypy/tool/release/windowsinstaller/sitepackages/sitepackages.wxs b/pypy/tool/release/windowsinstaller/site_packages/sitepackages.wxs rename from pypy/tool/release/windowsinstaller/sitepackages/sitepackages.wxs rename to pypy/tool/release/windowsinstaller/site_packages/sitepackages.wxs diff --git a/pypy/tool/release/windowsinstaller/sitepackages/sitepackages_en-US.wxl b/pypy/tool/release/windowsinstaller/site_packages/sitepackages_en-US.wxl rename from pypy/tool/release/windowsinstaller/sitepackages/sitepackages_en-US.wxl rename to pypy/tool/release/windowsinstaller/site_packages/sitepackages_en-US.wxl From pypy.commits at gmail.com Tue Feb 5 05:58:28 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:28 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Further renaming Message-ID: <5c596c54.1c69fb81.62dfc.5b1b@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95817:5dedb6d71a0c Date: 2018-12-31 19:40 +0000 http://bitbucket.org/pypy/pypy/changeset/5dedb6d71a0c/ Log: Further renaming diff --git a/pypy/tool/release/windowsinstaller/site_packages/sitepackages.wixproj b/pypy/tool/release/windowsinstaller/site_packages/site_packages.wixproj rename from pypy/tool/release/windowsinstaller/site_packages/sitepackages.wixproj rename to pypy/tool/release/windowsinstaller/site_packages/site_packages.wixproj diff --git a/pypy/tool/release/windowsinstaller/site_packages/sitepackages.wxs b/pypy/tool/release/windowsinstaller/site_packages/site_packages.wxs rename from pypy/tool/release/windowsinstaller/site_packages/sitepackages.wxs rename to pypy/tool/release/windowsinstaller/site_packages/site_packages.wxs diff --git a/pypy/tool/release/windowsinstaller/site_packages/sitepackages_en-US.wxl b/pypy/tool/release/windowsinstaller/site_packages/site_packages_en-US.wxl rename from pypy/tool/release/windowsinstaller/site_packages/sitepackages_en-US.wxl rename to pypy/tool/release/windowsinstaller/site_packages/site_packages_en-US.wxl From pypy.commits at gmail.com Tue Feb 5 05:58:29 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:29 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Added path. Fixed some file names. Message-ID: <5c596c55.1c69fb81.5d829.a05c@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95818:d5fcd44841c2 Date: 2019-01-02 14:28 +0000 http://bitbucket.org/pypy/pypy/changeset/d5fcd44841c2/ Log: Added path. Fixed some file names. diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.targets b/pypy/tool/release/windowsinstaller/bundle/bundle.targets --- a/pypy/tool/release/windowsinstaller/bundle/bundle.targets +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.targets @@ -68,6 +68,7 @@ + diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs --- a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs @@ -62,16 +62,13 @@ - - + - - - - - + + + @@ -101,7 +98,8 @@ - + + diff --git a/pypy/tool/release/windowsinstaller/bundle/packagegroups/postinstall.wxs b/pypy/tool/release/windowsinstaller/bundle/packagegroups/postinstall.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/bundle/packagegroups/postinstall.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/common.wxs b/pypy/tool/release/windowsinstaller/common.wxs --- a/pypy/tool/release/windowsinstaller/common.wxs +++ b/pypy/tool/release/windowsinstaller/common.wxs @@ -85,7 +85,11 @@ - + + + + + diff --git a/pypy/tool/release/windowsinstaller/exe/exe.wxs b/pypy/tool/release/windowsinstaller/exe/exe.wxs --- a/pypy/tool/release/windowsinstaller/exe/exe.wxs +++ b/pypy/tool/release/windowsinstaller/exe/exe.wxs @@ -19,7 +19,7 @@ - - + diff --git a/pypy/tool/release/windowsinstaller/external/external_files.wxs b/pypy/tool/release/windowsinstaller/external/external_files.wxs --- a/pypy/tool/release/windowsinstaller/external/external_files.wxs +++ b/pypy/tool/release/windowsinstaller/external/external_files.wxs @@ -3,13 +3,13 @@ - + - + - + diff --git a/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wixproj b/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wixproj --- a/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wixproj +++ b/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wixproj @@ -14,7 +14,7 @@ - + $(PySourcePath) !(bindpath.src) $(PySourcePath) diff --git a/pypy/tool/release/windowsinstaller/path/path.wixproj b/pypy/tool/release/windowsinstaller/path/path.wixproj new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/path/path.wixproj @@ -0,0 +1,19 @@ + + + + {91C99298-8E2E-4422-A5AF-CC4FFF9A58D3} + 2.0 + path + Package + ICE71 + + + + + + + + + + + \ No newline at end of file diff --git a/pypy/tool/release/windowsinstaller/path/path.wxs b/pypy/tool/release/windowsinstaller/path/path.wxs new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/path/path.wxs @@ -0,0 +1,39 @@ + + + + + + + + + + + NOT ALLUSERS=1 + + + + + + + + + + + + ALLUSERS=1 + + + + + + + + + + + + + + + + diff --git a/pypy/tool/release/windowsinstaller/path/path_en-US.wxl b/pypy/tool/release/windowsinstaller/path/path_en-US.wxl new file mode 100644 --- /dev/null +++ b/pypy/tool/release/windowsinstaller/path/path_en-US.wxl @@ -0,0 +1,6 @@ + + + Add to Path + Path + No !(loc.ProductName) installation was detected. + From pypy.commits at gmail.com Tue Feb 5 05:58:31 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:31 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Work in progress Message-ID: <5c596c57.1c69fb81.f4dfc.2aeb@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95819:498026a51df2 Date: 2019-01-06 19:00 +0000 http://bitbucket.org/pypy/pypy/changeset/498026a51df2/ Log: Work in progress diff --git a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs --- a/pypy/tool/release/windowsinstaller/bundle/bundle.wxs +++ b/pypy/tool/release/windowsinstaller/bundle/bundle.wxs @@ -68,7 +68,7 @@ - + From pypy.commits at gmail.com Tue Feb 5 05:58:33 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:33 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Fixed issue with lib_pypy directory Message-ID: <5c596c59.1c69fb81.2575a.186a@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95820:c5555c37b0cf Date: 2019-01-06 20:11 +0000 http://bitbucket.org/pypy/pypy/changeset/c5555c37b0cf/ Log: Fixed issue with lib_pypy directory diff --git a/pypy/tool/release/windowsinstaller/common.wxs b/pypy/tool/release/windowsinstaller/common.wxs --- a/pypy/tool/release/windowsinstaller/common.wxs +++ b/pypy/tool/release/windowsinstaller/common.wxs @@ -85,6 +85,11 @@ + + + + + diff --git a/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wixproj b/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wixproj --- a/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wixproj +++ b/pypy/tool/release/windowsinstaller/lib_pypy/lib_pypy.wixproj @@ -14,7 +14,11 @@ - + $(PySourcePath) !(bindpath.src) $(PySourcePath) From pypy.commits at gmail.com Tue Feb 5 05:58:34 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:34 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Modify installer to remove customise button. Message-ID: <5c596c5a.1c69fb81.d9af9.d73c@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95821:bdb9d8ea6391 Date: 2019-01-06 21:01 +0000 http://bitbucket.org/pypy/pypy/changeset/bdb9d8ea6391/ Log: Modify installer to remove customise button. diff --git a/pypy/tool/release/windowsinstaller/bundle/Default.thm b/pypy/tool/release/windowsinstaller/bundle/Default.thm --- a/pypy/tool/release/windowsinstaller/bundle/Default.thm +++ b/pypy/tool/release/windowsinstaller/bundle/Default.thm @@ -22,7 +22,6 @@ #(loc.InstallMessage) - #(loc.ShortInstallLauncherAllUsersLabel) #(loc.ShortPrependPathLabel) diff --git a/pypy/tool/release/windowsinstaller/bundle/Default.wxl b/pypy/tool/release/windowsinstaller/bundle/Default.wxl --- a/pypy/tool/release/windowsinstaller/bundle/Default.wxl +++ b/pypy/tool/release/windowsinstaller/bundle/Default.wxl @@ -19,7 +19,7 @@ &Cancel &Close Install [WixBundleName] - Select Install Now to install PyPy with default settings, or choose Customize to enable or disable features. + Select Install Now to install PyPy with default settings. Version [WixBundleVersion] Upgrade to [WixBundleName] Select Upgrade Now to keep your current settings, or choose Customize to enable or disable features. @@ -44,7 +44,6 @@ &Install Now [TargetDir] -Includes IDLE, pip and documentation Creates shortcuts and file associations C&ustomize installation Choose location and features From pypy.commits at gmail.com Tue Feb 5 05:58:36 2019 From: pypy.commits at gmail.com (andr...@siemens.com) Date: Tue, 05 Feb 2019 02:58:36 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Add find msbuild.bat Message-ID: <5c596c5c.1c69fb81.790da.3716@mx.google.com> Author: andrew.lawrence at siemens.com Branch: windowsinstaller Changeset: r95822:c43a3018916a Date: 2019-02-02 19:23 +0000 http://bitbucket.org/pypy/pypy/changeset/c43a3018916a/ Log: Add find msbuild.bat diff --git a/PCbuild/find_msbuild.bat b/PCbuild/find_msbuild.bat new file mode 100644 --- /dev/null +++ b/PCbuild/find_msbuild.bat @@ -0,0 +1,60 @@ + at rem + at rem Searches for MSBuild.exe. This is the only tool we need to initiate + at rem a build, so we no longer search for the full VC toolset. + at rem + at rem This file is supposed to modify the state of the caller (specifically + at rem the MSBUILD variable), so we do not use setlocal or echo, and avoid + at rem changing any other persistent state. + at rem + + at rem No arguments provided means do full search + at if '%1' EQU '' goto :begin_search + + at rem One argument may be the full path. Use a goto so we don't try to + at rem parse the next if statement - incorrect quoting in the multi-arg + at rem case can cause us to break immediately. + at if '%2' EQU '' goto :one_arg + + at rem Entire command line may represent the full path if quoting failed. + at if exist "%*" (set MSBUILD="%*") & (set _Py_MSBuild_Source=environment) & goto :found + at goto :begin_search + +:one_arg + at if exist "%~1" (set MSBUILD="%~1") & (set _Py_MSBuild_Source=environment) & goto :found + +:begin_search + at set MSBUILD= + + at rem If msbuild.exe is on the PATH, assume that the user wants that one. + at where msbuild > "%TEMP%\msbuild.loc" 2> nul && set /P MSBUILD= < "%TEMP%\msbuild.loc" & del "%TEMP%\msbuild.loc" + at if exist "%MSBUILD%" set MSBUILD="%MSBUILD%" & (set _Py_MSBuild_Source=PATH) & goto :found + + at rem VS 2017 sets exactly one install as the "main" install, so we may find MSBuild in there. + at reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32 >nul 2>nul + at if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\SxS\VS7" /v 15.0 /reg:32') DO @( + @if "%%i"=="15.0" @if exist "%%k\MSBuild\15.0\Bin\msbuild.exe" @(set MSBUILD="%%k\MSBuild\15.0\Bin\msbuild.exe") +) + at if exist %MSBUILD% (set _Py_MSBuild_Source=Visual Studio 2017 registry) & goto :found + + at rem VS 2015 and earlier register MSBuild separately, so we can find it. + at reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0" /v MSBuildToolsPath /reg:32 >nul 2>nul + at if NOT ERRORLEVEL 1 @for /F "tokens=1,2*" %%i in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0" /v MSBuildToolsPath /reg:32') DO @( + @if "%%i"=="MSBuildToolsPath" @if exist "%%k\msbuild.exe" @(set MSBUILD="%%k\msbuild.exe") +) + at if exist %MSBUILD% (set _Py_MSBuild_Source=registry) & goto :found + + + at exit /b 1 + +:found + at pushd %MSBUILD% >nul 2>nul + at if not ERRORLEVEL 1 @( + @if exist msbuild.exe @(set MSBUILD="%CD%\msbuild.exe") else @(set MSBUILD=) + @popd +) + + at if defined MSBUILD @echo Using %MSBUILD% (found in the %_Py_MSBuild_Source%) + at if not defined MSBUILD @echo Failed to find MSBuild + at set _Py_MSBuild_Source= + at if not defined MSBUILD @exit /b 1 + at exit /b 0 From pypy.commits at gmail.com Tue Feb 5 05:58:38 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Tue, 05 Feb 2019 02:58:38 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: Add missing file Message-ID: <5c596c5e.1c69fb81.1a273.7488@mx.google.com> Author: andrewjlawrence Branch: windowsinstaller Changeset: r95823:a92906227d30 Date: 2019-02-02 22:14 +0000 http://bitbucket.org/pypy/pypy/changeset/a92906227d30/ Log: Add missing file diff --git a/PCbuild/tcltk.props b/PCbuild/tcltk.props new file mode 100644 --- /dev/null +++ b/PCbuild/tcltk.props @@ -0,0 +1,45 @@ + + + + + 8 + 6 + 8 + 0 + $(TclMajorVersion) + $(TclMinorVersion) + $(TclPatchLevel) + $(TclRevision) + 8 + 4 + 3 + 6 + $(ExternalsDir)tcl-core-$(TclMajorVersion).$(TclMinorVersion).$(TclPatchLevel).$(TclRevision)\ + $(ExternalsDir)tk-$(TkMajorVersion).$(TkMinorVersion).$(TkPatchLevel).$(TkRevision)\ + $(ExternalsDir)tix-$(TixMajorVersion).$(TixMinorVersion).$(TixPatchLevel).$(TixRevision)\ + $(ExternalsDir)tcltk-$(TclMajorVersion).$(TclMinorVersion).$(TclPatchLevel).$(TclRevision)\$(ArchName)\ + + tcl$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).dll + tcl$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).lib + tclsh$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).exe + tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).dll + tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).lib + tix$(TixMajorVersion)$(TixMinorVersion)$(TclDebugExt).dll + $(tcltkDir)lib\tix$(TixMajorVersion).$(TixMinorVersion).$(TixPatchLevel)\$(tixDLLName) + $(tcltkDir)lib\tcl$(TclMajorVersion)$(TclMinorVersion)t$(TclDebugExt).lib;$(tcltkDir)lib\tk$(TkMajorVersion)$(TkMinorVersion)t$(TclDebugExt).lib + IX86 + AMD64 + TCL_MAJOR_VERSION=$(TclMajorVersion) TCL_MINOR_VERSION=$(TclMinorVersion) TCL_PATCH_LEVEL=$(TclPatchLevel) + TCL_MAJOR=$(TclMajorVersion) TCL_MINOR=$(TclMinorVersion) TCL_PATCH=$(TclPatchLevel) + TK_MAJOR_VERSION=$(TkMajorVersion) TK_MINOR_VERSION=$(TkMinorVersion) TK_PATCH_LEVEL=$(TkPatchLevel) + + Release + Debug + $(BuildDirTop)_$(TclMachine) + $(BuildDirTop)_VC13 + $(BuildDirTop)_VC13 + $(BuildDirTop)_VC12 + $(BuildDirTop)_VC11 + $(BuildDirTop)_VC10 + + From pypy.commits at gmail.com Tue Feb 5 08:07:55 2019 From: pypy.commits at gmail.com (ambv) Date: Tue, 05 Feb 2019 05:07:55 -0800 (PST) Subject: [pypy-commit] pypy py3.6: (cfbolz, ambv) Fix __qualname__ for __new__ of built-in types Message-ID: <5c598aab.1c69fb81.5b59c.c90c@mx.google.com> Author: Łukasz Langa Branch: py3.6 Changeset: r95824:c35aa59e82f6 Date: 2019-02-05 14:07 +0100 http://bitbucket.org/pypy/pypy/changeset/c35aa59e82f6/ Log: (cfbolz,ambv) Fix __qualname__ for __new__ of built-in types diff --git a/pypy/objspace/std/test/test_typeobject.py b/pypy/objspace/std/test/test_typeobject.py --- a/pypy/objspace/std/test/test_typeobject.py +++ b/pypy/objspace/std/test/test_typeobject.py @@ -427,6 +427,14 @@ def test_method_qualname(self): assert dict.copy.__qualname__ == 'dict.copy' + def test_staticmethod_qualname(self): + assert dict.__new__.__qualname__ == 'dict.__new__' + class A: + @staticmethod + def stat(): + pass + assert A.stat.__qualname__.endswith('A.stat') + def test_builtin_add(self): x = 5 assert x.__add__(6) == 11 diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py --- a/pypy/objspace/std/typeobject.py +++ b/pypy/objspace/std/typeobject.py @@ -1560,6 +1560,8 @@ w_obj = dict_w[name] if isinstance(w_obj, ClassMethod): w_obj = w_obj.w_function + if isinstance(w_obj, StaticMethod): + w_obj = w_obj.w_function if isinstance(w_obj, FunctionWithFixedCode): qualname = (w_type.getqualname(space).encode('utf-8') + '.' + name) From pypy.commits at gmail.com Tue Feb 5 08:35:36 2019 From: pypy.commits at gmail.com (fijal) Date: Tue, 05 Feb 2019 05:35:36 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8: remove dead code Message-ID: <5c599128.1c69fb81.3969e.8092@mx.google.com> Author: fijal Branch: unicode-utf8 Changeset: r95825:9c07b0c439aa Date: 2019-02-05 14:34 +0100 http://bitbucket.org/pypy/pypy/changeset/9c07b0c439aa/ Log: remove dead code diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -487,8 +487,6 @@ """ Create an index storage which stores index of each 4th character in utf8 encoded unicode string. """ -# if len(utf8) == utf8len < ASCII_INDEX_STORAGE_BLOCKS * 64: -# return ASCII_INDEX_STORAGE arraysize = utf8len // 64 + 1 storage = lltype.malloc(UTF8_INDEX_STORAGE, arraysize) baseindex = 0 From pypy.commits at gmail.com Tue Feb 5 08:39:28 2019 From: pypy.commits at gmail.com (arigo) Date: Tue, 05 Feb 2019 05:39:28 -0800 (PST) Subject: [pypy-commit] pypy default: oops Message-ID: <5c599210.1c69fb81.9776f.323f@mx.google.com> Author: Armin Rigo Branch: Changeset: r95826:a8f961c358d3 Date: 2019-02-05 14:39 +0100 http://bitbucket.org/pypy/pypy/changeset/a8f961c358d3/ Log: oops diff --git a/lib_pypy/cffi/pkgconfig.py b/lib_pypy/cffi/pkgconfig.py new file mode 100644 --- /dev/null +++ b/lib_pypy/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except EnvironmentError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret From pypy.commits at gmail.com Tue Feb 5 08:48:26 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 05 Feb 2019 05:48:26 -0800 (PST) Subject: [pypy-commit] pypy release-pypy2.7-7.x: fix merge (antocuni) Message-ID: <5c59942a.1c69fb81.3da66.763d@mx.google.com> Author: Matti Picus Branch: release-pypy2.7-7.x Changeset: r95827:bb774cc5b3f4 Date: 2019-02-05 14:39 +0100 http://bitbucket.org/pypy/pypy/changeset/bb774cc5b3f4/ Log: fix merge (antocuni) 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 @@ -32,14 +32,9 @@ * module/sys/version.py * doc/conf.py */ -<<<<<<< working copy #define PYPY_VERSION "7.0.0" #define PYPY_VERSION_NUM 0x07000000 -======= -#define PYPY_VERSION "7.1.0-alpha0" -#define PYPY_VERSION_NUM 0x07010000 ->>>>>>> merge rev /* Defined to mean a PyPy where cpyext holds more regular references to PyObjects, e.g. staying alive as long as the internal PyPy object stays alive. */ From pypy.commits at gmail.com Tue Feb 5 08:48:28 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 05 Feb 2019 05:48:28 -0800 (PST) Subject: [pypy-commit] pypy release-pypy2.7-7.x: merge default into release Message-ID: <5c59942c.1c69fb81.c0cda.a581@mx.google.com> Author: Matti Picus Branch: release-pypy2.7-7.x Changeset: r95828:9112c8071614 Date: 2019-02-05 14:42 +0100 http://bitbucket.org/pypy/pypy/changeset/9112c8071614/ Log: merge default into release diff --git a/lib_pypy/cffi/pkgconfig.py b/lib_pypy/cffi/pkgconfig.py new file mode 100644 --- /dev/null +++ b/lib_pypy/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except EnvironmentError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret diff --git a/pypy/module/_sre/interp_sre.py b/pypy/module/_sre/interp_sre.py --- a/pypy/module/_sre/interp_sre.py +++ b/pypy/module/_sre/interp_sre.py @@ -531,7 +531,7 @@ assert idx >= 0 return fmarks[idx], fmarks[idx+1] else: - raise oefmt(space.w_IndexError, "group index out of range") + raise oefmt(space.w_IndexError, "no such group") def _last_index(self): mark = self.ctx.match_marks diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -65,10 +65,10 @@ return OperationError(w_error, space.newtext(msg)) - at unwrap_spec(string='bufferstr', level=int) -def compress(space, string, level=rzlib.Z_DEFAULT_COMPRESSION): + at unwrap_spec(data='bufferstr', level=int) +def compress(space, data, level=rzlib.Z_DEFAULT_COMPRESSION): """ - compress(string[, level]) -- Returned compressed string. + compress(data[, level]) -- Returned compressed string. Optional arg level is the compression level, in 1-9. """ @@ -78,7 +78,7 @@ except ValueError: raise zlib_error(space, "Bad compression level") try: - result = rzlib.compress(stream, string, rzlib.Z_FINISH) + result = rzlib.compress(stream, data, rzlib.Z_FINISH) finally: rzlib.deflateEnd(stream) except rzlib.RZlibError as e: From pypy.commits at gmail.com Tue Feb 5 08:48:30 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 05 Feb 2019 05:48:30 -0800 (PST) Subject: [pypy-commit] pypy py3.5: merge default into py3.5 Message-ID: <5c59942e.1c69fb81.11ed8.4925@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r95829:7e2f5836e315 Date: 2019-02-05 14:43 +0100 http://bitbucket.org/pypy/pypy/changeset/7e2f5836e315/ Log: merge default into py3.5 diff --git a/lib_pypy/cffi/pkgconfig.py b/lib_pypy/cffi/pkgconfig.py new file mode 100644 --- /dev/null +++ b/lib_pypy/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except EnvironmentError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret diff --git a/pypy/module/_sre/interp_sre.py b/pypy/module/_sre/interp_sre.py --- a/pypy/module/_sre/interp_sre.py +++ b/pypy/module/_sre/interp_sre.py @@ -622,7 +622,7 @@ assert idx >= 0 return fmarks[idx], fmarks[idx+1] else: - raise oefmt(space.w_IndexError, "group index out of range") + raise oefmt(space.w_IndexError, "no such group") def _last_index(self): mark = self.ctx.match_marks diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -44,10 +44,10 @@ return OperationError(w_error, space.newtext(msg)) - at unwrap_spec(string='bufferstr', level=int) -def compress(space, string, level=rzlib.Z_DEFAULT_COMPRESSION): + at unwrap_spec(data='bufferstr', level=int) +def compress(space, data, level=rzlib.Z_DEFAULT_COMPRESSION): """ - compress(string[, level]) -- Returned compressed string. + compress(data[, level]) -- Returned compressed string. Optional arg level is the compression level, in 1-9. """ @@ -57,7 +57,7 @@ except ValueError: raise zlib_error(space, "Bad compression level") try: - result = rzlib.compress(stream, string, rzlib.Z_FINISH) + result = rzlib.compress(stream, data, rzlib.Z_FINISH) finally: rzlib.deflateEnd(stream) except rzlib.RZlibError as e: From pypy.commits at gmail.com Tue Feb 5 08:48:33 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 05 Feb 2019 05:48:33 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge py3.5 into py3.6 Message-ID: <5c599431.1c69fb81.972a6.dd3d@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r95830:33fe3b2cf186 Date: 2019-02-05 14:44 +0100 http://bitbucket.org/pypy/pypy/changeset/33fe3b2cf186/ Log: merge py3.5 into py3.6 diff too long, truncating to 2000 out of 22336 lines diff --git a/lib-python/3/collections/__main__.py b/lib-python/3/collections/__main__.py new file mode 100644 --- /dev/null +++ b/lib-python/3/collections/__main__.py @@ -0,0 +1,38 @@ +################################################################################ +### Simple tests +################################################################################ + +# verify that instances can be pickled +from collections import namedtuple +from pickle import loads, dumps +Point = namedtuple('Point', 'x, y', True) +p = Point(x=10, y=20) +assert p == loads(dumps(p)) + +# test and demonstrate ability to override methods +class Point(namedtuple('Point', 'x y')): + __slots__ = () + @property + def hypot(self): + return (self.x ** 2 + self.y ** 2) ** 0.5 + def __str__(self): + return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) + +for p in Point(3, 4), Point(14, 5/7.): + print (p) + +class Point(namedtuple('Point', 'x y')): + 'Point class with optimized _make() and _replace() without error-checking' + __slots__ = () + _make = classmethod(tuple.__new__) + def _replace(self, _map=map, **kwds): + return self._make(_map(kwds.get, ('x', 'y'), self)) + +print(Point(11, 22)._replace(x=100)) + +Point3D = namedtuple('Point3D', Point._fields + ('z',)) +print(Point3D.__doc__) + +import doctest, collections +TestResults = namedtuple('TestResults', 'failed attempted') +print(TestResults(*doctest.testmod(collections))) diff --git a/lib-python/3/idlelib/AutoCompleteWindow.py b/lib-python/3/idlelib/AutoCompleteWindow.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/AutoCompleteWindow.py @@ -0,0 +1,416 @@ +""" +An auto-completion window for IDLE, used by the AutoComplete extension +""" +from tkinter import * +from idlelib.MultiCall import MC_SHIFT +from idlelib.AutoComplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES + +HIDE_VIRTUAL_EVENT_NAME = "<>" +HIDE_SEQUENCES = ("", "") +KEYPRESS_VIRTUAL_EVENT_NAME = "<>" +# We need to bind event beyond so that the function will be called +# before the default specific IDLE function +KEYPRESS_SEQUENCES = ("", "", "", "", + "", "", "", "", + "", "") +KEYRELEASE_VIRTUAL_EVENT_NAME = "<>" +KEYRELEASE_SEQUENCE = "" +LISTUPDATE_SEQUENCE = "" +WINCONFIG_SEQUENCE = "" +DOUBLECLICK_SEQUENCE = "" + +class AutoCompleteWindow: + + def __init__(self, widget): + # The widget (Text) on which we place the AutoCompleteWindow + self.widget = widget + # The widgets we create + self.autocompletewindow = self.listbox = self.scrollbar = None + # The default foreground and background of a selection. Saved because + # they are changed to the regular colors of list items when the + # completion start is not a prefix of the selected completion + self.origselforeground = self.origselbackground = None + # The list of completions + self.completions = None + # A list with more completions, or None + self.morecompletions = None + # The completion mode. Either AutoComplete.COMPLETE_ATTRIBUTES or + # AutoComplete.COMPLETE_FILES + self.mode = None + # The current completion start, on the text box (a string) + self.start = None + # The index of the start of the completion + self.startindex = None + # The last typed start, used so that when the selection changes, + # the new start will be as close as possible to the last typed one. + self.lasttypedstart = None + # Do we have an indication that the user wants the completion window + # (for example, he clicked the list) + self.userwantswindow = None + # event ids + self.hideid = self.keypressid = self.listupdateid = self.winconfigid \ + = self.keyreleaseid = self.doubleclickid = None + # Flag set if last keypress was a tab + self.lastkey_was_tab = False + + def _change_start(self, newstart): + min_len = min(len(self.start), len(newstart)) + i = 0 + while i < min_len and self.start[i] == newstart[i]: + i += 1 + if i < len(self.start): + self.widget.delete("%s+%dc" % (self.startindex, i), + "%s+%dc" % (self.startindex, len(self.start))) + if i < len(newstart): + self.widget.insert("%s+%dc" % (self.startindex, i), + newstart[i:]) + self.start = newstart + + def _binary_search(self, s): + """Find the first index in self.completions where completions[i] is + greater or equal to s, or the last index if there is no such + one.""" + i = 0; j = len(self.completions) + while j > i: + m = (i + j) // 2 + if self.completions[m] >= s: + j = m + else: + i = m + 1 + return min(i, len(self.completions)-1) + + def _complete_string(self, s): + """Assuming that s is the prefix of a string in self.completions, + return the longest string which is a prefix of all the strings which + s is a prefix of them. If s is not a prefix of a string, return s.""" + first = self._binary_search(s) + if self.completions[first][:len(s)] != s: + # There is not even one completion which s is a prefix of. + return s + # Find the end of the range of completions where s is a prefix of. + i = first + 1 + j = len(self.completions) + while j > i: + m = (i + j) // 2 + if self.completions[m][:len(s)] != s: + j = m + else: + i = m + 1 + last = i-1 + + if first == last: # only one possible completion + return self.completions[first] + + # We should return the maximum prefix of first and last + first_comp = self.completions[first] + last_comp = self.completions[last] + min_len = min(len(first_comp), len(last_comp)) + i = len(s) + while i < min_len and first_comp[i] == last_comp[i]: + i += 1 + return first_comp[:i] + + def _selection_changed(self): + """Should be called when the selection of the Listbox has changed. + Updates the Listbox display and calls _change_start.""" + cursel = int(self.listbox.curselection()[0]) + + self.listbox.see(cursel) + + lts = self.lasttypedstart + selstart = self.completions[cursel] + if self._binary_search(lts) == cursel: + newstart = lts + else: + min_len = min(len(lts), len(selstart)) + i = 0 + while i < min_len and lts[i] == selstart[i]: + i += 1 + newstart = selstart[:i] + self._change_start(newstart) + + if self.completions[cursel][:len(self.start)] == self.start: + # start is a prefix of the selected completion + self.listbox.configure(selectbackground=self.origselbackground, + selectforeground=self.origselforeground) + else: + self.listbox.configure(selectbackground=self.listbox.cget("bg"), + selectforeground=self.listbox.cget("fg")) + # If there are more completions, show them, and call me again. + if self.morecompletions: + self.completions = self.morecompletions + self.morecompletions = None + self.listbox.delete(0, END) + for item in self.completions: + self.listbox.insert(END, item) + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + + def show_window(self, comp_lists, index, complete, mode, userWantsWin): + """Show the autocomplete list, bind events. + If complete is True, complete the text, and if there is exactly one + matching completion, don't open a list.""" + # Handle the start we already have + self.completions, self.morecompletions = comp_lists + self.mode = mode + self.startindex = self.widget.index(index) + self.start = self.widget.get(self.startindex, "insert") + if complete: + completed = self._complete_string(self.start) + start = self.start + self._change_start(completed) + i = self._binary_search(completed) + if self.completions[i] == completed and \ + (i == len(self.completions)-1 or + self.completions[i+1][:len(completed)] != completed): + # There is exactly one matching completion + return completed == start + self.userwantswindow = userWantsWin + self.lasttypedstart = self.start + + # Put widgets in place + self.autocompletewindow = acw = Toplevel(self.widget) + # Put it in a position so that it is not seen. + acw.wm_geometry("+10000+10000") + # Make it float + acw.wm_overrideredirect(1) + try: + # This command is only needed and available on Tk >= 8.4.0 for OSX + # Without it, call tips intrude on the typing process by grabbing + # the focus. + acw.tk.call("::tk::unsupported::MacWindowStyle", "style", acw._w, + "help", "noActivates") + except TclError: + pass + self.scrollbar = scrollbar = Scrollbar(acw, orient=VERTICAL) + self.listbox = listbox = Listbox(acw, yscrollcommand=scrollbar.set, + exportselection=False, bg="white") + for item in self.completions: + listbox.insert(END, item) + self.origselforeground = listbox.cget("selectforeground") + self.origselbackground = listbox.cget("selectbackground") + scrollbar.config(command=listbox.yview) + scrollbar.pack(side=RIGHT, fill=Y) + listbox.pack(side=LEFT, fill=BOTH, expand=True) + acw.lift() # work around bug in Tk 8.5.18+ (issue #24570) + + # Initialize the listbox selection + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + + # bind events + self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME, + self.hide_event) + for seq in HIDE_SEQUENCES: + self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq) + self.keypressid = self.widget.bind(KEYPRESS_VIRTUAL_EVENT_NAME, + self.keypress_event) + for seq in KEYPRESS_SEQUENCES: + self.widget.event_add(KEYPRESS_VIRTUAL_EVENT_NAME, seq) + self.keyreleaseid = self.widget.bind(KEYRELEASE_VIRTUAL_EVENT_NAME, + self.keyrelease_event) + self.widget.event_add(KEYRELEASE_VIRTUAL_EVENT_NAME,KEYRELEASE_SEQUENCE) + self.listupdateid = listbox.bind(LISTUPDATE_SEQUENCE, + self.listselect_event) + self.winconfigid = acw.bind(WINCONFIG_SEQUENCE, self.winconfig_event) + self.doubleclickid = listbox.bind(DOUBLECLICK_SEQUENCE, + self.doubleclick_event) + + def winconfig_event(self, event): + if not self.is_active(): + return + # Position the completion list window + text = self.widget + text.see(self.startindex) + x, y, cx, cy = text.bbox(self.startindex) + acw = self.autocompletewindow + acw_width, acw_height = acw.winfo_width(), acw.winfo_height() + text_width, text_height = text.winfo_width(), text.winfo_height() + new_x = text.winfo_rootx() + min(x, max(0, text_width - acw_width)) + new_y = text.winfo_rooty() + y + if (text_height - (y + cy) >= acw_height # enough height below + or y < acw_height): # not enough height above + # place acw below current line + new_y += cy + else: + # place acw above current line + new_y -= acw_height + acw.wm_geometry("+%d+%d" % (new_x, new_y)) + + def hide_event(self, event): + if not self.is_active(): + return + self.hide_window() + + def listselect_event(self, event): + if not self.is_active(): + return + self.userwantswindow = True + cursel = int(self.listbox.curselection()[0]) + self._change_start(self.completions[cursel]) + + def doubleclick_event(self, event): + # Put the selected completion in the text, and close the list + cursel = int(self.listbox.curselection()[0]) + self._change_start(self.completions[cursel]) + self.hide_window() + + def keypress_event(self, event): + if not self.is_active(): + return + keysym = event.keysym + if hasattr(event, "mc_state"): + state = event.mc_state + else: + state = 0 + if keysym != "Tab": + self.lastkey_was_tab = False + if (len(keysym) == 1 or keysym in ("underscore", "BackSpace") + or (self.mode == COMPLETE_FILES and keysym in + ("period", "minus"))) \ + and not (state & ~MC_SHIFT): + # Normal editing of text + if len(keysym) == 1: + self._change_start(self.start + keysym) + elif keysym == "underscore": + self._change_start(self.start + '_') + elif keysym == "period": + self._change_start(self.start + '.') + elif keysym == "minus": + self._change_start(self.start + '-') + else: + # keysym == "BackSpace" + if len(self.start) == 0: + self.hide_window() + return + self._change_start(self.start[:-1]) + self.lasttypedstart = self.start + self.listbox.select_clear(0, int(self.listbox.curselection()[0])) + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + return "break" + + elif keysym == "Return": + self.hide_window() + return + + elif (self.mode == COMPLETE_ATTRIBUTES and keysym in + ("period", "space", "parenleft", "parenright", "bracketleft", + "bracketright")) or \ + (self.mode == COMPLETE_FILES and keysym in + ("slash", "backslash", "quotedbl", "apostrophe")) \ + and not (state & ~MC_SHIFT): + # If start is a prefix of the selection, but is not '' when + # completing file names, put the whole + # selected completion. Anyway, close the list. + cursel = int(self.listbox.curselection()[0]) + if self.completions[cursel][:len(self.start)] == self.start \ + and (self.mode == COMPLETE_ATTRIBUTES or self.start): + self._change_start(self.completions[cursel]) + self.hide_window() + return + + elif keysym in ("Home", "End", "Prior", "Next", "Up", "Down") and \ + not state: + # Move the selection in the listbox + self.userwantswindow = True + cursel = int(self.listbox.curselection()[0]) + if keysym == "Home": + newsel = 0 + elif keysym == "End": + newsel = len(self.completions)-1 + elif keysym in ("Prior", "Next"): + jump = self.listbox.nearest(self.listbox.winfo_height()) - \ + self.listbox.nearest(0) + if keysym == "Prior": + newsel = max(0, cursel-jump) + else: + assert keysym == "Next" + newsel = min(len(self.completions)-1, cursel+jump) + elif keysym == "Up": + newsel = max(0, cursel-1) + else: + assert keysym == "Down" + newsel = min(len(self.completions)-1, cursel+1) + self.listbox.select_clear(cursel) + self.listbox.select_set(newsel) + self._selection_changed() + self._change_start(self.completions[newsel]) + return "break" + + elif (keysym == "Tab" and not state): + if self.lastkey_was_tab: + # two tabs in a row; insert current selection and close acw + cursel = int(self.listbox.curselection()[0]) + self._change_start(self.completions[cursel]) + self.hide_window() + return "break" + else: + # first tab; let AutoComplete handle the completion + self.userwantswindow = True + self.lastkey_was_tab = True + return + + elif any(s in keysym for s in ("Shift", "Control", "Alt", + "Meta", "Command", "Option")): + # A modifier key, so ignore + return + + elif event.char and event.char >= ' ': + # Regular character with a non-length-1 keycode + self._change_start(self.start + event.char) + self.lasttypedstart = self.start + self.listbox.select_clear(0, int(self.listbox.curselection()[0])) + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + return "break" + + else: + # Unknown event, close the window and let it through. + self.hide_window() + return + + def keyrelease_event(self, event): + if not self.is_active(): + return + if self.widget.index("insert") != \ + self.widget.index("%s+%dc" % (self.startindex, len(self.start))): + # If we didn't catch an event which moved the insert, close window + self.hide_window() + + def is_active(self): + return self.autocompletewindow is not None + + def complete(self): + self._change_start(self._complete_string(self.start)) + # The selection doesn't change. + + def hide_window(self): + if not self.is_active(): + return + + # unbind events + for seq in HIDE_SEQUENCES: + self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq) + self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid) + self.hideid = None + for seq in KEYPRESS_SEQUENCES: + self.widget.event_delete(KEYPRESS_VIRTUAL_EVENT_NAME, seq) + self.widget.unbind(KEYPRESS_VIRTUAL_EVENT_NAME, self.keypressid) + self.keypressid = None + self.widget.event_delete(KEYRELEASE_VIRTUAL_EVENT_NAME, + KEYRELEASE_SEQUENCE) + self.widget.unbind(KEYRELEASE_VIRTUAL_EVENT_NAME, self.keyreleaseid) + self.keyreleaseid = None + self.listbox.unbind(LISTUPDATE_SEQUENCE, self.listupdateid) + self.listupdateid = None + self.autocompletewindow.unbind(WINCONFIG_SEQUENCE, self.winconfigid) + self.winconfigid = None + + # destroy widgets + self.scrollbar.destroy() + self.scrollbar = None + self.listbox.destroy() + self.listbox = None + self.autocompletewindow.destroy() + self.autocompletewindow = None diff --git a/lib-python/3/idlelib/Bindings.py b/lib-python/3/idlelib/Bindings.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/Bindings.py @@ -0,0 +1,96 @@ +"""Define the menu contents, hotkeys, and event bindings. + +There is additional configuration information in the EditorWindow class (and +subclasses): the menus are created there based on the menu_specs (class) +variable, and menus not created are silently skipped in the code here. This +makes it possible, for example, to define a Debug menu which is only present in +the PythonShell window, and a Format menu which is only present in the Editor +windows. + +""" +from importlib.util import find_spec + +from idlelib.configHandler import idleConf + +# Warning: menudefs is altered in macosxSupport.overrideRootMenu() +# after it is determined that an OS X Aqua Tk is in use, +# which cannot be done until after Tk() is first called. +# Do not alter the 'file', 'options', or 'help' cascades here +# without altering overrideRootMenu() as well. +# TODO: Make this more robust + +menudefs = [ + # underscore prefixes character to underscore + ('file', [ + ('_New File', '<>'), + ('_Open...', '<>'), + ('Open _Module...', '<>'), + ('Class _Browser', '<>'), + ('_Path Browser', '<>'), + None, + ('_Save', '<>'), + ('Save _As...', '<>'), + ('Save Cop_y As...', '<>'), + None, + ('Prin_t Window', '<>'), + None, + ('_Close', '<>'), + ('E_xit', '<>'), + ]), + ('edit', [ + ('_Undo', '<>'), + ('_Redo', '<>'), + None, + ('Cu_t', '<>'), + ('_Copy', '<>'), + ('_Paste', '<>'), + ('Select _All', '<>'), + None, + ('_Find...', '<>'), + ('Find A_gain', '<>'), + ('Find _Selection', '<>'), + ('Find in Files...', '<>'), + ('R_eplace...', '<>'), + ('Go to _Line', '<>'), + ]), +('format', [ + ('_Indent Region', '<>'), + ('_Dedent Region', '<>'), + ('Comment _Out Region', '<>'), + ('U_ncomment Region', '<>'), + ('Tabify Region', '<>'), + ('Untabify Region', '<>'), + ('Toggle Tabs', '<>'), + ('New Indent Width', '<>'), + ]), + ('run', [ + ('Python Shell', '<>'), + ]), + ('shell', [ + ('_View Last Restart', '<>'), + ('_Restart Shell', '<>'), + None, + ('_Interrupt Execution', '<>'), + ]), + ('debug', [ + ('_Go to File/Line', '<>'), + ('!_Debugger', '<>'), + ('_Stack Viewer', '<>'), + ('!_Auto-open Stack Viewer', '<>'), + ]), + ('options', [ + ('Configure _IDLE', '<>'), + None, + ]), + ('help', [ + ('_About IDLE', '<>'), + None, + ('_IDLE Help', '<>'), + ('Python _Docs', '<>'), + ]), +] + +if find_spec('turtledemo'): + menudefs[-1][1].append(('Turtle Demo', '<>')) + +default_keydefs = idleConf.GetCurrentKeySet() diff --git a/lib-python/3/idlelib/CallTipWindow.py b/lib-python/3/idlelib/CallTipWindow.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/CallTipWindow.py @@ -0,0 +1,161 @@ +"""A CallTip window class for Tkinter/IDLE. + +After ToolTip.py, which uses ideas gleaned from PySol +Used by the CallTips IDLE extension. +""" +from tkinter import Toplevel, Label, LEFT, SOLID, TclError + +HIDE_VIRTUAL_EVENT_NAME = "<>" +HIDE_SEQUENCES = ("", "") +CHECKHIDE_VIRTUAL_EVENT_NAME = "<>" +CHECKHIDE_SEQUENCES = ("", "") +CHECKHIDE_TIME = 100 # milliseconds + +MARK_RIGHT = "calltipwindowregion_right" + +class CallTip: + + def __init__(self, widget): + self.widget = widget + self.tipwindow = self.label = None + self.parenline = self.parencol = None + self.lastline = None + self.hideid = self.checkhideid = None + self.checkhide_after_id = None + + def position_window(self): + """Check if needs to reposition the window, and if so - do it.""" + curline = int(self.widget.index("insert").split('.')[0]) + if curline == self.lastline: + return + self.lastline = curline + self.widget.see("insert") + if curline == self.parenline: + box = self.widget.bbox("%d.%d" % (self.parenline, + self.parencol)) + else: + box = self.widget.bbox("%d.0" % curline) + if not box: + box = list(self.widget.bbox("insert")) + # align to left of window + box[0] = 0 + box[2] = 0 + x = box[0] + self.widget.winfo_rootx() + 2 + y = box[1] + box[3] + self.widget.winfo_rooty() + self.tipwindow.wm_geometry("+%d+%d" % (x, y)) + + def showtip(self, text, parenleft, parenright): + """Show the calltip, bind events which will close it and reposition it. + """ + # Only called in CallTips, where lines are truncated + self.text = text + if self.tipwindow or not self.text: + return + + self.widget.mark_set(MARK_RIGHT, parenright) + self.parenline, self.parencol = map( + int, self.widget.index(parenleft).split(".")) + + self.tipwindow = tw = Toplevel(self.widget) + self.position_window() + # remove border on calltip window + tw.wm_overrideredirect(1) + try: + # This command is only needed and available on Tk >= 8.4.0 for OSX + # Without it, call tips intrude on the typing process by grabbing + # the focus. + tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w, + "help", "noActivates") + except TclError: + pass + self.label = Label(tw, text=self.text, justify=LEFT, + background="#ffffe0", relief=SOLID, borderwidth=1, + font = self.widget['font']) + self.label.pack() + tw.lift() # work around bug in Tk 8.5.18+ (issue #24570) + + self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME, + self.checkhide_event) + for seq in CHECKHIDE_SEQUENCES: + self.widget.event_add(CHECKHIDE_VIRTUAL_EVENT_NAME, seq) + self.widget.after(CHECKHIDE_TIME, self.checkhide_event) + self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME, + self.hide_event) + for seq in HIDE_SEQUENCES: + self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq) + + def checkhide_event(self, event=None): + if not self.tipwindow: + # If the event was triggered by the same event that unbinded + # this function, the function will be called nevertheless, + # so do nothing in this case. + return + curline, curcol = map(int, self.widget.index("insert").split('.')) + if curline < self.parenline or \ + (curline == self.parenline and curcol <= self.parencol) or \ + self.widget.compare("insert", ">", MARK_RIGHT): + self.hidetip() + else: + self.position_window() + if self.checkhide_after_id is not None: + self.widget.after_cancel(self.checkhide_after_id) + self.checkhide_after_id = \ + self.widget.after(CHECKHIDE_TIME, self.checkhide_event) + + def hide_event(self, event): + if not self.tipwindow: + # See the explanation in checkhide_event. + return + self.hidetip() + + def hidetip(self): + if not self.tipwindow: + return + + for seq in CHECKHIDE_SEQUENCES: + self.widget.event_delete(CHECKHIDE_VIRTUAL_EVENT_NAME, seq) + self.widget.unbind(CHECKHIDE_VIRTUAL_EVENT_NAME, self.checkhideid) + self.checkhideid = None + for seq in HIDE_SEQUENCES: + self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq) + self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid) + self.hideid = None + + self.label.destroy() + self.label = None + self.tipwindow.destroy() + self.tipwindow = None + + self.widget.mark_unset(MARK_RIGHT) + self.parenline = self.parencol = self.lastline = None + + def is_active(self): + return bool(self.tipwindow) + + +def _calltip_window(parent): # htest # + from tkinter import Toplevel, Text, LEFT, BOTH + + top = Toplevel(parent) + top.title("Test calltips") + top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200, + parent.winfo_rooty() + 150)) + text = Text(top) + text.pack(side=LEFT, fill=BOTH, expand=1) + text.insert("insert", "string.split") + top.update() + calltip = CallTip(text) + + def calltip_show(event): + calltip.showtip("(s=Hello world)", "insert", "end") + def calltip_hide(event): + calltip.hidetip() + text.event_add("<>", "(") + text.event_add("<>", ")") + text.bind("<>", calltip_show) + text.bind("<>", calltip_hide) + text.focus_set() + +if __name__=='__main__': + from idlelib.idle_test.htest import run + run(_calltip_window) diff --git a/lib-python/3/idlelib/ClassBrowser.py b/lib-python/3/idlelib/ClassBrowser.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/ClassBrowser.py @@ -0,0 +1,236 @@ +"""Class browser. + +XXX TO DO: + +- reparse when source changed (maybe just a button would be OK?) + (or recheck on window popup) +- add popup menu with more options (e.g. doc strings, base classes, imports) +- show function argument list? (have to do pattern matching on source) +- should the classes and methods lists also be in the module's menu bar? +- add base classes to class browser tree +""" + +import os +import sys +import pyclbr + +from idlelib import PyShell +from idlelib.WindowList import ListedToplevel +from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas +from idlelib.configHandler import idleConf + +file_open = None # Method...Item and Class...Item use this. +# Normally PyShell.flist.open, but there is no PyShell.flist for htest. + +class ClassBrowser: + + def __init__(self, flist, name, path, _htest=False): + # XXX This API should change, if the file doesn't end in ".py" + # XXX the code here is bogus! + """ + _htest - bool, change box when location running htest. + """ + global file_open + if not _htest: + file_open = PyShell.flist.open + self.name = name + self.file = os.path.join(path[0], self.name + ".py") + self._htest = _htest + self.init(flist) + + def close(self, event=None): + self.top.destroy() + self.node.destroy() + + def init(self, flist): + self.flist = flist + # reset pyclbr + pyclbr._modules.clear() + # create top + self.top = top = ListedToplevel(flist.root) + top.protocol("WM_DELETE_WINDOW", self.close) + top.bind("", self.close) + if self._htest: # place dialog below parent if running htest + top.geometry("+%d+%d" % + (flist.root.winfo_rootx(), flist.root.winfo_rooty() + 200)) + self.settitle() + top.focus_set() + # create scrolled canvas + theme = idleConf.CurrentTheme() + background = idleConf.GetHighlight(theme, 'normal')['background'] + sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1) + sc.frame.pack(expand=1, fill="both") + item = self.rootnode() + self.node = node = TreeNode(sc.canvas, None, item) + node.update() + node.expand() + + def settitle(self): + self.top.wm_title("Class Browser - " + self.name) + self.top.wm_iconname("Class Browser") + + def rootnode(self): + return ModuleBrowserTreeItem(self.file) + +class ModuleBrowserTreeItem(TreeItem): + + def __init__(self, file): + self.file = file + + def GetText(self): + return os.path.basename(self.file) + + def GetIconName(self): + return "python" + + def GetSubList(self): + sublist = [] + for name in self.listclasses(): + item = ClassBrowserTreeItem(name, self.classes, self.file) + sublist.append(item) + return sublist + + def OnDoubleClick(self): + if os.path.normcase(self.file[-3:]) != ".py": + return + if not os.path.exists(self.file): + return + PyShell.flist.open(self.file) + + def IsExpandable(self): + return os.path.normcase(self.file[-3:]) == ".py" + + def listclasses(self): + dir, file = os.path.split(self.file) + name, ext = os.path.splitext(file) + if os.path.normcase(ext) != ".py": + return [] + try: + dict = pyclbr.readmodule_ex(name, [dir] + sys.path) + except ImportError: + return [] + items = [] + self.classes = {} + for key, cl in dict.items(): + if cl.module == name: + s = key + if hasattr(cl, 'super') and cl.super: + supers = [] + for sup in cl.super: + if type(sup) is type(''): + sname = sup + else: + sname = sup.name + if sup.module != cl.module: + sname = "%s.%s" % (sup.module, sname) + supers.append(sname) + s = s + "(%s)" % ", ".join(supers) + items.append((cl.lineno, s)) + self.classes[s] = cl + items.sort() + list = [] + for item, s in items: + list.append(s) + return list + +class ClassBrowserTreeItem(TreeItem): + + def __init__(self, name, classes, file): + self.name = name + self.classes = classes + self.file = file + try: + self.cl = self.classes[self.name] + except (IndexError, KeyError): + self.cl = None + self.isfunction = isinstance(self.cl, pyclbr.Function) + + def GetText(self): + if self.isfunction: + return "def " + self.name + "(...)" + else: + return "class " + self.name + + def GetIconName(self): + if self.isfunction: + return "python" + else: + return "folder" + + def IsExpandable(self): + if self.cl: + try: + return not not self.cl.methods + except AttributeError: + return False + + def GetSubList(self): + if not self.cl: + return [] + sublist = [] + for name in self.listmethods(): + item = MethodBrowserTreeItem(name, self.cl, self.file) + sublist.append(item) + return sublist + + def OnDoubleClick(self): + if not os.path.exists(self.file): + return + edit = file_open(self.file) + if hasattr(self.cl, 'lineno'): + lineno = self.cl.lineno + edit.gotoline(lineno) + + def listmethods(self): + if not self.cl: + return [] + items = [] + for name, lineno in self.cl.methods.items(): + items.append((lineno, name)) + items.sort() + list = [] + for item, name in items: + list.append(name) + return list + +class MethodBrowserTreeItem(TreeItem): + + def __init__(self, name, cl, file): + self.name = name + self.cl = cl + self.file = file + + def GetText(self): + return "def " + self.name + "(...)" + + def GetIconName(self): + return "python" # XXX + + def IsExpandable(self): + return 0 + + def OnDoubleClick(self): + if not os.path.exists(self.file): + return + edit = file_open(self.file) + edit.gotoline(self.cl.methods[self.name]) + +def _class_browser(parent): #Wrapper for htest + try: + file = __file__ + except NameError: + file = sys.argv[0] + if sys.argv[1:]: + file = sys.argv[1] + else: + file = sys.argv[0] + dir, file = os.path.split(file) + name = os.path.splitext(file)[0] + flist = PyShell.PyShellFileList(parent) + global file_open + file_open = flist.open + ClassBrowser(flist, name, [dir], _htest=True) + +if __name__ == "__main__": + from idlelib.idle_test.htest import run + run(_class_browser) diff --git a/lib-python/3/idlelib/ColorDelegator.py b/lib-python/3/idlelib/ColorDelegator.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/ColorDelegator.py @@ -0,0 +1,281 @@ +import time +import re +import keyword +import builtins +from tkinter import TkVersion +from idlelib.Delegator import Delegator +from idlelib.configHandler import idleConf + +DEBUG = False + +def any(name, alternates): + "Return a named group pattern matching list of alternates." + return "(?P<%s>" % name + "|".join(alternates) + ")" + +def make_pat(): + kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b" + builtinlist = [str(name) for name in dir(builtins) + if not name.startswith('_') and \ + name not in keyword.kwlist] + # self.file = open("file") : + # 1st 'file' colorized normal, 2nd as builtin, 3rd as string + builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b" + comment = any("COMMENT", [r"#[^\n]*"]) + stringprefix = r"(\br|u|ur|R|U|UR|Ur|uR|b|B|br|Br|bR|BR|rb|rB|Rb|RB)?" + sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?" + dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?' + sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" + dq3string = stringprefix + r'"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' + string = any("STRING", [sq3string, dq3string, sqstring, dqstring]) + return kw + "|" + builtin + "|" + comment + "|" + string +\ + "|" + any("SYNC", [r"\n"]) + +prog = re.compile(make_pat(), re.S) +idprog = re.compile(r"\s+(\w+)", re.S) + +def color_config(text): # Called from htest, Editor, and Turtle Demo. + '''Set color opitons of Text widget. + + Should be called whenever ColorDelegator is called. + ''' + # Not automatic because ColorDelegator does not know 'text'. + theme = idleConf.CurrentTheme() + normal_colors = idleConf.GetHighlight(theme, 'normal') + cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg') + select_colors = idleConf.GetHighlight(theme, 'hilite') + text.config( + foreground=normal_colors['foreground'], + background=normal_colors['background'], + insertbackground=cursor_color, + selectforeground=select_colors['foreground'], + selectbackground=select_colors['background'], + ) + if TkVersion >= 8.5: + text.config( + inactiveselectbackground=select_colors['background']) + + +class ColorDelegator(Delegator): + + def __init__(self): + Delegator.__init__(self) + self.prog = prog + self.idprog = idprog + self.LoadTagDefs() + + def setdelegate(self, delegate): + if self.delegate is not None: + self.unbind("<>") + Delegator.setdelegate(self, delegate) + if delegate is not None: + self.config_colors() + self.bind("<>", self.toggle_colorize_event) + self.notify_range("1.0", "end") + else: + # No delegate - stop any colorizing + self.stop_colorizing = True + self.allow_colorizing = False + + def config_colors(self): + for tag, cnf in self.tagdefs.items(): + if cnf: + self.tag_configure(tag, **cnf) + self.tag_raise('sel') + + def LoadTagDefs(self): + theme = idleConf.CurrentTheme() + self.tagdefs = { + "COMMENT": idleConf.GetHighlight(theme, "comment"), + "KEYWORD": idleConf.GetHighlight(theme, "keyword"), + "BUILTIN": idleConf.GetHighlight(theme, "builtin"), + "STRING": idleConf.GetHighlight(theme, "string"), + "DEFINITION": idleConf.GetHighlight(theme, "definition"), + "SYNC": {'background':None,'foreground':None}, + "TODO": {'background':None,'foreground':None}, + "ERROR": idleConf.GetHighlight(theme, "error"), + # The following is used by ReplaceDialog: + "hit": idleConf.GetHighlight(theme, "hit"), + } + + if DEBUG: print('tagdefs',self.tagdefs) + + def insert(self, index, chars, tags=None): + index = self.index(index) + self.delegate.insert(index, chars, tags) + self.notify_range(index, index + "+%dc" % len(chars)) + + def delete(self, index1, index2=None): + index1 = self.index(index1) + self.delegate.delete(index1, index2) + self.notify_range(index1) + + after_id = None + allow_colorizing = True + colorizing = False + + def notify_range(self, index1, index2=None): + self.tag_add("TODO", index1, index2) + if self.after_id: + if DEBUG: print("colorizing already scheduled") + return + if self.colorizing: + self.stop_colorizing = True + if DEBUG: print("stop colorizing") + if self.allow_colorizing: + if DEBUG: print("schedule colorizing") + self.after_id = self.after(1, self.recolorize) + + close_when_done = None # Window to be closed when done colorizing + + def close(self, close_when_done=None): + if self.after_id: + after_id = self.after_id + self.after_id = None + if DEBUG: print("cancel scheduled recolorizer") + self.after_cancel(after_id) + self.allow_colorizing = False + self.stop_colorizing = True + if close_when_done: + if not self.colorizing: + close_when_done.destroy() + else: + self.close_when_done = close_when_done + + def toggle_colorize_event(self, event): + if self.after_id: + after_id = self.after_id + self.after_id = None + if DEBUG: print("cancel scheduled recolorizer") + self.after_cancel(after_id) + if self.allow_colorizing and self.colorizing: + if DEBUG: print("stop colorizing") + self.stop_colorizing = True + self.allow_colorizing = not self.allow_colorizing + if self.allow_colorizing and not self.colorizing: + self.after_id = self.after(1, self.recolorize) + if DEBUG: + print("auto colorizing turned",\ + self.allow_colorizing and "on" or "off") + return "break" + + def recolorize(self): + self.after_id = None + if not self.delegate: + if DEBUG: print("no delegate") + return + if not self.allow_colorizing: + if DEBUG: print("auto colorizing is off") + return + if self.colorizing: + if DEBUG: print("already colorizing") + return + try: + self.stop_colorizing = False + self.colorizing = True + if DEBUG: print("colorizing...") + t0 = time.perf_counter() + self.recolorize_main() + t1 = time.perf_counter() + if DEBUG: print("%.3f seconds" % (t1-t0)) + finally: + self.colorizing = False + if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"): + if DEBUG: print("reschedule colorizing") + self.after_id = self.after(1, self.recolorize) + if self.close_when_done: + top = self.close_when_done + self.close_when_done = None + top.destroy() + + def recolorize_main(self): + next = "1.0" + while True: + item = self.tag_nextrange("TODO", next) + if not item: + break + head, tail = item + self.tag_remove("SYNC", head, tail) + item = self.tag_prevrange("SYNC", head) + if item: + head = item[1] + else: + head = "1.0" + + chars = "" + next = head + lines_to_get = 1 + ok = False + while not ok: + mark = next + next = self.index(mark + "+%d lines linestart" % + lines_to_get) + lines_to_get = min(lines_to_get * 2, 100) + ok = "SYNC" in self.tag_names(next + "-1c") + line = self.get(mark, next) + ##print head, "get", mark, next, "->", repr(line) + if not line: + return + for tag in self.tagdefs: + self.tag_remove(tag, mark, next) + chars = chars + line + m = self.prog.search(chars) + while m: + for key, value in m.groupdict().items(): + if value: + a, b = m.span(key) + self.tag_add(key, + head + "+%dc" % a, + head + "+%dc" % b) + if value in ("def", "class"): + m1 = self.idprog.match(chars, b) + if m1: + a, b = m1.span(1) + self.tag_add("DEFINITION", + head + "+%dc" % a, + head + "+%dc" % b) + m = self.prog.search(chars, m.end()) + if "SYNC" in self.tag_names(next + "-1c"): + head = next + chars = "" + else: + ok = False + if not ok: + # We're in an inconsistent state, and the call to + # update may tell us to stop. It may also change + # the correct value for "next" (since this is a + # line.col string, not a true mark). So leave a + # crumb telling the next invocation to resume here + # in case update tells us to leave. + self.tag_add("TODO", next) + self.update() + if self.stop_colorizing: + if DEBUG: print("colorizing stopped") + return + + def removecolors(self): + for tag in self.tagdefs: + self.tag_remove(tag, "1.0", "end") + + +def _color_delegator(parent): # htest # + from tkinter import Toplevel, Text + from idlelib.Percolator import Percolator + + top = Toplevel(parent) + top.title("Test ColorDelegator") + top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200, + parent.winfo_rooty() + 150)) + source = "if somename: x = 'abc' # comment\nprint\n" + text = Text(top, background="white") + text.pack(expand=1, fill="both") + text.insert("insert", source) + text.focus_set() + + color_config(text) + p = Percolator(text) + d = ColorDelegator() + p.insertfilter(d) + +if __name__ == "__main__": + from idlelib.idle_test.htest import run + run(_color_delegator) diff --git a/lib-python/3/idlelib/EditorWindow.py b/lib-python/3/idlelib/EditorWindow.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/EditorWindow.py @@ -0,0 +1,1690 @@ +import importlib +import importlib.abc +import importlib.util +import os +import platform +import re +import string +import sys +from tkinter import * +import tkinter.simpledialog as tkSimpleDialog +import tkinter.messagebox as tkMessageBox +import traceback +import webbrowser + +from idlelib.MultiCall import MultiCallCreator +from idlelib import WindowList +from idlelib import SearchDialog +from idlelib import GrepDialog +from idlelib import ReplaceDialog +from idlelib import PyParse +from idlelib.configHandler import idleConf +from idlelib import aboutDialog, textView, configDialog +from idlelib import macosxSupport +from idlelib import help + +# The default tab setting for a Text widget, in average-width characters. +TK_TABWIDTH_DEFAULT = 8 + +_py_version = ' (%s)' % platform.python_version() + +def _sphinx_version(): + "Format sys.version_info to produce the Sphinx version string used to install the chm docs" + major, minor, micro, level, serial = sys.version_info + release = '%s%s' % (major, minor) + release += '%s' % (micro,) + if level == 'candidate': + release += 'rc%s' % (serial,) + elif level != 'final': + release += '%s%s' % (level[0], serial) + return release + + +class HelpDialog(object): + + def __init__(self): + self.parent = None # parent of help window + self.dlg = None # the help window iteself + + def display(self, parent, near=None): + """ Display the help dialog. + + parent - parent widget for the help window + + near - a Toplevel widget (e.g. EditorWindow or PyShell) + to use as a reference for placing the help window + """ + import warnings as w + w.warn("EditorWindow.HelpDialog is no longer used by Idle.\n" + "It will be removed in 3.6 or later.\n" + "It has been replaced by private help.HelpWindow\n", + DeprecationWarning, stacklevel=2) + if self.dlg is None: + self.show_dialog(parent) + if near: + self.nearwindow(near) + + def show_dialog(self, parent): + self.parent = parent + fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt') + self.dlg = dlg = textView.view_file(parent,'Help',fn, modal=False) + dlg.bind('', self.destroy, '+') + + def nearwindow(self, near): + # Place the help dialog near the window specified by parent. + # Note - this may not reposition the window in Metacity + # if "/apps/metacity/general/disable_workarounds" is enabled + dlg = self.dlg + geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10) + dlg.withdraw() + dlg.geometry("=+%d+%d" % geom) + dlg.deiconify() + dlg.lift() + + def destroy(self, ev=None): + self.dlg = None + self.parent = None + +helpDialog = HelpDialog() # singleton instance, no longer used + + +class EditorWindow(object): + from idlelib.Percolator import Percolator + from idlelib.ColorDelegator import ColorDelegator, color_config + from idlelib.UndoDelegator import UndoDelegator + from idlelib.IOBinding import IOBinding, filesystemencoding, encoding + from idlelib import Bindings + from tkinter import Toplevel + from idlelib.MultiStatusBar import MultiStatusBar + + help_url = None + + def __init__(self, flist=None, filename=None, key=None, root=None): + if EditorWindow.help_url is None: + dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html') + if sys.platform.count('linux'): + # look for html docs in a couple of standard places + pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3] + if os.path.isdir('/var/www/html/python/'): # "python2" rpm + dochome = '/var/www/html/python/index.html' + else: + basepath = '/usr/share/doc/' # standard location + dochome = os.path.join(basepath, pyver, + 'Doc', 'index.html') + elif sys.platform[:3] == 'win': + chmfile = os.path.join(sys.base_prefix, 'Doc', + 'Python%s.chm' % _sphinx_version()) + if os.path.isfile(chmfile): + dochome = chmfile + elif sys.platform == 'darwin': + # documentation may be stored inside a python framework + dochome = os.path.join(sys.base_prefix, + 'Resources/English.lproj/Documentation/index.html') + dochome = os.path.normpath(dochome) + if os.path.isfile(dochome): + EditorWindow.help_url = dochome + if sys.platform == 'darwin': + # Safari requires real file:-URLs + EditorWindow.help_url = 'file://' + EditorWindow.help_url + else: + EditorWindow.help_url = "https://docs.python.org/%d.%d/" % sys.version_info[:2] + self.flist = flist + root = root or flist.root + self.root = root + try: + sys.ps1 + except AttributeError: + sys.ps1 = '>>> ' + self.menubar = Menu(root) + self.top = top = WindowList.ListedToplevel(root, menu=self.menubar) + if flist: + self.tkinter_vars = flist.vars + #self.top.instance_dict makes flist.inversedict available to + #configDialog.py so it can access all EditorWindow instances + self.top.instance_dict = flist.inversedict + else: + self.tkinter_vars = {} # keys: Tkinter event names + # values: Tkinter variable instances + self.top.instance_dict = {} + self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(), + 'recent-files.lst') + self.text_frame = text_frame = Frame(top) + self.vbar = vbar = Scrollbar(text_frame, name='vbar') + self.width = idleConf.GetOption('main', 'EditorWindow', + 'width', type='int') + text_options = { + 'name': 'text', + 'padx': 5, + 'wrap': 'none', + 'highlightthickness': 0, + 'width': self.width, + 'height': idleConf.GetOption('main', 'EditorWindow', + 'height', type='int')} + if TkVersion >= 8.5: + # Starting with tk 8.5 we have to set the new tabstyle option + # to 'wordprocessor' to achieve the same display of tabs as in + # older tk versions. + text_options['tabstyle'] = 'wordprocessor' + self.text = text = MultiCallCreator(Text)(text_frame, **text_options) + self.top.focused_widget = self.text + + self.createmenubar() + self.apply_bindings() + + self.top.protocol("WM_DELETE_WINDOW", self.close) + self.top.bind("<>", self.close_event) + if macosxSupport.isAquaTk(): + # Command-W on editorwindows doesn't work without this. + text.bind('<>', self.close_event) + # Some OS X systems have only one mouse button, so use + # control-click for popup context menus there. For two + # buttons, AquaTk defines <2> as the right button, not <3>. + text.bind("",self.right_menu_event) + text.bind("<2>", self.right_menu_event) + else: + # Elsewhere, use right-click for popup menus. + text.bind("<3>",self.right_menu_event) + text.bind("<>", self.cut) + text.bind("<>", self.copy) + text.bind("<>", self.paste) + text.bind("<>", self.center_insert_event) + text.bind("<>", self.help_dialog) + text.bind("<>", self.python_docs) + text.bind("<>", self.about_dialog) + text.bind("<>", self.config_dialog) + text.bind("<>", self.open_module) + text.bind("<>", lambda event: "break") + text.bind("<>", self.select_all) + text.bind("<>", self.remove_selection) + text.bind("<>", self.find_event) + text.bind("<>", self.find_again_event) + text.bind("<>", self.find_in_files_event) + text.bind("<>", self.find_selection_event) + text.bind("<>", self.replace_event) + text.bind("<>", self.goto_line_event) + text.bind("<>",self.smart_backspace_event) + text.bind("<>",self.newline_and_indent_event) + text.bind("<>",self.smart_indent_event) + text.bind("<>",self.indent_region_event) + text.bind("<>",self.dedent_region_event) + text.bind("<>",self.comment_region_event) + text.bind("<>",self.uncomment_region_event) + text.bind("<>",self.tabify_region_event) + text.bind("<>",self.untabify_region_event) + text.bind("<>",self.toggle_tabs_event) + text.bind("<>",self.change_indentwidth_event) + text.bind("", self.move_at_edge_if_selection(0)) + text.bind("", self.move_at_edge_if_selection(1)) + text.bind("<>", self.del_word_left) + text.bind("<>", self.del_word_right) + text.bind("<>", self.home_callback) + + if flist: + flist.inversedict[self] = key + if key: + flist.dict[key] = self + text.bind("<>", self.new_callback) + text.bind("<>", self.flist.close_all_callback) + text.bind("<>", self.open_class_browser) + text.bind("<>", self.open_path_browser) + text.bind("<>", self.open_turtle_demo) + + self.set_status_bar() + vbar['command'] = text.yview + vbar.pack(side=RIGHT, fill=Y) + text['yscrollcommand'] = vbar.set + text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow') + text_frame.pack(side=LEFT, fill=BOTH, expand=1) + text.pack(side=TOP, fill=BOTH, expand=1) + text.focus_set() + + # usetabs true -> literal tab characters are used by indent and + # dedent cmds, possibly mixed with spaces if + # indentwidth is not a multiple of tabwidth, + # which will cause Tabnanny to nag! + # false -> tab characters are converted to spaces by indent + # and dedent cmds, and ditto TAB keystrokes + # Although use-spaces=0 can be configured manually in config-main.def, + # configuration of tabs v. spaces is not supported in the configuration + # dialog. IDLE promotes the preferred Python indentation: use spaces! + usespaces = idleConf.GetOption('main', 'Indent', + 'use-spaces', type='bool') + self.usetabs = not usespaces + + # tabwidth is the display width of a literal tab character. + # CAUTION: telling Tk to use anything other than its default + # tab setting causes it to use an entirely different tabbing algorithm, + # treating tab stops as fixed distances from the left margin. + # Nobody expects this, so for now tabwidth should never be changed. + self.tabwidth = 8 # must remain 8 until Tk is fixed. + + # indentwidth is the number of screen characters per indent level. + # The recommended Python indentation is four spaces. + self.indentwidth = self.tabwidth + self.set_notabs_indentwidth() + + # If context_use_ps1 is true, parsing searches back for a ps1 line; + # else searches for a popular (if, def, ...) Python stmt. + self.context_use_ps1 = False + + # When searching backwards for a reliable place to begin parsing, + # first start num_context_lines[0] lines back, then + # num_context_lines[1] lines back if that didn't work, and so on. + # The last value should be huge (larger than the # of lines in a + # conceivable file). + # Making the initial values larger slows things down more often. + self.num_context_lines = 50, 500, 5000000 + self.per = per = self.Percolator(text) + self.undo = undo = self.UndoDelegator() + per.insertfilter(undo) + text.undo_block_start = undo.undo_block_start + text.undo_block_stop = undo.undo_block_stop + undo.set_saved_change_hook(self.saved_change_hook) + # IOBinding implements file I/O and printing functionality + self.io = io = self.IOBinding(self) + io.set_filename_change_hook(self.filename_change_hook) + self.good_load = False + self.set_indentation_params(False) + self.color = None # initialized below in self.ResetColorizer + if filename: + if os.path.exists(filename) and not os.path.isdir(filename): + if io.loadfile(filename): + self.good_load = True + is_py_src = self.ispythonsource(filename) + self.set_indentation_params(is_py_src) + else: + io.set_filename(filename) + self.good_load = True + + self.ResetColorizer() + self.saved_change_hook() + self.update_recent_files_list() + self.load_extensions() + menu = self.menudict.get('windows') + if menu: + end = menu.index("end") + if end is None: + end = -1 + if end >= 0: + menu.add_separator() + end = end + 1 + self.wmenu_end = end + WindowList.register_callback(self.postwindowsmenu) + + # Some abstractions so IDLE extensions are cross-IDE + self.askyesno = tkMessageBox.askyesno + self.askinteger = tkSimpleDialog.askinteger + self.showerror = tkMessageBox.showerror + + def _filename_to_unicode(self, filename): + """Return filename as BMP unicode so diplayable in Tk.""" + # Decode bytes to unicode. + if isinstance(filename, bytes): + try: + filename = filename.decode(self.filesystemencoding) + except UnicodeDecodeError: + try: + filename = filename.decode(self.encoding) + except UnicodeDecodeError: + # byte-to-byte conversion + filename = filename.decode('iso8859-1') + # Replace non-BMP char with diamond questionmark. + return re.sub('[\U00010000-\U0010FFFF]', '\ufffd', filename) + + def new_callback(self, event): + dirname, basename = self.io.defaultfilename() + self.flist.new(dirname) + return "break" + + def home_callback(self, event): + if (event.state & 4) != 0 and event.keysym == "Home": + # state&4==Control. If , use the Tk binding. + return + if self.text.index("iomark") and \ + self.text.compare("iomark", "<=", "insert lineend") and \ + self.text.compare("insert linestart", "<=", "iomark"): + # In Shell on input line, go to just after prompt + insertpt = int(self.text.index("iomark").split(".")[1]) + else: + line = self.text.get("insert linestart", "insert lineend") + for insertpt in range(len(line)): + if line[insertpt] not in (' ','\t'): + break + else: + insertpt=len(line) + lineat = int(self.text.index("insert").split('.')[1]) + if insertpt == lineat: + insertpt = 0 + dest = "insert linestart+"+str(insertpt)+"c" + if (event.state&1) == 0: + # shift was not pressed + self.text.tag_remove("sel", "1.0", "end") + else: + if not self.text.index("sel.first"): + # there was no previous selection + self.text.mark_set("my_anchor", "insert") + else: + if self.text.compare(self.text.index("sel.first"), "<", + self.text.index("insert")): + self.text.mark_set("my_anchor", "sel.first") # extend back + else: + self.text.mark_set("my_anchor", "sel.last") # extend forward + first = self.text.index(dest) + last = self.text.index("my_anchor") + if self.text.compare(first,">",last): + first,last = last,first + self.text.tag_remove("sel", "1.0", "end") + self.text.tag_add("sel", first, last) + self.text.mark_set("insert", dest) + self.text.see("insert") + return "break" + + def set_status_bar(self): + self.status_bar = self.MultiStatusBar(self.top) + sep = Frame(self.top, height=1, borderwidth=1, background='grey75') + if sys.platform == "darwin": + # Insert some padding to avoid obscuring some of the statusbar + # by the resize widget. + self.status_bar.set_label('_padding1', ' ', side=RIGHT) + self.status_bar.set_label('column', 'Col: ?', side=RIGHT) + self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) + self.status_bar.pack(side=BOTTOM, fill=X) + sep.pack(side=BOTTOM, fill=X) + self.text.bind("<>", self.set_line_and_column) + self.text.event_add("<>", + "", "") + self.text.after_idle(self.set_line_and_column) + + def set_line_and_column(self, event=None): + line, column = self.text.index(INSERT).split('.') + self.status_bar.set_label('column', 'Col: %s' % column) + self.status_bar.set_label('line', 'Ln: %s' % line) + + menu_specs = [ + ("file", "_File"), + ("edit", "_Edit"), + ("format", "F_ormat"), + ("run", "_Run"), + ("options", "_Options"), + ("windows", "_Window"), + ("help", "_Help"), + ] + + + def createmenubar(self): + mbar = self.menubar + self.menudict = menudict = {} + for name, label in self.menu_specs: + underline, label = prepstr(label) + menudict[name] = menu = Menu(mbar, name=name, tearoff=0) + mbar.add_cascade(label=label, menu=menu, underline=underline) + if macosxSupport.isCarbonTk(): + # Insert the application menu + menudict['application'] = menu = Menu(mbar, name='apple', + tearoff=0) + mbar.add_cascade(label='IDLE', menu=menu) + self.fill_menus() + self.recent_files_menu = Menu(self.menubar, tearoff=0) + self.menudict['file'].insert_cascade(3, label='Recent Files', + underline=0, + menu=self.recent_files_menu) + self.base_helpmenu_length = self.menudict['help'].index(END) + self.reset_help_menu_entries() + + def postwindowsmenu(self): + # Only called when Windows menu exists + menu = self.menudict['windows'] + end = menu.index("end") + if end is None: + end = -1 + if end > self.wmenu_end: + menu.delete(self.wmenu_end+1, end) + WindowList.add_windows_to_menu(menu) + + rmenu = None + + def right_menu_event(self, event): + self.text.mark_set("insert", "@%d,%d" % (event.x, event.y)) + if not self.rmenu: + self.make_rmenu() + rmenu = self.rmenu + self.event = event + iswin = sys.platform[:3] == 'win' + if iswin: + self.text.config(cursor="arrow") + + for item in self.rmenu_specs: + try: + label, eventname, verify_state = item + except ValueError: # see issue1207589 + continue + + if verify_state is None: + continue + state = getattr(self, verify_state)() + rmenu.entryconfigure(label, state=state) + + + rmenu.tk_popup(event.x_root, event.y_root) + if iswin: + self.text.config(cursor="ibeam") + + rmenu_specs = [ + # ("Label", "<>", "statefuncname"), ... + ("Close", "<>", None), # Example + ] + + def make_rmenu(self): + rmenu = Menu(self.text, tearoff=0) + for item in self.rmenu_specs: + label, eventname = item[0], item[1] + if label is not None: + def command(text=self.text, eventname=eventname): + text.event_generate(eventname) + rmenu.add_command(label=label, command=command) + else: + rmenu.add_separator() + self.rmenu = rmenu + + def rmenu_check_cut(self): + return self.rmenu_check_copy() + + def rmenu_check_copy(self): + try: + indx = self.text.index('sel.first') + except TclError: + return 'disabled' + else: + return 'normal' if indx else 'disabled' + + def rmenu_check_paste(self): + try: + self.text.tk.call('tk::GetSelection', self.text, 'CLIPBOARD') + except TclError: + return 'disabled' + else: + return 'normal' + + def about_dialog(self, event=None): + "Handle Help 'About IDLE' event." + # Synchronize with macosxSupport.overrideRootMenu.about_dialog. + aboutDialog.AboutDialog(self.top,'About IDLE') + + def config_dialog(self, event=None): + "Handle Options 'Configure IDLE' event." + # Synchronize with macosxSupport.overrideRootMenu.config_dialog. + configDialog.ConfigDialog(self.top,'Settings') + + def help_dialog(self, event=None): + "Handle Help 'IDLE Help' event." + # Synchronize with macosxSupport.overrideRootMenu.help_dialog. + if self.root: + parent = self.root + else: + parent = self.top + help.show_idlehelp(parent) + + def python_docs(self, event=None): + if sys.platform[:3] == 'win': + try: + os.startfile(self.help_url) + except OSError as why: + tkMessageBox.showerror(title='Document Start Failure', + message=str(why), parent=self.text) + else: + webbrowser.open(self.help_url) + return "break" + + def cut(self,event): + self.text.event_generate("<>") + return "break" + + def copy(self,event): + if not self.text.tag_ranges("sel"): + # There is no selection, so do nothing and maybe interrupt. + return + self.text.event_generate("<>") + return "break" + + def paste(self,event): + self.text.event_generate("<>") + self.text.see("insert") + return "break" + + def select_all(self, event=None): + self.text.tag_add("sel", "1.0", "end-1c") + self.text.mark_set("insert", "1.0") + self.text.see("insert") + return "break" + + def remove_selection(self, event=None): + self.text.tag_remove("sel", "1.0", "end") + self.text.see("insert") + + def move_at_edge_if_selection(self, edge_index): + """Cursor move begins at start or end of selection + + When a left/right cursor key is pressed create and return to Tkinter a + function which causes a cursor move from the associated edge of the + selection. + + """ + self_text_index = self.text.index + self_text_mark_set = self.text.mark_set + edges_table = ("sel.first+1c", "sel.last-1c") + def move_at_edge(event): + if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed + try: + self_text_index("sel.first") + self_text_mark_set("insert", edges_table[edge_index]) + except TclError: + pass + return move_at_edge + + def del_word_left(self, event): + self.text.event_generate('') + return "break" + + def del_word_right(self, event): + self.text.event_generate('') + return "break" + + def find_event(self, event): + SearchDialog.find(self.text) + return "break" + + def find_again_event(self, event): + SearchDialog.find_again(self.text) + return "break" + + def find_selection_event(self, event): + SearchDialog.find_selection(self.text) + return "break" + + def find_in_files_event(self, event): + GrepDialog.grep(self.text, self.io, self.flist) + return "break" + + def replace_event(self, event): + ReplaceDialog.replace(self.text) + return "break" + + def goto_line_event(self, event): + text = self.text + lineno = tkSimpleDialog.askinteger("Goto", + "Go to line number:",parent=text) + if lineno is None: + return "break" + if lineno <= 0: + text.bell() + return "break" + text.mark_set("insert", "%d.0" % lineno) + text.see("insert") + + def open_module(self, event=None): + # XXX Shouldn't this be in IOBinding? + try: + name = self.text.get("sel.first", "sel.last") + except TclError: + name = "" + else: + name = name.strip() + name = tkSimpleDialog.askstring("Module", + "Enter the name of a Python module\n" + "to search on sys.path and open:", + parent=self.text, initialvalue=name) + if name: + name = name.strip() + if not name: + return + # XXX Ought to insert current file's directory in front of path + try: + spec = importlib.util.find_spec(name) + except (ValueError, ImportError) as msg: + tkMessageBox.showerror("Import error", str(msg), parent=self.text) + return + if spec is None: + tkMessageBox.showerror("Import error", "module not found", + parent=self.text) + return + if not isinstance(spec.loader, importlib.abc.SourceLoader): + tkMessageBox.showerror("Import error", "not a source-based module", + parent=self.text) + return + try: + file_path = spec.loader.get_filename(name) + except AttributeError: + tkMessageBox.showerror("Import error", + "loader does not support get_filename", + parent=self.text) + return + if self.flist: + self.flist.open(file_path) + else: + self.io.loadfile(file_path) + return file_path + + def open_class_browser(self, event=None): + filename = self.io.filename + if not (self.__class__.__name__ == 'PyShellEditorWindow' + and filename): + filename = self.open_module() + if filename is None: + return + head, tail = os.path.split(filename) + base, ext = os.path.splitext(tail) + from idlelib import ClassBrowser + ClassBrowser.ClassBrowser(self.flist, base, [head]) + + def open_path_browser(self, event=None): + from idlelib import PathBrowser + PathBrowser.PathBrowser(self.flist) + + def open_turtle_demo(self, event = None): + import subprocess + + cmd = [sys.executable, + '-c', + 'from turtledemo.__main__ import main; main()'] + subprocess.Popen(cmd, shell=False) + + def gotoline(self, lineno): + if lineno is not None and lineno > 0: + self.text.mark_set("insert", "%d.0" % lineno) + self.text.tag_remove("sel", "1.0", "end") + self.text.tag_add("sel", "insert", "insert +1l") + self.center() + + def ispythonsource(self, filename): + if not filename or os.path.isdir(filename): + return True + base, ext = os.path.splitext(os.path.basename(filename)) + if os.path.normcase(ext) in (".py", ".pyw"): + return True + line = self.text.get('1.0', '1.0 lineend') + return line.startswith('#!') and 'python' in line + + def close_hook(self): + if self.flist: + self.flist.unregister_maybe_terminate(self) + self.flist = None + + def set_close_hook(self, close_hook): + self.close_hook = close_hook + + def filename_change_hook(self): + if self.flist: + self.flist.filename_changed_edit(self) + self.saved_change_hook() + self.top.update_windowlist_registry(self) + self.ResetColorizer() + + def _addcolorizer(self): + if self.color: + return + if self.ispythonsource(self.io.filename): + self.color = self.ColorDelegator() + # can add more colorizers here... + if self.color: + self.per.removefilter(self.undo) + self.per.insertfilter(self.color) + self.per.insertfilter(self.undo) + + def _rmcolorizer(self): + if not self.color: + return + self.color.removecolors() + self.per.removefilter(self.color) From pypy.commits at gmail.com Tue Feb 5 08:48:35 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 05 Feb 2019 05:48:35 -0800 (PST) Subject: [pypy-commit] pypy release-pypy3.5-7.x: merge py3.5 into release Message-ID: <5c599433.1c69fb81.ad4a3.0063@mx.google.com> Author: Matti Picus Branch: release-pypy3.5-7.x Changeset: r95831:1f86f25937b6 Date: 2019-02-05 14:45 +0100 http://bitbucket.org/pypy/pypy/changeset/1f86f25937b6/ Log: merge py3.5 into release diff --git a/lib_pypy/cffi/pkgconfig.py b/lib_pypy/cffi/pkgconfig.py new file mode 100644 --- /dev/null +++ b/lib_pypy/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except EnvironmentError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret diff --git a/pypy/module/_sre/interp_sre.py b/pypy/module/_sre/interp_sre.py --- a/pypy/module/_sre/interp_sre.py +++ b/pypy/module/_sre/interp_sre.py @@ -622,7 +622,7 @@ assert idx >= 0 return fmarks[idx], fmarks[idx+1] else: - raise oefmt(space.w_IndexError, "group index out of range") + raise oefmt(space.w_IndexError, "no such group") def _last_index(self): mark = self.ctx.match_marks diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -44,10 +44,10 @@ return OperationError(w_error, space.newtext(msg)) - at unwrap_spec(string='bufferstr', level=int) -def compress(space, string, level=rzlib.Z_DEFAULT_COMPRESSION): + at unwrap_spec(data='bufferstr', level=int) +def compress(space, data, level=rzlib.Z_DEFAULT_COMPRESSION): """ - compress(string[, level]) -- Returned compressed string. + compress(data[, level]) -- Returned compressed string. Optional arg level is the compression level, in 1-9. """ @@ -57,7 +57,7 @@ except ValueError: raise zlib_error(space, "Bad compression level") try: - result = rzlib.compress(stream, string, rzlib.Z_FINISH) + result = rzlib.compress(stream, data, rzlib.Z_FINISH) finally: rzlib.deflateEnd(stream) except rzlib.RZlibError as e: From pypy.commits at gmail.com Tue Feb 5 08:48:37 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 05 Feb 2019 05:48:37 -0800 (PST) Subject: [pypy-commit] pypy release-pypy3.6-7.x: merge 3.6 into release Message-ID: <5c599435.1c69fb81.d08e8.3d08@mx.google.com> Author: Matti Picus Branch: release-pypy3.6-7.x Changeset: r95832:dab365a46514 Date: 2019-02-05 14:46 +0100 http://bitbucket.org/pypy/pypy/changeset/dab365a46514/ Log: merge 3.6 into release diff too long, truncating to 2000 out of 22472 lines diff --git a/lib-python/3/collections/__main__.py b/lib-python/3/collections/__main__.py new file mode 100644 --- /dev/null +++ b/lib-python/3/collections/__main__.py @@ -0,0 +1,38 @@ +################################################################################ +### Simple tests +################################################################################ + +# verify that instances can be pickled +from collections import namedtuple +from pickle import loads, dumps +Point = namedtuple('Point', 'x, y', True) +p = Point(x=10, y=20) +assert p == loads(dumps(p)) + +# test and demonstrate ability to override methods +class Point(namedtuple('Point', 'x y')): + __slots__ = () + @property + def hypot(self): + return (self.x ** 2 + self.y ** 2) ** 0.5 + def __str__(self): + return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) + +for p in Point(3, 4), Point(14, 5/7.): + print (p) + +class Point(namedtuple('Point', 'x y')): + 'Point class with optimized _make() and _replace() without error-checking' + __slots__ = () + _make = classmethod(tuple.__new__) + def _replace(self, _map=map, **kwds): + return self._make(_map(kwds.get, ('x', 'y'), self)) + +print(Point(11, 22)._replace(x=100)) + +Point3D = namedtuple('Point3D', Point._fields + ('z',)) +print(Point3D.__doc__) + +import doctest, collections +TestResults = namedtuple('TestResults', 'failed attempted') +print(TestResults(*doctest.testmod(collections))) diff --git a/lib-python/3/idlelib/AutoCompleteWindow.py b/lib-python/3/idlelib/AutoCompleteWindow.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/AutoCompleteWindow.py @@ -0,0 +1,416 @@ +""" +An auto-completion window for IDLE, used by the AutoComplete extension +""" +from tkinter import * +from idlelib.MultiCall import MC_SHIFT +from idlelib.AutoComplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES + +HIDE_VIRTUAL_EVENT_NAME = "<>" +HIDE_SEQUENCES = ("", "") +KEYPRESS_VIRTUAL_EVENT_NAME = "<>" +# We need to bind event beyond so that the function will be called +# before the default specific IDLE function +KEYPRESS_SEQUENCES = ("", "", "", "", + "", "", "", "", + "", "") +KEYRELEASE_VIRTUAL_EVENT_NAME = "<>" +KEYRELEASE_SEQUENCE = "" +LISTUPDATE_SEQUENCE = "" +WINCONFIG_SEQUENCE = "" +DOUBLECLICK_SEQUENCE = "" + +class AutoCompleteWindow: + + def __init__(self, widget): + # The widget (Text) on which we place the AutoCompleteWindow + self.widget = widget + # The widgets we create + self.autocompletewindow = self.listbox = self.scrollbar = None + # The default foreground and background of a selection. Saved because + # they are changed to the regular colors of list items when the + # completion start is not a prefix of the selected completion + self.origselforeground = self.origselbackground = None + # The list of completions + self.completions = None + # A list with more completions, or None + self.morecompletions = None + # The completion mode. Either AutoComplete.COMPLETE_ATTRIBUTES or + # AutoComplete.COMPLETE_FILES + self.mode = None + # The current completion start, on the text box (a string) + self.start = None + # The index of the start of the completion + self.startindex = None + # The last typed start, used so that when the selection changes, + # the new start will be as close as possible to the last typed one. + self.lasttypedstart = None + # Do we have an indication that the user wants the completion window + # (for example, he clicked the list) + self.userwantswindow = None + # event ids + self.hideid = self.keypressid = self.listupdateid = self.winconfigid \ + = self.keyreleaseid = self.doubleclickid = None + # Flag set if last keypress was a tab + self.lastkey_was_tab = False + + def _change_start(self, newstart): + min_len = min(len(self.start), len(newstart)) + i = 0 + while i < min_len and self.start[i] == newstart[i]: + i += 1 + if i < len(self.start): + self.widget.delete("%s+%dc" % (self.startindex, i), + "%s+%dc" % (self.startindex, len(self.start))) + if i < len(newstart): + self.widget.insert("%s+%dc" % (self.startindex, i), + newstart[i:]) + self.start = newstart + + def _binary_search(self, s): + """Find the first index in self.completions where completions[i] is + greater or equal to s, or the last index if there is no such + one.""" + i = 0; j = len(self.completions) + while j > i: + m = (i + j) // 2 + if self.completions[m] >= s: + j = m + else: + i = m + 1 + return min(i, len(self.completions)-1) + + def _complete_string(self, s): + """Assuming that s is the prefix of a string in self.completions, + return the longest string which is a prefix of all the strings which + s is a prefix of them. If s is not a prefix of a string, return s.""" + first = self._binary_search(s) + if self.completions[first][:len(s)] != s: + # There is not even one completion which s is a prefix of. + return s + # Find the end of the range of completions where s is a prefix of. + i = first + 1 + j = len(self.completions) + while j > i: + m = (i + j) // 2 + if self.completions[m][:len(s)] != s: + j = m + else: + i = m + 1 + last = i-1 + + if first == last: # only one possible completion + return self.completions[first] + + # We should return the maximum prefix of first and last + first_comp = self.completions[first] + last_comp = self.completions[last] + min_len = min(len(first_comp), len(last_comp)) + i = len(s) + while i < min_len and first_comp[i] == last_comp[i]: + i += 1 + return first_comp[:i] + + def _selection_changed(self): + """Should be called when the selection of the Listbox has changed. + Updates the Listbox display and calls _change_start.""" + cursel = int(self.listbox.curselection()[0]) + + self.listbox.see(cursel) + + lts = self.lasttypedstart + selstart = self.completions[cursel] + if self._binary_search(lts) == cursel: + newstart = lts + else: + min_len = min(len(lts), len(selstart)) + i = 0 + while i < min_len and lts[i] == selstart[i]: + i += 1 + newstart = selstart[:i] + self._change_start(newstart) + + if self.completions[cursel][:len(self.start)] == self.start: + # start is a prefix of the selected completion + self.listbox.configure(selectbackground=self.origselbackground, + selectforeground=self.origselforeground) + else: + self.listbox.configure(selectbackground=self.listbox.cget("bg"), + selectforeground=self.listbox.cget("fg")) + # If there are more completions, show them, and call me again. + if self.morecompletions: + self.completions = self.morecompletions + self.morecompletions = None + self.listbox.delete(0, END) + for item in self.completions: + self.listbox.insert(END, item) + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + + def show_window(self, comp_lists, index, complete, mode, userWantsWin): + """Show the autocomplete list, bind events. + If complete is True, complete the text, and if there is exactly one + matching completion, don't open a list.""" + # Handle the start we already have + self.completions, self.morecompletions = comp_lists + self.mode = mode + self.startindex = self.widget.index(index) + self.start = self.widget.get(self.startindex, "insert") + if complete: + completed = self._complete_string(self.start) + start = self.start + self._change_start(completed) + i = self._binary_search(completed) + if self.completions[i] == completed and \ + (i == len(self.completions)-1 or + self.completions[i+1][:len(completed)] != completed): + # There is exactly one matching completion + return completed == start + self.userwantswindow = userWantsWin + self.lasttypedstart = self.start + + # Put widgets in place + self.autocompletewindow = acw = Toplevel(self.widget) + # Put it in a position so that it is not seen. + acw.wm_geometry("+10000+10000") + # Make it float + acw.wm_overrideredirect(1) + try: + # This command is only needed and available on Tk >= 8.4.0 for OSX + # Without it, call tips intrude on the typing process by grabbing + # the focus. + acw.tk.call("::tk::unsupported::MacWindowStyle", "style", acw._w, + "help", "noActivates") + except TclError: + pass + self.scrollbar = scrollbar = Scrollbar(acw, orient=VERTICAL) + self.listbox = listbox = Listbox(acw, yscrollcommand=scrollbar.set, + exportselection=False, bg="white") + for item in self.completions: + listbox.insert(END, item) + self.origselforeground = listbox.cget("selectforeground") + self.origselbackground = listbox.cget("selectbackground") + scrollbar.config(command=listbox.yview) + scrollbar.pack(side=RIGHT, fill=Y) + listbox.pack(side=LEFT, fill=BOTH, expand=True) + acw.lift() # work around bug in Tk 8.5.18+ (issue #24570) + + # Initialize the listbox selection + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + + # bind events + self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME, + self.hide_event) + for seq in HIDE_SEQUENCES: + self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq) + self.keypressid = self.widget.bind(KEYPRESS_VIRTUAL_EVENT_NAME, + self.keypress_event) + for seq in KEYPRESS_SEQUENCES: + self.widget.event_add(KEYPRESS_VIRTUAL_EVENT_NAME, seq) + self.keyreleaseid = self.widget.bind(KEYRELEASE_VIRTUAL_EVENT_NAME, + self.keyrelease_event) + self.widget.event_add(KEYRELEASE_VIRTUAL_EVENT_NAME,KEYRELEASE_SEQUENCE) + self.listupdateid = listbox.bind(LISTUPDATE_SEQUENCE, + self.listselect_event) + self.winconfigid = acw.bind(WINCONFIG_SEQUENCE, self.winconfig_event) + self.doubleclickid = listbox.bind(DOUBLECLICK_SEQUENCE, + self.doubleclick_event) + + def winconfig_event(self, event): + if not self.is_active(): + return + # Position the completion list window + text = self.widget + text.see(self.startindex) + x, y, cx, cy = text.bbox(self.startindex) + acw = self.autocompletewindow + acw_width, acw_height = acw.winfo_width(), acw.winfo_height() + text_width, text_height = text.winfo_width(), text.winfo_height() + new_x = text.winfo_rootx() + min(x, max(0, text_width - acw_width)) + new_y = text.winfo_rooty() + y + if (text_height - (y + cy) >= acw_height # enough height below + or y < acw_height): # not enough height above + # place acw below current line + new_y += cy + else: + # place acw above current line + new_y -= acw_height + acw.wm_geometry("+%d+%d" % (new_x, new_y)) + + def hide_event(self, event): + if not self.is_active(): + return + self.hide_window() + + def listselect_event(self, event): + if not self.is_active(): + return + self.userwantswindow = True + cursel = int(self.listbox.curselection()[0]) + self._change_start(self.completions[cursel]) + + def doubleclick_event(self, event): + # Put the selected completion in the text, and close the list + cursel = int(self.listbox.curselection()[0]) + self._change_start(self.completions[cursel]) + self.hide_window() + + def keypress_event(self, event): + if not self.is_active(): + return + keysym = event.keysym + if hasattr(event, "mc_state"): + state = event.mc_state + else: + state = 0 + if keysym != "Tab": + self.lastkey_was_tab = False + if (len(keysym) == 1 or keysym in ("underscore", "BackSpace") + or (self.mode == COMPLETE_FILES and keysym in + ("period", "minus"))) \ + and not (state & ~MC_SHIFT): + # Normal editing of text + if len(keysym) == 1: + self._change_start(self.start + keysym) + elif keysym == "underscore": + self._change_start(self.start + '_') + elif keysym == "period": + self._change_start(self.start + '.') + elif keysym == "minus": + self._change_start(self.start + '-') + else: + # keysym == "BackSpace" + if len(self.start) == 0: + self.hide_window() + return + self._change_start(self.start[:-1]) + self.lasttypedstart = self.start + self.listbox.select_clear(0, int(self.listbox.curselection()[0])) + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + return "break" + + elif keysym == "Return": + self.hide_window() + return + + elif (self.mode == COMPLETE_ATTRIBUTES and keysym in + ("period", "space", "parenleft", "parenright", "bracketleft", + "bracketright")) or \ + (self.mode == COMPLETE_FILES and keysym in + ("slash", "backslash", "quotedbl", "apostrophe")) \ + and not (state & ~MC_SHIFT): + # If start is a prefix of the selection, but is not '' when + # completing file names, put the whole + # selected completion. Anyway, close the list. + cursel = int(self.listbox.curselection()[0]) + if self.completions[cursel][:len(self.start)] == self.start \ + and (self.mode == COMPLETE_ATTRIBUTES or self.start): + self._change_start(self.completions[cursel]) + self.hide_window() + return + + elif keysym in ("Home", "End", "Prior", "Next", "Up", "Down") and \ + not state: + # Move the selection in the listbox + self.userwantswindow = True + cursel = int(self.listbox.curselection()[0]) + if keysym == "Home": + newsel = 0 + elif keysym == "End": + newsel = len(self.completions)-1 + elif keysym in ("Prior", "Next"): + jump = self.listbox.nearest(self.listbox.winfo_height()) - \ + self.listbox.nearest(0) + if keysym == "Prior": + newsel = max(0, cursel-jump) + else: + assert keysym == "Next" + newsel = min(len(self.completions)-1, cursel+jump) + elif keysym == "Up": + newsel = max(0, cursel-1) + else: + assert keysym == "Down" + newsel = min(len(self.completions)-1, cursel+1) + self.listbox.select_clear(cursel) + self.listbox.select_set(newsel) + self._selection_changed() + self._change_start(self.completions[newsel]) + return "break" + + elif (keysym == "Tab" and not state): + if self.lastkey_was_tab: + # two tabs in a row; insert current selection and close acw + cursel = int(self.listbox.curselection()[0]) + self._change_start(self.completions[cursel]) + self.hide_window() + return "break" + else: + # first tab; let AutoComplete handle the completion + self.userwantswindow = True + self.lastkey_was_tab = True + return + + elif any(s in keysym for s in ("Shift", "Control", "Alt", + "Meta", "Command", "Option")): + # A modifier key, so ignore + return + + elif event.char and event.char >= ' ': + # Regular character with a non-length-1 keycode + self._change_start(self.start + event.char) + self.lasttypedstart = self.start + self.listbox.select_clear(0, int(self.listbox.curselection()[0])) + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + return "break" + + else: + # Unknown event, close the window and let it through. + self.hide_window() + return + + def keyrelease_event(self, event): + if not self.is_active(): + return + if self.widget.index("insert") != \ + self.widget.index("%s+%dc" % (self.startindex, len(self.start))): + # If we didn't catch an event which moved the insert, close window + self.hide_window() + + def is_active(self): + return self.autocompletewindow is not None + + def complete(self): + self._change_start(self._complete_string(self.start)) + # The selection doesn't change. + + def hide_window(self): + if not self.is_active(): + return + + # unbind events + for seq in HIDE_SEQUENCES: + self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq) + self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid) + self.hideid = None + for seq in KEYPRESS_SEQUENCES: + self.widget.event_delete(KEYPRESS_VIRTUAL_EVENT_NAME, seq) + self.widget.unbind(KEYPRESS_VIRTUAL_EVENT_NAME, self.keypressid) + self.keypressid = None + self.widget.event_delete(KEYRELEASE_VIRTUAL_EVENT_NAME, + KEYRELEASE_SEQUENCE) + self.widget.unbind(KEYRELEASE_VIRTUAL_EVENT_NAME, self.keyreleaseid) + self.keyreleaseid = None + self.listbox.unbind(LISTUPDATE_SEQUENCE, self.listupdateid) + self.listupdateid = None + self.autocompletewindow.unbind(WINCONFIG_SEQUENCE, self.winconfigid) + self.winconfigid = None + + # destroy widgets + self.scrollbar.destroy() + self.scrollbar = None + self.listbox.destroy() + self.listbox = None + self.autocompletewindow.destroy() + self.autocompletewindow = None diff --git a/lib-python/3/idlelib/Bindings.py b/lib-python/3/idlelib/Bindings.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/Bindings.py @@ -0,0 +1,96 @@ +"""Define the menu contents, hotkeys, and event bindings. + +There is additional configuration information in the EditorWindow class (and +subclasses): the menus are created there based on the menu_specs (class) +variable, and menus not created are silently skipped in the code here. This +makes it possible, for example, to define a Debug menu which is only present in +the PythonShell window, and a Format menu which is only present in the Editor +windows. + +""" +from importlib.util import find_spec + +from idlelib.configHandler import idleConf + +# Warning: menudefs is altered in macosxSupport.overrideRootMenu() +# after it is determined that an OS X Aqua Tk is in use, +# which cannot be done until after Tk() is first called. +# Do not alter the 'file', 'options', or 'help' cascades here +# without altering overrideRootMenu() as well. +# TODO: Make this more robust + +menudefs = [ + # underscore prefixes character to underscore + ('file', [ + ('_New File', '<>'), + ('_Open...', '<>'), + ('Open _Module...', '<>'), + ('Class _Browser', '<>'), + ('_Path Browser', '<>'), + None, + ('_Save', '<>'), + ('Save _As...', '<>'), + ('Save Cop_y As...', '<>'), + None, + ('Prin_t Window', '<>'), + None, + ('_Close', '<>'), + ('E_xit', '<>'), + ]), + ('edit', [ + ('_Undo', '<>'), + ('_Redo', '<>'), + None, + ('Cu_t', '<>'), + ('_Copy', '<>'), + ('_Paste', '<>'), + ('Select _All', '<>'), + None, + ('_Find...', '<>'), + ('Find A_gain', '<>'), + ('Find _Selection', '<>'), + ('Find in Files...', '<>'), + ('R_eplace...', '<>'), + ('Go to _Line', '<>'), + ]), +('format', [ + ('_Indent Region', '<>'), + ('_Dedent Region', '<>'), + ('Comment _Out Region', '<>'), + ('U_ncomment Region', '<>'), + ('Tabify Region', '<>'), + ('Untabify Region', '<>'), + ('Toggle Tabs', '<>'), + ('New Indent Width', '<>'), + ]), + ('run', [ + ('Python Shell', '<>'), + ]), + ('shell', [ + ('_View Last Restart', '<>'), + ('_Restart Shell', '<>'), + None, + ('_Interrupt Execution', '<>'), + ]), + ('debug', [ + ('_Go to File/Line', '<>'), + ('!_Debugger', '<>'), + ('_Stack Viewer', '<>'), + ('!_Auto-open Stack Viewer', '<>'), + ]), + ('options', [ + ('Configure _IDLE', '<>'), + None, + ]), + ('help', [ + ('_About IDLE', '<>'), + None, + ('_IDLE Help', '<>'), + ('Python _Docs', '<>'), + ]), +] + +if find_spec('turtledemo'): + menudefs[-1][1].append(('Turtle Demo', '<>')) + +default_keydefs = idleConf.GetCurrentKeySet() diff --git a/lib-python/3/idlelib/CallTipWindow.py b/lib-python/3/idlelib/CallTipWindow.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/CallTipWindow.py @@ -0,0 +1,161 @@ +"""A CallTip window class for Tkinter/IDLE. + +After ToolTip.py, which uses ideas gleaned from PySol +Used by the CallTips IDLE extension. +""" +from tkinter import Toplevel, Label, LEFT, SOLID, TclError + +HIDE_VIRTUAL_EVENT_NAME = "<>" +HIDE_SEQUENCES = ("", "") +CHECKHIDE_VIRTUAL_EVENT_NAME = "<>" +CHECKHIDE_SEQUENCES = ("", "") +CHECKHIDE_TIME = 100 # milliseconds + +MARK_RIGHT = "calltipwindowregion_right" + +class CallTip: + + def __init__(self, widget): + self.widget = widget + self.tipwindow = self.label = None + self.parenline = self.parencol = None + self.lastline = None + self.hideid = self.checkhideid = None + self.checkhide_after_id = None + + def position_window(self): + """Check if needs to reposition the window, and if so - do it.""" + curline = int(self.widget.index("insert").split('.')[0]) + if curline == self.lastline: + return + self.lastline = curline + self.widget.see("insert") + if curline == self.parenline: + box = self.widget.bbox("%d.%d" % (self.parenline, + self.parencol)) + else: + box = self.widget.bbox("%d.0" % curline) + if not box: + box = list(self.widget.bbox("insert")) + # align to left of window + box[0] = 0 + box[2] = 0 + x = box[0] + self.widget.winfo_rootx() + 2 + y = box[1] + box[3] + self.widget.winfo_rooty() + self.tipwindow.wm_geometry("+%d+%d" % (x, y)) + + def showtip(self, text, parenleft, parenright): + """Show the calltip, bind events which will close it and reposition it. + """ + # Only called in CallTips, where lines are truncated + self.text = text + if self.tipwindow or not self.text: + return + + self.widget.mark_set(MARK_RIGHT, parenright) + self.parenline, self.parencol = map( + int, self.widget.index(parenleft).split(".")) + + self.tipwindow = tw = Toplevel(self.widget) + self.position_window() + # remove border on calltip window + tw.wm_overrideredirect(1) + try: + # This command is only needed and available on Tk >= 8.4.0 for OSX + # Without it, call tips intrude on the typing process by grabbing + # the focus. + tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w, + "help", "noActivates") + except TclError: + pass + self.label = Label(tw, text=self.text, justify=LEFT, + background="#ffffe0", relief=SOLID, borderwidth=1, + font = self.widget['font']) + self.label.pack() + tw.lift() # work around bug in Tk 8.5.18+ (issue #24570) + + self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME, + self.checkhide_event) + for seq in CHECKHIDE_SEQUENCES: + self.widget.event_add(CHECKHIDE_VIRTUAL_EVENT_NAME, seq) + self.widget.after(CHECKHIDE_TIME, self.checkhide_event) + self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME, + self.hide_event) + for seq in HIDE_SEQUENCES: + self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq) + + def checkhide_event(self, event=None): + if not self.tipwindow: + # If the event was triggered by the same event that unbinded + # this function, the function will be called nevertheless, + # so do nothing in this case. + return + curline, curcol = map(int, self.widget.index("insert").split('.')) + if curline < self.parenline or \ + (curline == self.parenline and curcol <= self.parencol) or \ + self.widget.compare("insert", ">", MARK_RIGHT): + self.hidetip() + else: + self.position_window() + if self.checkhide_after_id is not None: + self.widget.after_cancel(self.checkhide_after_id) + self.checkhide_after_id = \ + self.widget.after(CHECKHIDE_TIME, self.checkhide_event) + + def hide_event(self, event): + if not self.tipwindow: + # See the explanation in checkhide_event. + return + self.hidetip() + + def hidetip(self): + if not self.tipwindow: + return + + for seq in CHECKHIDE_SEQUENCES: + self.widget.event_delete(CHECKHIDE_VIRTUAL_EVENT_NAME, seq) + self.widget.unbind(CHECKHIDE_VIRTUAL_EVENT_NAME, self.checkhideid) + self.checkhideid = None + for seq in HIDE_SEQUENCES: + self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq) + self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid) + self.hideid = None + + self.label.destroy() + self.label = None + self.tipwindow.destroy() + self.tipwindow = None + + self.widget.mark_unset(MARK_RIGHT) + self.parenline = self.parencol = self.lastline = None + + def is_active(self): + return bool(self.tipwindow) + + +def _calltip_window(parent): # htest # + from tkinter import Toplevel, Text, LEFT, BOTH + + top = Toplevel(parent) + top.title("Test calltips") + top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200, + parent.winfo_rooty() + 150)) + text = Text(top) + text.pack(side=LEFT, fill=BOTH, expand=1) + text.insert("insert", "string.split") + top.update() + calltip = CallTip(text) + + def calltip_show(event): + calltip.showtip("(s=Hello world)", "insert", "end") + def calltip_hide(event): + calltip.hidetip() + text.event_add("<>", "(") + text.event_add("<>", ")") + text.bind("<>", calltip_show) + text.bind("<>", calltip_hide) + text.focus_set() + +if __name__=='__main__': + from idlelib.idle_test.htest import run + run(_calltip_window) diff --git a/lib-python/3/idlelib/ClassBrowser.py b/lib-python/3/idlelib/ClassBrowser.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/ClassBrowser.py @@ -0,0 +1,236 @@ +"""Class browser. + +XXX TO DO: + +- reparse when source changed (maybe just a button would be OK?) + (or recheck on window popup) +- add popup menu with more options (e.g. doc strings, base classes, imports) +- show function argument list? (have to do pattern matching on source) +- should the classes and methods lists also be in the module's menu bar? +- add base classes to class browser tree +""" + +import os +import sys +import pyclbr + +from idlelib import PyShell +from idlelib.WindowList import ListedToplevel +from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas +from idlelib.configHandler import idleConf + +file_open = None # Method...Item and Class...Item use this. +# Normally PyShell.flist.open, but there is no PyShell.flist for htest. + +class ClassBrowser: + + def __init__(self, flist, name, path, _htest=False): + # XXX This API should change, if the file doesn't end in ".py" + # XXX the code here is bogus! + """ + _htest - bool, change box when location running htest. + """ + global file_open + if not _htest: + file_open = PyShell.flist.open + self.name = name + self.file = os.path.join(path[0], self.name + ".py") + self._htest = _htest + self.init(flist) + + def close(self, event=None): + self.top.destroy() + self.node.destroy() + + def init(self, flist): + self.flist = flist + # reset pyclbr + pyclbr._modules.clear() + # create top + self.top = top = ListedToplevel(flist.root) + top.protocol("WM_DELETE_WINDOW", self.close) + top.bind("", self.close) + if self._htest: # place dialog below parent if running htest + top.geometry("+%d+%d" % + (flist.root.winfo_rootx(), flist.root.winfo_rooty() + 200)) + self.settitle() + top.focus_set() + # create scrolled canvas + theme = idleConf.CurrentTheme() + background = idleConf.GetHighlight(theme, 'normal')['background'] + sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1) + sc.frame.pack(expand=1, fill="both") + item = self.rootnode() + self.node = node = TreeNode(sc.canvas, None, item) + node.update() + node.expand() + + def settitle(self): + self.top.wm_title("Class Browser - " + self.name) + self.top.wm_iconname("Class Browser") + + def rootnode(self): + return ModuleBrowserTreeItem(self.file) + +class ModuleBrowserTreeItem(TreeItem): + + def __init__(self, file): + self.file = file + + def GetText(self): + return os.path.basename(self.file) + + def GetIconName(self): + return "python" + + def GetSubList(self): + sublist = [] + for name in self.listclasses(): + item = ClassBrowserTreeItem(name, self.classes, self.file) + sublist.append(item) + return sublist + + def OnDoubleClick(self): + if os.path.normcase(self.file[-3:]) != ".py": + return + if not os.path.exists(self.file): + return + PyShell.flist.open(self.file) + + def IsExpandable(self): + return os.path.normcase(self.file[-3:]) == ".py" + + def listclasses(self): + dir, file = os.path.split(self.file) + name, ext = os.path.splitext(file) + if os.path.normcase(ext) != ".py": + return [] + try: + dict = pyclbr.readmodule_ex(name, [dir] + sys.path) + except ImportError: + return [] + items = [] + self.classes = {} + for key, cl in dict.items(): + if cl.module == name: + s = key + if hasattr(cl, 'super') and cl.super: + supers = [] + for sup in cl.super: + if type(sup) is type(''): + sname = sup + else: + sname = sup.name + if sup.module != cl.module: + sname = "%s.%s" % (sup.module, sname) + supers.append(sname) + s = s + "(%s)" % ", ".join(supers) + items.append((cl.lineno, s)) + self.classes[s] = cl + items.sort() + list = [] + for item, s in items: + list.append(s) + return list + +class ClassBrowserTreeItem(TreeItem): + + def __init__(self, name, classes, file): + self.name = name + self.classes = classes + self.file = file + try: + self.cl = self.classes[self.name] + except (IndexError, KeyError): + self.cl = None + self.isfunction = isinstance(self.cl, pyclbr.Function) + + def GetText(self): + if self.isfunction: + return "def " + self.name + "(...)" + else: + return "class " + self.name + + def GetIconName(self): + if self.isfunction: + return "python" + else: + return "folder" + + def IsExpandable(self): + if self.cl: + try: + return not not self.cl.methods + except AttributeError: + return False + + def GetSubList(self): + if not self.cl: + return [] + sublist = [] + for name in self.listmethods(): + item = MethodBrowserTreeItem(name, self.cl, self.file) + sublist.append(item) + return sublist + + def OnDoubleClick(self): + if not os.path.exists(self.file): + return + edit = file_open(self.file) + if hasattr(self.cl, 'lineno'): + lineno = self.cl.lineno + edit.gotoline(lineno) + + def listmethods(self): + if not self.cl: + return [] + items = [] + for name, lineno in self.cl.methods.items(): + items.append((lineno, name)) + items.sort() + list = [] + for item, name in items: + list.append(name) + return list + +class MethodBrowserTreeItem(TreeItem): + + def __init__(self, name, cl, file): + self.name = name + self.cl = cl + self.file = file + + def GetText(self): + return "def " + self.name + "(...)" + + def GetIconName(self): + return "python" # XXX + + def IsExpandable(self): + return 0 + + def OnDoubleClick(self): + if not os.path.exists(self.file): + return + edit = file_open(self.file) + edit.gotoline(self.cl.methods[self.name]) + +def _class_browser(parent): #Wrapper for htest + try: + file = __file__ + except NameError: + file = sys.argv[0] + if sys.argv[1:]: + file = sys.argv[1] + else: + file = sys.argv[0] + dir, file = os.path.split(file) + name = os.path.splitext(file)[0] + flist = PyShell.PyShellFileList(parent) + global file_open + file_open = flist.open + ClassBrowser(flist, name, [dir], _htest=True) + +if __name__ == "__main__": + from idlelib.idle_test.htest import run + run(_class_browser) diff --git a/lib-python/3/idlelib/ColorDelegator.py b/lib-python/3/idlelib/ColorDelegator.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/ColorDelegator.py @@ -0,0 +1,281 @@ +import time +import re +import keyword +import builtins +from tkinter import TkVersion +from idlelib.Delegator import Delegator +from idlelib.configHandler import idleConf + +DEBUG = False + +def any(name, alternates): + "Return a named group pattern matching list of alternates." + return "(?P<%s>" % name + "|".join(alternates) + ")" + +def make_pat(): + kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b" + builtinlist = [str(name) for name in dir(builtins) + if not name.startswith('_') and \ + name not in keyword.kwlist] + # self.file = open("file") : + # 1st 'file' colorized normal, 2nd as builtin, 3rd as string + builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b" + comment = any("COMMENT", [r"#[^\n]*"]) + stringprefix = r"(\br|u|ur|R|U|UR|Ur|uR|b|B|br|Br|bR|BR|rb|rB|Rb|RB)?" + sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?" + dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?' + sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" + dq3string = stringprefix + r'"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' + string = any("STRING", [sq3string, dq3string, sqstring, dqstring]) + return kw + "|" + builtin + "|" + comment + "|" + string +\ + "|" + any("SYNC", [r"\n"]) + +prog = re.compile(make_pat(), re.S) +idprog = re.compile(r"\s+(\w+)", re.S) + +def color_config(text): # Called from htest, Editor, and Turtle Demo. + '''Set color opitons of Text widget. + + Should be called whenever ColorDelegator is called. + ''' + # Not automatic because ColorDelegator does not know 'text'. + theme = idleConf.CurrentTheme() + normal_colors = idleConf.GetHighlight(theme, 'normal') + cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg') + select_colors = idleConf.GetHighlight(theme, 'hilite') + text.config( + foreground=normal_colors['foreground'], + background=normal_colors['background'], + insertbackground=cursor_color, + selectforeground=select_colors['foreground'], + selectbackground=select_colors['background'], + ) + if TkVersion >= 8.5: + text.config( + inactiveselectbackground=select_colors['background']) + + +class ColorDelegator(Delegator): + + def __init__(self): + Delegator.__init__(self) + self.prog = prog + self.idprog = idprog + self.LoadTagDefs() + + def setdelegate(self, delegate): + if self.delegate is not None: + self.unbind("<>") + Delegator.setdelegate(self, delegate) + if delegate is not None: + self.config_colors() + self.bind("<>", self.toggle_colorize_event) + self.notify_range("1.0", "end") + else: + # No delegate - stop any colorizing + self.stop_colorizing = True + self.allow_colorizing = False + + def config_colors(self): + for tag, cnf in self.tagdefs.items(): + if cnf: + self.tag_configure(tag, **cnf) + self.tag_raise('sel') + + def LoadTagDefs(self): + theme = idleConf.CurrentTheme() + self.tagdefs = { + "COMMENT": idleConf.GetHighlight(theme, "comment"), + "KEYWORD": idleConf.GetHighlight(theme, "keyword"), + "BUILTIN": idleConf.GetHighlight(theme, "builtin"), + "STRING": idleConf.GetHighlight(theme, "string"), + "DEFINITION": idleConf.GetHighlight(theme, "definition"), + "SYNC": {'background':None,'foreground':None}, + "TODO": {'background':None,'foreground':None}, + "ERROR": idleConf.GetHighlight(theme, "error"), + # The following is used by ReplaceDialog: + "hit": idleConf.GetHighlight(theme, "hit"), + } + + if DEBUG: print('tagdefs',self.tagdefs) + + def insert(self, index, chars, tags=None): + index = self.index(index) + self.delegate.insert(index, chars, tags) + self.notify_range(index, index + "+%dc" % len(chars)) + + def delete(self, index1, index2=None): + index1 = self.index(index1) + self.delegate.delete(index1, index2) + self.notify_range(index1) + + after_id = None + allow_colorizing = True + colorizing = False + + def notify_range(self, index1, index2=None): + self.tag_add("TODO", index1, index2) + if self.after_id: + if DEBUG: print("colorizing already scheduled") + return + if self.colorizing: + self.stop_colorizing = True + if DEBUG: print("stop colorizing") + if self.allow_colorizing: + if DEBUG: print("schedule colorizing") + self.after_id = self.after(1, self.recolorize) + + close_when_done = None # Window to be closed when done colorizing + + def close(self, close_when_done=None): + if self.after_id: + after_id = self.after_id + self.after_id = None + if DEBUG: print("cancel scheduled recolorizer") + self.after_cancel(after_id) + self.allow_colorizing = False + self.stop_colorizing = True + if close_when_done: + if not self.colorizing: + close_when_done.destroy() + else: + self.close_when_done = close_when_done + + def toggle_colorize_event(self, event): + if self.after_id: + after_id = self.after_id + self.after_id = None + if DEBUG: print("cancel scheduled recolorizer") + self.after_cancel(after_id) + if self.allow_colorizing and self.colorizing: + if DEBUG: print("stop colorizing") + self.stop_colorizing = True + self.allow_colorizing = not self.allow_colorizing + if self.allow_colorizing and not self.colorizing: + self.after_id = self.after(1, self.recolorize) + if DEBUG: + print("auto colorizing turned",\ + self.allow_colorizing and "on" or "off") + return "break" + + def recolorize(self): + self.after_id = None + if not self.delegate: + if DEBUG: print("no delegate") + return + if not self.allow_colorizing: + if DEBUG: print("auto colorizing is off") + return + if self.colorizing: + if DEBUG: print("already colorizing") + return + try: + self.stop_colorizing = False + self.colorizing = True + if DEBUG: print("colorizing...") + t0 = time.perf_counter() + self.recolorize_main() + t1 = time.perf_counter() + if DEBUG: print("%.3f seconds" % (t1-t0)) + finally: + self.colorizing = False + if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"): + if DEBUG: print("reschedule colorizing") + self.after_id = self.after(1, self.recolorize) + if self.close_when_done: + top = self.close_when_done + self.close_when_done = None + top.destroy() + + def recolorize_main(self): + next = "1.0" + while True: + item = self.tag_nextrange("TODO", next) + if not item: + break + head, tail = item + self.tag_remove("SYNC", head, tail) + item = self.tag_prevrange("SYNC", head) + if item: + head = item[1] + else: + head = "1.0" + + chars = "" + next = head + lines_to_get = 1 + ok = False + while not ok: + mark = next + next = self.index(mark + "+%d lines linestart" % + lines_to_get) + lines_to_get = min(lines_to_get * 2, 100) + ok = "SYNC" in self.tag_names(next + "-1c") + line = self.get(mark, next) + ##print head, "get", mark, next, "->", repr(line) + if not line: + return + for tag in self.tagdefs: + self.tag_remove(tag, mark, next) + chars = chars + line + m = self.prog.search(chars) + while m: + for key, value in m.groupdict().items(): + if value: + a, b = m.span(key) + self.tag_add(key, + head + "+%dc" % a, + head + "+%dc" % b) + if value in ("def", "class"): + m1 = self.idprog.match(chars, b) + if m1: + a, b = m1.span(1) + self.tag_add("DEFINITION", + head + "+%dc" % a, + head + "+%dc" % b) + m = self.prog.search(chars, m.end()) + if "SYNC" in self.tag_names(next + "-1c"): + head = next + chars = "" + else: + ok = False + if not ok: + # We're in an inconsistent state, and the call to + # update may tell us to stop. It may also change + # the correct value for "next" (since this is a + # line.col string, not a true mark). So leave a + # crumb telling the next invocation to resume here + # in case update tells us to leave. + self.tag_add("TODO", next) + self.update() + if self.stop_colorizing: + if DEBUG: print("colorizing stopped") + return + + def removecolors(self): + for tag in self.tagdefs: + self.tag_remove(tag, "1.0", "end") + + +def _color_delegator(parent): # htest # + from tkinter import Toplevel, Text + from idlelib.Percolator import Percolator + + top = Toplevel(parent) + top.title("Test ColorDelegator") + top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200, + parent.winfo_rooty() + 150)) + source = "if somename: x = 'abc' # comment\nprint\n" + text = Text(top, background="white") + text.pack(expand=1, fill="both") + text.insert("insert", source) + text.focus_set() + + color_config(text) + p = Percolator(text) + d = ColorDelegator() + p.insertfilter(d) + +if __name__ == "__main__": + from idlelib.idle_test.htest import run + run(_color_delegator) diff --git a/lib-python/3/idlelib/EditorWindow.py b/lib-python/3/idlelib/EditorWindow.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/EditorWindow.py @@ -0,0 +1,1690 @@ +import importlib +import importlib.abc +import importlib.util +import os +import platform +import re +import string +import sys +from tkinter import * +import tkinter.simpledialog as tkSimpleDialog +import tkinter.messagebox as tkMessageBox +import traceback +import webbrowser + +from idlelib.MultiCall import MultiCallCreator +from idlelib import WindowList +from idlelib import SearchDialog +from idlelib import GrepDialog +from idlelib import ReplaceDialog +from idlelib import PyParse +from idlelib.configHandler import idleConf +from idlelib import aboutDialog, textView, configDialog +from idlelib import macosxSupport +from idlelib import help + +# The default tab setting for a Text widget, in average-width characters. +TK_TABWIDTH_DEFAULT = 8 + +_py_version = ' (%s)' % platform.python_version() + +def _sphinx_version(): + "Format sys.version_info to produce the Sphinx version string used to install the chm docs" + major, minor, micro, level, serial = sys.version_info + release = '%s%s' % (major, minor) + release += '%s' % (micro,) + if level == 'candidate': + release += 'rc%s' % (serial,) + elif level != 'final': + release += '%s%s' % (level[0], serial) + return release + + +class HelpDialog(object): + + def __init__(self): + self.parent = None # parent of help window + self.dlg = None # the help window iteself + + def display(self, parent, near=None): + """ Display the help dialog. + + parent - parent widget for the help window + + near - a Toplevel widget (e.g. EditorWindow or PyShell) + to use as a reference for placing the help window + """ + import warnings as w + w.warn("EditorWindow.HelpDialog is no longer used by Idle.\n" + "It will be removed in 3.6 or later.\n" + "It has been replaced by private help.HelpWindow\n", + DeprecationWarning, stacklevel=2) + if self.dlg is None: + self.show_dialog(parent) + if near: + self.nearwindow(near) + + def show_dialog(self, parent): + self.parent = parent + fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt') + self.dlg = dlg = textView.view_file(parent,'Help',fn, modal=False) + dlg.bind('', self.destroy, '+') + + def nearwindow(self, near): + # Place the help dialog near the window specified by parent. + # Note - this may not reposition the window in Metacity + # if "/apps/metacity/general/disable_workarounds" is enabled + dlg = self.dlg + geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10) + dlg.withdraw() + dlg.geometry("=+%d+%d" % geom) + dlg.deiconify() + dlg.lift() + + def destroy(self, ev=None): + self.dlg = None + self.parent = None + +helpDialog = HelpDialog() # singleton instance, no longer used + + +class EditorWindow(object): + from idlelib.Percolator import Percolator + from idlelib.ColorDelegator import ColorDelegator, color_config + from idlelib.UndoDelegator import UndoDelegator + from idlelib.IOBinding import IOBinding, filesystemencoding, encoding + from idlelib import Bindings + from tkinter import Toplevel + from idlelib.MultiStatusBar import MultiStatusBar + + help_url = None + + def __init__(self, flist=None, filename=None, key=None, root=None): + if EditorWindow.help_url is None: + dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html') + if sys.platform.count('linux'): + # look for html docs in a couple of standard places + pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3] + if os.path.isdir('/var/www/html/python/'): # "python2" rpm + dochome = '/var/www/html/python/index.html' + else: + basepath = '/usr/share/doc/' # standard location + dochome = os.path.join(basepath, pyver, + 'Doc', 'index.html') + elif sys.platform[:3] == 'win': + chmfile = os.path.join(sys.base_prefix, 'Doc', + 'Python%s.chm' % _sphinx_version()) + if os.path.isfile(chmfile): + dochome = chmfile + elif sys.platform == 'darwin': + # documentation may be stored inside a python framework + dochome = os.path.join(sys.base_prefix, + 'Resources/English.lproj/Documentation/index.html') + dochome = os.path.normpath(dochome) + if os.path.isfile(dochome): + EditorWindow.help_url = dochome + if sys.platform == 'darwin': + # Safari requires real file:-URLs + EditorWindow.help_url = 'file://' + EditorWindow.help_url + else: + EditorWindow.help_url = "https://docs.python.org/%d.%d/" % sys.version_info[:2] + self.flist = flist + root = root or flist.root + self.root = root + try: + sys.ps1 + except AttributeError: + sys.ps1 = '>>> ' + self.menubar = Menu(root) + self.top = top = WindowList.ListedToplevel(root, menu=self.menubar) + if flist: + self.tkinter_vars = flist.vars + #self.top.instance_dict makes flist.inversedict available to + #configDialog.py so it can access all EditorWindow instances + self.top.instance_dict = flist.inversedict + else: + self.tkinter_vars = {} # keys: Tkinter event names + # values: Tkinter variable instances + self.top.instance_dict = {} + self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(), + 'recent-files.lst') + self.text_frame = text_frame = Frame(top) + self.vbar = vbar = Scrollbar(text_frame, name='vbar') + self.width = idleConf.GetOption('main', 'EditorWindow', + 'width', type='int') + text_options = { + 'name': 'text', + 'padx': 5, + 'wrap': 'none', + 'highlightthickness': 0, + 'width': self.width, + 'height': idleConf.GetOption('main', 'EditorWindow', + 'height', type='int')} + if TkVersion >= 8.5: + # Starting with tk 8.5 we have to set the new tabstyle option + # to 'wordprocessor' to achieve the same display of tabs as in + # older tk versions. + text_options['tabstyle'] = 'wordprocessor' + self.text = text = MultiCallCreator(Text)(text_frame, **text_options) + self.top.focused_widget = self.text + + self.createmenubar() + self.apply_bindings() + + self.top.protocol("WM_DELETE_WINDOW", self.close) + self.top.bind("<>", self.close_event) + if macosxSupport.isAquaTk(): + # Command-W on editorwindows doesn't work without this. + text.bind('<>', self.close_event) + # Some OS X systems have only one mouse button, so use + # control-click for popup context menus there. For two + # buttons, AquaTk defines <2> as the right button, not <3>. + text.bind("",self.right_menu_event) + text.bind("<2>", self.right_menu_event) + else: + # Elsewhere, use right-click for popup menus. + text.bind("<3>",self.right_menu_event) + text.bind("<>", self.cut) + text.bind("<>", self.copy) + text.bind("<>", self.paste) + text.bind("<>", self.center_insert_event) + text.bind("<>", self.help_dialog) + text.bind("<>", self.python_docs) + text.bind("<>", self.about_dialog) + text.bind("<>", self.config_dialog) + text.bind("<>", self.open_module) + text.bind("<>", lambda event: "break") + text.bind("<>", self.select_all) + text.bind("<>", self.remove_selection) + text.bind("<>", self.find_event) + text.bind("<>", self.find_again_event) + text.bind("<>", self.find_in_files_event) + text.bind("<>", self.find_selection_event) + text.bind("<>", self.replace_event) + text.bind("<>", self.goto_line_event) + text.bind("<>",self.smart_backspace_event) + text.bind("<>",self.newline_and_indent_event) + text.bind("<>",self.smart_indent_event) + text.bind("<>",self.indent_region_event) + text.bind("<>",self.dedent_region_event) + text.bind("<>",self.comment_region_event) + text.bind("<>",self.uncomment_region_event) + text.bind("<>",self.tabify_region_event) + text.bind("<>",self.untabify_region_event) + text.bind("<>",self.toggle_tabs_event) + text.bind("<>",self.change_indentwidth_event) + text.bind("", self.move_at_edge_if_selection(0)) + text.bind("", self.move_at_edge_if_selection(1)) + text.bind("<>", self.del_word_left) + text.bind("<>", self.del_word_right) + text.bind("<>", self.home_callback) + + if flist: + flist.inversedict[self] = key + if key: + flist.dict[key] = self + text.bind("<>", self.new_callback) + text.bind("<>", self.flist.close_all_callback) + text.bind("<>", self.open_class_browser) + text.bind("<>", self.open_path_browser) + text.bind("<>", self.open_turtle_demo) + + self.set_status_bar() + vbar['command'] = text.yview + vbar.pack(side=RIGHT, fill=Y) + text['yscrollcommand'] = vbar.set + text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow') + text_frame.pack(side=LEFT, fill=BOTH, expand=1) + text.pack(side=TOP, fill=BOTH, expand=1) + text.focus_set() + + # usetabs true -> literal tab characters are used by indent and + # dedent cmds, possibly mixed with spaces if + # indentwidth is not a multiple of tabwidth, + # which will cause Tabnanny to nag! + # false -> tab characters are converted to spaces by indent + # and dedent cmds, and ditto TAB keystrokes + # Although use-spaces=0 can be configured manually in config-main.def, + # configuration of tabs v. spaces is not supported in the configuration + # dialog. IDLE promotes the preferred Python indentation: use spaces! + usespaces = idleConf.GetOption('main', 'Indent', + 'use-spaces', type='bool') + self.usetabs = not usespaces + + # tabwidth is the display width of a literal tab character. + # CAUTION: telling Tk to use anything other than its default + # tab setting causes it to use an entirely different tabbing algorithm, + # treating tab stops as fixed distances from the left margin. + # Nobody expects this, so for now tabwidth should never be changed. + self.tabwidth = 8 # must remain 8 until Tk is fixed. + + # indentwidth is the number of screen characters per indent level. + # The recommended Python indentation is four spaces. + self.indentwidth = self.tabwidth + self.set_notabs_indentwidth() + + # If context_use_ps1 is true, parsing searches back for a ps1 line; + # else searches for a popular (if, def, ...) Python stmt. + self.context_use_ps1 = False + + # When searching backwards for a reliable place to begin parsing, + # first start num_context_lines[0] lines back, then + # num_context_lines[1] lines back if that didn't work, and so on. + # The last value should be huge (larger than the # of lines in a + # conceivable file). + # Making the initial values larger slows things down more often. + self.num_context_lines = 50, 500, 5000000 + self.per = per = self.Percolator(text) + self.undo = undo = self.UndoDelegator() + per.insertfilter(undo) + text.undo_block_start = undo.undo_block_start + text.undo_block_stop = undo.undo_block_stop + undo.set_saved_change_hook(self.saved_change_hook) + # IOBinding implements file I/O and printing functionality + self.io = io = self.IOBinding(self) + io.set_filename_change_hook(self.filename_change_hook) + self.good_load = False + self.set_indentation_params(False) + self.color = None # initialized below in self.ResetColorizer + if filename: + if os.path.exists(filename) and not os.path.isdir(filename): + if io.loadfile(filename): + self.good_load = True + is_py_src = self.ispythonsource(filename) + self.set_indentation_params(is_py_src) + else: + io.set_filename(filename) + self.good_load = True + + self.ResetColorizer() + self.saved_change_hook() + self.update_recent_files_list() + self.load_extensions() + menu = self.menudict.get('windows') + if menu: + end = menu.index("end") + if end is None: + end = -1 + if end >= 0: + menu.add_separator() + end = end + 1 + self.wmenu_end = end + WindowList.register_callback(self.postwindowsmenu) + + # Some abstractions so IDLE extensions are cross-IDE + self.askyesno = tkMessageBox.askyesno + self.askinteger = tkSimpleDialog.askinteger + self.showerror = tkMessageBox.showerror + + def _filename_to_unicode(self, filename): + """Return filename as BMP unicode so diplayable in Tk.""" + # Decode bytes to unicode. + if isinstance(filename, bytes): + try: + filename = filename.decode(self.filesystemencoding) + except UnicodeDecodeError: + try: + filename = filename.decode(self.encoding) + except UnicodeDecodeError: + # byte-to-byte conversion + filename = filename.decode('iso8859-1') + # Replace non-BMP char with diamond questionmark. + return re.sub('[\U00010000-\U0010FFFF]', '\ufffd', filename) + + def new_callback(self, event): + dirname, basename = self.io.defaultfilename() + self.flist.new(dirname) + return "break" + + def home_callback(self, event): + if (event.state & 4) != 0 and event.keysym == "Home": + # state&4==Control. If , use the Tk binding. + return + if self.text.index("iomark") and \ + self.text.compare("iomark", "<=", "insert lineend") and \ + self.text.compare("insert linestart", "<=", "iomark"): + # In Shell on input line, go to just after prompt + insertpt = int(self.text.index("iomark").split(".")[1]) + else: + line = self.text.get("insert linestart", "insert lineend") + for insertpt in range(len(line)): + if line[insertpt] not in (' ','\t'): + break + else: + insertpt=len(line) + lineat = int(self.text.index("insert").split('.')[1]) + if insertpt == lineat: + insertpt = 0 + dest = "insert linestart+"+str(insertpt)+"c" + if (event.state&1) == 0: + # shift was not pressed + self.text.tag_remove("sel", "1.0", "end") + else: + if not self.text.index("sel.first"): + # there was no previous selection + self.text.mark_set("my_anchor", "insert") + else: + if self.text.compare(self.text.index("sel.first"), "<", + self.text.index("insert")): + self.text.mark_set("my_anchor", "sel.first") # extend back + else: + self.text.mark_set("my_anchor", "sel.last") # extend forward + first = self.text.index(dest) + last = self.text.index("my_anchor") + if self.text.compare(first,">",last): + first,last = last,first + self.text.tag_remove("sel", "1.0", "end") + self.text.tag_add("sel", first, last) + self.text.mark_set("insert", dest) + self.text.see("insert") + return "break" + + def set_status_bar(self): + self.status_bar = self.MultiStatusBar(self.top) + sep = Frame(self.top, height=1, borderwidth=1, background='grey75') + if sys.platform == "darwin": + # Insert some padding to avoid obscuring some of the statusbar + # by the resize widget. + self.status_bar.set_label('_padding1', ' ', side=RIGHT) + self.status_bar.set_label('column', 'Col: ?', side=RIGHT) + self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) + self.status_bar.pack(side=BOTTOM, fill=X) + sep.pack(side=BOTTOM, fill=X) + self.text.bind("<>", self.set_line_and_column) + self.text.event_add("<>", + "", "") + self.text.after_idle(self.set_line_and_column) + + def set_line_and_column(self, event=None): + line, column = self.text.index(INSERT).split('.') + self.status_bar.set_label('column', 'Col: %s' % column) + self.status_bar.set_label('line', 'Ln: %s' % line) + + menu_specs = [ + ("file", "_File"), + ("edit", "_Edit"), + ("format", "F_ormat"), + ("run", "_Run"), + ("options", "_Options"), + ("windows", "_Window"), + ("help", "_Help"), + ] + + + def createmenubar(self): + mbar = self.menubar + self.menudict = menudict = {} + for name, label in self.menu_specs: + underline, label = prepstr(label) + menudict[name] = menu = Menu(mbar, name=name, tearoff=0) + mbar.add_cascade(label=label, menu=menu, underline=underline) + if macosxSupport.isCarbonTk(): + # Insert the application menu + menudict['application'] = menu = Menu(mbar, name='apple', + tearoff=0) + mbar.add_cascade(label='IDLE', menu=menu) + self.fill_menus() + self.recent_files_menu = Menu(self.menubar, tearoff=0) + self.menudict['file'].insert_cascade(3, label='Recent Files', + underline=0, + menu=self.recent_files_menu) + self.base_helpmenu_length = self.menudict['help'].index(END) + self.reset_help_menu_entries() + + def postwindowsmenu(self): + # Only called when Windows menu exists + menu = self.menudict['windows'] + end = menu.index("end") + if end is None: + end = -1 + if end > self.wmenu_end: + menu.delete(self.wmenu_end+1, end) + WindowList.add_windows_to_menu(menu) + + rmenu = None + + def right_menu_event(self, event): + self.text.mark_set("insert", "@%d,%d" % (event.x, event.y)) + if not self.rmenu: + self.make_rmenu() + rmenu = self.rmenu + self.event = event + iswin = sys.platform[:3] == 'win' + if iswin: + self.text.config(cursor="arrow") + + for item in self.rmenu_specs: + try: + label, eventname, verify_state = item + except ValueError: # see issue1207589 + continue + + if verify_state is None: + continue + state = getattr(self, verify_state)() + rmenu.entryconfigure(label, state=state) + + + rmenu.tk_popup(event.x_root, event.y_root) + if iswin: + self.text.config(cursor="ibeam") + + rmenu_specs = [ + # ("Label", "<>", "statefuncname"), ... + ("Close", "<>", None), # Example + ] + + def make_rmenu(self): + rmenu = Menu(self.text, tearoff=0) + for item in self.rmenu_specs: + label, eventname = item[0], item[1] + if label is not None: + def command(text=self.text, eventname=eventname): + text.event_generate(eventname) + rmenu.add_command(label=label, command=command) + else: + rmenu.add_separator() + self.rmenu = rmenu + + def rmenu_check_cut(self): + return self.rmenu_check_copy() + + def rmenu_check_copy(self): + try: + indx = self.text.index('sel.first') + except TclError: + return 'disabled' + else: + return 'normal' if indx else 'disabled' + + def rmenu_check_paste(self): + try: + self.text.tk.call('tk::GetSelection', self.text, 'CLIPBOARD') + except TclError: + return 'disabled' + else: + return 'normal' + + def about_dialog(self, event=None): + "Handle Help 'About IDLE' event." + # Synchronize with macosxSupport.overrideRootMenu.about_dialog. + aboutDialog.AboutDialog(self.top,'About IDLE') + + def config_dialog(self, event=None): + "Handle Options 'Configure IDLE' event." + # Synchronize with macosxSupport.overrideRootMenu.config_dialog. + configDialog.ConfigDialog(self.top,'Settings') + + def help_dialog(self, event=None): + "Handle Help 'IDLE Help' event." + # Synchronize with macosxSupport.overrideRootMenu.help_dialog. + if self.root: + parent = self.root + else: + parent = self.top + help.show_idlehelp(parent) + + def python_docs(self, event=None): + if sys.platform[:3] == 'win': + try: + os.startfile(self.help_url) + except OSError as why: + tkMessageBox.showerror(title='Document Start Failure', + message=str(why), parent=self.text) + else: + webbrowser.open(self.help_url) + return "break" + + def cut(self,event): + self.text.event_generate("<>") + return "break" + + def copy(self,event): + if not self.text.tag_ranges("sel"): + # There is no selection, so do nothing and maybe interrupt. + return + self.text.event_generate("<>") + return "break" + + def paste(self,event): + self.text.event_generate("<>") + self.text.see("insert") + return "break" + + def select_all(self, event=None): + self.text.tag_add("sel", "1.0", "end-1c") + self.text.mark_set("insert", "1.0") + self.text.see("insert") + return "break" + + def remove_selection(self, event=None): + self.text.tag_remove("sel", "1.0", "end") + self.text.see("insert") + + def move_at_edge_if_selection(self, edge_index): + """Cursor move begins at start or end of selection + + When a left/right cursor key is pressed create and return to Tkinter a + function which causes a cursor move from the associated edge of the + selection. + + """ + self_text_index = self.text.index + self_text_mark_set = self.text.mark_set + edges_table = ("sel.first+1c", "sel.last-1c") + def move_at_edge(event): + if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed + try: + self_text_index("sel.first") + self_text_mark_set("insert", edges_table[edge_index]) + except TclError: + pass + return move_at_edge + + def del_word_left(self, event): + self.text.event_generate('') + return "break" + + def del_word_right(self, event): + self.text.event_generate('') + return "break" + + def find_event(self, event): + SearchDialog.find(self.text) + return "break" + + def find_again_event(self, event): + SearchDialog.find_again(self.text) + return "break" + + def find_selection_event(self, event): + SearchDialog.find_selection(self.text) + return "break" + + def find_in_files_event(self, event): + GrepDialog.grep(self.text, self.io, self.flist) + return "break" + + def replace_event(self, event): + ReplaceDialog.replace(self.text) + return "break" + + def goto_line_event(self, event): + text = self.text + lineno = tkSimpleDialog.askinteger("Goto", + "Go to line number:",parent=text) + if lineno is None: + return "break" + if lineno <= 0: + text.bell() + return "break" + text.mark_set("insert", "%d.0" % lineno) + text.see("insert") + + def open_module(self, event=None): + # XXX Shouldn't this be in IOBinding? + try: + name = self.text.get("sel.first", "sel.last") + except TclError: + name = "" + else: + name = name.strip() + name = tkSimpleDialog.askstring("Module", + "Enter the name of a Python module\n" + "to search on sys.path and open:", + parent=self.text, initialvalue=name) + if name: + name = name.strip() + if not name: + return + # XXX Ought to insert current file's directory in front of path + try: + spec = importlib.util.find_spec(name) + except (ValueError, ImportError) as msg: + tkMessageBox.showerror("Import error", str(msg), parent=self.text) + return + if spec is None: + tkMessageBox.showerror("Import error", "module not found", + parent=self.text) + return + if not isinstance(spec.loader, importlib.abc.SourceLoader): + tkMessageBox.showerror("Import error", "not a source-based module", + parent=self.text) + return + try: + file_path = spec.loader.get_filename(name) + except AttributeError: + tkMessageBox.showerror("Import error", + "loader does not support get_filename", + parent=self.text) + return + if self.flist: + self.flist.open(file_path) + else: + self.io.loadfile(file_path) + return file_path + + def open_class_browser(self, event=None): + filename = self.io.filename + if not (self.__class__.__name__ == 'PyShellEditorWindow' + and filename): + filename = self.open_module() + if filename is None: + return + head, tail = os.path.split(filename) + base, ext = os.path.splitext(tail) + from idlelib import ClassBrowser + ClassBrowser.ClassBrowser(self.flist, base, [head]) + + def open_path_browser(self, event=None): + from idlelib import PathBrowser + PathBrowser.PathBrowser(self.flist) + + def open_turtle_demo(self, event = None): + import subprocess + + cmd = [sys.executable, + '-c', + 'from turtledemo.__main__ import main; main()'] + subprocess.Popen(cmd, shell=False) + + def gotoline(self, lineno): + if lineno is not None and lineno > 0: + self.text.mark_set("insert", "%d.0" % lineno) + self.text.tag_remove("sel", "1.0", "end") + self.text.tag_add("sel", "insert", "insert +1l") + self.center() + + def ispythonsource(self, filename): + if not filename or os.path.isdir(filename): + return True + base, ext = os.path.splitext(os.path.basename(filename)) + if os.path.normcase(ext) in (".py", ".pyw"): + return True + line = self.text.get('1.0', '1.0 lineend') + return line.startswith('#!') and 'python' in line + + def close_hook(self): + if self.flist: + self.flist.unregister_maybe_terminate(self) + self.flist = None + + def set_close_hook(self, close_hook): + self.close_hook = close_hook + + def filename_change_hook(self): + if self.flist: + self.flist.filename_changed_edit(self) + self.saved_change_hook() + self.top.update_windowlist_registry(self) + self.ResetColorizer() + + def _addcolorizer(self): + if self.color: + return + if self.ispythonsource(self.io.filename): + self.color = self.ColorDelegator() + # can add more colorizers here... + if self.color: + self.per.removefilter(self.undo) + self.per.insertfilter(self.color) + self.per.insertfilter(self.undo) + + def _rmcolorizer(self): + if not self.color: + return + self.color.removecolors() + self.per.removefilter(self.color) From pypy.commits at gmail.com Tue Feb 5 08:55:55 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Tue, 05 Feb 2019 05:55:55 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying: Teach rzlib to copy decompression streams. Message-ID: <5c5995eb.1c69fb81.bb039.5052@mx.google.com> Author: Julian Berman Branch: zlib-copying Changeset: r95833:6237bdf72bdb Date: 2019-02-04 17:13 +0100 http://bitbucket.org/pypy/pypy/changeset/6237bdf72bdb/ Log: Teach rzlib to copy decompression streams. diff --git a/rpython/rlib/rzlib.py b/rpython/rlib/rzlib.py --- a/rpython/rlib/rzlib.py +++ b/rpython/rlib/rzlib.py @@ -35,6 +35,7 @@ MAX_WBITS MAX_MEM_LEVEL Z_BEST_SPEED Z_BEST_COMPRESSION Z_DEFAULT_COMPRESSION Z_FILTERED Z_HUFFMAN_ONLY Z_DEFAULT_STRATEGY Z_NEED_DICT + Z_NULL '''.split() class SimpleCConfig: @@ -160,6 +161,7 @@ rffi.INT) _inflate = zlib_external('inflate', [z_stream_p, rffi.INT], rffi.INT) +_inflateCopy = zlib_external('inflateCopy', [z_stream_p, z_stream_p], rffi.INT) _inflateEnd = zlib_external('inflateEnd', [z_stream_p], rffi.INT, releasegil=False) @@ -330,6 +332,19 @@ lltype.free(stream, flavor='raw') +def inflateCopy(source): + """ + Allocate and return an independent copy of the provided stream object. + """ + dest = inflateInit() + err = _inflateCopy(dest, source) + if err != Z_OK: + inflateEnd(dest) + raise RZlibError.fromstream(source, err, + "while copying decompression object") + return dest + + def inflateEnd(stream): """ Free the resources associated with the inflate stream. diff --git a/rpython/rlib/test/test_rzlib.py b/rpython/rlib/test/test_rzlib.py --- a/rpython/rlib/test/test_rzlib.py +++ b/rpython/rlib/test/test_rzlib.py @@ -246,6 +246,60 @@ rzlib.deflateEnd(stream) +def test_decompress_copy(): + """ + inflateCopy produces an independent copy of a stream. + """ + + stream = rzlib.inflateInit() + + bytes1, finished1, unused1 = rzlib.decompress(stream, compressed[:10]) + assert bytes1 + assert finished1 is False + + copied = rzlib.inflateCopy(stream) + + bytes_stream, finished_stream, unused_stream = rzlib.decompress( + stream, + compressed[10:], + rzlib.Z_FINISH, + ) + assert bytes1 + bytes_stream == expanded + assert finished_stream is True + assert unused1 == 0 + assert unused_stream == 0 + rzlib.inflateEnd(stream) + + bytes_copy, finished_copy, unused_copy = rzlib.decompress( + copied, + compressed[10:], + rzlib.Z_FINISH, + ) + rzlib.inflateEnd(copied) + assert bytes1 + bytes_copy == expanded + assert finished_copy is True + assert unused_copy == 0 + + +def test_unsuccessful_decompress_copy(): + """ + Errors during unsuccesful inflateCopy operations raise RZlibErrors. + """ + stream = rzlib.inflateInit() + + # From zlib.h: + # + # "inflateCopy returns [...] Z_STREAM_ERROR if the source stream + # state was inconsistent (such as zalloc being Z_NULL)" + from rpython.rtyper.lltypesystem import rffi, lltype + stream.c_zalloc = rffi.cast(lltype.typeOf(stream.c_zalloc), rzlib.Z_NULL) + + exc = py.test.raises(rzlib.RZlibError, rzlib.inflateCopy, stream) + msg = "Error -2 while copying decompression object: inconsistent stream state" + assert str(exc.value) == msg + rzlib.inflateEnd(stream) + + def test_cornercases(): """ Test degenerate arguments. From pypy.commits at gmail.com Tue Feb 5 08:55:56 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Tue, 05 Feb 2019 05:55:56 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying: And the same for compression streams. Message-ID: <5c5995ec.1c69fb81.a9bca.6ed0@mx.google.com> Author: Julian Berman Branch: zlib-copying Changeset: r95834:17e6b069ffdd Date: 2019-02-04 17:21 +0100 http://bitbucket.org/pypy/pypy/changeset/17e6b069ffdd/ Log: And the same for compression streams. diff --git a/rpython/rlib/rzlib.py b/rpython/rlib/rzlib.py --- a/rpython/rlib/rzlib.py +++ b/rpython/rlib/rzlib.py @@ -142,6 +142,7 @@ rffi.INT) _deflate = zlib_external('deflate', [z_stream_p, rffi.INT], rffi.INT) +_deflateCopy = zlib_external('deflateCopy', [z_stream_p, z_stream_p], rffi.INT) _deflateEnd = zlib_external('deflateEnd', [z_stream_p], rffi.INT, releasegil=False) @@ -292,6 +293,19 @@ lltype.free(stream, flavor='raw') +def deflateCopy(source): + """ + Allocate and return an independent copy of the provided stream object. + """ + dest = deflateInit() + err = _deflateCopy(dest, source) + if err != Z_OK: + deflateEnd(dest) + raise RZlibError.fromstream(source, err, + "while copying compression object") + return dest + + def deflateEnd(stream): """ Free the resources associated with the deflate stream. diff --git a/rpython/rlib/test/test_rzlib.py b/rpython/rlib/test/test_rzlib.py --- a/rpython/rlib/test/test_rzlib.py +++ b/rpython/rlib/test/test_rzlib.py @@ -246,6 +246,54 @@ rzlib.deflateEnd(stream) +def test_compress_copy(): + """ + inflateCopy produces an independent copy of a stream. + """ + + stream = rzlib.deflateInit() + + bytes1 = rzlib.compress(stream, expanded[:10]) + assert bytes1 + + copied = rzlib.deflateCopy(stream) + + bytes_stream = rzlib.compress( + stream, + expanded[10:], + rzlib.Z_FINISH, + ) + assert bytes1 + bytes_stream == compressed + rzlib.deflateEnd(stream) + + bytes_copy = rzlib.compress( + copied, + expanded[10:], + rzlib.Z_FINISH, + ) + rzlib.deflateEnd(copied) + assert bytes1 + bytes_copy == compressed + + +def test_unsuccessful_compress_copy(): + """ + Errors during unsuccesful deflateCopy operations raise RZlibErrors. + """ + stream = rzlib.deflateInit() + + # From zlib.h: + # + # "deflateCopy returns [...] Z_STREAM_ERROR if the source stream + # state was inconsistent (such as zalloc being Z_NULL)" + from rpython.rtyper.lltypesystem import rffi, lltype + stream.c_zalloc = rffi.cast(lltype.typeOf(stream.c_zalloc), rzlib.Z_NULL) + + exc = py.test.raises(rzlib.RZlibError, rzlib.deflateCopy, stream) + msg = "Error -2 while copying compression object: inconsistent stream state" + assert str(exc.value) == msg + rzlib.deflateEnd(stream) + + def test_decompress_copy(): """ inflateCopy produces an independent copy of a stream. From pypy.commits at gmail.com Tue Feb 5 08:55:59 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Tue, 05 Feb 2019 05:55:59 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying: Now implement decompressobj.copy at the language level. Message-ID: <5c5995ef.1c69fb81.19a4a.62af@mx.google.com> Author: Julian Berman Branch: zlib-copying Changeset: r95835:51b646896e26 Date: 2019-02-05 13:46 +0100 http://bitbucket.org/pypy/pypy/changeset/51b646896e26/ Log: Now implement decompressobj.copy at the language level. Something seems likely to be broken still because the contents of unused_data and unconsumed_tail aren't passed along to the copy... But we can come back and fix that in a minute, no tests are currently failing. diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -241,7 +241,14 @@ Wrapper around zlib's z_stream structure which provides convenient decompression functionality. """ - def __init__(self, space, wbits=rzlib.MAX_WBITS): + def __init__( + self, + space, + wbits=rzlib.MAX_WBITS, + stream=None, + unused_data="", + unconsumed_tail="", + ): """ Initialize a new decompression object. @@ -251,14 +258,18 @@ inflateInit2. """ ZLibObject.__init__(self, space) - self.unused_data = '' - self.unconsumed_tail = '' - try: - self.stream = rzlib.inflateInit(wbits) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") + + if stream is None: + try: + stream = rzlib.inflateInit(wbits) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + self.space = space + self.stream = stream + self.unused_data = unused_data + self.unconsumed_tail = unconsumed_tail self.register_finalizer(space) def _finalize_(self): @@ -305,6 +316,19 @@ self._save_unconsumed_input(data, finished, unused_len) return space.newbytes(string) + def copy(self, space): + try: + copied = rzlib.inflateCopy(self.stream) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + + return Decompress( + space=self.space, + stream=copied, + # unused_data=self.unused_data, + # unconsumed_tail=self.unconsumed_tail, + ) + def flush(self, space, w_length=None): """ flush( [length] ) -- This is kept for backward compatibility, @@ -345,6 +369,7 @@ Decompress.typedef = TypeDef( 'Decompress', __new__ = interp2app(Decompress___new__), + copy = interp2app(Decompress.copy), decompress = interp2app(Decompress.decompress), flush = interp2app(Decompress.flush), unused_data = interp_attrproperty('unused_data', Decompress, wrapfn="newbytes"), diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -8,8 +8,10 @@ except ImportError: import py; py.test.skip("no zlib module on this host Python") +from pypy.interpreter.gateway import interp2app try: from pypy.module.zlib import interp_zlib + from rpython.rlib import rzlib except ImportError: import py; py.test.skip("no zlib C library on this machine") @@ -38,6 +40,22 @@ cls.w_expanded = cls.space.wrap(expanded) cls.w_compressed = cls.space.wrap(zlib.compress(expanded)) + def intentionally_break_a_z_stream(space, w_zobj): + """ + Intentionally break the z_stream associated with a + compressobj or decompressobj in a way that causes their copy + methods to raise RZlibErrors. + """ + from rpython.rtyper.lltypesystem import rffi, lltype + w_zobj.stream.c_zalloc = rffi.cast( + lltype.typeOf(w_zobj.stream.c_zalloc), + rzlib.Z_NULL, + ) + + cls.w_intentionally_break_a_z_stream = cls.space.wrap( + interp2app(intentionally_break_a_z_stream), + ) + def test_error(self): """ zlib.error should be an exception class. @@ -276,3 +294,20 @@ assert dco.flush(1) == input1[1:] assert dco.unused_data == b'' assert dco.unconsumed_tail == b'' + + def test_decompress_copy(self): + decompressor = self.zlib.decompressobj() + d1 = decompressor.decompress(self.compressed[:10]) + assert d1 + + copied = decompressor.copy() + + from_copy = copied.decompress(self.compressed[10:]) + from_decompressor = decompressor.decompress(self.compressed[10:]) + + assert (d1 + from_copy) == (d1 + from_decompressor) + + def test_unsuccessful_decompress_copy(self): + decompressor = self.zlib.decompressobj() + self.intentionally_break_a_z_stream(zobj=decompressor) + raises(self.zlib.error, decompressor.copy) From pypy.commits at gmail.com Tue Feb 5 08:56:00 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Tue, 05 Feb 2019 05:56:00 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying: Acquire the lock around the Decompressor before copying. Message-ID: <5c5995f0.1c69fb81.a6b08.01cb@mx.google.com> Author: Julian Berman Branch: zlib-copying Changeset: r95836:7d112d29d885 Date: 2019-02-05 13:59 +0100 http://bitbucket.org/pypy/pypy/changeset/7d112d29d885/ Log: Acquire the lock around the Decompressor before copying. Also handles copying already-flushed decompressobjs. Basically the same as what happens in other methods. diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -318,7 +318,14 @@ def copy(self, space): try: - copied = rzlib.inflateCopy(self.stream) + self.lock() + try: + if not self.stream: + raise zlib_error(space, + "decompressor object already flushed") + copied = rzlib.inflateCopy(self.stream) + finally: + self.unlock() except rzlib.RZlibError as e: raise zlib_error(space, e.msg) From pypy.commits at gmail.com Tue Feb 5 08:56:02 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Tue, 05 Feb 2019 05:56:02 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying: And now the same for compressobj... Message-ID: <5c5995f2.1c69fb81.1e707.f55e@mx.google.com> Author: Julian Berman Branch: zlib-copying Changeset: r95837:9b6800b03333 Date: 2019-02-05 14:12 +0100 http://bitbucket.org/pypy/pypy/changeset/9b6800b03333/ Log: And now the same for compressobj... diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -138,15 +138,18 @@ method=rzlib.Z_DEFLATED, # \ wbits=rzlib.MAX_WBITS, # \ undocumented memLevel=rzlib.DEF_MEM_LEVEL, # / parameters - strategy=rzlib.Z_DEFAULT_STRATEGY): # / + strategy=rzlib.Z_DEFAULT_STRATEGY, # / + stream=None): ZLibObject.__init__(self, space) - try: - self.stream = rzlib.deflateInit(level, method, wbits, - memLevel, strategy) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") + if stream is None: + try: + stream = rzlib.deflateInit(level, method, wbits, + memLevel, strategy) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + self.stream = stream self.register_finalizer(space) def _finalize_(self): @@ -178,6 +181,17 @@ raise zlib_error(space, e.msg) return space.newbytes(result) + def copy(self, space): + try: + self.lock() + try: + copied = rzlib.deflateCopy(self.stream) + finally: + self.unlock() + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + return Compress(space=space, stream=copied) + @unwrap_spec(mode="c_int") def flush(self, space, mode=rzlib.Z_FINISH): """ @@ -228,6 +242,7 @@ Compress.typedef = TypeDef( 'Compress', __new__ = interp2app(Compress___new__), + copy = interp2app(Compress.copy), compress = interp2app(Compress.compress), flush = interp2app(Compress.flush), __doc__ = """compressobj([level]) -- Return a compressor object. @@ -266,7 +281,6 @@ raise zlib_error(space, e.msg) except ValueError: raise oefmt(space.w_ValueError, "Invalid initialization option") - self.space = space self.stream = stream self.unused_data = unused_data self.unconsumed_tail = unconsumed_tail @@ -330,7 +344,7 @@ raise zlib_error(space, e.msg) return Decompress( - space=self.space, + space=space, stream=copied, # unused_data=self.unused_data, # unconsumed_tail=self.unconsumed_tail, diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -311,3 +311,20 @@ decompressor = self.zlib.decompressobj() self.intentionally_break_a_z_stream(zobj=decompressor) raises(self.zlib.error, decompressor.copy) + + def test_compress_copy(self): + compressor = self.zlib.compressobj() + d1 = compressor.compress(self.expanded[:10]) + assert d1 + + copied = compressor.copy() + + from_copy = copied.compress(self.expanded[10:]) + from_compressor = compressor.compress(self.expanded[10:]) + + assert (d1 + from_copy) == (d1 + from_compressor) + + def test_unsuccessful_compress_copy(self): + compressor = self.zlib.compressobj() + self.intentionally_break_a_z_stream(zobj=compressor) + raises(self.zlib.error, compressor.copy) From pypy.commits at gmail.com Tue Feb 5 08:56:03 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Tue, 05 Feb 2019 05:56:03 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying: Docstrings... Message-ID: <5c5995f3.1c69fb81.86f50.ad92@mx.google.com> Author: Julian Berman Branch: zlib-copying Changeset: r95838:17161dab8936 Date: 2019-02-05 14:14 +0100 http://bitbucket.org/pypy/pypy/changeset/17161dab8936/ Log: Docstrings... diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -182,6 +182,9 @@ return space.newbytes(result) def copy(self, space): + """ + copy() -- Return a copy of the compression object. + """ try: self.lock() try: @@ -331,6 +334,9 @@ return space.newbytes(string) def copy(self, space): + """ + copy() -- Return a copy of the decompression object. + """ try: self.lock() try: From pypy.commits at gmail.com Tue Feb 5 08:56:05 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Tue, 05 Feb 2019 05:56:05 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying: Make sure we pass unused_data and unconsumed_tail along. Message-ID: <5c5995f5.1c69fb81.ae074.36eb@mx.google.com> Author: Julian Berman Branch: zlib-copying Changeset: r95839:9709a7461303 Date: 2019-02-05 14:49 +0100 http://bitbucket.org/pypy/pypy/changeset/9709a7461303/ Log: Make sure we pass unused_data and unconsumed_tail along. diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -352,8 +352,8 @@ return Decompress( space=space, stream=copied, - # unused_data=self.unused_data, - # unconsumed_tail=self.unconsumed_tail, + unused_data=self.unused_data, + unconsumed_tail=self.unconsumed_tail, ) def flush(self, space, w_length=None): diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -312,6 +312,23 @@ self.intentionally_break_a_z_stream(zobj=decompressor) raises(self.zlib.error, decompressor.copy) + def test_decompress_copy_carries_along_state(self): + """ + Decompressor.unused_data and unconsumed_tail are carried along when a + copy is done. + """ + decompressor = self.zlib.decompressobj() + decompressor.decompress(self.compressed, max_length=5) + unconsumed_tail = decompressor.unconsumed_tail + assert unconsumed_tail + assert decompressor.copy().unconsumed_tail == unconsumed_tail + + decompressor.decompress(decompressor.unconsumed_tail) + decompressor.decompress("xxx") + unused_data = decompressor.unused_data + assert unused_data + assert decompressor.copy().unused_data == unused_data + def test_compress_copy(self): compressor = self.zlib.compressobj() d1 = compressor.compress(self.expanded[:10]) From pypy.commits at gmail.com Tue Feb 5 09:28:01 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Tue, 05 Feb 2019 06:28:01 -0800 (PST) Subject: [pypy-commit] pypy default: Add in the missing .copy methods for zlib (de)compression. Message-ID: <5c599d71.1c69fb81.995cb.276f@mx.google.com> Author: Julian Berman Branch: Changeset: r95840:e0997a28d6e8 Date: 2019-02-05 15:12 +0100 http://bitbucket.org/pypy/pypy/changeset/e0997a28d6e8/ Log: Add in the missing .copy methods for zlib (de)compression. diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -138,15 +138,18 @@ method=rzlib.Z_DEFLATED, # \ wbits=rzlib.MAX_WBITS, # \ undocumented memLevel=rzlib.DEF_MEM_LEVEL, # / parameters - strategy=rzlib.Z_DEFAULT_STRATEGY): # / + strategy=rzlib.Z_DEFAULT_STRATEGY, # / + stream=None): ZLibObject.__init__(self, space) - try: - self.stream = rzlib.deflateInit(level, method, wbits, - memLevel, strategy) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") + if stream is None: + try: + stream = rzlib.deflateInit(level, method, wbits, + memLevel, strategy) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + self.stream = stream self.register_finalizer(space) def _finalize_(self): @@ -178,6 +181,20 @@ raise zlib_error(space, e.msg) return space.newbytes(result) + def copy(self, space): + """ + copy() -- Return a copy of the compression object. + """ + try: + self.lock() + try: + copied = rzlib.deflateCopy(self.stream) + finally: + self.unlock() + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + return Compress(space=space, stream=copied) + @unwrap_spec(mode="c_int") def flush(self, space, mode=rzlib.Z_FINISH): """ @@ -228,6 +245,7 @@ Compress.typedef = TypeDef( 'Compress', __new__ = interp2app(Compress___new__), + copy = interp2app(Compress.copy), compress = interp2app(Compress.compress), flush = interp2app(Compress.flush), __doc__ = """compressobj([level]) -- Return a compressor object. @@ -241,7 +259,14 @@ Wrapper around zlib's z_stream structure which provides convenient decompression functionality. """ - def __init__(self, space, wbits=rzlib.MAX_WBITS): + def __init__( + self, + space, + wbits=rzlib.MAX_WBITS, + stream=None, + unused_data="", + unconsumed_tail="", + ): """ Initialize a new decompression object. @@ -251,14 +276,17 @@ inflateInit2. """ ZLibObject.__init__(self, space) - self.unused_data = '' - self.unconsumed_tail = '' - try: - self.stream = rzlib.inflateInit(wbits) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") + + if stream is None: + try: + stream = rzlib.inflateInit(wbits) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + self.stream = stream + self.unused_data = unused_data + self.unconsumed_tail = unconsumed_tail self.register_finalizer(space) def _finalize_(self): @@ -305,6 +333,29 @@ self._save_unconsumed_input(data, finished, unused_len) return space.newbytes(string) + def copy(self, space): + """ + copy() -- Return a copy of the decompression object. + """ + try: + self.lock() + try: + if not self.stream: + raise zlib_error(space, + "decompressor object already flushed") + copied = rzlib.inflateCopy(self.stream) + finally: + self.unlock() + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + + return Decompress( + space=space, + stream=copied, + unused_data=self.unused_data, + unconsumed_tail=self.unconsumed_tail, + ) + def flush(self, space, w_length=None): """ flush( [length] ) -- This is kept for backward compatibility, @@ -345,6 +396,7 @@ Decompress.typedef = TypeDef( 'Decompress', __new__ = interp2app(Decompress___new__), + copy = interp2app(Decompress.copy), decompress = interp2app(Decompress.decompress), flush = interp2app(Decompress.flush), unused_data = interp_attrproperty('unused_data', Decompress, wrapfn="newbytes"), diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -8,8 +8,10 @@ except ImportError: import py; py.test.skip("no zlib module on this host Python") +from pypy.interpreter.gateway import interp2app try: from pypy.module.zlib import interp_zlib + from rpython.rlib import rzlib except ImportError: import py; py.test.skip("no zlib C library on this machine") @@ -38,6 +40,22 @@ cls.w_expanded = cls.space.wrap(expanded) cls.w_compressed = cls.space.wrap(zlib.compress(expanded)) + def intentionally_break_a_z_stream(space, w_zobj): + """ + Intentionally break the z_stream associated with a + compressobj or decompressobj in a way that causes their copy + methods to raise RZlibErrors. + """ + from rpython.rtyper.lltypesystem import rffi, lltype + w_zobj.stream.c_zalloc = rffi.cast( + lltype.typeOf(w_zobj.stream.c_zalloc), + rzlib.Z_NULL, + ) + + cls.w_intentionally_break_a_z_stream = cls.space.wrap( + interp2app(intentionally_break_a_z_stream), + ) + def test_error(self): """ zlib.error should be an exception class. @@ -276,3 +294,54 @@ assert dco.flush(1) == input1[1:] assert dco.unused_data == b'' assert dco.unconsumed_tail == b'' + + def test_decompress_copy(self): + decompressor = self.zlib.decompressobj() + d1 = decompressor.decompress(self.compressed[:10]) + assert d1 + + copied = decompressor.copy() + + from_copy = copied.decompress(self.compressed[10:]) + from_decompressor = decompressor.decompress(self.compressed[10:]) + + assert (d1 + from_copy) == (d1 + from_decompressor) + + def test_unsuccessful_decompress_copy(self): + decompressor = self.zlib.decompressobj() + self.intentionally_break_a_z_stream(zobj=decompressor) + raises(self.zlib.error, decompressor.copy) + + def test_decompress_copy_carries_along_state(self): + """ + Decompressor.unused_data and unconsumed_tail are carried along when a + copy is done. + """ + decompressor = self.zlib.decompressobj() + decompressor.decompress(self.compressed, max_length=5) + unconsumed_tail = decompressor.unconsumed_tail + assert unconsumed_tail + assert decompressor.copy().unconsumed_tail == unconsumed_tail + + decompressor.decompress(decompressor.unconsumed_tail) + decompressor.decompress("xxx") + unused_data = decompressor.unused_data + assert unused_data + assert decompressor.copy().unused_data == unused_data + + def test_compress_copy(self): + compressor = self.zlib.compressobj() + d1 = compressor.compress(self.expanded[:10]) + assert d1 + + copied = compressor.copy() + + from_copy = copied.compress(self.expanded[10:]) + from_compressor = compressor.compress(self.expanded[10:]) + + assert (d1 + from_copy) == (d1 + from_compressor) + + def test_unsuccessful_compress_copy(self): + compressor = self.zlib.compressobj() + self.intentionally_break_a_z_stream(zobj=compressor) + raises(self.zlib.error, compressor.copy) diff --git a/rpython/rlib/rzlib.py b/rpython/rlib/rzlib.py --- a/rpython/rlib/rzlib.py +++ b/rpython/rlib/rzlib.py @@ -35,6 +35,7 @@ MAX_WBITS MAX_MEM_LEVEL Z_BEST_SPEED Z_BEST_COMPRESSION Z_DEFAULT_COMPRESSION Z_FILTERED Z_HUFFMAN_ONLY Z_DEFAULT_STRATEGY Z_NEED_DICT + Z_NULL '''.split() class SimpleCConfig: @@ -141,6 +142,7 @@ rffi.INT) _deflate = zlib_external('deflate', [z_stream_p, rffi.INT], rffi.INT) +_deflateCopy = zlib_external('deflateCopy', [z_stream_p, z_stream_p], rffi.INT) _deflateEnd = zlib_external('deflateEnd', [z_stream_p], rffi.INT, releasegil=False) @@ -160,6 +162,7 @@ rffi.INT) _inflate = zlib_external('inflate', [z_stream_p, rffi.INT], rffi.INT) +_inflateCopy = zlib_external('inflateCopy', [z_stream_p, z_stream_p], rffi.INT) _inflateEnd = zlib_external('inflateEnd', [z_stream_p], rffi.INT, releasegil=False) @@ -290,6 +293,19 @@ lltype.free(stream, flavor='raw') +def deflateCopy(source): + """ + Allocate and return an independent copy of the provided stream object. + """ + dest = deflateInit() + err = _deflateCopy(dest, source) + if err != Z_OK: + deflateEnd(dest) + raise RZlibError.fromstream(source, err, + "while copying compression object") + return dest + + def deflateEnd(stream): """ Free the resources associated with the deflate stream. @@ -330,6 +346,19 @@ lltype.free(stream, flavor='raw') +def inflateCopy(source): + """ + Allocate and return an independent copy of the provided stream object. + """ + dest = inflateInit() + err = _inflateCopy(dest, source) + if err != Z_OK: + inflateEnd(dest) + raise RZlibError.fromstream(source, err, + "while copying decompression object") + return dest + + def inflateEnd(stream): """ Free the resources associated with the inflate stream. diff --git a/rpython/rlib/test/test_rzlib.py b/rpython/rlib/test/test_rzlib.py --- a/rpython/rlib/test/test_rzlib.py +++ b/rpython/rlib/test/test_rzlib.py @@ -246,6 +246,108 @@ rzlib.deflateEnd(stream) +def test_compress_copy(): + """ + inflateCopy produces an independent copy of a stream. + """ + + stream = rzlib.deflateInit() + + bytes1 = rzlib.compress(stream, expanded[:10]) + assert bytes1 + + copied = rzlib.deflateCopy(stream) + + bytes_stream = rzlib.compress( + stream, + expanded[10:], + rzlib.Z_FINISH, + ) + assert bytes1 + bytes_stream == compressed + rzlib.deflateEnd(stream) + + bytes_copy = rzlib.compress( + copied, + expanded[10:], + rzlib.Z_FINISH, + ) + rzlib.deflateEnd(copied) + assert bytes1 + bytes_copy == compressed + + +def test_unsuccessful_compress_copy(): + """ + Errors during unsuccesful deflateCopy operations raise RZlibErrors. + """ + stream = rzlib.deflateInit() + + # From zlib.h: + # + # "deflateCopy returns [...] Z_STREAM_ERROR if the source stream + # state was inconsistent (such as zalloc being Z_NULL)" + from rpython.rtyper.lltypesystem import rffi, lltype + stream.c_zalloc = rffi.cast(lltype.typeOf(stream.c_zalloc), rzlib.Z_NULL) + + exc = py.test.raises(rzlib.RZlibError, rzlib.deflateCopy, stream) + msg = "Error -2 while copying compression object: inconsistent stream state" + assert str(exc.value) == msg + rzlib.deflateEnd(stream) + + +def test_decompress_copy(): + """ + inflateCopy produces an independent copy of a stream. + """ + + stream = rzlib.inflateInit() + + bytes1, finished1, unused1 = rzlib.decompress(stream, compressed[:10]) + assert bytes1 + assert finished1 is False + + copied = rzlib.inflateCopy(stream) + + bytes_stream, finished_stream, unused_stream = rzlib.decompress( + stream, + compressed[10:], + rzlib.Z_FINISH, + ) + assert bytes1 + bytes_stream == expanded + assert finished_stream is True + assert unused1 == 0 + assert unused_stream == 0 + rzlib.inflateEnd(stream) + + bytes_copy, finished_copy, unused_copy = rzlib.decompress( + copied, + compressed[10:], + rzlib.Z_FINISH, + ) + rzlib.inflateEnd(copied) + assert bytes1 + bytes_copy == expanded + assert finished_copy is True + assert unused_copy == 0 + + +def test_unsuccessful_decompress_copy(): + """ + Errors during unsuccesful inflateCopy operations raise RZlibErrors. + """ + stream = rzlib.inflateInit() + + # From zlib.h: + # + # "inflateCopy returns [...] Z_STREAM_ERROR if the source stream + # state was inconsistent (such as zalloc being Z_NULL)" + from rpython.rtyper.lltypesystem import rffi, lltype + stream.c_zalloc = rffi.cast(lltype.typeOf(stream.c_zalloc), rzlib.Z_NULL) + + exc = py.test.raises(rzlib.RZlibError, rzlib.inflateCopy, stream) + msg = "Error -2 while copying decompression object: inconsistent stream state" + assert str(exc.value) == msg + rzlib.inflateEnd(stream) + + def test_cornercases(): """ Test degenerate arguments. From pypy.commits at gmail.com Tue Feb 5 10:10:24 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 05 Feb 2019 07:10:24 -0800 (PST) Subject: [pypy-commit] pypy windowsinstaller: expand readme Message-ID: <5c59a760.1c69fb81.9776f.51bf@mx.google.com> Author: Matti Picus Branch: windowsinstaller Changeset: r95841:d3a8e6163133 Date: 2019-02-05 17:09 +0200 http://bitbucket.org/pypy/pypy/changeset/d3a8e6163133/ Log: expand readme diff --git a/pypy/tool/release/windowsinstaller/README b/pypy/tool/release/windowsinstaller/README --- a/pypy/tool/release/windowsinstaller/README +++ b/pypy/tool/release/windowsinstaller/README @@ -1,8 +1,15 @@ Prerequisites: - Visual Studio 2017 - Wix 3.11.1 - Wix VSExtension for VS 2017 - + Visual Studio 2017 or at least the compilers and desktop cevelopment +with c++ + .Net 3.5.1 + Wix 3.11.1 http://wixtoolset.org/releases/ + Wix VSExtension for VS 2017 +https://marketplace.visualstudio.com/items?itemName=RobMensching.WixToolsetVisualStudio2017Extension + Windows 10 SDK (10.0.17134.0) + Steps: - 1. Translate the interpretter from the \pypy\goal folder. - 2. Run buildrelease.bat from the visual studio command line. \ No newline at end of file + 1. Translate the interpretter from the \pypy\goal folder. + 2. Remove all __pycache__ directories (maybe do this in the script?) + 3. Run buildrelease.bat from the visual studio command line. + +Now you should have .\PCbuild\win32\en-us\pypy-3.5.3.6609.exe From pypy.commits at gmail.com Tue Feb 5 10:28:49 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Tue, 05 Feb 2019 07:28:49 -0800 (PST) Subject: [pypy-commit] pypy default: Add the whatsnew snippet. Message-ID: <5c59abb1.1c69fb81.4509f.4749@mx.google.com> Author: Julian Berman Branch: Changeset: r95842:fab87641b3e5 Date: 2019-02-05 16:28 +0100 http://bitbucket.org/pypy/pypy/changeset/fab87641b3e5/ Log: Add the whatsnew snippet. 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 @@ -5,3 +5,7 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f +.. branch: zlib-copying + +The zlib module's compressobj and decompressobj now expose copy methods +as they do on CPython. From pypy.commits at gmail.com Tue Feb 5 10:41:56 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 05 Feb 2019 07:41:56 -0800 (PST) Subject: [pypy-commit] pypy default: prefer dlls from externals/bin Message-ID: <5c59aec4.1c69fb81.b8c45.6e1d@mx.google.com> Author: Matti Picus Branch: Changeset: r95843:b24f0dd6cf2a Date: 2019-02-05 17:37 +0200 http://bitbucket.org/pypy/pypy/changeset/b24f0dd6cf2a/ Log: prefer dlls from externals/bin diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py --- a/pypy/tool/release/package.py +++ b/pypy/tool/release/package.py @@ -116,6 +116,8 @@ pypydir.ensure('include', dir=True) if sys.platform == 'win32': + os.environ['PATH'] = str(basedir.join('externals').join('bin')) + ';' + \ + os.environ.get('PATH', '') src,tgt = binaries[0] pypyw = src.new(purebasename=src.purebasename + 'w') if pypyw.exists(): From pypy.commits at gmail.com Tue Feb 5 10:53:02 2019 From: pypy.commits at gmail.com (ambv) Date: Tue, 05 Feb 2019 07:53:02 -0800 (PST) Subject: [pypy-commit] pypy py3.6: (cfbolz, ambv) Make _csv.Dialect unpicklable like on CPython Message-ID: <5c59b15e.1c69fb81.406ec.3419@mx.google.com> Author: Łukasz Langa Branch: py3.6 Changeset: r95844:ac6ace011b43 Date: 2019-02-05 16:50 +0100 http://bitbucket.org/pypy/pypy/changeset/ac6ace011b43/ Log: (cfbolz,ambv) Make _csv.Dialect unpicklable like on CPython diff --git a/pypy/module/_csv/interp_csv.py b/pypy/module/_csv/interp_csv.py --- a/pypy/module/_csv/interp_csv.py +++ b/pypy/module/_csv/interp_csv.py @@ -21,6 +21,10 @@ "strict", ] + def reduce_ex_w(self, space, w_protocol): + raise OperationError(space.w_TypeError, + space.newtext("can't pickle _csv.Dialect objects")) + def _fetch(space, w_dialect, name): return space.findattr(w_dialect, space.newtext(name)) @@ -167,6 +171,7 @@ W_Dialect.typedef = TypeDef( '_csv.Dialect', __new__ = interp2app(W_Dialect___new__), + __reduce_ex__ = interp2app(W_Dialect.reduce_ex_w), delimiter = interp_attrproperty('delimiter', W_Dialect, wrapfn='newunicode'), diff --git a/pypy/module/_csv/test/test_dialect.py b/pypy/module/_csv/test/test_dialect.py --- a/pypy/module/_csv/test/test_dialect.py +++ b/pypy/module/_csv/test/test_dialect.py @@ -126,3 +126,9 @@ _csv.unregister_dialect('neverseen') lst = _csv.list_dialects() assert 'neverseen' not in lst + + def test_pickle_dialect(self): + import _csv + import copy + _csv.register_dialect('foo') + raises(TypeError, copy.copy, _csv.get_dialect('foo')) From pypy.commits at gmail.com Tue Feb 5 11:04:46 2019 From: pypy.commits at gmail.com (ambv) Date: Tue, 05 Feb 2019 08:04:46 -0800 (PST) Subject: [pypy-commit] pypy default: (cfbolz,ambv) Remove pure Python _csv Message-ID: <5c59b41e.1c69fb81.67ad.4201@mx.google.com> Author: Łukasz Langa Branch: Changeset: r95845:dacb9ecfea69 Date: 2019-02-05 16:56 +0100 http://bitbucket.org/pypy/pypy/changeset/dacb9ecfea69/ Log: (cfbolz,ambv) Remove pure Python _csv There is an RPython _csv in pypy/module/_csv that is used by default anyway. diff --git a/lib_pypy/_csv.py b/lib_pypy/_csv.py deleted file mode 100644 --- a/lib_pypy/_csv.py +++ /dev/null @@ -1,573 +0,0 @@ -"""CSV parsing and writing. - -This module provides classes that assist in the reading and writing -of Comma Separated Value (CSV) files, and implements the interface -described by PEP 305. Although many CSV files are simple to parse, -the format is not formally defined by a stable specification and -is subtle enough that parsing lines of a CSV file with something -like line.split(\",\") is bound to fail. The module supports three -basic APIs: reading, writing, and registration of dialects. - - -DIALECT REGISTRATION: - -Readers and writers support a dialect argument, which is a convenient -handle on a group of settings. When the dialect argument is a string, -it identifies one of the dialects previously registered with the module. -If it is a class or instance, the attributes of the argument are used as -the settings for the reader or writer: - - class excel: - delimiter = ',' - quotechar = '\"' - escapechar = None - doublequote = True - skipinitialspace = False - lineterminator = '\\r\\n' - quoting = QUOTE_MINIMAL - -SETTINGS: - - * quotechar - specifies a one-character string to use as the - quoting character. It defaults to '\"'. - * delimiter - specifies a one-character string to use as the - field separator. It defaults to ','. - * skipinitialspace - specifies how to interpret whitespace which - immediately follows a delimiter. It defaults to False, which - means that whitespace immediately following a delimiter is part - of the following field. - * lineterminator - specifies the character sequence which should - terminate rows. - * quoting - controls when quotes should be generated by the writer. - It can take on any of the following module constants: - - csv.QUOTE_MINIMAL means only when required, for example, when a - field contains either the quotechar or the delimiter - csv.QUOTE_ALL means that quotes are always placed around fields. - csv.QUOTE_NONNUMERIC means that quotes are always placed around - fields which do not parse as integers or floating point - numbers. - csv.QUOTE_NONE means that quotes are never placed around fields. - * escapechar - specifies a one-character string used to escape - the delimiter when quoting is set to QUOTE_NONE. - * doublequote - controls the handling of quotes inside fields. When - True, two consecutive quotes are interpreted as one during read, - and when writing, each quote character embedded in the data is - written as two quotes. -""" - -__version__ = "1.0" - -QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE = range(4) -_dialects = {} -_field_limit = 128 * 1024 # max parsed field size - -class Error(Exception): - pass - -class Dialect(object): - """CSV dialect - - The Dialect type records CSV parsing and generation options.""" - - __slots__ = ["_delimiter", "_doublequote", "_escapechar", - "_lineterminator", "_quotechar", "_quoting", - "_skipinitialspace", "_strict"] - - def __new__(cls, dialect, **kwargs): - - for name in kwargs: - if '_' + name not in Dialect.__slots__: - raise TypeError("unexpected keyword argument '%s'" % - (name,)) - - if dialect is not None: - if isinstance(dialect, basestring): - dialect = get_dialect(dialect) - - # Can we reuse this instance? - if (isinstance(dialect, Dialect) - and all(value is None for value in kwargs.itervalues())): - return dialect - - self = object.__new__(cls) - - - def set_char(x): - if x is None: - return None - if isinstance(x, str) and len(x) <= 1: - return x - raise TypeError("%r must be a 1-character string" % (name,)) - def set_str(x): - if isinstance(x, str): - return x - raise TypeError("%r must be a string" % (name,)) - def set_quoting(x): - if x in range(4): - return x - raise TypeError("bad 'quoting' value") - - attributes = {"delimiter": (',', set_char), - "doublequote": (True, bool), - "escapechar": (None, set_char), - "lineterminator": ("\r\n", set_str), - "quotechar": ('"', set_char), - "quoting": (QUOTE_MINIMAL, set_quoting), - "skipinitialspace": (False, bool), - "strict": (False, bool), - } - - # Copy attributes - notset = object() - for name in Dialect.__slots__: - name = name[1:] - value = notset - if name in kwargs: - value = kwargs[name] - elif dialect is not None: - value = getattr(dialect, name, notset) - - # mapping by name: (default, converter) - if value is notset: - value = attributes[name][0] - if name == 'quoting' and not self.quotechar: - value = QUOTE_NONE - else: - converter = attributes[name][1] - if converter: - value = converter(value) - - setattr(self, '_' + name, value) - - if not self.delimiter: - raise TypeError("delimiter must be set") - - if self.quoting != QUOTE_NONE and not self.quotechar: - raise TypeError("quotechar must be set if quoting enabled") - - if not self.lineterminator: - raise TypeError("lineterminator must be set") - - return self - - delimiter = property(lambda self: self._delimiter) - doublequote = property(lambda self: self._doublequote) - escapechar = property(lambda self: self._escapechar) - lineterminator = property(lambda self: self._lineterminator) - quotechar = property(lambda self: self._quotechar) - quoting = property(lambda self: self._quoting) - skipinitialspace = property(lambda self: self._skipinitialspace) - strict = property(lambda self: self._strict) - - -def _call_dialect(dialect_inst, kwargs): - return Dialect(dialect_inst, **kwargs) - -def register_dialect(name, dialect=None, **kwargs): - """Create a mapping from a string name to a dialect class. - dialect = csv.register_dialect(name, dialect)""" - if not isinstance(name, basestring): - raise TypeError("dialect name must be a string or unicode") - - dialect = _call_dialect(dialect, kwargs) - _dialects[name] = dialect - -def unregister_dialect(name): - """Delete the name/dialect mapping associated with a string name.\n - csv.unregister_dialect(name)""" - try: - del _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def get_dialect(name): - """Return the dialect instance associated with name. - dialect = csv.get_dialect(name)""" - try: - return _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def list_dialects(): - """Return a list of all know dialect names - names = csv.list_dialects()""" - return list(_dialects) - -class Reader(object): - """CSV reader - - Reader objects are responsible for reading and parsing tabular data - in CSV format.""" - - - (START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD, - IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD, - EAT_CRNL) = range(8) - - def __init__(self, iterator, dialect=None, **kwargs): - self.dialect = _call_dialect(dialect, kwargs) - self.input_iter = iter(iterator) - self.line_num = 0 - - self._parse_reset() - - def _parse_reset(self): - self.field = '' - self.fields = [] - self.state = self.START_RECORD - self.numeric_field = False - - def __iter__(self): - return self - - def next(self): - self._parse_reset() - while True: - try: - line = next(self.input_iter) - except StopIteration: - # End of input OR exception - if len(self.field) > 0: - raise Error("newline inside string") - raise - - self.line_num += 1 - - if '\0' in line: - raise Error("line contains NULL byte") - pos = 0 - while pos < len(line): - pos = self._parse_process_char(line, pos) - self._parse_eol() - - if self.state == self.START_RECORD: - break - - fields = self.fields - self.fields = [] - return fields - - def _parse_process_char(self, line, pos): - c = line[pos] - if self.state == self.IN_FIELD: - # in unquoted field - pos2 = pos - while True: - if c in '\n\r': - # end of line - return [fields] - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.EAT_CRNL - elif c == self.dialect.escapechar: - # possible escaped character - pos2 -= 1 - self.state = self.ESCAPED_CHAR - elif c == self.dialect.delimiter: - # save field - wait for new field - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.START_FIELD - else: - # normal character - save in field - pos2 += 1 - if pos2 < len(line): - c = line[pos2] - continue - break - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - 1 - - elif self.state == self.START_RECORD: - if c in '\n\r': - self.state = self.EAT_CRNL - else: - self.state = self.START_FIELD - # restart process - self._parse_process_char(line, pos) - - elif self.state == self.START_FIELD: - if c in '\n\r': - # save empty field - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # start quoted field - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.escapechar: - # possible escaped character - self.state = self.ESCAPED_CHAR - elif c == ' ' and self.dialect.skipinitialspace: - # ignore space at start of field - pass - elif c == self.dialect.delimiter: - # save empty field - self._parse_save_field() - else: - # begin new unquoted field - if self.dialect.quoting == QUOTE_NONNUMERIC: - self.numeric_field = True - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.IN_QUOTED_FIELD: - if c == self.dialect.escapechar: - # possible escape character - self.state = self.ESCAPE_IN_QUOTED_FIELD - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - if self.dialect.doublequote: - # doublequote; " represented by "" - self.state = self.QUOTE_IN_QUOTED_FIELD - else: - #end of quote part of field - self.state = self.IN_FIELD - else: - # normal character - save in field - self._parse_add_char(c) - - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # doublequote - seen a quote in a quoted field - if (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # save "" as " - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.delimiter: - # save field - wait for new field - self._parse_save_field() - self.state = self.START_FIELD - elif c in '\r\n': - # end of line - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif not self.dialect.strict: - self._parse_add_char(c) - self.state = self.IN_FIELD - else: - raise Error("'%c' expected after '%c'" % - (self.dialect.delimiter, self.dialect.quotechar)) - - elif self.state == self.EAT_CRNL: - if c not in '\r\n': - raise Error("new-line character seen in unquoted field - " - "do you need to open the file " - "in universal-newline mode?") - - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - return pos + 1 - - def _parse_eol(self): - if self.state == self.EAT_CRNL: - self.state = self.START_RECORD - elif self.state == self.START_RECORD: - # empty line - return [] - pass - elif self.state == self.IN_FIELD: - # in unquoted field - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.START_FIELD: - # save empty field - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char('\n') - self.state = self.IN_FIELD - elif self.state == self.IN_QUOTED_FIELD: - pass - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char('\n') - self.state = self.IN_QUOTED_FIELD - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - def _parse_save_field(self): - field, self.field = self.field, '' - if self.numeric_field: - self.numeric_field = False - field = float(field) - self.fields.append(field) - - def _parse_add_char(self, c): - if len(self.field) + len(c) > _field_limit: - raise Error("field larger than field limit (%d)" % (_field_limit)) - self.field += c - - -class Writer(object): - """CSV writer - - Writer objects are responsible for generating tabular data - in CSV format from sequence input.""" - - def __init__(self, file, dialect=None, **kwargs): - if not (hasattr(file, 'write') and callable(file.write)): - raise TypeError("argument 1 must have a 'write' method") - self.writeline = file.write - self.dialect = _call_dialect(dialect, kwargs) - - def _join_reset(self): - self.rec = [] - self.num_fields = 0 - - def _join_append(self, field, quoted, quote_empty): - dialect = self.dialect - # If this is not the first field we need a field separator - if self.num_fields > 0: - self.rec.append(dialect.delimiter) - - if dialect.quoting == QUOTE_NONE: - need_escape = tuple(dialect.lineterminator) + ( - dialect.escapechar, # escapechar always first - dialect.delimiter, dialect.quotechar) - - else: - for c in tuple(dialect.lineterminator) + ( - dialect.delimiter, dialect.escapechar): - if c and c in field: - quoted = True - - need_escape = () - if dialect.quotechar in field: - if dialect.doublequote: - field = field.replace(dialect.quotechar, - dialect.quotechar * 2) - quoted = True - else: - need_escape = (dialect.quotechar,) - - - for c in need_escape: - if c and c in field: - if not dialect.escapechar: - raise Error("need to escape, but no escapechar set") - field = field.replace(c, dialect.escapechar + c) - - # If field is empty check if it needs to be quoted - if field == '' and quote_empty: - if dialect.quoting == QUOTE_NONE: - raise Error("single empty field record must be quoted") - quoted = 1 - - if quoted: - field = dialect.quotechar + field + dialect.quotechar - - self.rec.append(field) - self.num_fields += 1 - - - - def writerow(self, row): - dialect = self.dialect - try: - rowlen = len(row) - except TypeError: - raise Error("sequence expected") - - # join all fields in internal buffer - self._join_reset() - - for field in row: - quoted = False - if dialect.quoting == QUOTE_NONNUMERIC: - try: - float(field) - except: - quoted = True - # This changed since 2.5: - # quoted = not isinstance(field, (int, long, float)) - elif dialect.quoting == QUOTE_ALL: - quoted = True - - if field is None: - value = "" - elif isinstance(field, float): - value = repr(field) - else: - value = str(field) - self._join_append(value, quoted, rowlen == 1) - - # add line terminator - self.rec.append(dialect.lineterminator) - - self.writeline(''.join(self.rec)) - - def writerows(self, rows): - for row in rows: - self.writerow(row) - -def reader(*args, **kwargs): - """ - csv_reader = reader(iterable [, dialect='excel'] - [optional keyword args]) - for row in csv_reader: - process(row) - - The "iterable" argument can be any object that returns a line - of input for each iteration, such as a file object or a list. The - optional \"dialect\" parameter is discussed below. The function - also accepts optional keyword arguments which override settings - provided by the dialect. - - The returned object is an iterator. Each iteration returns a row - of the CSV file (which can span multiple input lines)""" - - return Reader(*args, **kwargs) - -def writer(*args, **kwargs): - """ - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - for row in sequence: - csv_writer.writerow(row) - - [or] - - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - csv_writer.writerows(rows) - - The \"fileobj\" argument can be any object that supports the file API.""" - return Writer(*args, **kwargs) - - -undefined = object() -def field_size_limit(limit=undefined): - """Sets an upper limit on parsed fields. - csv.field_size_limit([limit]) - - Returns old limit. If limit is not given, no new limit is set and - the old limit is returned""" - - global _field_limit - old_limit = _field_limit - - if limit is not undefined: - if not isinstance(limit, (int, long)): - raise TypeError("int expected, got %s" % - (limit.__class__.__name__,)) - _field_limit = limit - - return old_limit From pypy.commits at gmail.com Tue Feb 5 11:04:48 2019 From: pypy.commits at gmail.com (ambv) Date: Tue, 05 Feb 2019 08:04:48 -0800 (PST) Subject: [pypy-commit] pypy default: (cfbolz,ambv) Remove pure Python pwd Message-ID: <5c59b420.1c69fb81.8cf7a.557c@mx.google.com> Author: Łukasz Langa Branch: Changeset: r95846:852477e9ba17 Date: 2019-02-05 17:00 +0100 http://bitbucket.org/pypy/pypy/changeset/852477e9ba17/ Log: (cfbolz,ambv) Remove pure Python pwd There is an RPython version in pypy/module/pwd/ which is used by default anyway. diff --git a/lib_pypy/pwd.py b/lib_pypy/pwd.py deleted file mode 100644 --- a/lib_pypy/pwd.py +++ /dev/null @@ -1,114 +0,0 @@ -# indirectly based on ctypes implementation: Victor Stinner, 2008-05-08 -""" -This module provides access to the Unix password database. -It is available on all Unix versions. - -Password database entries are reported as 7-tuples containing the following -items from the password database (see `'), in order: -pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell. -The uid and gid items are integers, all others are strings. An -exception is raised if the entry asked for cannot be found. -""" - -from _pwdgrp_cffi import ffi, lib -import _structseq -import thread -_lock = thread.allocate_lock() - -try: from __pypy__ import builtinify -except ImportError: builtinify = lambda f: f - - -class struct_passwd: - """ - pwd.struct_passwd: Results from getpw*() routines. - - This object may be accessed either as a tuple of - (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell) - or via the object attributes as named in the above tuple. - """ - __metaclass__ = _structseq.structseqtype - name = "pwd.struct_passwd" - - pw_name = _structseq.structseqfield(0) - pw_passwd = _structseq.structseqfield(1) - pw_uid = _structseq.structseqfield(2) - pw_gid = _structseq.structseqfield(3) - pw_gecos = _structseq.structseqfield(4) - pw_dir = _structseq.structseqfield(5) - pw_shell = _structseq.structseqfield(6) - - -def _mkpwent(pw): - return struct_passwd([ - ffi.string(pw.pw_name), - ffi.string(pw.pw_passwd), - pw.pw_uid, - pw.pw_gid, - ffi.string(pw.pw_gecos), - ffi.string(pw.pw_dir), - ffi.string(pw.pw_shell)]) - - at builtinify -def getpwuid(uid): - """ - getpwuid(uid) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given numeric user ID. - See pwd.__doc__ for more on password database entries. - """ - with _lock: - pw = lib.getpwuid(uid) - if not pw: - raise KeyError("getpwuid(): uid not found: %s" % uid) - return _mkpwent(pw) - - at builtinify -def getpwnam(name): - """ - getpwnam(name) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given user name. - See pwd.__doc__ for more on password database entries. - """ - if not isinstance(name, basestring): - raise TypeError("expected string") - name = str(name) - with _lock: - pw = lib.getpwnam(name) - if not pw: - raise KeyError("getpwname(): name not found: %s" % name) - return _mkpwent(pw) - - at builtinify -def getpwall(): - """ - getpwall() -> list_of_entries - Return a list of all available password database entries, in arbitrary order. - See pwd.__doc__ for more on password database entries. - """ - users = [] - with _lock: - lib.setpwent() - while True: - pw = lib.getpwent() - if not pw: - break - users.append(_mkpwent(pw)) - lib.endpwent() - return users - -__all__ = ('struct_passwd', 'getpwuid', 'getpwnam', 'getpwall') - -if __name__ == "__main__": -# Uncomment next line to test CPython implementation -# from pwd import getpwuid, getpwnam, getpwall - from os import getuid - uid = getuid() - pw = getpwuid(uid) - print("uid %s: %s" % (pw.pw_uid, pw)) - name = pw.pw_name - print("name %r: %s" % (name, getpwnam(name))) - print("All:") - for pw in getpwall(): - print(pw) From pypy.commits at gmail.com Tue Feb 5 12:19:51 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Tue, 05 Feb 2019 09:19:51 -0800 (PST) Subject: [pypy-commit] pypy 35windowsfixes: Fixed nan sign on windows Message-ID: <5c59c5b7.1c69fb81.720a.43d3@mx.google.com> Author: andrewjlawrence Branch: 35windowsfixes Changeset: r95848:25c28e745b5a Date: 2019-02-05 17:17 +0000 http://bitbucket.org/pypy/pypy/changeset/25c28e745b5a/ Log: Fixed nan sign on windows diff --git a/rpython/rtyper/lltypesystem/module/test/math_cases.py b/rpython/rtyper/lltypesystem/module/test/math_cases.py --- a/rpython/rtyper/lltypesystem/module/test/math_cases.py +++ b/rpython/rtyper/lltypesystem/module/test/math_cases.py @@ -59,9 +59,6 @@ ('copysign', (1.5, -0.0), -1.5), ('copysign', (1.5, INFINITY), 1.5), ('copysign', (1.5, -INFINITY), -1.5), - ] - if sys.platform != 'win32': # all NaNs seem to be negative there...? - IRREGCASES += [ ('copysign', (1.5, NAN), 1.5), ('copysign', (1.75, -NAN), -1.75), # special case for -NAN here ] diff --git a/rpython/translator/c/primitive.py b/rpython/translator/c/primitive.py --- a/rpython/translator/c/primitive.py +++ b/rpython/translator/c/primitive.py @@ -123,9 +123,9 @@ return '(-Py_HUGE_VAL)' elif math.isnan(value): if is_positive_nan(value): - return '(Py_HUGE_VAL/Py_HUGE_VAL)' + return '(_PyPy_dg_stdnan(0))' else: - return '(-(Py_HUGE_VAL/Py_HUGE_VAL))' + return '(_PyPy_dg_stdnan(1))' else: x = repr(value) assert not x.startswith('n') @@ -142,9 +142,9 @@ elif math.isnan(value): # XXX are these expressions ok? if is_positive_nan(value): - return '((float)(Py_HUGE_VAL/Py_HUGE_VAL))' + return '((float)(_PyPy_dg_stdnan(0)))' else: - return '(-(float)(Py_HUGE_VAL/Py_HUGE_VAL))' + return '((float)(_PyPy_dg_stdnan(1)))' else: return repr(value) + 'f' diff --git a/rpython/translator/c/src/support.c b/rpython/translator/c/src/support.c --- a/rpython/translator/c/src/support.c +++ b/rpython/translator/c/src/support.c @@ -9,6 +9,26 @@ #include /*** misc ***/ +#define Sign_bit 0x80000000 +#define NAN_WORD0 0x7ff80000 +#define NAN_WORD1 0 +#define PY_UINT32_T unsigned int + +#ifndef __BIG_ENDIAN__ +#define IEEE_8087 +#endif + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d + +typedef PY_UINT32_T ULong; +typedef union { double d; ULong L[2]; } U; RPY_EXTERN void RPyAssertFailed(const char* filename, long lineno, @@ -25,3 +45,20 @@ fprintf(stderr, "Invalid RPython operation (NULL ptr or bad array index)\n"); abort(); } + +/* Return a 'standard' NaN value. + There are exactly two quiet NaNs that don't arise by 'quieting' signaling + NaNs (see IEEE 754-2008, section 6.2.1). If sign == 0, return the one whose + sign bit is cleared. Otherwise, return the one whose sign bit is set. +*/ + +double +_PyPy_dg_stdnan(int sign) +{ + U rv; + word0(&rv) = NAN_WORD0; + word1(&rv) = NAN_WORD1; + if (sign) + word0(&rv) |= Sign_bit; + return dval(&rv); +} diff --git a/rpython/translator/c/src/support.h b/rpython/translator/c/src/support.h --- a/rpython/translator/c/src/support.h +++ b/rpython/translator/c/src/support.h @@ -38,6 +38,9 @@ RPY_EXTERN void RPyAbort(void); +RPY_EXTERN +double _PyPy_dg_stdnan(int sign); + #if defined(RPY_LL_ASSERT) || defined(RPY_SANDBOXED) /* obscure macros that can be used as expressions and lvalues to refer * to a field of a structure or an item in an array in a "safe" way -- From pypy.commits at gmail.com Tue Feb 5 16:26:38 2019 From: pypy.commits at gmail.com (Alexander Schremmer) Date: Tue, 05 Feb 2019 13:26:38 -0800 (PST) Subject: [pypy-commit] pypy math-improvements: (xoraxax, arigo) Implement correct ULLL and LLL constants for {, u}int128_t. Message-ID: <5c59ff8e.1c69fb81.9776f.eceb@mx.google.com> Author: Alexander Schremmer Branch: math-improvements Changeset: r95849:9c346098e834 Date: 2019-02-05 18:38 +0100 http://bitbucket.org/pypy/pypy/changeset/9c346098e834/ Log: (xoraxax, arigo) Implement correct ULLL and LLL constants for {,u}int128_t. diff --git a/rpython/translator/c/primitive.py b/rpython/translator/c/primitive.py --- a/rpython/translator/c/primitive.py +++ b/rpython/translator/c/primitive.py @@ -247,6 +247,22 @@ PrimitiveName[ll_type] = lambda value, db: name_str % value PrimitiveType[ll_type] = '%s @' % c_name +def define_shifted_primitive(ll_type, signed): + suffix = "LL" if signed else "ULL" + c_name = "__int128_t" if signed else "__uint128_t" + def convert(value, db): + left_part = value >> 64 + right_part = value - (left_part << 64) + if signed: + assert -2**63 <= left_part < 2**63 + else: + assert 0 <= left_part < 2**64 + assert 0 <= right_part < 2**64 + name_str = '((((%s) %d%s) << 64) | ((%s) %dULL))' % (c_name, left_part, suffix, c_name, right_part) + return name_str + PrimitiveName[ll_type] = convert + PrimitiveType[ll_type] = '%s @' % c_name + define_c_primitive(rffi.SIGNEDCHAR, 'signed char') define_c_primitive(rffi.UCHAR, 'unsigned char') define_c_primitive(rffi.SHORT, 'short') @@ -259,5 +275,5 @@ define_c_primitive(rffi.LONGLONG, 'long long', 'LL') define_c_primitive(rffi.ULONGLONG, 'unsigned long long', 'ULL') if SUPPORT_INT128: - define_c_primitive(rffi.__INT128_T, '__int128_t', 'LL') # Unless it's a 128bit platform, LL is the biggest - define_c_primitive(rffi.__UINT128_T, '__uint128_t', 'ULL') + define_shifted_primitive(rffi.__INT128_T, signed=True) + define_shifted_primitive(rffi.__UINT128_T, signed=False) diff --git a/rpython/translator/c/test/test_typed.py b/rpython/translator/c/test/test_typed.py --- a/rpython/translator/c/test/test_typed.py +++ b/rpython/translator/c/test/test_typed.py @@ -6,6 +6,7 @@ from rpython.rlib.rstackovf import StackOverflow from rpython.rlib.objectmodel import compute_hash, current_object_addr_as_int +from rpython.rlib.nonconst import NonConstant from rpython.rlib.rarithmetic import r_uint, r_ulonglong, r_longlong, intmask, longlongmask from rpython.rtyper.lltypesystem import rffi, lltype from rpython.translator.test import snippet @@ -969,6 +970,35 @@ res = f(217) assert res == 305123851 + def test_uint128_constant(self): + if not hasattr(rffi, '__UINT128_T'): + py.test.skip("no '__uint128_t'") + x = rffi.cast(getattr(rffi, '__UINT128_T'), 41) + x <<= 60 + x *= 7 + def func(n): + y = NonConstant(x) + y >>= 50 + return intmask(y) + f = self.getcompiled(func, [int]) + res = f(1) + assert res == ((41 << 60) * 7) >> 50 + + def test_int128_constant(self): + if not hasattr(rffi, '__INT128_T'): + py.test.skip("no '__int128_t'") + x = rffi.cast(getattr(rffi, '__INT128_T'), -41) + x <<= 60 + x *= 7 + x |= 2**63 + def func(n): + y = NonConstant(x) + y >>= 50 + return intmask(y) + f = self.getcompiled(func, [int]) + res = f(1) + assert res == (((-41 << 60) * 7) | 2**63) >> 50 + def test_bool_2(self): def func(n): x = rffi.cast(lltype.Bool, n) From pypy.commits at gmail.com Tue Feb 5 19:58:19 2019 From: pypy.commits at gmail.com (mjacob) Date: Tue, 05 Feb 2019 16:58:19 -0800 (PST) Subject: [pypy-commit] pypy default: Rename stream -> w_stream. Message-ID: <5c5a312b.1c69fb81.9f807.1c6d@mx.google.com> Author: Manuel Jacob Branch: Changeset: r95850:130e45a48a66 Date: 2019-02-06 01:17 +0100 http://bitbucket.org/pypy/pypy/changeset/130e45a48a66/ Log: Rename stream -> w_stream. diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -235,11 +235,11 @@ """ Create a new z_stream and call its initializer. """ - stream = space.allocate_instance(Compress, w_subtype) - stream = space.interp_w(Compress, stream) - Compress.__init__(stream, space, level, + w_stream = space.allocate_instance(Compress, w_subtype) + w_stream = space.interp_w(Compress, w_stream) + Compress.__init__(w_stream, space, level, method, wbits, memLevel, strategy) - return stream + return w_stream Compress.typedef = TypeDef( @@ -387,10 +387,10 @@ """ Create a new Decompress and call its initializer. """ - stream = space.allocate_instance(Decompress, w_subtype) - stream = space.interp_w(Decompress, stream) - Decompress.__init__(stream, space, wbits) - return stream + w_stream = space.allocate_instance(Decompress, w_subtype) + w_stream = space.interp_w(Decompress, w_stream) + Decompress.__init__(w_stream, space, wbits) + return w_stream Decompress.typedef = TypeDef( From pypy.commits at gmail.com Tue Feb 5 19:58:21 2019 From: pypy.commits at gmail.com (mjacob) Date: Tue, 05 Feb 2019 16:58:21 -0800 (PST) Subject: [pypy-commit] pypy default: Initialize rzlib stream in factory function. This should fix translation. Message-ID: <5c5a312d.1c69fb81.720a.c591@mx.google.com> Author: Manuel Jacob Branch: Changeset: r95851:c5c3ad13d149 Date: 2019-02-06 01:43 +0100 http://bitbucket.org/pypy/pypy/changeset/c5c3ad13d149/ Log: Initialize rzlib stream in factory function. This should fix translation. diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -134,21 +134,8 @@ Wrapper around zlib's z_stream structure which provides convenient compression functionality. """ - def __init__(self, space, level=rzlib.Z_DEFAULT_COMPRESSION, - method=rzlib.Z_DEFLATED, # \ - wbits=rzlib.MAX_WBITS, # \ undocumented - memLevel=rzlib.DEF_MEM_LEVEL, # / parameters - strategy=rzlib.Z_DEFAULT_STRATEGY, # / - stream=None): + def __init__(self, space, stream): ZLibObject.__init__(self, space) - if stream is None: - try: - stream = rzlib.deflateInit(level, method, wbits, - memLevel, strategy) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") self.stream = stream self.register_finalizer(space) @@ -237,8 +224,13 @@ """ w_stream = space.allocate_instance(Compress, w_subtype) w_stream = space.interp_w(Compress, w_stream) - Compress.__init__(w_stream, space, level, - method, wbits, memLevel, strategy) + try: + stream = rzlib.deflateInit(level, method, wbits, memLevel, strategy) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Compress.__init__(w_stream, space, stream) return w_stream @@ -259,14 +251,7 @@ Wrapper around zlib's z_stream structure which provides convenient decompression functionality. """ - def __init__( - self, - space, - wbits=rzlib.MAX_WBITS, - stream=None, - unused_data="", - unconsumed_tail="", - ): + def __init__(self, space, stream, unused_data, unconsumed_tail): """ Initialize a new decompression object. @@ -277,13 +262,6 @@ """ ZLibObject.__init__(self, space) - if stream is None: - try: - stream = rzlib.inflateInit(wbits) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") self.stream = stream self.unused_data = unused_data self.unconsumed_tail = unconsumed_tail @@ -389,7 +367,13 @@ """ w_stream = space.allocate_instance(Decompress, w_subtype) w_stream = space.interp_w(Decompress, w_stream) - Decompress.__init__(w_stream, space, wbits) + try: + stream = rzlib.inflateInit(wbits) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Decompress.__init__(w_stream, space, stream, '', '') return w_stream From pypy.commits at gmail.com Tue Feb 5 21:04:25 2019 From: pypy.commits at gmail.com (mjacob) Date: Tue, 05 Feb 2019 18:04:25 -0800 (PST) Subject: [pypy-commit] pypy default: Raise ValueError in nonmoving_raw_ptr_for_resizable_list() if running on RevDB. Message-ID: <5c5a40a9.1c69fb81.b26d2.a8b6@mx.google.com> Author: Manuel Jacob Branch: Changeset: r95852:1a274c144a73 Date: 2019-02-06 02:29 +0100 http://bitbucket.org/pypy/pypy/changeset/1a274c144a73/ Log: Raise ValueError in nonmoving_raw_ptr_for_resizable_list() if running on RevDB. On RevDB, raw and GC'ed memory must be separated. All callers are supposed to handle this ValueError and fall back to the unoptimized case. diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py --- a/rpython/rlib/rgc.py +++ b/rpython/rlib/rgc.py @@ -1406,6 +1406,11 @@ return _ResizableListSupportingRawPtr(lst) def nonmoving_raw_ptr_for_resizable_list(lst): + if must_split_gc_address_space(): + raise ValueError + return _nonmoving_raw_ptr_for_resizable_list(lst) + +def _nonmoving_raw_ptr_for_resizable_list(lst): assert isinstance(lst, _ResizableListSupportingRawPtr) return lst._nonmoving_raw_ptr_for_resizable_list() @@ -1450,7 +1455,7 @@ return hop.inputarg(hop.args_r[0], 0) class Entry(ExtRegistryEntry): - _about_ = nonmoving_raw_ptr_for_resizable_list + _about_ = _nonmoving_raw_ptr_for_resizable_list def compute_result_annotation(self, s_list): from rpython.rtyper.lltypesystem import lltype, rffi From pypy.commits at gmail.com Tue Feb 5 21:04:27 2019 From: pypy.commits at gmail.com (mjacob) Date: Tue, 05 Feb 2019 18:04:27 -0800 (PST) Subject: [pypy-commit] pypy py3.5: hg merge default Message-ID: <5c5a40ab.1c69fb81.1710f.e669@mx.google.com> Author: Manuel Jacob Branch: py3.5 Changeset: r95853:4a4b466ea459 Date: 2019-02-06 02:42 +0100 http://bitbucket.org/pypy/pypy/changeset/4a4b466ea459/ Log: hg merge default diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -113,22 +113,8 @@ Wrapper around zlib's z_stream structure which provides convenient compression functionality. """ - def __init__(self, space, level=rzlib.Z_DEFAULT_COMPRESSION, - method=rzlib.Z_DEFLATED, # \ - wbits=rzlib.MAX_WBITS, # \ undocumented - memLevel=rzlib.DEF_MEM_LEVEL, # / parameters - strategy=rzlib.Z_DEFAULT_STRATEGY, # / - zdict=None, - stream=None): + def __init__(self, space, stream): ZLibObject.__init__(self, space) - if stream is None: - try: - stream = rzlib.deflateInit(level, method, wbits, - memLevel, strategy, zdict=zdict) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") self.stream = stream self.register_finalizer(space) @@ -220,11 +206,17 @@ zdict = None else: zdict = space.charbuf_w(w_zdict) - stream = space.allocate_instance(Compress, w_subtype) - stream = space.interp_w(Compress, stream) - Compress.__init__(stream, space, level, - method, wbits, memLevel, strategy, zdict) - return stream + w_stream = space.allocate_instance(Compress, w_subtype) + w_stream = space.interp_w(Compress, w_stream) + try: + stream = rzlib.deflateInit(level, method, wbits, memLevel, strategy, + zdict=zdict) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Compress.__init__(w_stream, space, stream) + return w_stream Compress.typedef = TypeDef( @@ -244,15 +236,7 @@ Wrapper around zlib's z_stream structure which provides convenient decompression functionality. """ - def __init__( - self, - space, - wbits=rzlib.MAX_WBITS, - zdict=None, - stream=None, - unused_data="", - unconsumed_tail="", - ): + def __init__(self, space, stream, zdict, unused_data, unconsumed_tail): """ Initialize a new decompression object. @@ -262,13 +246,7 @@ inflateInit2. """ ZLibObject.__init__(self, space) - if stream is None: - try: - stream = rzlib.inflateInit(wbits, zdict=zdict) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") + self.stream = stream self.zdict = zdict self.unused_data = unused_data @@ -383,10 +361,16 @@ zdict = None else: zdict = space.charbuf_w(w_zdict) - stream = space.allocate_instance(Decompress, w_subtype) - stream = space.interp_w(Decompress, stream) - Decompress.__init__(stream, space, wbits, zdict) - return stream + w_stream = space.allocate_instance(Decompress, w_subtype) + w_stream = space.interp_w(Decompress, w_stream) + try: + stream = rzlib.inflateInit(wbits, zdict=zdict) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Decompress.__init__(w_stream, space, stream, zdict, '', '') + return w_stream def default_buffer_size(space): return space.newint(rzlib.OUTPUT_BUFFER_SIZE) diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py --- a/rpython/rlib/rgc.py +++ b/rpython/rlib/rgc.py @@ -1406,6 +1406,11 @@ return _ResizableListSupportingRawPtr(lst) def nonmoving_raw_ptr_for_resizable_list(lst): + if must_split_gc_address_space(): + raise ValueError + return _nonmoving_raw_ptr_for_resizable_list(lst) + +def _nonmoving_raw_ptr_for_resizable_list(lst): assert isinstance(lst, _ResizableListSupportingRawPtr) return lst._nonmoving_raw_ptr_for_resizable_list() @@ -1450,7 +1455,7 @@ return hop.inputarg(hop.args_r[0], 0) class Entry(ExtRegistryEntry): - _about_ = nonmoving_raw_ptr_for_resizable_list + _about_ = _nonmoving_raw_ptr_for_resizable_list def compute_result_annotation(self, s_list): from rpython.rtyper.lltypesystem import lltype, rffi From pypy.commits at gmail.com Wed Feb 6 03:53:51 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 06 Feb 2019 00:53:51 -0800 (PST) Subject: [pypy-commit] pypy default: typo in c5c3ad13d149 ? Message-ID: <5c5aa09f.1c69fb81.8cf7a.6be0@mx.google.com> Author: Matti Picus Branch: Changeset: r95854:236bea4de5e1 Date: 2019-02-06 09:53 +0100 http://bitbucket.org/pypy/pypy/changeset/236bea4de5e1/ Log: typo in c5c3ad13d149 ? diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -225,7 +225,7 @@ w_stream = space.allocate_instance(Compress, w_subtype) w_stream = space.interp_w(Compress, w_stream) try: - stream = rzlib.deflateInit(level, method, wbits, memLevel, strategy) + w_stream = rzlib.deflateInit(level, method, wbits, memLevel, strategy) except rzlib.RZlibError as e: raise zlib_error(space, e.msg) except ValueError: @@ -262,9 +262,9 @@ """ ZLibObject.__init__(self, space) - self.stream = stream self.unused_data = unused_data self.unconsumed_tail = unconsumed_tail + self.stream = stream self.register_finalizer(space) def _finalize_(self): @@ -368,7 +368,7 @@ w_stream = space.allocate_instance(Decompress, w_subtype) w_stream = space.interp_w(Decompress, w_stream) try: - stream = rzlib.inflateInit(wbits) + w_stream = rzlib.inflateInit(wbits) except rzlib.RZlibError as e: raise zlib_error(space, e.msg) except ValueError: From pypy.commits at gmail.com Wed Feb 6 04:17:18 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 06 Feb 2019 01:17:18 -0800 (PST) Subject: [pypy-commit] pypy default: Backed out changeset: 236bea4de5e1 Message-ID: <5c5aa61e.1c69fb81.d99e.0a0e@mx.google.com> Author: Matti Picus Branch: Changeset: r95855:3935d273ac91 Date: 2019-02-06 10:16 +0100 http://bitbucket.org/pypy/pypy/changeset/3935d273ac91/ Log: Backed out changeset: 236bea4de5e1 diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -225,7 +225,7 @@ w_stream = space.allocate_instance(Compress, w_subtype) w_stream = space.interp_w(Compress, w_stream) try: - w_stream = rzlib.deflateInit(level, method, wbits, memLevel, strategy) + stream = rzlib.deflateInit(level, method, wbits, memLevel, strategy) except rzlib.RZlibError as e: raise zlib_error(space, e.msg) except ValueError: @@ -262,9 +262,9 @@ """ ZLibObject.__init__(self, space) + self.stream = stream self.unused_data = unused_data self.unconsumed_tail = unconsumed_tail - self.stream = stream self.register_finalizer(space) def _finalize_(self): @@ -368,7 +368,7 @@ w_stream = space.allocate_instance(Decompress, w_subtype) w_stream = space.interp_w(Decompress, w_stream) try: - w_stream = rzlib.inflateInit(wbits) + stream = rzlib.inflateInit(wbits) except rzlib.RZlibError as e: raise zlib_error(space, e.msg) except ValueError: From pypy.commits at gmail.com Wed Feb 6 05:01:05 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 02:01:05 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying-redux: Add a test for copying flushed zlib compressobjs. Message-ID: <5c5ab061.1c69fb81.dd76f.90b6@mx.google.com> Author: Julian Berman Branch: zlib-copying-redux Changeset: r95856:bf5838a3edac Date: 2019-02-06 11:00 +0100 http://bitbucket.org/pypy/pypy/changeset/bf5838a3edac/ Log: Add a test for copying flushed zlib compressobjs. diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -307,7 +307,7 @@ assert (d1 + from_copy) == (d1 + from_decompressor) - def test_unsuccessful_decompress_copy(self): + def test_cannot_copy_decompressor_with_stream_in_inconsistent_state(self): decompressor = self.zlib.decompressobj() self.intentionally_break_a_z_stream(zobj=decompressor) raises(self.zlib.error, decompressor.copy) @@ -341,7 +341,12 @@ assert (d1 + from_copy) == (d1 + from_compressor) - def test_unsuccessful_compress_copy(self): + def test_cannot_copy_compressor_with_stream_in_inconsistent_state(self): compressor = self.zlib.compressobj() self.intentionally_break_a_z_stream(zobj=compressor) raises(self.zlib.error, compressor.copy) + + def test_cannot_copy_compressor_with_flushed_stream(self): + compressor = self.zlib.compressobj() + compressor.flush() + raises(ValueError, compressor.copy) From pypy.commits at gmail.com Wed Feb 6 05:02:23 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Wed, 06 Feb 2019 02:02:23 -0800 (PST) Subject: [pypy-commit] pypy default: Fixed nan sign on windows Message-ID: <5c5ab0af.1c69fb81.6599d.aeae@mx.google.com> Author: andrewjlawrence Branch: Changeset: r95857:f8548d5c059a Date: 2019-02-05 17:17 +0000 http://bitbucket.org/pypy/pypy/changeset/f8548d5c059a/ Log: Fixed nan sign on windows diff --git a/rpython/rtyper/lltypesystem/module/test/math_cases.py b/rpython/rtyper/lltypesystem/module/test/math_cases.py --- a/rpython/rtyper/lltypesystem/module/test/math_cases.py +++ b/rpython/rtyper/lltypesystem/module/test/math_cases.py @@ -59,9 +59,6 @@ ('copysign', (1.5, -0.0), -1.5), ('copysign', (1.5, INFINITY), 1.5), ('copysign', (1.5, -INFINITY), -1.5), - ] - if sys.platform != 'win32': # all NaNs seem to be negative there...? - IRREGCASES += [ ('copysign', (1.5, NAN), 1.5), ('copysign', (1.75, -NAN), -1.75), # special case for -NAN here ] diff --git a/rpython/translator/c/primitive.py b/rpython/translator/c/primitive.py --- a/rpython/translator/c/primitive.py +++ b/rpython/translator/c/primitive.py @@ -123,9 +123,9 @@ return '(-Py_HUGE_VAL)' elif math.isnan(value): if is_positive_nan(value): - return '(Py_HUGE_VAL/Py_HUGE_VAL)' + return '(_PyPy_dg_stdnan(0))' else: - return '(-(Py_HUGE_VAL/Py_HUGE_VAL))' + return '(_PyPy_dg_stdnan(1))' else: x = repr(value) assert not x.startswith('n') @@ -142,9 +142,9 @@ elif math.isnan(value): # XXX are these expressions ok? if is_positive_nan(value): - return '((float)(Py_HUGE_VAL/Py_HUGE_VAL))' + return '((float)(_PyPy_dg_stdnan(0)))' else: - return '(-(float)(Py_HUGE_VAL/Py_HUGE_VAL))' + return '((float)(_PyPy_dg_stdnan(1)))' else: return repr(value) + 'f' diff --git a/rpython/translator/c/src/support.c b/rpython/translator/c/src/support.c --- a/rpython/translator/c/src/support.c +++ b/rpython/translator/c/src/support.c @@ -9,6 +9,26 @@ #include /*** misc ***/ +#define Sign_bit 0x80000000 +#define NAN_WORD0 0x7ff80000 +#define NAN_WORD1 0 +#define PY_UINT32_T unsigned int + +#ifndef __BIG_ENDIAN__ +#define IEEE_8087 +#endif + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d + +typedef PY_UINT32_T ULong; +typedef union { double d; ULong L[2]; } U; RPY_EXTERN void RPyAssertFailed(const char* filename, long lineno, @@ -25,3 +45,20 @@ fprintf(stderr, "Invalid RPython operation (NULL ptr or bad array index)\n"); abort(); } + +/* Return a 'standard' NaN value. + There are exactly two quiet NaNs that don't arise by 'quieting' signaling + NaNs (see IEEE 754-2008, section 6.2.1). If sign == 0, return the one whose + sign bit is cleared. Otherwise, return the one whose sign bit is set. +*/ + +double +_PyPy_dg_stdnan(int sign) +{ + U rv; + word0(&rv) = NAN_WORD0; + word1(&rv) = NAN_WORD1; + if (sign) + word0(&rv) |= Sign_bit; + return dval(&rv); +} diff --git a/rpython/translator/c/src/support.h b/rpython/translator/c/src/support.h --- a/rpython/translator/c/src/support.h +++ b/rpython/translator/c/src/support.h @@ -38,6 +38,9 @@ RPY_EXTERN void RPyAbort(void); +RPY_EXTERN +double _PyPy_dg_stdnan(int sign); + #if defined(RPY_LL_ASSERT) || defined(RPY_SANDBOXED) /* obscure macros that can be used as expressions and lvalues to refer * to a field of a structure or an item in an array in a "safe" way -- From pypy.commits at gmail.com Wed Feb 6 05:03:56 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 02:03:56 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying-redux: Move our check for stream being nullptr to the right place. Message-ID: <5c5ab10c.1c69fb81.bd826.613d@mx.google.com> Author: Julian Berman Branch: zlib-copying-redux Changeset: r95858:6c3c1a0558e6 Date: 2019-02-06 11:03 +0100 http://bitbucket.org/pypy/pypy/changeset/6c3c1a0558e6/ Log: Move our check for stream being nullptr to the right place. It's compressobj that sets this to nullptr on flush, not decompressobj. diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -175,6 +175,11 @@ try: self.lock() try: + if not self.stream: + raise oefmt( + space.w_ValueError, + "Compressor was already flushed", + ) copied = rzlib.deflateCopy(self.stream) finally: self.unlock() @@ -318,9 +323,6 @@ try: self.lock() try: - if not self.stream: - raise zlib_error(space, - "decompressor object already flushed") copied = rzlib.inflateCopy(self.stream) finally: self.unlock() From pypy.commits at gmail.com Wed Feb 6 05:18:44 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 02:18:44 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying-redux: Skip the tests that need to rpython-munge when we run app-level. Message-ID: <5c5ab484.1c69fb81.eec18.fb84@mx.google.com> Author: Julian Berman Branch: zlib-copying-redux Changeset: r95859:ab1d3b141472 Date: 2019-02-06 11:18 +0100 http://bitbucket.org/pypy/pypy/changeset/ab1d3b141472/ Log: Skip the tests that need to rpython-munge when we run app-level. diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -308,6 +308,7 @@ assert (d1 + from_copy) == (d1 + from_decompressor) def test_cannot_copy_decompressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") decompressor = self.zlib.decompressobj() self.intentionally_break_a_z_stream(zobj=decompressor) raises(self.zlib.error, decompressor.copy) @@ -342,6 +343,7 @@ assert (d1 + from_copy) == (d1 + from_compressor) def test_cannot_copy_compressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") compressor = self.zlib.compressobj() self.intentionally_break_a_z_stream(zobj=compressor) raises(self.zlib.error, compressor.copy) From pypy.commits at gmail.com Wed Feb 6 05:35:35 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 02:35:35 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying-redux: Add the whatsnew snippet. Message-ID: <5c5ab877.1c69fb81.ff1e7.1d32@mx.google.com> Author: Julian Berman Branch: zlib-copying-redux Changeset: r95860:9b1847feb4f2 Date: 2019-02-06 11:34 +0100 http://bitbucket.org/pypy/pypy/changeset/9b1847feb4f2/ Log: Add the whatsnew snippet. 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 @@ -5,6 +5,10 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f +.. branch: zlib-copying-redux + +Fix calling copy on already-flushed compressobjs. + .. branch: zlib-copying The zlib module's compressobj and decompressobj now expose copy methods From pypy.commits at gmail.com Wed Feb 6 05:41:10 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 02:41:10 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying-redux: Close to-be-merged branch. Message-ID: <5c5ab9c6.1c69fb81.972a6.52a1@mx.google.com> Author: Julian Berman Branch: zlib-copying-redux Changeset: r95861:32412a37b57f Date: 2019-02-06 11:40 +0100 http://bitbucket.org/pypy/pypy/changeset/32412a37b57f/ Log: Close to-be-merged branch. From pypy.commits at gmail.com Wed Feb 6 06:02:33 2019 From: pypy.commits at gmail.com (ambv) Date: Wed, 06 Feb 2019 03:02:33 -0800 (PST) Subject: [pypy-commit] pypy default: [.hgignore] Add mypy_cache and vscode Message-ID: <5c5abec9.1c69fb81.e1ad4.13d4@mx.google.com> Author: Łukasz Langa Branch: Changeset: r95862:1a36a013597d Date: 2019-02-06 12:00 +0100 http://bitbucket.org/pypy/pypy/changeset/1a36a013597d/ Log: [.hgignore] Add mypy_cache and vscode diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -4,8 +4,10 @@ *~ .*.swp .idea +.mypy_cache .project .pydevproject +.vscode __pycache__ .cache/ From pypy.commits at gmail.com Wed Feb 6 06:02:35 2019 From: pypy.commits at gmail.com (ambv) Date: Wed, 06 Feb 2019 03:02:35 -0800 (PST) Subject: [pypy-commit] pypy py3.5: hg merge default Message-ID: <5c5abecb.1c69fb81.fac5b.b215@mx.google.com> Author: Łukasz Langa Branch: py3.5 Changeset: r95863:706d2757733b Date: 2019-02-06 12:01 +0100 http://bitbucket.org/pypy/pypy/changeset/706d2757733b/ Log: hg merge default diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -4,8 +4,10 @@ *~ .*.swp .idea +.mypy_cache .project .pydevproject +.vscode __pycache__ .venv .cache diff --git a/rpython/rtyper/lltypesystem/module/test/math_cases.py b/rpython/rtyper/lltypesystem/module/test/math_cases.py --- a/rpython/rtyper/lltypesystem/module/test/math_cases.py +++ b/rpython/rtyper/lltypesystem/module/test/math_cases.py @@ -59,9 +59,6 @@ ('copysign', (1.5, -0.0), -1.5), ('copysign', (1.5, INFINITY), 1.5), ('copysign', (1.5, -INFINITY), -1.5), - ] - if sys.platform != 'win32': # all NaNs seem to be negative there...? - IRREGCASES += [ ('copysign', (1.5, NAN), 1.5), ('copysign', (1.75, -NAN), -1.75), # special case for -NAN here ] diff --git a/rpython/translator/c/primitive.py b/rpython/translator/c/primitive.py --- a/rpython/translator/c/primitive.py +++ b/rpython/translator/c/primitive.py @@ -123,9 +123,9 @@ return '(-Py_HUGE_VAL)' elif math.isnan(value): if is_positive_nan(value): - return '(Py_HUGE_VAL/Py_HUGE_VAL)' + return '(_PyPy_dg_stdnan(0))' else: - return '(-(Py_HUGE_VAL/Py_HUGE_VAL))' + return '(_PyPy_dg_stdnan(1))' else: x = repr(value) assert not x.startswith('n') @@ -142,9 +142,9 @@ elif math.isnan(value): # XXX are these expressions ok? if is_positive_nan(value): - return '((float)(Py_HUGE_VAL/Py_HUGE_VAL))' + return '((float)(_PyPy_dg_stdnan(0)))' else: - return '(-(float)(Py_HUGE_VAL/Py_HUGE_VAL))' + return '((float)(_PyPy_dg_stdnan(1)))' else: return repr(value) + 'f' diff --git a/rpython/translator/c/src/support.c b/rpython/translator/c/src/support.c --- a/rpython/translator/c/src/support.c +++ b/rpython/translator/c/src/support.c @@ -9,6 +9,26 @@ #include /*** misc ***/ +#define Sign_bit 0x80000000 +#define NAN_WORD0 0x7ff80000 +#define NAN_WORD1 0 +#define PY_UINT32_T unsigned int + +#ifndef __BIG_ENDIAN__ +#define IEEE_8087 +#endif + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d + +typedef PY_UINT32_T ULong; +typedef union { double d; ULong L[2]; } U; RPY_EXTERN void RPyAssertFailed(const char* filename, long lineno, @@ -25,3 +45,20 @@ fprintf(stderr, "Invalid RPython operation (NULL ptr or bad array index)\n"); abort(); } + +/* Return a 'standard' NaN value. + There are exactly two quiet NaNs that don't arise by 'quieting' signaling + NaNs (see IEEE 754-2008, section 6.2.1). If sign == 0, return the one whose + sign bit is cleared. Otherwise, return the one whose sign bit is set. +*/ + +double +_PyPy_dg_stdnan(int sign) +{ + U rv; + word0(&rv) = NAN_WORD0; + word1(&rv) = NAN_WORD1; + if (sign) + word0(&rv) |= Sign_bit; + return dval(&rv); +} diff --git a/rpython/translator/c/src/support.h b/rpython/translator/c/src/support.h --- a/rpython/translator/c/src/support.h +++ b/rpython/translator/c/src/support.h @@ -38,6 +38,9 @@ RPY_EXTERN void RPyAbort(void); +RPY_EXTERN +double _PyPy_dg_stdnan(int sign); + #if defined(RPY_LL_ASSERT) || defined(RPY_SANDBOXED) /* obscure macros that can be used as expressions and lvalues to refer * to a field of a structure or an item in an array in a "safe" way -- From pypy.commits at gmail.com Wed Feb 6 06:02:37 2019 From: pypy.commits at gmail.com (ambv) Date: Wed, 06 Feb 2019 03:02:37 -0800 (PST) Subject: [pypy-commit] pypy py3.6: hg merge py3.5 Message-ID: <5c5abecd.1c69fb81.a26f6.6394@mx.google.com> Author: Łukasz Langa Branch: py3.6 Changeset: r95864:baff18bae4fb Date: 2019-02-06 12:01 +0100 http://bitbucket.org/pypy/pypy/changeset/baff18bae4fb/ Log: hg merge py3.5 diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -4,8 +4,10 @@ *~ .*.swp .idea +.mypy_cache .project .pydevproject +.vscode __pycache__ .venv .cache diff --git a/lib_pypy/_csv.py b/lib_pypy/_csv.py deleted file mode 100644 --- a/lib_pypy/_csv.py +++ /dev/null @@ -1,573 +0,0 @@ -"""CSV parsing and writing. - -This module provides classes that assist in the reading and writing -of Comma Separated Value (CSV) files, and implements the interface -described by PEP 305. Although many CSV files are simple to parse, -the format is not formally defined by a stable specification and -is subtle enough that parsing lines of a CSV file with something -like line.split(\",\") is bound to fail. The module supports three -basic APIs: reading, writing, and registration of dialects. - - -DIALECT REGISTRATION: - -Readers and writers support a dialect argument, which is a convenient -handle on a group of settings. When the dialect argument is a string, -it identifies one of the dialects previously registered with the module. -If it is a class or instance, the attributes of the argument are used as -the settings for the reader or writer: - - class excel: - delimiter = ',' - quotechar = '\"' - escapechar = None - doublequote = True - skipinitialspace = False - lineterminator = '\\r\\n' - quoting = QUOTE_MINIMAL - -SETTINGS: - - * quotechar - specifies a one-character string to use as the - quoting character. It defaults to '\"'. - * delimiter - specifies a one-character string to use as the - field separator. It defaults to ','. - * skipinitialspace - specifies how to interpret whitespace which - immediately follows a delimiter. It defaults to False, which - means that whitespace immediately following a delimiter is part - of the following field. - * lineterminator - specifies the character sequence which should - terminate rows. - * quoting - controls when quotes should be generated by the writer. - It can take on any of the following module constants: - - csv.QUOTE_MINIMAL means only when required, for example, when a - field contains either the quotechar or the delimiter - csv.QUOTE_ALL means that quotes are always placed around fields. - csv.QUOTE_NONNUMERIC means that quotes are always placed around - fields which do not parse as integers or floating point - numbers. - csv.QUOTE_NONE means that quotes are never placed around fields. - * escapechar - specifies a one-character string used to escape - the delimiter when quoting is set to QUOTE_NONE. - * doublequote - controls the handling of quotes inside fields. When - True, two consecutive quotes are interpreted as one during read, - and when writing, each quote character embedded in the data is - written as two quotes. -""" - -__version__ = "1.0" - -QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE = range(4) -_dialects = {} -_field_limit = 128 * 1024 # max parsed field size - -class Error(Exception): - pass - -class Dialect(object): - """CSV dialect - - The Dialect type records CSV parsing and generation options.""" - - __slots__ = ["_delimiter", "_doublequote", "_escapechar", - "_lineterminator", "_quotechar", "_quoting", - "_skipinitialspace", "_strict"] - - def __new__(cls, dialect, **kwargs): - - for name in kwargs: - if '_' + name not in Dialect.__slots__: - raise TypeError("unexpected keyword argument '%s'" % - (name,)) - - if dialect is not None: - if isinstance(dialect, str): - dialect = get_dialect(dialect) - - # Can we reuse this instance? - if (isinstance(dialect, Dialect) - and all(value is None for value in kwargs.values())): - return dialect - - self = object.__new__(cls) - - - def set_char(x): - if x is None: - return None - if isinstance(x, str) and len(x) <= 1: - return x - raise TypeError("%r must be a 1-character string" % (name,)) - def set_str(x): - if isinstance(x, str): - return x - raise TypeError("%r must be a string" % (name,)) - def set_quoting(x): - if x in range(4): - return x - raise TypeError("bad 'quoting' value") - - attributes = {"delimiter": (',', set_char), - "doublequote": (True, bool), - "escapechar": (None, set_char), - "lineterminator": ("\r\n", set_str), - "quotechar": ('"', set_char), - "quoting": (QUOTE_MINIMAL, set_quoting), - "skipinitialspace": (False, bool), - "strict": (False, bool), - } - - # Copy attributes - notset = object() - for name in Dialect.__slots__: - name = name[1:] - value = notset - if name in kwargs: - value = kwargs[name] - elif dialect is not None: - value = getattr(dialect, name, notset) - - # mapping by name: (default, converter) - if value is notset: - value = attributes[name][0] - if name == 'quoting' and not self.quotechar: - value = QUOTE_NONE - else: - converter = attributes[name][1] - if converter: - value = converter(value) - - setattr(self, '_' + name, value) - - if not self.delimiter: - raise TypeError("delimiter must be set") - - if self.quoting != QUOTE_NONE and not self.quotechar: - raise TypeError("quotechar must be set if quoting enabled") - - if not self.lineterminator: - raise TypeError("lineterminator must be set") - - return self - - delimiter = property(lambda self: self._delimiter) - doublequote = property(lambda self: self._doublequote) - escapechar = property(lambda self: self._escapechar) - lineterminator = property(lambda self: self._lineterminator) - quotechar = property(lambda self: self._quotechar) - quoting = property(lambda self: self._quoting) - skipinitialspace = property(lambda self: self._skipinitialspace) - strict = property(lambda self: self._strict) - - -def _call_dialect(dialect_inst, kwargs): - return Dialect(dialect_inst, **kwargs) - -def register_dialect(name, dialect=None, **kwargs): - """Create a mapping from a string name to a dialect class. - dialect = csv.register_dialect(name, dialect)""" - if not isinstance(name, str): - raise TypeError("dialect name must be a string or unicode") - - dialect = _call_dialect(dialect, kwargs) - _dialects[name] = dialect - -def unregister_dialect(name): - """Delete the name/dialect mapping associated with a string name.\n - csv.unregister_dialect(name)""" - try: - del _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def get_dialect(name): - """Return the dialect instance associated with name. - dialect = csv.get_dialect(name)""" - try: - return _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def list_dialects(): - """Return a list of all know dialect names - names = csv.list_dialects()""" - return list(_dialects) - -class Reader(object): - """CSV reader - - Reader objects are responsible for reading and parsing tabular data - in CSV format.""" - - - (START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD, - IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD, - EAT_CRNL) = range(8) - - def __init__(self, iterator, dialect=None, **kwargs): - self.dialect = _call_dialect(dialect, kwargs) - self.input_iter = iter(iterator) - self.line_num = 0 - - self._parse_reset() - - def _parse_reset(self): - self.field = '' - self.fields = [] - self.state = self.START_RECORD - self.numeric_field = False - - def __iter__(self): - return self - - def __next__(self): - self._parse_reset() - while True: - try: - line = next(self.input_iter) - except StopIteration: - # End of input OR exception - if len(self.field) > 0: - raise Error("newline inside string") - raise - - self.line_num += 1 - - if '\0' in line: - raise Error("line contains NULL byte") - pos = 0 - while pos < len(line): - pos = self._parse_process_char(line, pos) - self._parse_eol() - - if self.state == self.START_RECORD: - break - - fields = self.fields - self.fields = [] - return fields - - def _parse_process_char(self, line, pos): - c = line[pos] - if self.state == self.IN_FIELD: - # in unquoted field - pos2 = pos - while True: - if c in '\n\r': - # end of line - return [fields] - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.EAT_CRNL - elif c == self.dialect.escapechar: - # possible escaped character - pos2 -= 1 - self.state = self.ESCAPED_CHAR - elif c == self.dialect.delimiter: - # save field - wait for new field - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.START_FIELD - else: - # normal character - save in field - pos2 += 1 - if pos2 < len(line): - c = line[pos2] - continue - break - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - 1 - - elif self.state == self.START_RECORD: - if c in '\n\r': - self.state = self.EAT_CRNL - else: - self.state = self.START_FIELD - # restart process - self._parse_process_char(line, pos) - - elif self.state == self.START_FIELD: - if c in '\n\r': - # save empty field - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # start quoted field - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.escapechar: - # possible escaped character - self.state = self.ESCAPED_CHAR - elif c == ' ' and self.dialect.skipinitialspace: - # ignore space at start of field - pass - elif c == self.dialect.delimiter: - # save empty field - self._parse_save_field() - else: - # begin new unquoted field - if self.dialect.quoting == QUOTE_NONNUMERIC: - self.numeric_field = True - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.IN_QUOTED_FIELD: - if c == self.dialect.escapechar: - # possible escape character - self.state = self.ESCAPE_IN_QUOTED_FIELD - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - if self.dialect.doublequote: - # doublequote; " represented by "" - self.state = self.QUOTE_IN_QUOTED_FIELD - else: - #end of quote part of field - self.state = self.IN_FIELD - else: - # normal character - save in field - self._parse_add_char(c) - - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # doublequote - seen a quote in a quoted field - if (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # save "" as " - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.delimiter: - # save field - wait for new field - self._parse_save_field() - self.state = self.START_FIELD - elif c in '\r\n': - # end of line - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif not self.dialect.strict: - self._parse_add_char(c) - self.state = self.IN_FIELD - else: - raise Error("'%c' expected after '%c'" % - (self.dialect.delimiter, self.dialect.quotechar)) - - elif self.state == self.EAT_CRNL: - if c not in '\r\n': - raise Error("new-line character seen in unquoted field - " - "do you need to open the file " - "in universal-newline mode?") - - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - return pos + 1 - - def _parse_eol(self): - if self.state == self.EAT_CRNL: - self.state = self.START_RECORD - elif self.state == self.START_RECORD: - # empty line - return [] - pass - elif self.state == self.IN_FIELD: - # in unquoted field - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.START_FIELD: - # save empty field - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char('\n') - self.state = self.IN_FIELD - elif self.state == self.IN_QUOTED_FIELD: - pass - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char('\n') - self.state = self.IN_QUOTED_FIELD - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - def _parse_save_field(self): - field, self.field = self.field, '' - if self.numeric_field: - self.numeric_field = False - field = float(field) - self.fields.append(field) - - def _parse_add_char(self, c): - if len(self.field) + len(c) > _field_limit: - raise Error("field larger than field limit (%d)" % (_field_limit)) - self.field += c - - -class Writer(object): - """CSV writer - - Writer objects are responsible for generating tabular data - in CSV format from sequence input.""" - - def __init__(self, file, dialect=None, **kwargs): - if not (hasattr(file, 'write') and callable(file.write)): - raise TypeError("argument 1 must have a 'write' method") - self.writeline = file.write - self.dialect = _call_dialect(dialect, kwargs) - - def _join_reset(self): - self.rec = [] - self.num_fields = 0 - - def _join_append(self, field, quoted, quote_empty): - dialect = self.dialect - # If this is not the first field we need a field separator - if self.num_fields > 0: - self.rec.append(dialect.delimiter) - - if dialect.quoting == QUOTE_NONE: - need_escape = tuple(dialect.lineterminator) + ( - dialect.escapechar, # escapechar always first - dialect.delimiter, dialect.quotechar) - - else: - for c in tuple(dialect.lineterminator) + ( - dialect.delimiter, dialect.escapechar): - if c and c in field: - quoted = True - - need_escape = () - if dialect.quotechar in field: - if dialect.doublequote: - field = field.replace(dialect.quotechar, - dialect.quotechar * 2) - quoted = True - else: - need_escape = (dialect.quotechar,) - - - for c in need_escape: - if c and c in field: - if not dialect.escapechar: - raise Error("need to escape, but no escapechar set") - field = field.replace(c, dialect.escapechar + c) - - # If field is empty check if it needs to be quoted - if field == '' and quote_empty: - if dialect.quoting == QUOTE_NONE: - raise Error("single empty field record must be quoted") - quoted = 1 - - if quoted: - field = dialect.quotechar + field + dialect.quotechar - - self.rec.append(field) - self.num_fields += 1 - - - - def writerow(self, row): - dialect = self.dialect - try: - rowlen = len(row) - except TypeError: - raise Error("sequence expected") - - # join all fields in internal buffer - self._join_reset() - - for field in row: - quoted = False - if dialect.quoting == QUOTE_NONNUMERIC: - try: - float(field) - except: - quoted = True - # This changed since 2.5: - # quoted = not isinstance(field, (int, long, float)) - elif dialect.quoting == QUOTE_ALL: - quoted = True - - if field is None: - value = "" - elif isinstance(field, float): - value = repr(field) - else: - value = str(field) - self._join_append(value, quoted, rowlen == 1) - - # add line terminator - self.rec.append(dialect.lineterminator) - - self.writeline(''.join(self.rec)) - - def writerows(self, rows): - for row in rows: - self.writerow(row) - -def reader(*args, **kwargs): - """ - csv_reader = reader(iterable [, dialect='excel'] - [optional keyword args]) - for row in csv_reader: - process(row) - - The "iterable" argument can be any object that returns a line - of input for each iteration, such as a file object or a list. The - optional \"dialect\" parameter is discussed below. The function - also accepts optional keyword arguments which override settings - provided by the dialect. - - The returned object is an iterator. Each iteration returns a row - of the CSV file (which can span multiple input lines)""" - - return Reader(*args, **kwargs) - -def writer(*args, **kwargs): - """ - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - for row in sequence: - csv_writer.writerow(row) - - [or] - - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - csv_writer.writerows(rows) - - The \"fileobj\" argument can be any object that supports the file API.""" - return Writer(*args, **kwargs) - - -undefined = object() -def field_size_limit(limit=undefined): - """Sets an upper limit on parsed fields. - csv.field_size_limit([limit]) - - Returns old limit. If limit is not given, no new limit is set and - the old limit is returned""" - - global _field_limit - old_limit = _field_limit - - if limit is not undefined: - if not isinstance(limit, int): - raise TypeError("int expected, got %s" % - (limit.__class__.__name__,)) - _field_limit = limit - - return old_limit diff --git a/lib_pypy/pwd.py b/lib_pypy/pwd.py deleted file mode 100644 --- a/lib_pypy/pwd.py +++ /dev/null @@ -1,113 +0,0 @@ -# indirectly based on ctypes implementation: Victor Stinner, 2008-05-08 -""" -This module provides access to the Unix password database. -It is available on all Unix versions. - -Password database entries are reported as 7-tuples containing the following -items from the password database (see `'), in order: -pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell. -The uid and gid items are integers, all others are strings. An -exception is raised if the entry asked for cannot be found. -""" - -from _pwdgrp_cffi import ffi, lib -import _structseq -import _thread -_lock = _thread.allocate_lock() - -try: from __pypy__ import builtinify -except ImportError: builtinify = lambda f: f - - -class struct_passwd(metaclass=_structseq.structseqtype): - """ - pwd.struct_passwd: Results from getpw*() routines. - - This object may be accessed either as a tuple of - (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell) - or via the object attributes as named in the above tuple. - """ - name = "pwd.struct_passwd" - - pw_name = _structseq.structseqfield(0) - pw_passwd = _structseq.structseqfield(1) - pw_uid = _structseq.structseqfield(2) - pw_gid = _structseq.structseqfield(3) - pw_gecos = _structseq.structseqfield(4) - pw_dir = _structseq.structseqfield(5) - pw_shell = _structseq.structseqfield(6) - - -def _mkpwent(pw): - return struct_passwd([ - ffi.string(pw.pw_name), - ffi.string(pw.pw_passwd), - pw.pw_uid, - pw.pw_gid, - ffi.string(pw.pw_gecos), - ffi.string(pw.pw_dir), - ffi.string(pw.pw_shell)]) - - at builtinify -def getpwuid(uid): - """ - getpwuid(uid) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given numeric user ID. - See pwd.__doc__ for more on password database entries. - """ - with _lock: - pw = lib.getpwuid(uid) - if not pw: - raise KeyError("getpwuid(): uid not found: %s" % uid) - return _mkpwent(pw) - - at builtinify -def getpwnam(name): - """ - getpwnam(name) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given user name. - See pwd.__doc__ for more on password database entries. - """ - if not isinstance(name, basestring): - raise TypeError("expected string") - name = str(name) - with _lock: - pw = lib.getpwnam(name) - if not pw: - raise KeyError("getpwname(): name not found: %s" % name) - return _mkpwent(pw) - - at builtinify -def getpwall(): - """ - getpwall() -> list_of_entries - Return a list of all available password database entries, in arbitrary order. - See pwd.__doc__ for more on password database entries. - """ - users = [] - with _lock: - lib.setpwent() - while True: - pw = lib.getpwent() - if not pw: - break - users.append(_mkpwent(pw)) - lib.endpwent() - return users - -__all__ = ('struct_passwd', 'getpwuid', 'getpwnam', 'getpwall') - -if __name__ == "__main__": -# Uncomment next line to test CPython implementation -# from pwd import getpwuid, getpwnam, getpwall - from os import getuid - uid = getuid() - pw = getpwuid(uid) - print("uid %s: %s" % (pw.pw_uid, pw)) - name = pw.pw_name - print("name %r: %s" % (name, getpwnam(name))) - print("All:") - for pw in getpwall(): - print(pw) diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -113,20 +113,9 @@ Wrapper around zlib's z_stream structure which provides convenient compression functionality. """ - def __init__(self, space, level=rzlib.Z_DEFAULT_COMPRESSION, - method=rzlib.Z_DEFLATED, # \ - wbits=rzlib.MAX_WBITS, # \ undocumented - memLevel=rzlib.DEF_MEM_LEVEL, # / parameters - strategy=rzlib.Z_DEFAULT_STRATEGY, # / - zdict=None): + def __init__(self, space, stream): ZLibObject.__init__(self, space) - try: - self.stream = rzlib.deflateInit(level, method, wbits, - memLevel, strategy, zdict=zdict) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") + self.stream = stream self.register_finalizer(space) def _finalize_(self): @@ -158,6 +147,20 @@ raise zlib_error(space, e.msg) return space.newbytes(result) + def copy(self, space): + """ + copy() -- Return a copy of the compression object. + """ + try: + self.lock() + try: + copied = rzlib.deflateCopy(self.stream) + finally: + self.unlock() + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + return Compress(space=space, stream=copied) + @unwrap_spec(mode="c_int") def flush(self, space, mode=rzlib.Z_FINISH): """ @@ -203,16 +206,23 @@ zdict = None else: zdict = space.charbuf_w(w_zdict) - stream = space.allocate_instance(Compress, w_subtype) - stream = space.interp_w(Compress, stream) - Compress.__init__(stream, space, level, - method, wbits, memLevel, strategy, zdict) - return stream + w_stream = space.allocate_instance(Compress, w_subtype) + w_stream = space.interp_w(Compress, w_stream) + try: + stream = rzlib.deflateInit(level, method, wbits, memLevel, strategy, + zdict=zdict) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Compress.__init__(w_stream, space, stream) + return w_stream Compress.typedef = TypeDef( 'Compress', __new__ = interp2app(Compress___new__), + copy = interp2app(Compress.copy), compress = interp2app(Compress.compress), flush = interp2app(Compress.flush), __doc__ = """compressobj([level]) -- Return a compressor object. @@ -226,7 +236,7 @@ Wrapper around zlib's z_stream structure which provides convenient decompression functionality. """ - def __init__(self, space, wbits=rzlib.MAX_WBITS, zdict=None): + def __init__(self, space, stream, zdict, unused_data, unconsumed_tail): """ Initialize a new decompression object. @@ -236,16 +246,12 @@ inflateInit2. """ ZLibObject.__init__(self, space) - self.unused_data = '' - self.unconsumed_tail = '' + + self.stream = stream + self.zdict = zdict + self.unused_data = unused_data + self.unconsumed_tail = unconsumed_tail self.eof = False - try: - self.stream = rzlib.inflateInit(wbits, zdict=zdict) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") - self.zdict = zdict self.register_finalizer(space) def _finalize_(self): @@ -295,6 +301,30 @@ self._save_unconsumed_input(data, finished, unused_len) return space.newbytes(string) + def copy(self, space): + """ + copy() -- Return a copy of the decompression object. + """ + try: + self.lock() + try: + if not self.stream: + raise zlib_error(space, + "decompressor object already flushed") + copied = rzlib.inflateCopy(self.stream) + finally: + self.unlock() + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + + return Decompress( + space=space, + stream=copied, + unused_data=self.unused_data, + unconsumed_tail=self.unconsumed_tail, + zdict=self.zdict, + ) + def flush(self, space, w_length=None): """ flush( [length] ) -- This is kept for backward compatibility, @@ -331,10 +361,16 @@ zdict = None else: zdict = space.charbuf_w(w_zdict) - stream = space.allocate_instance(Decompress, w_subtype) - stream = space.interp_w(Decompress, stream) - Decompress.__init__(stream, space, wbits, zdict) - return stream + w_stream = space.allocate_instance(Decompress, w_subtype) + w_stream = space.interp_w(Decompress, w_stream) + try: + stream = rzlib.inflateInit(wbits, zdict=zdict) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Decompress.__init__(w_stream, space, stream, zdict, '', '') + return w_stream def default_buffer_size(space): return space.newint(rzlib.OUTPUT_BUFFER_SIZE) @@ -342,6 +378,7 @@ Decompress.typedef = TypeDef( 'Decompress', __new__ = interp2app(Decompress___new__), + copy = interp2app(Decompress.copy), decompress = interp2app(Decompress.decompress), flush = interp2app(Decompress.flush), unused_data = interp_attrproperty('unused_data', Decompress, wrapfn="newbytes"), diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -10,6 +10,13 @@ except ImportError: py.test.skip("no zlib module on this host Python") +from pypy.interpreter.gateway import interp2app +try: + from pypy.module.zlib import interp_zlib + from rpython.rlib import rzlib +except ImportError: + import py; py.test.skip("no zlib C library on this machine") + class AppTestZlib(object): spaceconfig = dict(usemodules=['zlib']) @@ -29,6 +36,22 @@ py.path.local(pypy.__file__).dirpath().dirpath() .join('LICENSE').read()) + def intentionally_break_a_z_stream(space, w_zobj): + """ + Intentionally break the z_stream associated with a + compressobj or decompressobj in a way that causes their copy + methods to raise RZlibErrors. + """ + from rpython.rtyper.lltypesystem import rffi, lltype + w_zobj.stream.c_zalloc = rffi.cast( + lltype.typeOf(w_zobj.stream.c_zalloc), + rzlib.Z_NULL, + ) + + cls.w_intentionally_break_a_z_stream = cls.space.wrap( + interp2app(intentionally_break_a_z_stream), + ) + def test_def_buf_size(self): assert self.zlib.DEF_BUF_SIZE >= 0 @@ -330,3 +353,54 @@ dco = zlib.decompressobj(wbits=-zlib.MAX_WBITS, zdict=zdict) uncomp = dco.decompress(comp) + dco.flush() assert zdict == uncomp + + def test_decompress_copy(self): + decompressor = self.zlib.decompressobj() + d1 = decompressor.decompress(self.compressed[:10]) + assert d1 + + copied = decompressor.copy() + + from_copy = copied.decompress(self.compressed[10:]) + from_decompressor = decompressor.decompress(self.compressed[10:]) + + assert (d1 + from_copy) == (d1 + from_decompressor) + + def test_unsuccessful_decompress_copy(self): + decompressor = self.zlib.decompressobj() + self.intentionally_break_a_z_stream(zobj=decompressor) + raises(self.zlib.error, decompressor.copy) + + def test_decompress_copy_carries_along_state(self): + """ + Decompressor.unused_data and unconsumed_tail are carried along when a + copy is done. + """ + decompressor = self.zlib.decompressobj() + decompressor.decompress(self.compressed, max_length=5) + unconsumed_tail = decompressor.unconsumed_tail + assert unconsumed_tail + assert decompressor.copy().unconsumed_tail == unconsumed_tail + + decompressor.decompress(decompressor.unconsumed_tail) + decompressor.decompress(b"xxx") + unused_data = decompressor.unused_data + assert unused_data + assert decompressor.copy().unused_data == unused_data + + def test_compress_copy(self): + compressor = self.zlib.compressobj() + d1 = compressor.compress(self.expanded[:10]) + assert d1 + + copied = compressor.copy() + + from_copy = copied.compress(self.expanded[10:]) + from_compressor = compressor.compress(self.expanded[10:]) + + assert (d1 + from_copy) == (d1 + from_compressor) + + def test_unsuccessful_compress_copy(self): + compressor = self.zlib.compressobj() + self.intentionally_break_a_z_stream(zobj=compressor) + raises(self.zlib.error, compressor.copy) diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py --- a/pypy/tool/release/package.py +++ b/pypy/tool/release/package.py @@ -121,6 +121,8 @@ pypydir.ensure('include', dir=True) if sys.platform == 'win32': + os.environ['PATH'] = str(basedir.join('externals').join('bin')) + ';' + \ + os.environ.get('PATH', '') src,tgt = binaries[0] pypyw = src.new(purebasename=src.purebasename + 'w') if pypyw.exists(): diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py --- a/rpython/rlib/rgc.py +++ b/rpython/rlib/rgc.py @@ -1406,6 +1406,11 @@ return _ResizableListSupportingRawPtr(lst) def nonmoving_raw_ptr_for_resizable_list(lst): + if must_split_gc_address_space(): + raise ValueError + return _nonmoving_raw_ptr_for_resizable_list(lst) + +def _nonmoving_raw_ptr_for_resizable_list(lst): assert isinstance(lst, _ResizableListSupportingRawPtr) return lst._nonmoving_raw_ptr_for_resizable_list() @@ -1450,7 +1455,7 @@ return hop.inputarg(hop.args_r[0], 0) class Entry(ExtRegistryEntry): - _about_ = nonmoving_raw_ptr_for_resizable_list + _about_ = _nonmoving_raw_ptr_for_resizable_list def compute_result_annotation(self, s_list): from rpython.rtyper.lltypesystem import lltype, rffi diff --git a/rpython/rlib/rzlib.py b/rpython/rlib/rzlib.py --- a/rpython/rlib/rzlib.py +++ b/rpython/rlib/rzlib.py @@ -35,6 +35,7 @@ MAX_WBITS MAX_MEM_LEVEL Z_BEST_SPEED Z_BEST_COMPRESSION Z_DEFAULT_COMPRESSION Z_FILTERED Z_HUFFMAN_ONLY Z_DEFAULT_STRATEGY Z_NEED_DICT + Z_NULL '''.split() class SimpleCConfig: @@ -141,6 +142,7 @@ rffi.INT) _deflate = zlib_external('deflate', [z_stream_p, rffi.INT], rffi.INT) +_deflateCopy = zlib_external('deflateCopy', [z_stream_p, z_stream_p], rffi.INT) _deflateEnd = zlib_external('deflateEnd', [z_stream_p], rffi.INT, releasegil=False) @@ -160,6 +162,7 @@ rffi.INT) _inflate = zlib_external('inflate', [z_stream_p, rffi.INT], rffi.INT) +_inflateCopy = zlib_external('inflateCopy', [z_stream_p, z_stream_p], rffi.INT) _inflateEnd = zlib_external('inflateEnd', [z_stream_p], rffi.INT, releasegil=False) @@ -290,6 +293,19 @@ lltype.free(stream, flavor='raw') +def deflateCopy(source): + """ + Allocate and return an independent copy of the provided stream object. + """ + dest = deflateInit() + err = _deflateCopy(dest, source) + if err != Z_OK: + deflateEnd(dest) + raise RZlibError.fromstream(source, err, + "while copying compression object") + return dest + + def deflateEnd(stream): """ Free the resources associated with the deflate stream. @@ -330,6 +346,19 @@ lltype.free(stream, flavor='raw') +def inflateCopy(source): + """ + Allocate and return an independent copy of the provided stream object. + """ + dest = inflateInit() + err = _inflateCopy(dest, source) + if err != Z_OK: + inflateEnd(dest) + raise RZlibError.fromstream(source, err, + "while copying decompression object") + return dest + + def inflateEnd(stream): """ Free the resources associated with the inflate stream. diff --git a/rpython/rlib/test/test_rzlib.py b/rpython/rlib/test/test_rzlib.py --- a/rpython/rlib/test/test_rzlib.py +++ b/rpython/rlib/test/test_rzlib.py @@ -246,6 +246,108 @@ rzlib.deflateEnd(stream) +def test_compress_copy(): + """ + inflateCopy produces an independent copy of a stream. + """ + + stream = rzlib.deflateInit() + + bytes1 = rzlib.compress(stream, expanded[:10]) + assert bytes1 + + copied = rzlib.deflateCopy(stream) + + bytes_stream = rzlib.compress( + stream, + expanded[10:], + rzlib.Z_FINISH, + ) + assert bytes1 + bytes_stream == compressed + rzlib.deflateEnd(stream) + + bytes_copy = rzlib.compress( + copied, + expanded[10:], + rzlib.Z_FINISH, + ) + rzlib.deflateEnd(copied) + assert bytes1 + bytes_copy == compressed + + +def test_unsuccessful_compress_copy(): + """ + Errors during unsuccesful deflateCopy operations raise RZlibErrors. + """ + stream = rzlib.deflateInit() + + # From zlib.h: + # + # "deflateCopy returns [...] Z_STREAM_ERROR if the source stream + # state was inconsistent (such as zalloc being Z_NULL)" + from rpython.rtyper.lltypesystem import rffi, lltype + stream.c_zalloc = rffi.cast(lltype.typeOf(stream.c_zalloc), rzlib.Z_NULL) + + exc = py.test.raises(rzlib.RZlibError, rzlib.deflateCopy, stream) + msg = "Error -2 while copying compression object: inconsistent stream state" + assert str(exc.value) == msg + rzlib.deflateEnd(stream) + + +def test_decompress_copy(): + """ + inflateCopy produces an independent copy of a stream. + """ + + stream = rzlib.inflateInit() + + bytes1, finished1, unused1 = rzlib.decompress(stream, compressed[:10]) + assert bytes1 + assert finished1 is False + + copied = rzlib.inflateCopy(stream) + + bytes_stream, finished_stream, unused_stream = rzlib.decompress( + stream, + compressed[10:], + rzlib.Z_FINISH, + ) + assert bytes1 + bytes_stream == expanded + assert finished_stream is True + assert unused1 == 0 + assert unused_stream == 0 + rzlib.inflateEnd(stream) + + bytes_copy, finished_copy, unused_copy = rzlib.decompress( + copied, + compressed[10:], + rzlib.Z_FINISH, + ) + rzlib.inflateEnd(copied) + assert bytes1 + bytes_copy == expanded + assert finished_copy is True + assert unused_copy == 0 + + +def test_unsuccessful_decompress_copy(): + """ + Errors during unsuccesful inflateCopy operations raise RZlibErrors. + """ + stream = rzlib.inflateInit() + + # From zlib.h: + # + # "inflateCopy returns [...] Z_STREAM_ERROR if the source stream + # state was inconsistent (such as zalloc being Z_NULL)" + from rpython.rtyper.lltypesystem import rffi, lltype + stream.c_zalloc = rffi.cast(lltype.typeOf(stream.c_zalloc), rzlib.Z_NULL) + + exc = py.test.raises(rzlib.RZlibError, rzlib.inflateCopy, stream) + msg = "Error -2 while copying decompression object: inconsistent stream state" + assert str(exc.value) == msg + rzlib.inflateEnd(stream) + + def test_cornercases(): """ Test degenerate arguments. diff --git a/rpython/rtyper/lltypesystem/module/test/math_cases.py b/rpython/rtyper/lltypesystem/module/test/math_cases.py --- a/rpython/rtyper/lltypesystem/module/test/math_cases.py +++ b/rpython/rtyper/lltypesystem/module/test/math_cases.py @@ -59,9 +59,6 @@ ('copysign', (1.5, -0.0), -1.5), ('copysign', (1.5, INFINITY), 1.5), ('copysign', (1.5, -INFINITY), -1.5), - ] - if sys.platform != 'win32': # all NaNs seem to be negative there...? - IRREGCASES += [ ('copysign', (1.5, NAN), 1.5), ('copysign', (1.75, -NAN), -1.75), # special case for -NAN here ] diff --git a/rpython/translator/c/primitive.py b/rpython/translator/c/primitive.py --- a/rpython/translator/c/primitive.py +++ b/rpython/translator/c/primitive.py @@ -123,9 +123,9 @@ return '(-Py_HUGE_VAL)' elif math.isnan(value): if is_positive_nan(value): - return '(Py_HUGE_VAL/Py_HUGE_VAL)' + return '(_PyPy_dg_stdnan(0))' else: - return '(-(Py_HUGE_VAL/Py_HUGE_VAL))' + return '(_PyPy_dg_stdnan(1))' else: x = repr(value) assert not x.startswith('n') @@ -142,9 +142,9 @@ elif math.isnan(value): # XXX are these expressions ok? if is_positive_nan(value): - return '((float)(Py_HUGE_VAL/Py_HUGE_VAL))' + return '((float)(_PyPy_dg_stdnan(0)))' else: - return '(-(float)(Py_HUGE_VAL/Py_HUGE_VAL))' + return '((float)(_PyPy_dg_stdnan(1)))' else: return repr(value) + 'f' diff --git a/rpython/translator/c/src/support.c b/rpython/translator/c/src/support.c --- a/rpython/translator/c/src/support.c +++ b/rpython/translator/c/src/support.c @@ -9,6 +9,26 @@ #include /*** misc ***/ +#define Sign_bit 0x80000000 +#define NAN_WORD0 0x7ff80000 +#define NAN_WORD1 0 +#define PY_UINT32_T unsigned int + +#ifndef __BIG_ENDIAN__ +#define IEEE_8087 +#endif + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d + +typedef PY_UINT32_T ULong; +typedef union { double d; ULong L[2]; } U; RPY_EXTERN void RPyAssertFailed(const char* filename, long lineno, @@ -25,3 +45,20 @@ fprintf(stderr, "Invalid RPython operation (NULL ptr or bad array index)\n"); abort(); } + +/* Return a 'standard' NaN value. + There are exactly two quiet NaNs that don't arise by 'quieting' signaling + NaNs (see IEEE 754-2008, section 6.2.1). If sign == 0, return the one whose + sign bit is cleared. Otherwise, return the one whose sign bit is set. +*/ + +double +_PyPy_dg_stdnan(int sign) +{ + U rv; + word0(&rv) = NAN_WORD0; + word1(&rv) = NAN_WORD1; + if (sign) + word0(&rv) |= Sign_bit; + return dval(&rv); +} diff --git a/rpython/translator/c/src/support.h b/rpython/translator/c/src/support.h --- a/rpython/translator/c/src/support.h +++ b/rpython/translator/c/src/support.h @@ -38,6 +38,9 @@ RPY_EXTERN void RPyAbort(void); +RPY_EXTERN +double _PyPy_dg_stdnan(int sign); + #if defined(RPY_LL_ASSERT) || defined(RPY_SANDBOXED) /* obscure macros that can be used as expressions and lvalues to refer * to a field of a structure or an item in an array in a "safe" way -- From pypy.commits at gmail.com Wed Feb 6 06:07:07 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 03:07:07 -0800 (PST) Subject: [pypy-commit] pypy default: Merge zlib-copying-redux Message-ID: <5c5abfdb.1c69fb81.972a6.5bbd@mx.google.com> Author: Julian Berman Branch: Changeset: r95865:ec33801be3ff Date: 2019-02-06 11:43 +0100 http://bitbucket.org/pypy/pypy/changeset/ec33801be3ff/ Log: Merge zlib-copying-redux 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 @@ -5,6 +5,10 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f +.. branch: zlib-copying-redux + +Fix calling copy on already-flushed compressobjs. + .. branch: zlib-copying The zlib module's compressobj and decompressobj now expose copy methods diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -175,6 +175,11 @@ try: self.lock() try: + if not self.stream: + raise oefmt( + space.w_ValueError, + "Compressor was already flushed", + ) copied = rzlib.deflateCopy(self.stream) finally: self.unlock() @@ -318,9 +323,6 @@ try: self.lock() try: - if not self.stream: - raise zlib_error(space, - "decompressor object already flushed") copied = rzlib.inflateCopy(self.stream) finally: self.unlock() diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -307,7 +307,8 @@ assert (d1 + from_copy) == (d1 + from_decompressor) - def test_unsuccessful_decompress_copy(self): + def test_cannot_copy_decompressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") decompressor = self.zlib.decompressobj() self.intentionally_break_a_z_stream(zobj=decompressor) raises(self.zlib.error, decompressor.copy) @@ -341,7 +342,13 @@ assert (d1 + from_copy) == (d1 + from_compressor) - def test_unsuccessful_compress_copy(self): + def test_cannot_copy_compressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") compressor = self.zlib.compressobj() self.intentionally_break_a_z_stream(zobj=compressor) raises(self.zlib.error, compressor.copy) + + def test_cannot_copy_compressor_with_flushed_stream(self): + compressor = self.zlib.compressobj() + compressor.flush() + raises(ValueError, compressor.copy) From pypy.commits at gmail.com Wed Feb 6 06:07:08 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 03:07:08 -0800 (PST) Subject: [pypy-commit] pypy default: Merge default Message-ID: <5c5abfdc.1c69fb81.37d51.dc0b@mx.google.com> Author: Julian Berman Branch: Changeset: r95866:24f1b599a64d Date: 2019-02-06 11:59 +0100 http://bitbucket.org/pypy/pypy/changeset/24f1b599a64d/ Log: Merge default 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 @@ -5,6 +5,10 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f +.. branch: zlib-copying-redux + +Fix calling copy on already-flushed compressobjs. + .. branch: zlib-copying The zlib module's compressobj and decompressobj now expose copy methods diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -175,6 +175,11 @@ try: self.lock() try: + if not self.stream: + raise oefmt( + space.w_ValueError, + "Compressor was already flushed", + ) copied = rzlib.deflateCopy(self.stream) finally: self.unlock() @@ -318,9 +323,6 @@ try: self.lock() try: - if not self.stream: - raise zlib_error(space, - "decompressor object already flushed") copied = rzlib.inflateCopy(self.stream) finally: self.unlock() diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -307,7 +307,8 @@ assert (d1 + from_copy) == (d1 + from_decompressor) - def test_unsuccessful_decompress_copy(self): + def test_cannot_copy_decompressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") decompressor = self.zlib.decompressobj() self.intentionally_break_a_z_stream(zobj=decompressor) raises(self.zlib.error, decompressor.copy) @@ -341,7 +342,13 @@ assert (d1 + from_copy) == (d1 + from_compressor) - def test_unsuccessful_compress_copy(self): + def test_cannot_copy_compressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") compressor = self.zlib.compressobj() self.intentionally_break_a_z_stream(zobj=compressor) raises(self.zlib.error, compressor.copy) + + def test_cannot_copy_compressor_with_flushed_stream(self): + compressor = self.zlib.compressobj() + compressor.flush() + raises(ValueError, compressor.copy) From pypy.commits at gmail.com Wed Feb 6 06:07:10 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 03:07:10 -0800 (PST) Subject: [pypy-commit] pypy default: Merge default heads Message-ID: <5c5abfde.1c69fb81.947f8.4545@mx.google.com> Author: Julian Berman Branch: Changeset: r95867:60fa14799cf4 Date: 2019-02-06 12:06 +0100 http://bitbucket.org/pypy/pypy/changeset/60fa14799cf4/ Log: Merge default heads 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 @@ -5,6 +5,10 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f +.. branch: zlib-copying-redux + +Fix calling copy on already-flushed compressobjs. + .. branch: zlib-copying The zlib module's compressobj and decompressobj now expose copy methods diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -175,6 +175,11 @@ try: self.lock() try: + if not self.stream: + raise oefmt( + space.w_ValueError, + "Compressor was already flushed", + ) copied = rzlib.deflateCopy(self.stream) finally: self.unlock() @@ -318,9 +323,6 @@ try: self.lock() try: - if not self.stream: - raise zlib_error(space, - "decompressor object already flushed") copied = rzlib.inflateCopy(self.stream) finally: self.unlock() diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -307,7 +307,8 @@ assert (d1 + from_copy) == (d1 + from_decompressor) - def test_unsuccessful_decompress_copy(self): + def test_cannot_copy_decompressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") decompressor = self.zlib.decompressobj() self.intentionally_break_a_z_stream(zobj=decompressor) raises(self.zlib.error, decompressor.copy) @@ -341,7 +342,13 @@ assert (d1 + from_copy) == (d1 + from_compressor) - def test_unsuccessful_compress_copy(self): + def test_cannot_copy_compressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") compressor = self.zlib.compressobj() self.intentionally_break_a_z_stream(zobj=compressor) raises(self.zlib.error, compressor.copy) + + def test_cannot_copy_compressor_with_flushed_stream(self): + compressor = self.zlib.compressobj() + compressor.flush() + raises(ValueError, compressor.copy) From pypy.commits at gmail.com Wed Feb 6 06:10:35 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Wed, 06 Feb 2019 03:10:35 -0800 (PST) Subject: [pypy-commit] pypy 35windowsfixes: Closed branch. The commit was grafted into the default branch Message-ID: <5c5ac0ab.1c69fb81.ebec2.d969@mx.google.com> Author: andrewjlawrence Branch: 35windowsfixes Changeset: r95868:284828e44f37 Date: 2019-02-06 11:08 +0000 http://bitbucket.org/pypy/pypy/changeset/284828e44f37/ Log: Closed branch. The commit was grafted into the default branch From pypy.commits at gmail.com Wed Feb 6 06:25:24 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 03:25:24 -0800 (PST) Subject: [pypy-commit] pypy py3.5: Merge default. Message-ID: <5c5ac424.1c69fb81.2ee5d.55e8@mx.google.com> Author: Julian Berman Branch: py3.5 Changeset: r95869:5186243836bc Date: 2019-02-06 12:24 +0100 http://bitbucket.org/pypy/pypy/changeset/5186243836bc/ Log: Merge default. 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 @@ -5,6 +5,10 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f +.. branch: zlib-copying-redux + +Fix calling copy on already-flushed compressobjs. + .. branch: zlib-copying The zlib module's compressobj and decompressobj now expose copy methods diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -154,6 +154,11 @@ try: self.lock() try: + if not self.stream: + raise oefmt( + space.w_ValueError, + "Compressor was already flushed", + ) copied = rzlib.deflateCopy(self.stream) finally: self.unlock() @@ -308,9 +313,6 @@ try: self.lock() try: - if not self.stream: - raise zlib_error(space, - "decompressor object already flushed") copied = rzlib.inflateCopy(self.stream) finally: self.unlock() diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -366,7 +366,8 @@ assert (d1 + from_copy) == (d1 + from_decompressor) - def test_unsuccessful_decompress_copy(self): + def test_cannot_copy_decompressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") decompressor = self.zlib.decompressobj() self.intentionally_break_a_z_stream(zobj=decompressor) raises(self.zlib.error, decompressor.copy) @@ -400,7 +401,13 @@ assert (d1 + from_copy) == (d1 + from_compressor) - def test_unsuccessful_compress_copy(self): + def test_cannot_copy_compressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") compressor = self.zlib.compressobj() self.intentionally_break_a_z_stream(zobj=compressor) raises(self.zlib.error, compressor.copy) + + def test_cannot_copy_compressor_with_flushed_stream(self): + compressor = self.zlib.compressobj() + compressor.flush() + raises(ValueError, compressor.copy) From pypy.commits at gmail.com Wed Feb 6 08:19:00 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 05:19:00 -0800 (PST) Subject: [pypy-commit] pypy default: This appears to be necessary to actually have runappdirect available for app tests. Message-ID: <5c5adec4.1c69fb81.68f7f.0f92@mx.google.com> Author: Julian Berman Branch: Changeset: r95870:fee9d13fa77c Date: 2019-02-06 14:18 +0100 http://bitbucket.org/pypy/pypy/changeset/fee9d13fa77c/ Log: This appears to be necessary to actually have runappdirect available for app tests. diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -35,6 +35,8 @@ compression and decompression tests have a little real data to assert against. """ + cls.w_runappdirect = cls.space.wrap(cls.runappdirect) + cls.w_zlib = cls.space.getbuiltinmodule('zlib') expanded = 'some bytes which will be compressed' cls.w_expanded = cls.space.wrap(expanded) From pypy.commits at gmail.com Wed Feb 6 08:22:25 2019 From: pypy.commits at gmail.com (antocuni) Date: Wed, 06 Feb 2019 05:22:25 -0800 (PST) Subject: [pypy-commit] pypy default: Added tag release-pypy2.7-v7.0.0 for changeset 9112c8071614 Message-ID: <5c5adf91.1c69fb81.1a4a7.941a@mx.google.com> Author: Antonio Cuni Branch: Changeset: r95871:a41775a6bcf2 Date: 2019-02-06 14:14 +0100 http://bitbucket.org/pypy/pypy/changeset/a41775a6bcf2/ Log: Added tag release-pypy2.7-v7.0.0 for changeset 9112c8071614 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -58,3 +58,4 @@ 3f6eaa010fce78cc7973bdc1dfdb95970f08fed2 release-pypy3.5-v5.10.1 ab0b9caf307db6592905a80b8faffd69b39005b8 release-pypy2.7-v6.0.0 fdd60ed87e941677e8ea11acf9f1819466521bf2 release-pypy3.5-v6.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 From pypy.commits at gmail.com Wed Feb 6 08:22:27 2019 From: pypy.commits at gmail.com (antocuni) Date: Wed, 06 Feb 2019 05:22:27 -0800 (PST) Subject: [pypy-commit] pypy default: Added tag release-pypy3.5-v7.0.0 for changeset 1f86f25937b6 Message-ID: <5c5adf93.1c69fb81.77a67.02b9@mx.google.com> Author: Antonio Cuni Branch: Changeset: r95872:13e6a9467268 Date: 2019-02-06 14:15 +0100 http://bitbucket.org/pypy/pypy/changeset/13e6a9467268/ Log: Added tag release-pypy3.5-v7.0.0 for changeset 1f86f25937b6 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -59,3 +59,4 @@ ab0b9caf307db6592905a80b8faffd69b39005b8 release-pypy2.7-v6.0.0 fdd60ed87e941677e8ea11acf9f1819466521bf2 release-pypy3.5-v6.0.0 9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 From pypy.commits at gmail.com Wed Feb 6 08:22:28 2019 From: pypy.commits at gmail.com (antocuni) Date: Wed, 06 Feb 2019 05:22:28 -0800 (PST) Subject: [pypy-commit] pypy default: Added tag release-pypy3.6-v7.0.0 for changeset dab365a46514 Message-ID: <5c5adf94.1c69fb81.67ad.6416@mx.google.com> Author: Antonio Cuni Branch: Changeset: r95873:e239e0b2814c Date: 2019-02-06 14:15 +0100 http://bitbucket.org/pypy/pypy/changeset/e239e0b2814c/ Log: Added tag release-pypy3.6-v7.0.0 for changeset dab365a46514 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -60,3 +60,4 @@ fdd60ed87e941677e8ea11acf9f1819466521bf2 release-pypy3.5-v6.0.0 9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 From pypy.commits at gmail.com Wed Feb 6 08:22:30 2019 From: pypy.commits at gmail.com (antocuni) Date: Wed, 06 Feb 2019 05:22:30 -0800 (PST) Subject: [pypy-commit] pypy default: merge heads Message-ID: <5c5adf96.1c69fb81.b7150.6078@mx.google.com> Author: Antonio Cuni Branch: Changeset: r95874:35673b0b2571 Date: 2019-02-06 14:21 +0100 http://bitbucket.org/pypy/pypy/changeset/35673b0b2571/ Log: merge heads diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -35,6 +35,8 @@ compression and decompression tests have a little real data to assert against. """ + cls.w_runappdirect = cls.space.wrap(cls.runappdirect) + cls.w_zlib = cls.space.getbuiltinmodule('zlib') expanded = 'some bytes which will be compressed' cls.w_expanded = cls.space.wrap(expanded) From pypy.commits at gmail.com Wed Feb 6 08:57:07 2019 From: pypy.commits at gmail.com (rlamy) Date: Wed, 06 Feb 2019 05:57:07 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: Kill dead code Message-ID: <5c5ae7b3.1c69fb81.3da66.59d1@mx.google.com> Author: Ronan Lamy Branch: unicode-utf8-py3 Changeset: r95875:daa08b2d951c Date: 2019-02-06 14:56 +0100 http://bitbucket.org/pypy/pypy/changeset/daa08b2d951c/ Log: Kill dead code diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py --- a/pypy/module/array/interp_array.py +++ b/pypy/module/array/interp_array.py @@ -1033,11 +1033,6 @@ "unsigned %d-byte integer out of range", mytype.bytes) return rffi.cast(mytype.itemtype, item) - if mytype.unwrap == 'unicode_w': - if len(item) != 1: - raise oefmt(space.w_TypeError, "array item must be char") - item = item[0] - return rffi.cast(mytype.itemtype, item) if mytype.unwrap == 'utf8_len_w': utf8, lgt = item if lgt != 1: From pypy.commits at gmail.com Wed Feb 6 12:11:38 2019 From: pypy.commits at gmail.com (antocuni) Date: Wed, 06 Feb 2019 09:11:38 -0800 (PST) Subject: [pypy-commit] pypy default: commit the tweaked version of the script which I used for the release Message-ID: <5c5b154a.1c69fb81.bd66c.befe@mx.google.com> Author: Antonio Cuni Branch: Changeset: r95878:4c920e4d6d68 Date: 2019-02-06 18:11 +0100 http://bitbucket.org/pypy/pypy/changeset/4c920e4d6d68/ Log: commit the tweaked version of the script which I used for the release 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 pmaj=2 # python main version pmin=7 # python minor version -maj=6 +maj=7 min=0 rev=0 branchname=release-pypy$pmaj.$pmin-$maj.x # ==OR== release-$maj.x # ==OR== release-$maj.$min.x @@ -13,7 +13,7 @@ hg log -r $tagname || exit 1 hgrev=`hg id -r $tagname -i` -rel=pypy$pmaj-v$maj.$min.$rev +rel=pypy$pmaj.$pmin-v$maj.$min.$rev # The script should be run in an empty in the pypy tree, i.e. pypy/tmp if [ "`ls . | wc -l`" != "0" ] then @@ -23,7 +23,7 @@ # Download latest builds from the buildmaster, rename the top # level directory, and repackage ready to be uploaded to bitbucket -for plat in linux linux64 linux-armhf-raspbian linux-armel osx64 s390x +for plat in linux linux64 osx64 s390x # linux-armhf-raspbian linux-armel do echo downloading package for $plat if wget -q --show-progress http://buildbot.pypy.org/nightly/$branchname/pypy-c-jit-latest-$plat.tar.bz2 From pypy.commits at gmail.com Wed Feb 6 12:33:33 2019 From: pypy.commits at gmail.com (antocuni) Date: Wed, 06 Feb 2019 09:33:33 -0800 (PST) Subject: [pypy-commit] pypy.org extradoc: (antocuni, mattip): update the website for the new release; only the source, the HTML will be regenerated/uploaded later Message-ID: <5c5b1a6d.1c69fb81.3dcf3.90fc@mx.google.com> Author: Antonio Cuni Branch: extradoc Changeset: r930:3d49c68495b0 Date: 2019-02-06 18:33 +0100 http://bitbucket.org/pypy/pypy.org/changeset/3d49c68495b0/ Log: (antocuni, mattip): update the website for the new release; only the source, the HTML will be regenerated/uploaded later diff --git a/source/download.txt b/source/download.txt --- a/source/download.txt +++ b/source/download.txt @@ -13,16 +13,17 @@ performance improvements. We provide binaries for x86, ARM, PPC and s390x running on different operating systems such as -Linux, Mac OS X and Windows: +Linux, Mac OS X and Windows (`what's new in PyPy 7.0?`_): -* the Python2.7 compatible release — **PyPy2.7 v6.0** — (`what's new in PyPy2.7?`_) +* the Python2.7 compatible release — **PyPy2.7 v7.0** -* the Python3.5 compatible release — **PyPy3.5 v6.0** — (`what's new in PyPy3.5?`_). +* the Python3.5 compatible release — **PyPy3.5 v7.0** + +* the Python3.6 compatible release, alpha quality — **PyPy3.6 v7.0** * the Python2.7 Software Transactional Memory special release — **PyPy-STM 2.5.1** (Linux x86-64 only) -.. _what's new in PyPy2.7?: http://doc.pypy.org/en/latest/release-v6.0.0.html -.. _what's new in PyPy3.5?: http://doc.pypy.org/en/latest/release-v6.0.0.html +.. _what's new in PyPy 7.0?: http://doc.pypy.org/en/latest/release-v7.0.0.html .. class:: download_menu @@ -79,15 +80,13 @@ .. _release: -Python2.7 compatible PyPy 6.0.0 +Python2.7 compatible PyPy 7.0 ------------------------------- .. class:: download_menu * `Linux x86 binary (32bit, built on Ubuntu 12.04 - 16.04)`__ (see ``[1]`` below) * `Linux x86-64 binary (64bit, built on Ubuntu 12.04 - 16.04)`__ (see ``[1]`` below) -* `ARM Hardfloat Linux binary (ARMHF/gnueabihf, Raspbian)`__ (see ``[1]`` below) -* `ARM Hardfloat Linux binary (ARMHF/gnueabihf, Ubuntu Raring)`__ (see ``[1]`` below) * `Mac OS X binary (64bit)`__ * FreeBSD x86 and x86_64: see FreshPorts_ * `Windows binary (32bit)`__ (you might need the VS 2008 runtime library @@ -99,31 +98,27 @@ * `All our downloads,`__ including previous versions. We also have a mirror_, but please use only if you have troubles accessing the links above -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-linux32.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-linux64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-linux-armhf-raspbian.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-linux-armel.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-osx64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-win32.zip -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.10.0-ppc64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v5.10.0-ppc64le.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-s390x.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-src.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v6.0.0-src.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-linux32.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-linux64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-osx64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-win32.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-ppc64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-ppc64le.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-s390x.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-src.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-src.zip .. _`vcredist_x86.exe`: http://www.microsoft.com/en-us/download/details.aspx?id=5582 .. __: https://bitbucket.org/pypy/pypy/downloads .. _mirror: http://buildbot.pypy.org/mirror/ .. _FreshPorts: http://www.freshports.org/lang/pypy -Python 3.5.3 compatible PyPy3.5 v6.0.0 +Python 3.5.3 compatible PyPy3.5 v7.0.0 --------------------------------------- .. class:: download_menu * `Linux x86 binary (32bit, built on Ubuntu 12.04 - 16.04)`__ (see ``[1]`` below) * `Linux x86-64 binary (64bit, built on Ubuntu 12.04 - 16.04)`__ (see ``[1]`` below) -* `ARM Hardfloat Linux binary (ARMHF/gnueabihf, Raspbian)`__ (see ``[1]`` below) -* `ARM Softfloat Linux binary (ARMEL/gnueabi, Ubuntu Raring)`__ (see ``[1]`` below) * `Mac OS X binary (64bit)`__ (High Sierra >= 10.13, not for Sierra and below) * `Windows binary (32bit)`__ **BETA** * `s390x Linux binary (built on Redhat Linux 7.2)`__ (see ``[1]`` below) @@ -131,15 +126,13 @@ * `All our downloads,`__ including previous versions. We also have a mirror_, but please use only if you have troubles accessing the links above -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-linux32.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-linux64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-linux-armhf-raspbian.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-linux-armel.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-osx64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-win32.zip -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-s390x.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-src.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-src.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-linux32.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-linux64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-osx64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-win32.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-s390x.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-src.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-src.zip .. __: https://bitbucket.org/pypy/pypy/downloads If your CPU is really, really old, it may be a x86-32 without SSE2. @@ -154,6 +147,26 @@ libraries: ...``. Unless you want to hack a lot, try out the `portable Linux binaries`_. +Python 3.6 compatible PyPy3.6 v7.0.0-alpha +------------------------------------------- + +.. class:: download_menu + +* `Linux x86-64 binary (64bit, built on Ubuntu 12.04 - 16.04)`__ (see ``[1]`` below) +* `Mac OS X binary (64bit)`__ (High Sierra >= 10.13, not for Sierra and below) +* `Windows binary (32bit)`__ **BETA** +* `Source (tar.bz2)`__; `Source (zip)`__. See below for more about the sources. +* `All our downloads,`__ including previous versions. We also have a + mirror_, but please use only if you have troubles accessing the links above + +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-linux64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-osx64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-win32.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-src.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-src.zip +.. __: https://bitbucket.org/pypy/pypy/downloads + + PyPy-STM 2.5.1 ------------------------------ @@ -440,20 +453,6 @@ Here are the checksums for each of the downloads -pypy2.7-5.10.0 sha256:: - - ee1980467ac8cc9fa9d609f7da93c5282503e59a548781248fe1914a7199d540 pypy2-v5.10.0-linux32.tar.bz2 - da85af9240220179493ad66c857934dc7ea91aef8f168cd293a2d99af8346ee2 pypy2-v5.10.0-linux64.tar.bz2 - 6fdd55dd8f674efd06f76edb60a09a03b9b04a5fbc56741f416a94a0b9d2ff91 pypy2-v5.10.0-linux-armel.tar.bz2 - 5ec3617bb9a07a0a0b2f3c8fbe69912345da4696cdb0a2aca7889b6f1e74435c pypy2-v5.10.0-linux-armhf-raspbian.tar.bz2 - 7e4120f0a83529a6851cbae0ec107dc7085ba8a4aeff4e7bd9da9aadb1ef37a4 pypy2-v5.10.0-osx64.tar.bz2 - dab4dccfa71820c4f803f5a82e13f76517bfde5fafe1e5fba6ff58ef2ba318ab pypy2-v5.10.0-s390x.tar.bz2 - 1209f2db718e6afda17528baa5138177a14a0938588a7d3e1b7c722c483079a8 pypy2-v5.10.0-src.tar.bz2 - 89304eb886f84b5c65f3f4079445ef018cdb9a6e59ef4ed2095d37248a3fefcc pypy2-v5.10.0-src.zip - 350914f9b70404781674f2f188f84d440d9d25da46ed9733b3f98269a510e033 pypy2-v5.10.0-win32.zip - 9afa1a36a5fc55ebc3e80576f05f44294f2b0de279862286fe00f5ee139965b1 pypy2-v5.10.0-ppc64.tar.bz2 - 2c32ccfa80e3e2ec56b4cc848526046d7b0de1f2f1a92b0cedeb414ec76745ab pypy2-v5.10.0-ppc64le.tar.bz2 - pypy2.7-6.0.0 sha256:: ad1082d4328ae8f32617b14628648583b82b6d29df3aa42b97bd1853c08c4bc8 pypy2-v6.0.0-linux32.tar.bz2 @@ -466,21 +465,15 @@ 3553b19447cdb627919cc37d76979e15dc755b085e979f5ffa9b25933ec343b3 pypy2-v6.0.0-src.zip 6e2210dae1ae721ed4eb9cba19f15453514b64111511c84f24843c4fdefdaf7f pypy2-v6.0.0-win32.zip -pypy 3.5-v5.10.0 sha256:: +pypy2.7-7.0.0 sha256:: - f5ced20934fff78e55c72aa82a4703954349a5a8099b94e77d74b96a94326a2c pypy3-v5.10.0-osx64-2.tar.bz2 + 446fc208dd77a0048368da830564e6e4180bcd786e524b5369c61785af5c903a pypy2.7-v7.0.0-linux32.tar.bz2 + 971b1909f9fe960c4c643a6940d3f8a60d9a7a2937119535ab0cfaf83498ecd7 pypy2.7-v7.0.0-linux64.tar.bz2 + e7ecb029d9c7a59388838fc4820a50a2f5bee6536010031060e3dfa882730dc8 pypy2.7-v7.0.0-osx64.tar.bz2 + 2ce390d93fa57ba912066a8b6439588bd9cf6aa9cef44d892b8e3e6dba64615e pypy2.7-v7.0.0-s390x.tar.bz2 + 8e60c3fc261d4e42fc59d1f46c88dd1f6d952d0ce6b9cb197d62652ed03df3f9 pypy2.7-v7.0.0-src.tar.bz2 + 7819066fe4b1eaba961ba166434263683cf9184137ca238b26e1fa44906db24b pypy2.7-v7.0.0-src.zip -pypy 3.5-v5.10.1 sha256:: - - a6ceca9ee5dc511de7902164464b88311fec9366c5673d0c00528eda862bbe54 pypy3-v5.10.1-linux32.tar.bz2 - 75a276e1ee1863967bbacb70c5bff636de200768c0ec90e72f7ec17aace0aefe pypy3-v5.10.1-linux64.tar.bz2 - 5065e9ad958d06b9612ba974f43997d20168d4245c054dd43270e4b458782282 pypy3-v5.10.1-linux-armel.tar.bz2 - 203dd595fbad7055340b23326f20c85b0d6c11c4877e3559a437611fc2ac40c2 pypy3-v5.10.1-linux-armhf-raspbian.tar.bz2 - 52f006611513c995fdebba6e72d394186d4085460408cbbe086e5467bf3fb9b6 pypy3-v5.10.1-osx64.tar.bz2 - f5548e06e2fc0c24ec8b6e3c5b09f90081818f7caa3e436dc312592611724713 pypy3-v5.10.1-src.tar.bz2 - 182378d7aab395ee6cf539fb011ec0e384624282834aaaed4a663972a5aa8797 pypy3-v5.10.1-src.zip - 4edf4f021689a529e5a631c5cca72a1a9dc19a6ea2091e64289cdd5b60eaf929 pypy3-v5.10.1-win32.zip - 9ce98481cddede40a3357f7462f2c894bb96f178e2e8715d04feda1476ec1563 pypy3-v5.10.1-s390x.tar.bz2 pypy 3.5-v6.0.0 sha256:: @@ -494,3 +487,22 @@ 8cd3cc2ef362e774edf9c7a6b79dbe42fff75217c5ed96b235a0a792e4421dc4 pypy3-v6.0.0-src.zip 72dddb3746a51f7672c77d619c818e27efe899e08ae82762448e50dbfdc2f5f3 pypy3-v6.0.0-win32.zip + +pypy 3.5-v7.0.0 sha256:: + + 41eb18fe7deb44df9359666c9e659aa7eadf62fa08688ae80a8f6d168111f45b pypy3.5-v7.0.0-linux32.tar.bz2 + c5bc684cfc2ce1545b9b98bc461aea2c185466d142d5dd48fbf245b1beb9279e pypy3.5-v7.0.0-linux64.tar.bz2 + 74b7aca83690f9578250a65a2a565d1ff4aba57c5821eed94b7bca96ec64ba88 pypy3.5-v7.0.0-osx64.tar.bz2 + 641c3e75a987d12d8a685d68e27e57f19627434b389f6f3512a7bcc8c0fc2f60 pypy3.5-v7.0.0-s390x.tar.bz2 + ef62eb73f416e2de8c9fec974c79434f7a650a6aaf9a640d5a2bec12984b0a45 pypy3.5-v7.0.0-src.tar.bz2 + dfecdc6a51a1cda963b3e7582da28b197460e72af23adb7b3b65138ae223171e pypy3.5-v7.0.0-src.zip + d8a1d3fb3fb40c45a6ce5c7ca1f4c072dd850612b7702d70673a23e96781334b pypy3.5-v7.0.0-win32.zip + + +pypy 3.6-v7.0.0-alpha sha256:: + + 8576bde0760c239040706cf4952995eb0e77938b175885392a465a0d1616173d pypy3.6-v7.0.0-linux64.tar.bz2 + 4a95ffd61fd2d626a9c099db6e44889c2a7eecee9cb1cbc29e06603c218ba8e2 pypy3.6-v7.0.0-osx64.tar.bz2 + e1d441789473896d08d51958503ea24a570b9e4a97611edb0c95f69788363a24 pypy3.6-v7.0.0-src.tar.bz2 + 7450e9ecb91b627fec707fd74249f39cb92b96e8889e82faefc5f9494fcf5560 pypy3.6-v7.0.0-src.zip + 645d81472d16922fd592e9261da449cb19847ff7d5eaa89bcf05d9214b6b2698 pypy3.6-v7.0.0-win32.zip diff --git a/source/index.txt b/source/index.txt --- a/source/index.txt +++ b/source/index.txt @@ -4,7 +4,7 @@ --- PyPy is a `fast`_, `compliant`_ alternative implementation of the `Python`_ -language (2.7.13 and 3.5.3). It has several advantages and distinct features: +language (2.7.13 and 3.5.3, 3.6). It has several advantages and distinct features: * **Speed:** thanks to its Just-in-Time compiler, Python programs often run `faster`_ on PyPy. `(What is a JIT compiler?)`_ From pypy.commits at gmail.com Thu Feb 7 01:55:12 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 22:55:12 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying-third-time-a-charm: Immediately free the z_stream after flushing a decompressobj. Message-ID: <5c5bd650.1c69fb81.3da66.f95a@mx.google.com> Author: Julian Berman Branch: zlib-copying-third-time-a-charm Changeset: r95879:4ae1b51c0133 Date: 2019-02-07 07:53 +0100 http://bitbucket.org/pypy/pypy/changeset/4ae1b51c0133/ Log: Immediately free the z_stream after flushing a decompressobj. Matches what CPython does, and doesn't wait for the finalizer, but more importantly will help us actually pass the lib-python test in a minute... diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -359,6 +359,9 @@ else: string, finished, unused_len = result self._save_unconsumed_input(data, finished, unused_len) + if finished: + rzlib.inflateEnd(self.stream) + self.stream = rzlib.null_stream return space.newbytes(string) From pypy.commits at gmail.com Thu Feb 7 01:55:14 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 22:55:14 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying-third-time-a-charm: Notice when we're copying a flushed decompressobj too. Message-ID: <5c5bd652.1c69fb81.8121f.75e0@mx.google.com> Author: Julian Berman Branch: zlib-copying-third-time-a-charm Changeset: r95880:1c38b412ceb1 Date: 2019-02-07 07:54 +0100 http://bitbucket.org/pypy/pypy/changeset/1c38b412ceb1/ Log: Notice when we're copying a flushed decompressobj too. Hopefully we go green. Otherwise we go home... diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -323,12 +323,16 @@ try: self.lock() try: + if not self.stream: + raise oefmt( + space.w_ValueError, + "Decompressor was already flushed", + ) copied = rzlib.inflateCopy(self.stream) finally: self.unlock() except rzlib.RZlibError as e: raise zlib_error(space, e.msg) - return Decompress( space=space, stream=copied, From pypy.commits at gmail.com Thu Feb 7 01:57:06 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Wed, 06 Feb 2019 22:57:06 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying-third-time-a-charm: Whatsnew. Message-ID: <5c5bd6c2.1c69fb81.f85c8.42c2@mx.google.com> Author: Julian Berman Branch: zlib-copying-third-time-a-charm Changeset: r95881:a95cf340ec7c Date: 2019-02-07 07:56 +0100 http://bitbucket.org/pypy/pypy/changeset/a95cf340ec7c/ Log: Whatsnew. 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 @@ -5,6 +5,11 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f +.. branch: zlib-copying-third-time-a-charm + +Make sure zlib decompressobjs have their streams deallocated immediately +on flush. + .. branch: zlib-copying-redux Fix calling copy on already-flushed compressobjs. From pypy.commits at gmail.com Thu Feb 7 05:26:15 2019 From: pypy.commits at gmail.com (antocuni) Date: Thu, 07 Feb 2019 02:26:15 -0800 (PST) Subject: [pypy-commit] pypy.org extradoc: add sha256 for pypy2.7-7.0-win32 Message-ID: <5c5c07c7.1c69fb81.ee7a3.e0f7@mx.google.com> Author: Antonio Cuni Branch: extradoc Changeset: r931:80d526951b3b Date: 2019-02-07 11:26 +0100 http://bitbucket.org/pypy/pypy.org/changeset/80d526951b3b/ Log: add sha256 for pypy2.7-7.0-win32 diff --git a/source/download.txt b/source/download.txt --- a/source/download.txt +++ b/source/download.txt @@ -473,6 +473,7 @@ 2ce390d93fa57ba912066a8b6439588bd9cf6aa9cef44d892b8e3e6dba64615e pypy2.7-v7.0.0-s390x.tar.bz2 8e60c3fc261d4e42fc59d1f46c88dd1f6d952d0ce6b9cb197d62652ed03df3f9 pypy2.7-v7.0.0-src.tar.bz2 7819066fe4b1eaba961ba166434263683cf9184137ca238b26e1fa44906db24b pypy2.7-v7.0.0-src.zip + 04477a41194240cd71e485c3f41dec35a787d1b3bc030f9aa59e5e81bcf4118b pypy2.7-v7.0.0-win32.zip pypy 3.5-v6.0.0 sha256:: From pypy.commits at gmail.com Thu Feb 7 06:13:43 2019 From: pypy.commits at gmail.com (stian) Date: Thu, 07 Feb 2019 03:13:43 -0800 (PST) Subject: [pypy-commit] pypy math-improvements: Fix for issue #2946 ? Message-ID: <5c5c12e7.1c69fb81.c208c.03b3@mx.google.com> Author: stian Branch: math-improvements Changeset: r95882:9bb63f07b007 Date: 2019-02-07 12:13 +0100 http://bitbucket.org/pypy/pypy/changeset/9bb63f07b007/ Log: Fix for issue #2946 ? diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py --- a/pypy/objspace/std/intobject.py +++ b/pypy/objspace/std/intobject.py @@ -506,8 +506,14 @@ try: result = _pow(space, x, y, z) - except (OverflowError, ValueError): + except OverflowError: return _pow_ovf2long(space, x, self, y, w_exponent, w_modulus) + except ValueError: + # float result, so let avoid a roundtrip in rbigint. + self = self.descr_float(space) + w_exponent = w_exponent.descr_float(space) + return space.pow(self, w_exponent, space.w_None) + return space.newint(result) @unwrap_spec(w_modulus=WrappedDefault(None)) From pypy.commits at gmail.com Fri Feb 8 04:51:11 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Fri, 08 Feb 2019 01:51:11 -0800 (PST) Subject: [pypy-commit] pypy default: remove posix restriction on inet_pton Message-ID: <5c5d510f.1c69fb81.f0fcd.0522@mx.google.com> Author: andrewjlawrence Branch: Changeset: r95885:c95a472b8e59 Date: 2019-02-06 22:03 +0000 http://bitbucket.org/pypy/pypy/changeset/c95a472b8e59/ Log: remove posix restriction on inet_pton diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py --- a/rpython/rlib/_rsocket_rffi.py +++ b/rpython/rlib/_rsocket_rffi.py @@ -1191,14 +1191,14 @@ inet_ntoa = external('inet_ntoa', [in_addr], rffi.CCHARP) -if _POSIX: - inet_pton = external('inet_pton', [rffi.INT, rffi.CCHARP, - rffi.VOIDP], rffi.INT, - save_err=SAVE_ERR) - inet_ntop = external('inet_ntop', [rffi.INT, rffi.VOIDP, CCHARP, - socklen_t], CCHARP, - save_err=SAVE_ERR) +inet_pton = external('inet_pton', [rffi.INT, rffi.CCHARP, + rffi.VOIDP], rffi.INT, + save_err=SAVE_ERR) + +inet_ntop = external('inet_ntop', [rffi.INT, rffi.VOIDP, CCHARP, + socklen_t], CCHARP, + save_err=SAVE_ERR) inet_addr = external('inet_addr', [rffi.CCHARP], rffi.UINT) socklen_t_ptr = lltype.Ptr(rffi.CFixedArray(socklen_t, 1)) From pypy.commits at gmail.com Fri Feb 8 04:51:13 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Fri, 08 Feb 2019 01:51:13 -0800 (PST) Subject: [pypy-commit] pypy default: Merged inet_pton change Message-ID: <5c5d5111.1c69fb81.48a2c.4eac@mx.google.com> Author: andrewjlawrence Branch: Changeset: r95886:d8e2362354de Date: 2019-02-08 09:39 +0000 http://bitbucket.org/pypy/pypy/changeset/d8e2362354de/ Log: Merged inet_pton change diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py --- a/rpython/rlib/_rsocket_rffi.py +++ b/rpython/rlib/_rsocket_rffi.py @@ -1191,14 +1191,14 @@ inet_ntoa = external('inet_ntoa', [in_addr], rffi.CCHARP) -if _POSIX: - inet_pton = external('inet_pton', [rffi.INT, rffi.CCHARP, - rffi.VOIDP], rffi.INT, - save_err=SAVE_ERR) - inet_ntop = external('inet_ntop', [rffi.INT, rffi.VOIDP, CCHARP, - socklen_t], CCHARP, - save_err=SAVE_ERR) +inet_pton = external('inet_pton', [rffi.INT, rffi.CCHARP, + rffi.VOIDP], rffi.INT, + save_err=SAVE_ERR) + +inet_ntop = external('inet_ntop', [rffi.INT, rffi.VOIDP, CCHARP, + socklen_t], CCHARP, + save_err=SAVE_ERR) inet_addr = external('inet_addr', [rffi.CCHARP], rffi.UINT) socklen_t_ptr = lltype.Ptr(rffi.CFixedArray(socklen_t, 1)) From pypy.commits at gmail.com Fri Feb 8 04:57:57 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Fri, 08 Feb 2019 01:57:57 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: Add _overlapped.py Message-ID: <5c5d52a5.1c69fb81.4360f.95a9@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r95887:293f10f71a75 Date: 2019-02-06 13:19 +0000 http://bitbucket.org/pypy/pypy/changeset/293f10f71a75/ Log: Add _overlapped.py diff --git a/lib_pypy/_overlapped.py b/lib_pypy/_overlapped.py new file mode 100644 --- /dev/null +++ b/lib_pypy/_overlapped.py @@ -0,0 +1,97 @@ +""" +Support routines for overlapping io. +Currently, this extension module is only required when using the +modules on Windows. +""" + +import sys +if sys.platform != 'win32': + raise ImportError("The '_winapi' module is only available on Windows") + +# Declare external Win32 functions + +from _pypy_winbase_cffi import ffi as _ffi +_kernel32 = _ffi.dlopen('kernel32') + +GetVersion = _kernel32.GetVersion +NULL = _ffi.NULL + +class Overlapped(object): + def __init__(self, handle): + self.overlapped = _ffi.new('OVERLAPPED[1]') + self.handle = handle + self.readbuffer = None + self.pending = 0 + self.completed = 0 + self.writebuffer = None + self.overlapped[0].hEvent = \ + _kernel32.CreateEventW(NULL, True, False, NULL) + + def __del__(self): + # do this somehow else + xxx + err = _kernel32.GetLastError() + bytes = _ffi.new('DWORD[1]') + o = overlapped[0] + if overlapped[0].pending: + if _kernel32.CancelIoEx(o.handle, o.overlapped) & \ + self.GetOverlappedResult(o.handle, o.overlapped, _ffi.addressof(bytes), True): + # The operation is no longer pending, nothing to do + pass + else: + raise RuntimeError('deleting an overlapped struct with a pending operation not supported') + + @property + def event(self): + return None + + def GetOverlappedResult(self, wait): + transferred = _ffi.new('DWORD[1]', [0]) + res = _kernel32.GetOverlappedResult(self.handle, self.overlapped, transferred, wait != 0) + if res: + err = ERROR_SUCCESS + else: + err = GetLastError() + if err in (ERROR_SUCCESS, ERROR_MORE_DATA, ERROR_OPERATION_ABORTED): + self.completed = 1 + self.pending = 0 + elif res == ERROR_IO_INCOMPLETE: + pass + else: + self.pending = 0 + raise _WinError() + if self.completed and self.read_buffer: + if transferred != len(self.read_buffer): + raise _WinError() + return transferred[0], err + + def getbuffer(self): + xxx + return None + + def cancel(self): + xxx + return None + + +def ConnectNamedPipe(handle, overlapped=False): + if overlapped: + ov = Overlapped(handle) + else: + ov = Overlapped(None) + success = _kernel32.ConnectNamedPipe(handle, ov.overlapped) + if overlapped: + # Overlapped ConnectNamedPipe never returns a success code + assert success == 0 + err = _kernel32.GetLastError() + if err == ERROR_IO_PENDING: + ov.pending = 1 + elif err == ERROR_PIPE_CONNECTED: + _kernel32.SetEvent(ov.overlapped[0].hEvent) + else: + del ov + raise _WinError() + return ov + elif not success: + raise _WinError() + diff --git a/lib_pypy/_winapi.py b/lib_pypy/_winapi.py --- a/lib_pypy/_winapi.py +++ b/lib_pypy/_winapi.py @@ -89,7 +89,7 @@ # The operation is no longer pending, nothing to do pass else: - raise RuntimeError('deleting an overlapped strucwith a pending operation not supported') + raise RuntimeError('deleting an overlapped struct with a pending operation not supported') @property def event(self): From pypy.commits at gmail.com Fri Feb 8 04:57:59 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Fri, 08 Feb 2019 01:57:59 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: Work in progress Message-ID: <5c5d52a7.1c69fb81.56f68.9a45@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r95888:5282280ffad4 Date: 2019-02-06 21:30 +0000 http://bitbucket.org/pypy/pypy/changeset/5282280ffad4/ Log: Work in progress diff --git a/lib_pypy/_overlapped.py b/lib_pypy/_overlapped.py --- a/lib_pypy/_overlapped.py +++ b/lib_pypy/_overlapped.py @@ -6,7 +6,7 @@ import sys if sys.platform != 'win32': - raise ImportError("The '_winapi' module is only available on Windows") + raise ImportError("The '_overlapped' module is only available on Windows") # Declare external Win32 functions @@ -16,6 +16,10 @@ GetVersion = _kernel32.GetVersion NULL = _ffi.NULL + +from _winapi import INVALID_HANDLE_VALUE, _MAX_PATH , _Z +import _winapi + class Overlapped(object): def __init__(self, handle): self.overlapped = _ffi.new('OVERLAPPED[1]') @@ -49,20 +53,20 @@ transferred = _ffi.new('DWORD[1]', [0]) res = _kernel32.GetOverlappedResult(self.handle, self.overlapped, transferred, wait != 0) if res: - err = ERROR_SUCCESS + err = _winapi.ERROR_SUCCESS else: err = GetLastError() - if err in (ERROR_SUCCESS, ERROR_MORE_DATA, ERROR_OPERATION_ABORTED): + if err in (_winapi.ERROR_SUCCESS, _winapi.ERROR_MORE_DATA, _winapi.ERROR_OPERATION_ABORTED): self.completed = 1 self.pending = 0 - elif res == ERROR_IO_INCOMPLETE: + elif res == _winapi.ERROR_IO_INCOMPLETE: pass else: self.pending = 0 - raise _WinError() + raise _winapi._WinError() if self.completed and self.read_buffer: if transferred != len(self.read_buffer): - raise _WinError() + raise _winapi._WinError() return transferred[0], err def getbuffer(self): @@ -73,6 +77,12 @@ xxx return None + +def CreateEvent(eventattributes, manualreset, initialstate, name): + event = _kernel32.CreateEventW(NULL, manualreset, initialstate, _Z(name)) + if not event: + raise _winapi._WinError() + return event def ConnectNamedPipe(handle, overlapped=False): if overlapped: @@ -84,14 +94,36 @@ # Overlapped ConnectNamedPipe never returns a success code assert success == 0 err = _kernel32.GetLastError() - if err == ERROR_IO_PENDING: + if err == _winapi.ERROR_IO_PENDING: ov.pending = 1 - elif err == ERROR_PIPE_CONNECTED: + elif err == _winapi.ERROR_PIPE_CONNECTED: _kernel32.SetEvent(ov.overlapped[0].hEvent) else: del ov - raise _WinError() + raise _winapi._WinError() return ov elif not success: - raise _WinError() + raise _winapi._WinError() +def CreateIoCompletionPort(handle, existingcompletionport, completionkey, numberofconcurrentthreads): + return None + + +def GetQueuedCompletionStatus(handle, milliseconds): + return None + + +def post_to_queue_callback(lpparameter, timerorwaitfired) + pdata = _ffi.cast("PostCallbackData", lpparameter) + _kernel32.PostQueuedCompletionStatus(pdata.hCompletionPort, timeorwaitfired, 0, pdata.Overlapped) + + +def RegisterWaitWithQueue(object, completionport, ovaddress, miliseconds) + data = _ffi.new('PostCallbackData[1]') + newwaitobject = _ffi.new("HANDLE[1]") + + data.hCompletionPort = completionport + data.Overlapped = ovaddress + success = _kernel32.RegisterWaitForSingleObject(newwaitobject, object, data, _ffi.post_to_queue_callback, + + return None diff --git a/lib_pypy/_pypy_winbase_build.py b/lib_pypy/_pypy_winbase_build.py --- a/lib_pypy/_pypy_winbase_build.py +++ b/lib_pypy/_pypy_winbase_build.py @@ -91,6 +91,11 @@ } OVERLAPPED, *LPOVERLAPPED; +typedef struct _PostCallbackData { + HANDLE hCompletionPort; + LPOVERLAPPED Overlapped; +} PostCallbackData, *LPPostCallbackData; + DWORD WINAPI GetVersion(void); BOOL WINAPI CreatePipe(PHANDLE, PHANDLE, void *, DWORD); HANDLE WINAPI CreateNamedPipeA(LPCSTR, DWORD, DWORD, DWORD, DWORD, DWORD, diff --git a/lib_pypy/_pypy_winbase_cffi.py b/lib_pypy/_pypy_winbase_cffi.py --- a/lib_pypy/_pypy_winbase_cffi.py +++ b/lib_pypy/_pypy_winbase_cffi.py @@ -3,8 +3,8 @@ ffi = _cffi_backend.FFI('_pypy_winbase_cffi', _version = 0x2601, - _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xAB\x03\x00\x00\x13\x11\x00\x00\xB0\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xAA\x03\x00\x00\xA8\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xA7\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x29\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x2E\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x2E\x11\x00\x00\x2E\x11\x00\x00\x2E\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x6B\x03\x00\x00\x49\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x49\x11\x00\x00\x49\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x33\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x49\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x66\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x66\x0D\x00\x00\x00\x0F\x00\x00\x66\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xA9\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xAB\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x6E\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x6B\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x71\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x71\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x49\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x77\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xB0\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\x05\x09\x00\x00\x03\x09\x00\x00\x02\x01\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xAF\x03\x00\x00\x04\x01\x00\x00\x00\x01', + _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xAD\x03\x00\x00\x13\x11\x00\x00\xB2\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xAC\x03\x00\x00\xA8\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xA7\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x29\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x2E\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x2E\x11\x00\x00\x2E\x11\x00\x00\x2E\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x6B\x03\x00\x00\x49\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x49\x11\x00\x00\x49\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x33\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x49\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x66\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x66\x0D\x00\x00\x00\x0F\x00\x00\x66\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xAB\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xAD\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x6E\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x6B\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x71\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x71\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x49\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x77\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xB2\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xAA\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x02\x01\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xB1\x03\x00\x00\x04\x01\x00\x00\x00\x01', _globals = (b'\x00\x00\x27\x23CancelIoEx',0,b'\x00\x00\x24\x23CloseHandle',0,b'\x00\x00\x27\x23ConnectNamedPipe',0,b'\x00\x00\x6D\x23CreateEventA',0,b'\x00\x00\x73\x23CreateEventW',0,b'\x00\x00\x79\x23CreateFileA',0,b'\x00\x00\x9B\x23CreateFileW',0,b'\x00\x00\x82\x23CreateNamedPipeA',0,b'\x00\x00\x91\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x48\x23CreateProcessW',0,b'\x00\x00\x3F\x23DuplicateHandle',0,b'\x00\x00\x8F\x23GetCurrentProcess',0,b'\x00\x00\x35\x23GetExitCodeProcess',0,b'\x00\x00\x63\x23GetLastError',0,b'\x00\x00\x5E\x23GetModuleFileNameW',0,b'\x00\x00\x2B\x23GetOverlappedResult',0,b'\x00\x00\x8C\x23GetStdHandle',0,b'\x00\x00\x63\x23GetVersion',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x57\x23SetErrorMode',0,b'\x00\x00\xA4\x23SetEvent',0,b'\x00\x00\x39\x23SetNamedPipeHandleState',0,b'\x00\x00\x31\x23TerminateProcess',0,b'\x00\x00\x5A\x23WaitForSingleObject',0,b'\x00\x00\x54\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x68\x23_getwch',0,b'\x00\x00\x68\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x6A\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x65\x23_ungetwch',0), - _struct_unions = ((b'\x00\x00\x00\xAD\x00\x00\x00\x03$1',b'\x00\x00\xAC\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xAC\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xA8\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xAA\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x66\x11wShowWindow',b'\x00\x00\x66\x11cbReserved2',b'\x00\x00\xAE\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xA7\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xAD\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xA9\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle')), - _typenames = (b'\x00\x00\x00\x29LPOVERLAPPED',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\x6ELPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\xA7OVERLAPPED',b'\x00\x00\x00\xA8PROCESS_INFORMATION',b'\x00\x00\x00\x6EPSECURITY_ATTRIBUTES',b'\x00\x00\x00\xA9SECURITY_ATTRIBUTES',b'\x00\x00\x00\xAASTARTUPINFO',b'\x00\x00\x00\x66wint_t'), + _struct_unions = ((b'\x00\x00\x00\xAF\x00\x00\x00\x03$1',b'\x00\x00\xAE\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xAE\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xA8\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xAC\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x66\x11wShowWindow',b'\x00\x00\x66\x11cbReserved2',b'\x00\x00\xB0\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xA7\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xAF\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xAA\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x29\x11Overlapped'),(b'\x00\x00\x00\xAB\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle')), + _typenames = (b'\x00\x00\x00\x29LPOVERLAPPED',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xA9LPPostCallbackData',b'\x00\x00\x00\x6ELPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\xA7OVERLAPPED',b'\x00\x00\x00\xA8PROCESS_INFORMATION',b'\x00\x00\x00\x6EPSECURITY_ATTRIBUTES',b'\x00\x00\x00\xAAPostCallbackData',b'\x00\x00\x00\xABSECURITY_ATTRIBUTES',b'\x00\x00\x00\xACSTARTUPINFO',b'\x00\x00\x00\x66wint_t'), ) From pypy.commits at gmail.com Fri Feb 8 04:58:01 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Fri, 08 Feb 2019 01:58:01 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: Merge branch Message-ID: <5c5d52a9.1c69fb81.4c056.df7e@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r95889:45806a696752 Date: 2019-02-08 09:56 +0000 http://bitbucket.org/pypy/pypy/changeset/45806a696752/ Log: Merge branch diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -4,8 +4,10 @@ *~ .*.swp .idea +.mypy_cache .project .pydevproject +.vscode __pycache__ .venv .cache diff --git a/lib_pypy/_csv.py b/lib_pypy/_csv.py deleted file mode 100644 --- a/lib_pypy/_csv.py +++ /dev/null @@ -1,573 +0,0 @@ -"""CSV parsing and writing. - -This module provides classes that assist in the reading and writing -of Comma Separated Value (CSV) files, and implements the interface -described by PEP 305. Although many CSV files are simple to parse, -the format is not formally defined by a stable specification and -is subtle enough that parsing lines of a CSV file with something -like line.split(\",\") is bound to fail. The module supports three -basic APIs: reading, writing, and registration of dialects. - - -DIALECT REGISTRATION: - -Readers and writers support a dialect argument, which is a convenient -handle on a group of settings. When the dialect argument is a string, -it identifies one of the dialects previously registered with the module. -If it is a class or instance, the attributes of the argument are used as -the settings for the reader or writer: - - class excel: - delimiter = ',' - quotechar = '\"' - escapechar = None - doublequote = True - skipinitialspace = False - lineterminator = '\\r\\n' - quoting = QUOTE_MINIMAL - -SETTINGS: - - * quotechar - specifies a one-character string to use as the - quoting character. It defaults to '\"'. - * delimiter - specifies a one-character string to use as the - field separator. It defaults to ','. - * skipinitialspace - specifies how to interpret whitespace which - immediately follows a delimiter. It defaults to False, which - means that whitespace immediately following a delimiter is part - of the following field. - * lineterminator - specifies the character sequence which should - terminate rows. - * quoting - controls when quotes should be generated by the writer. - It can take on any of the following module constants: - - csv.QUOTE_MINIMAL means only when required, for example, when a - field contains either the quotechar or the delimiter - csv.QUOTE_ALL means that quotes are always placed around fields. - csv.QUOTE_NONNUMERIC means that quotes are always placed around - fields which do not parse as integers or floating point - numbers. - csv.QUOTE_NONE means that quotes are never placed around fields. - * escapechar - specifies a one-character string used to escape - the delimiter when quoting is set to QUOTE_NONE. - * doublequote - controls the handling of quotes inside fields. When - True, two consecutive quotes are interpreted as one during read, - and when writing, each quote character embedded in the data is - written as two quotes. -""" - -__version__ = "1.0" - -QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE = range(4) -_dialects = {} -_field_limit = 128 * 1024 # max parsed field size - -class Error(Exception): - pass - -class Dialect(object): - """CSV dialect - - The Dialect type records CSV parsing and generation options.""" - - __slots__ = ["_delimiter", "_doublequote", "_escapechar", - "_lineterminator", "_quotechar", "_quoting", - "_skipinitialspace", "_strict"] - - def __new__(cls, dialect, **kwargs): - - for name in kwargs: - if '_' + name not in Dialect.__slots__: - raise TypeError("unexpected keyword argument '%s'" % - (name,)) - - if dialect is not None: - if isinstance(dialect, str): - dialect = get_dialect(dialect) - - # Can we reuse this instance? - if (isinstance(dialect, Dialect) - and all(value is None for value in kwargs.values())): - return dialect - - self = object.__new__(cls) - - - def set_char(x): - if x is None: - return None - if isinstance(x, str) and len(x) <= 1: - return x - raise TypeError("%r must be a 1-character string" % (name,)) - def set_str(x): - if isinstance(x, str): - return x - raise TypeError("%r must be a string" % (name,)) - def set_quoting(x): - if x in range(4): - return x - raise TypeError("bad 'quoting' value") - - attributes = {"delimiter": (',', set_char), - "doublequote": (True, bool), - "escapechar": (None, set_char), - "lineterminator": ("\r\n", set_str), - "quotechar": ('"', set_char), - "quoting": (QUOTE_MINIMAL, set_quoting), - "skipinitialspace": (False, bool), - "strict": (False, bool), - } - - # Copy attributes - notset = object() - for name in Dialect.__slots__: - name = name[1:] - value = notset - if name in kwargs: - value = kwargs[name] - elif dialect is not None: - value = getattr(dialect, name, notset) - - # mapping by name: (default, converter) - if value is notset: - value = attributes[name][0] - if name == 'quoting' and not self.quotechar: - value = QUOTE_NONE - else: - converter = attributes[name][1] - if converter: - value = converter(value) - - setattr(self, '_' + name, value) - - if not self.delimiter: - raise TypeError("delimiter must be set") - - if self.quoting != QUOTE_NONE and not self.quotechar: - raise TypeError("quotechar must be set if quoting enabled") - - if not self.lineterminator: - raise TypeError("lineterminator must be set") - - return self - - delimiter = property(lambda self: self._delimiter) - doublequote = property(lambda self: self._doublequote) - escapechar = property(lambda self: self._escapechar) - lineterminator = property(lambda self: self._lineterminator) - quotechar = property(lambda self: self._quotechar) - quoting = property(lambda self: self._quoting) - skipinitialspace = property(lambda self: self._skipinitialspace) - strict = property(lambda self: self._strict) - - -def _call_dialect(dialect_inst, kwargs): - return Dialect(dialect_inst, **kwargs) - -def register_dialect(name, dialect=None, **kwargs): - """Create a mapping from a string name to a dialect class. - dialect = csv.register_dialect(name, dialect)""" - if not isinstance(name, str): - raise TypeError("dialect name must be a string or unicode") - - dialect = _call_dialect(dialect, kwargs) - _dialects[name] = dialect - -def unregister_dialect(name): - """Delete the name/dialect mapping associated with a string name.\n - csv.unregister_dialect(name)""" - try: - del _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def get_dialect(name): - """Return the dialect instance associated with name. - dialect = csv.get_dialect(name)""" - try: - return _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def list_dialects(): - """Return a list of all know dialect names - names = csv.list_dialects()""" - return list(_dialects) - -class Reader(object): - """CSV reader - - Reader objects are responsible for reading and parsing tabular data - in CSV format.""" - - - (START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD, - IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD, - EAT_CRNL) = range(8) - - def __init__(self, iterator, dialect=None, **kwargs): - self.dialect = _call_dialect(dialect, kwargs) - self.input_iter = iter(iterator) - self.line_num = 0 - - self._parse_reset() - - def _parse_reset(self): - self.field = '' - self.fields = [] - self.state = self.START_RECORD - self.numeric_field = False - - def __iter__(self): - return self - - def __next__(self): - self._parse_reset() - while True: - try: - line = next(self.input_iter) - except StopIteration: - # End of input OR exception - if len(self.field) > 0: - raise Error("newline inside string") - raise - - self.line_num += 1 - - if '\0' in line: - raise Error("line contains NULL byte") - pos = 0 - while pos < len(line): - pos = self._parse_process_char(line, pos) - self._parse_eol() - - if self.state == self.START_RECORD: - break - - fields = self.fields - self.fields = [] - return fields - - def _parse_process_char(self, line, pos): - c = line[pos] - if self.state == self.IN_FIELD: - # in unquoted field - pos2 = pos - while True: - if c in '\n\r': - # end of line - return [fields] - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.EAT_CRNL - elif c == self.dialect.escapechar: - # possible escaped character - pos2 -= 1 - self.state = self.ESCAPED_CHAR - elif c == self.dialect.delimiter: - # save field - wait for new field - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.START_FIELD - else: - # normal character - save in field - pos2 += 1 - if pos2 < len(line): - c = line[pos2] - continue - break - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - 1 - - elif self.state == self.START_RECORD: - if c in '\n\r': - self.state = self.EAT_CRNL - else: - self.state = self.START_FIELD - # restart process - self._parse_process_char(line, pos) - - elif self.state == self.START_FIELD: - if c in '\n\r': - # save empty field - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # start quoted field - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.escapechar: - # possible escaped character - self.state = self.ESCAPED_CHAR - elif c == ' ' and self.dialect.skipinitialspace: - # ignore space at start of field - pass - elif c == self.dialect.delimiter: - # save empty field - self._parse_save_field() - else: - # begin new unquoted field - if self.dialect.quoting == QUOTE_NONNUMERIC: - self.numeric_field = True - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.IN_QUOTED_FIELD: - if c == self.dialect.escapechar: - # possible escape character - self.state = self.ESCAPE_IN_QUOTED_FIELD - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - if self.dialect.doublequote: - # doublequote; " represented by "" - self.state = self.QUOTE_IN_QUOTED_FIELD - else: - #end of quote part of field - self.state = self.IN_FIELD - else: - # normal character - save in field - self._parse_add_char(c) - - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # doublequote - seen a quote in a quoted field - if (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # save "" as " - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.delimiter: - # save field - wait for new field - self._parse_save_field() - self.state = self.START_FIELD - elif c in '\r\n': - # end of line - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif not self.dialect.strict: - self._parse_add_char(c) - self.state = self.IN_FIELD - else: - raise Error("'%c' expected after '%c'" % - (self.dialect.delimiter, self.dialect.quotechar)) - - elif self.state == self.EAT_CRNL: - if c not in '\r\n': - raise Error("new-line character seen in unquoted field - " - "do you need to open the file " - "in universal-newline mode?") - - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - return pos + 1 - - def _parse_eol(self): - if self.state == self.EAT_CRNL: - self.state = self.START_RECORD - elif self.state == self.START_RECORD: - # empty line - return [] - pass - elif self.state == self.IN_FIELD: - # in unquoted field - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.START_FIELD: - # save empty field - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char('\n') - self.state = self.IN_FIELD - elif self.state == self.IN_QUOTED_FIELD: - pass - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char('\n') - self.state = self.IN_QUOTED_FIELD - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - def _parse_save_field(self): - field, self.field = self.field, '' - if self.numeric_field: - self.numeric_field = False - field = float(field) - self.fields.append(field) - - def _parse_add_char(self, c): - if len(self.field) + len(c) > _field_limit: - raise Error("field larger than field limit (%d)" % (_field_limit)) - self.field += c - - -class Writer(object): - """CSV writer - - Writer objects are responsible for generating tabular data - in CSV format from sequence input.""" - - def __init__(self, file, dialect=None, **kwargs): - if not (hasattr(file, 'write') and callable(file.write)): - raise TypeError("argument 1 must have a 'write' method") - self.writeline = file.write - self.dialect = _call_dialect(dialect, kwargs) - - def _join_reset(self): - self.rec = [] - self.num_fields = 0 - - def _join_append(self, field, quoted, quote_empty): - dialect = self.dialect - # If this is not the first field we need a field separator - if self.num_fields > 0: - self.rec.append(dialect.delimiter) - - if dialect.quoting == QUOTE_NONE: - need_escape = tuple(dialect.lineterminator) + ( - dialect.escapechar, # escapechar always first - dialect.delimiter, dialect.quotechar) - - else: - for c in tuple(dialect.lineterminator) + ( - dialect.delimiter, dialect.escapechar): - if c and c in field: - quoted = True - - need_escape = () - if dialect.quotechar in field: - if dialect.doublequote: - field = field.replace(dialect.quotechar, - dialect.quotechar * 2) - quoted = True - else: - need_escape = (dialect.quotechar,) - - - for c in need_escape: - if c and c in field: - if not dialect.escapechar: - raise Error("need to escape, but no escapechar set") - field = field.replace(c, dialect.escapechar + c) - - # If field is empty check if it needs to be quoted - if field == '' and quote_empty: - if dialect.quoting == QUOTE_NONE: - raise Error("single empty field record must be quoted") - quoted = 1 - - if quoted: - field = dialect.quotechar + field + dialect.quotechar - - self.rec.append(field) - self.num_fields += 1 - - - - def writerow(self, row): - dialect = self.dialect - try: - rowlen = len(row) - except TypeError: - raise Error("sequence expected") - - # join all fields in internal buffer - self._join_reset() - - for field in row: - quoted = False - if dialect.quoting == QUOTE_NONNUMERIC: - try: - float(field) - except: - quoted = True - # This changed since 2.5: - # quoted = not isinstance(field, (int, long, float)) - elif dialect.quoting == QUOTE_ALL: - quoted = True - - if field is None: - value = "" - elif isinstance(field, float): - value = repr(field) - else: - value = str(field) - self._join_append(value, quoted, rowlen == 1) - - # add line terminator - self.rec.append(dialect.lineterminator) - - self.writeline(''.join(self.rec)) - - def writerows(self, rows): - for row in rows: - self.writerow(row) - -def reader(*args, **kwargs): - """ - csv_reader = reader(iterable [, dialect='excel'] - [optional keyword args]) - for row in csv_reader: - process(row) - - The "iterable" argument can be any object that returns a line - of input for each iteration, such as a file object or a list. The - optional \"dialect\" parameter is discussed below. The function - also accepts optional keyword arguments which override settings - provided by the dialect. - - The returned object is an iterator. Each iteration returns a row - of the CSV file (which can span multiple input lines)""" - - return Reader(*args, **kwargs) - -def writer(*args, **kwargs): - """ - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - for row in sequence: - csv_writer.writerow(row) - - [or] - - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - csv_writer.writerows(rows) - - The \"fileobj\" argument can be any object that supports the file API.""" - return Writer(*args, **kwargs) - - -undefined = object() -def field_size_limit(limit=undefined): - """Sets an upper limit on parsed fields. - csv.field_size_limit([limit]) - - Returns old limit. If limit is not given, no new limit is set and - the old limit is returned""" - - global _field_limit - old_limit = _field_limit - - if limit is not undefined: - if not isinstance(limit, int): - raise TypeError("int expected, got %s" % - (limit.__class__.__name__,)) - _field_limit = limit - - return old_limit diff --git a/lib_pypy/cffi/pkgconfig.py b/lib_pypy/cffi/pkgconfig.py new file mode 100644 --- /dev/null +++ b/lib_pypy/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except EnvironmentError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret diff --git a/lib_pypy/pwd.py b/lib_pypy/pwd.py deleted file mode 100644 --- a/lib_pypy/pwd.py +++ /dev/null @@ -1,113 +0,0 @@ -# indirectly based on ctypes implementation: Victor Stinner, 2008-05-08 -""" -This module provides access to the Unix password database. -It is available on all Unix versions. - -Password database entries are reported as 7-tuples containing the following -items from the password database (see `'), in order: -pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell. -The uid and gid items are integers, all others are strings. An -exception is raised if the entry asked for cannot be found. -""" - -from _pwdgrp_cffi import ffi, lib -import _structseq -import _thread -_lock = _thread.allocate_lock() - -try: from __pypy__ import builtinify -except ImportError: builtinify = lambda f: f - - -class struct_passwd(metaclass=_structseq.structseqtype): - """ - pwd.struct_passwd: Results from getpw*() routines. - - This object may be accessed either as a tuple of - (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell) - or via the object attributes as named in the above tuple. - """ - name = "pwd.struct_passwd" - - pw_name = _structseq.structseqfield(0) - pw_passwd = _structseq.structseqfield(1) - pw_uid = _structseq.structseqfield(2) - pw_gid = _structseq.structseqfield(3) - pw_gecos = _structseq.structseqfield(4) - pw_dir = _structseq.structseqfield(5) - pw_shell = _structseq.structseqfield(6) - - -def _mkpwent(pw): - return struct_passwd([ - ffi.string(pw.pw_name), - ffi.string(pw.pw_passwd), - pw.pw_uid, - pw.pw_gid, - ffi.string(pw.pw_gecos), - ffi.string(pw.pw_dir), - ffi.string(pw.pw_shell)]) - - at builtinify -def getpwuid(uid): - """ - getpwuid(uid) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given numeric user ID. - See pwd.__doc__ for more on password database entries. - """ - with _lock: - pw = lib.getpwuid(uid) - if not pw: - raise KeyError("getpwuid(): uid not found: %s" % uid) - return _mkpwent(pw) - - at builtinify -def getpwnam(name): - """ - getpwnam(name) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given user name. - See pwd.__doc__ for more on password database entries. - """ - if not isinstance(name, basestring): - raise TypeError("expected string") - name = str(name) - with _lock: - pw = lib.getpwnam(name) - if not pw: - raise KeyError("getpwname(): name not found: %s" % name) - return _mkpwent(pw) - - at builtinify -def getpwall(): - """ - getpwall() -> list_of_entries - Return a list of all available password database entries, in arbitrary order. - See pwd.__doc__ for more on password database entries. - """ - users = [] - with _lock: - lib.setpwent() - while True: - pw = lib.getpwent() - if not pw: - break - users.append(_mkpwent(pw)) - lib.endpwent() - return users - -__all__ = ('struct_passwd', 'getpwuid', 'getpwnam', 'getpwall') - -if __name__ == "__main__": -# Uncomment next line to test CPython implementation -# from pwd import getpwuid, getpwnam, getpwall - from os import getuid - uid = getuid() - pw = getpwuid(uid) - print("uid %s: %s" % (pw.pw_uid, pw)) - name = pw.pw_name - print("name %r: %s" % (name, getpwnam(name))) - print("All:") - for pw in getpwall(): - print(pw) 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 @@ -5,3 +5,11 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f +.. branch: zlib-copying-redux + +Fix calling copy on already-flushed compressobjs. + +.. branch: zlib-copying + +The zlib module's compressobj and decompressobj now expose copy methods +as they do on CPython. diff --git a/pypy/module/_sre/interp_sre.py b/pypy/module/_sre/interp_sre.py --- a/pypy/module/_sre/interp_sre.py +++ b/pypy/module/_sre/interp_sre.py @@ -622,7 +622,7 @@ assert idx >= 0 return fmarks[idx], fmarks[idx+1] else: - raise oefmt(space.w_IndexError, "group index out of range") + raise oefmt(space.w_IndexError, "no such group") def _last_index(self): mark = self.ctx.match_marks diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -44,10 +44,10 @@ return OperationError(w_error, space.newtext(msg)) - at unwrap_spec(string='bufferstr', level=int) -def compress(space, string, level=rzlib.Z_DEFAULT_COMPRESSION): + at unwrap_spec(data='bufferstr', level=int) +def compress(space, data, level=rzlib.Z_DEFAULT_COMPRESSION): """ - compress(string[, level]) -- Returned compressed string. + compress(data[, level]) -- Returned compressed string. Optional arg level is the compression level, in 1-9. """ @@ -57,7 +57,7 @@ except ValueError: raise zlib_error(space, "Bad compression level") try: - result = rzlib.compress(stream, string, rzlib.Z_FINISH) + result = rzlib.compress(stream, data, rzlib.Z_FINISH) finally: rzlib.deflateEnd(stream) except rzlib.RZlibError as e: @@ -113,20 +113,9 @@ Wrapper around zlib's z_stream structure which provides convenient compression functionality. """ - def __init__(self, space, level=rzlib.Z_DEFAULT_COMPRESSION, - method=rzlib.Z_DEFLATED, # \ - wbits=rzlib.MAX_WBITS, # \ undocumented - memLevel=rzlib.DEF_MEM_LEVEL, # / parameters - strategy=rzlib.Z_DEFAULT_STRATEGY, # / - zdict=None): + def __init__(self, space, stream): ZLibObject.__init__(self, space) - try: - self.stream = rzlib.deflateInit(level, method, wbits, - memLevel, strategy, zdict=zdict) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") + self.stream = stream self.register_finalizer(space) def _finalize_(self): @@ -158,6 +147,25 @@ raise zlib_error(space, e.msg) return space.newbytes(result) + def copy(self, space): + """ + copy() -- Return a copy of the compression object. + """ + try: + self.lock() + try: + if not self.stream: + raise oefmt( + space.w_ValueError, + "Compressor was already flushed", + ) + copied = rzlib.deflateCopy(self.stream) + finally: + self.unlock() + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + return Compress(space=space, stream=copied) + @unwrap_spec(mode="c_int") def flush(self, space, mode=rzlib.Z_FINISH): """ @@ -203,16 +211,23 @@ zdict = None else: zdict = space.charbuf_w(w_zdict) - stream = space.allocate_instance(Compress, w_subtype) - stream = space.interp_w(Compress, stream) - Compress.__init__(stream, space, level, - method, wbits, memLevel, strategy, zdict) - return stream + w_stream = space.allocate_instance(Compress, w_subtype) + w_stream = space.interp_w(Compress, w_stream) + try: + stream = rzlib.deflateInit(level, method, wbits, memLevel, strategy, + zdict=zdict) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Compress.__init__(w_stream, space, stream) + return w_stream Compress.typedef = TypeDef( 'Compress', __new__ = interp2app(Compress___new__), + copy = interp2app(Compress.copy), compress = interp2app(Compress.compress), flush = interp2app(Compress.flush), __doc__ = """compressobj([level]) -- Return a compressor object. @@ -226,7 +241,7 @@ Wrapper around zlib's z_stream structure which provides convenient decompression functionality. """ - def __init__(self, space, wbits=rzlib.MAX_WBITS, zdict=None): + def __init__(self, space, stream, zdict, unused_data, unconsumed_tail): """ Initialize a new decompression object. @@ -236,16 +251,12 @@ inflateInit2. """ ZLibObject.__init__(self, space) - self.unused_data = '' - self.unconsumed_tail = '' + + self.stream = stream + self.zdict = zdict + self.unused_data = unused_data + self.unconsumed_tail = unconsumed_tail self.eof = False - try: - self.stream = rzlib.inflateInit(wbits, zdict=zdict) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") - self.zdict = zdict self.register_finalizer(space) def _finalize_(self): @@ -295,6 +306,27 @@ self._save_unconsumed_input(data, finished, unused_len) return space.newbytes(string) + def copy(self, space): + """ + copy() -- Return a copy of the decompression object. + """ + try: + self.lock() + try: + copied = rzlib.inflateCopy(self.stream) + finally: + self.unlock() + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + + return Decompress( + space=space, + stream=copied, + unused_data=self.unused_data, + unconsumed_tail=self.unconsumed_tail, + zdict=self.zdict, + ) + def flush(self, space, w_length=None): """ flush( [length] ) -- This is kept for backward compatibility, @@ -331,10 +363,16 @@ zdict = None else: zdict = space.charbuf_w(w_zdict) - stream = space.allocate_instance(Decompress, w_subtype) - stream = space.interp_w(Decompress, stream) - Decompress.__init__(stream, space, wbits, zdict) - return stream + w_stream = space.allocate_instance(Decompress, w_subtype) + w_stream = space.interp_w(Decompress, w_stream) + try: + stream = rzlib.inflateInit(wbits, zdict=zdict) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Decompress.__init__(w_stream, space, stream, zdict, '', '') + return w_stream def default_buffer_size(space): return space.newint(rzlib.OUTPUT_BUFFER_SIZE) @@ -342,6 +380,7 @@ Decompress.typedef = TypeDef( 'Decompress', __new__ = interp2app(Decompress___new__), + copy = interp2app(Decompress.copy), decompress = interp2app(Decompress.decompress), flush = interp2app(Decompress.flush), unused_data = interp_attrproperty('unused_data', Decompress, wrapfn="newbytes"), diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -10,6 +10,13 @@ except ImportError: py.test.skip("no zlib module on this host Python") +from pypy.interpreter.gateway import interp2app +try: + from pypy.module.zlib import interp_zlib + from rpython.rlib import rzlib +except ImportError: + import py; py.test.skip("no zlib C library on this machine") + class AppTestZlib(object): spaceconfig = dict(usemodules=['zlib']) @@ -29,6 +36,22 @@ py.path.local(pypy.__file__).dirpath().dirpath() .join('LICENSE').read()) + def intentionally_break_a_z_stream(space, w_zobj): + """ + Intentionally break the z_stream associated with a + compressobj or decompressobj in a way that causes their copy + methods to raise RZlibErrors. + """ + from rpython.rtyper.lltypesystem import rffi, lltype + w_zobj.stream.c_zalloc = rffi.cast( + lltype.typeOf(w_zobj.stream.c_zalloc), + rzlib.Z_NULL, + ) + + cls.w_intentionally_break_a_z_stream = cls.space.wrap( + interp2app(intentionally_break_a_z_stream), + ) + def test_def_buf_size(self): assert self.zlib.DEF_BUF_SIZE >= 0 @@ -330,3 +353,61 @@ dco = zlib.decompressobj(wbits=-zlib.MAX_WBITS, zdict=zdict) uncomp = dco.decompress(comp) + dco.flush() assert zdict == uncomp + + def test_decompress_copy(self): + decompressor = self.zlib.decompressobj() + d1 = decompressor.decompress(self.compressed[:10]) + assert d1 + + copied = decompressor.copy() + + from_copy = copied.decompress(self.compressed[10:]) + from_decompressor = decompressor.decompress(self.compressed[10:]) + + assert (d1 + from_copy) == (d1 + from_decompressor) + + def test_cannot_copy_decompressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") + decompressor = self.zlib.decompressobj() + self.intentionally_break_a_z_stream(zobj=decompressor) + raises(self.zlib.error, decompressor.copy) + + def test_decompress_copy_carries_along_state(self): + """ + Decompressor.unused_data and unconsumed_tail are carried along when a + copy is done. + """ + decompressor = self.zlib.decompressobj() + decompressor.decompress(self.compressed, max_length=5) + unconsumed_tail = decompressor.unconsumed_tail + assert unconsumed_tail + assert decompressor.copy().unconsumed_tail == unconsumed_tail + + decompressor.decompress(decompressor.unconsumed_tail) + decompressor.decompress(b"xxx") + unused_data = decompressor.unused_data + assert unused_data + assert decompressor.copy().unused_data == unused_data + + def test_compress_copy(self): + compressor = self.zlib.compressobj() + d1 = compressor.compress(self.expanded[:10]) + assert d1 + + copied = compressor.copy() + + from_copy = copied.compress(self.expanded[10:]) + from_compressor = compressor.compress(self.expanded[10:]) + + assert (d1 + from_copy) == (d1 + from_compressor) + + def test_cannot_copy_compressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") + compressor = self.zlib.compressobj() + self.intentionally_break_a_z_stream(zobj=compressor) + raises(self.zlib.error, compressor.copy) + + def test_cannot_copy_compressor_with_flushed_stream(self): + compressor = self.zlib.compressobj() + compressor.flush() + raises(ValueError, compressor.copy) diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py --- a/pypy/tool/release/package.py +++ b/pypy/tool/release/package.py @@ -121,6 +121,8 @@ pypydir.ensure('include', dir=True) if sys.platform == 'win32': + os.environ['PATH'] = str(basedir.join('externals').join('bin')) + ';' + \ + os.environ.get('PATH', '') src,tgt = binaries[0] pypyw = src.new(purebasename=src.purebasename + 'w') if pypyw.exists(): diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py --- a/rpython/rlib/rgc.py +++ b/rpython/rlib/rgc.py @@ -1406,6 +1406,11 @@ return _ResizableListSupportingRawPtr(lst) def nonmoving_raw_ptr_for_resizable_list(lst): + if must_split_gc_address_space(): + raise ValueError + return _nonmoving_raw_ptr_for_resizable_list(lst) + +def _nonmoving_raw_ptr_for_resizable_list(lst): assert isinstance(lst, _ResizableListSupportingRawPtr) return lst._nonmoving_raw_ptr_for_resizable_list() @@ -1450,7 +1455,7 @@ return hop.inputarg(hop.args_r[0], 0) class Entry(ExtRegistryEntry): - _about_ = nonmoving_raw_ptr_for_resizable_list + _about_ = _nonmoving_raw_ptr_for_resizable_list def compute_result_annotation(self, s_list): from rpython.rtyper.lltypesystem import lltype, rffi diff --git a/rpython/rlib/rzlib.py b/rpython/rlib/rzlib.py --- a/rpython/rlib/rzlib.py +++ b/rpython/rlib/rzlib.py @@ -35,6 +35,7 @@ MAX_WBITS MAX_MEM_LEVEL Z_BEST_SPEED Z_BEST_COMPRESSION Z_DEFAULT_COMPRESSION Z_FILTERED Z_HUFFMAN_ONLY Z_DEFAULT_STRATEGY Z_NEED_DICT + Z_NULL '''.split() class SimpleCConfig: @@ -141,6 +142,7 @@ rffi.INT) _deflate = zlib_external('deflate', [z_stream_p, rffi.INT], rffi.INT) +_deflateCopy = zlib_external('deflateCopy', [z_stream_p, z_stream_p], rffi.INT) _deflateEnd = zlib_external('deflateEnd', [z_stream_p], rffi.INT, releasegil=False) @@ -160,6 +162,7 @@ rffi.INT) _inflate = zlib_external('inflate', [z_stream_p, rffi.INT], rffi.INT) +_inflateCopy = zlib_external('inflateCopy', [z_stream_p, z_stream_p], rffi.INT) _inflateEnd = zlib_external('inflateEnd', [z_stream_p], rffi.INT, releasegil=False) @@ -290,6 +293,19 @@ lltype.free(stream, flavor='raw') +def deflateCopy(source): + """ + Allocate and return an independent copy of the provided stream object. + """ + dest = deflateInit() + err = _deflateCopy(dest, source) + if err != Z_OK: + deflateEnd(dest) + raise RZlibError.fromstream(source, err, + "while copying compression object") + return dest + + def deflateEnd(stream): """ Free the resources associated with the deflate stream. @@ -330,6 +346,19 @@ lltype.free(stream, flavor='raw') +def inflateCopy(source): + """ + Allocate and return an independent copy of the provided stream object. + """ + dest = inflateInit() + err = _inflateCopy(dest, source) + if err != Z_OK: + inflateEnd(dest) + raise RZlibError.fromstream(source, err, + "while copying decompression object") + return dest + + def inflateEnd(stream): """ Free the resources associated with the inflate stream. diff --git a/rpython/rlib/test/test_rzlib.py b/rpython/rlib/test/test_rzlib.py --- a/rpython/rlib/test/test_rzlib.py +++ b/rpython/rlib/test/test_rzlib.py @@ -246,6 +246,108 @@ rzlib.deflateEnd(stream) +def test_compress_copy(): + """ + inflateCopy produces an independent copy of a stream. + """ + + stream = rzlib.deflateInit() + + bytes1 = rzlib.compress(stream, expanded[:10]) + assert bytes1 + + copied = rzlib.deflateCopy(stream) + + bytes_stream = rzlib.compress( + stream, + expanded[10:], + rzlib.Z_FINISH, + ) + assert bytes1 + bytes_stream == compressed + rzlib.deflateEnd(stream) + + bytes_copy = rzlib.compress( + copied, + expanded[10:], + rzlib.Z_FINISH, + ) + rzlib.deflateEnd(copied) + assert bytes1 + bytes_copy == compressed + + +def test_unsuccessful_compress_copy(): + """ + Errors during unsuccesful deflateCopy operations raise RZlibErrors. + """ + stream = rzlib.deflateInit() + + # From zlib.h: + # + # "deflateCopy returns [...] Z_STREAM_ERROR if the source stream + # state was inconsistent (such as zalloc being Z_NULL)" + from rpython.rtyper.lltypesystem import rffi, lltype + stream.c_zalloc = rffi.cast(lltype.typeOf(stream.c_zalloc), rzlib.Z_NULL) + + exc = py.test.raises(rzlib.RZlibError, rzlib.deflateCopy, stream) + msg = "Error -2 while copying compression object: inconsistent stream state" + assert str(exc.value) == msg + rzlib.deflateEnd(stream) + + +def test_decompress_copy(): + """ + inflateCopy produces an independent copy of a stream. + """ + + stream = rzlib.inflateInit() + + bytes1, finished1, unused1 = rzlib.decompress(stream, compressed[:10]) + assert bytes1 + assert finished1 is False + + copied = rzlib.inflateCopy(stream) + + bytes_stream, finished_stream, unused_stream = rzlib.decompress( + stream, + compressed[10:], + rzlib.Z_FINISH, + ) + assert bytes1 + bytes_stream == expanded + assert finished_stream is True + assert unused1 == 0 + assert unused_stream == 0 + rzlib.inflateEnd(stream) + + bytes_copy, finished_copy, unused_copy = rzlib.decompress( + copied, + compressed[10:], + rzlib.Z_FINISH, + ) + rzlib.inflateEnd(copied) + assert bytes1 + bytes_copy == expanded + assert finished_copy is True + assert unused_copy == 0 + + +def test_unsuccessful_decompress_copy(): + """ + Errors during unsuccesful inflateCopy operations raise RZlibErrors. + """ + stream = rzlib.inflateInit() + + # From zlib.h: + # + # "inflateCopy returns [...] Z_STREAM_ERROR if the source stream + # state was inconsistent (such as zalloc being Z_NULL)" + from rpython.rtyper.lltypesystem import rffi, lltype + stream.c_zalloc = rffi.cast(lltype.typeOf(stream.c_zalloc), rzlib.Z_NULL) + + exc = py.test.raises(rzlib.RZlibError, rzlib.inflateCopy, stream) + msg = "Error -2 while copying decompression object: inconsistent stream state" + assert str(exc.value) == msg + rzlib.inflateEnd(stream) + + def test_cornercases(): """ Test degenerate arguments. diff --git a/rpython/rtyper/lltypesystem/module/test/math_cases.py b/rpython/rtyper/lltypesystem/module/test/math_cases.py --- a/rpython/rtyper/lltypesystem/module/test/math_cases.py +++ b/rpython/rtyper/lltypesystem/module/test/math_cases.py @@ -59,9 +59,6 @@ ('copysign', (1.5, -0.0), -1.5), ('copysign', (1.5, INFINITY), 1.5), ('copysign', (1.5, -INFINITY), -1.5), - ] - if sys.platform != 'win32': # all NaNs seem to be negative there...? - IRREGCASES += [ ('copysign', (1.5, NAN), 1.5), ('copysign', (1.75, -NAN), -1.75), # special case for -NAN here ] diff --git a/rpython/translator/c/primitive.py b/rpython/translator/c/primitive.py --- a/rpython/translator/c/primitive.py +++ b/rpython/translator/c/primitive.py @@ -123,9 +123,9 @@ return '(-Py_HUGE_VAL)' elif math.isnan(value): if is_positive_nan(value): - return '(Py_HUGE_VAL/Py_HUGE_VAL)' + return '(_PyPy_dg_stdnan(0))' else: - return '(-(Py_HUGE_VAL/Py_HUGE_VAL))' + return '(_PyPy_dg_stdnan(1))' else: x = repr(value) assert not x.startswith('n') @@ -142,9 +142,9 @@ elif math.isnan(value): # XXX are these expressions ok? if is_positive_nan(value): - return '((float)(Py_HUGE_VAL/Py_HUGE_VAL))' + return '((float)(_PyPy_dg_stdnan(0)))' else: - return '(-(float)(Py_HUGE_VAL/Py_HUGE_VAL))' + return '((float)(_PyPy_dg_stdnan(1)))' else: return repr(value) + 'f' diff --git a/rpython/translator/c/src/support.c b/rpython/translator/c/src/support.c --- a/rpython/translator/c/src/support.c +++ b/rpython/translator/c/src/support.c @@ -9,6 +9,26 @@ #include /*** misc ***/ +#define Sign_bit 0x80000000 +#define NAN_WORD0 0x7ff80000 +#define NAN_WORD1 0 +#define PY_UINT32_T unsigned int + +#ifndef __BIG_ENDIAN__ +#define IEEE_8087 +#endif + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d + +typedef PY_UINT32_T ULong; +typedef union { double d; ULong L[2]; } U; RPY_EXTERN void RPyAssertFailed(const char* filename, long lineno, @@ -25,3 +45,20 @@ fprintf(stderr, "Invalid RPython operation (NULL ptr or bad array index)\n"); abort(); } + +/* Return a 'standard' NaN value. + There are exactly two quiet NaNs that don't arise by 'quieting' signaling + NaNs (see IEEE 754-2008, section 6.2.1). If sign == 0, return the one whose + sign bit is cleared. Otherwise, return the one whose sign bit is set. +*/ + +double +_PyPy_dg_stdnan(int sign) +{ + U rv; + word0(&rv) = NAN_WORD0; + word1(&rv) = NAN_WORD1; + if (sign) + word0(&rv) |= Sign_bit; + return dval(&rv); +} diff --git a/rpython/translator/c/src/support.h b/rpython/translator/c/src/support.h --- a/rpython/translator/c/src/support.h +++ b/rpython/translator/c/src/support.h @@ -38,6 +38,9 @@ RPY_EXTERN void RPyAbort(void); +RPY_EXTERN +double _PyPy_dg_stdnan(int sign); + #if defined(RPY_LL_ASSERT) || defined(RPY_SANDBOXED) /* obscure macros that can be used as expressions and lvalues to refer * to a field of a structure or an item in an array in a "safe" way -- From pypy.commits at gmail.com Fri Feb 8 05:35:34 2019 From: pypy.commits at gmail.com (antocuni) Date: Fri, 08 Feb 2019 02:35:34 -0800 (PST) Subject: [pypy-commit] pypy release-pypy3.5-7.x: fix the version number AGAIN Message-ID: <5c5d5b76.1c69fb81.65c40.690e@mx.google.com> Author: Antonio Cuni Branch: release-pypy3.5-7.x Changeset: r95890:aaff09311f03 Date: 2019-02-08 11:31 +0100 http://bitbucket.org/pypy/pypy/changeset/aaff09311f03/ Log: fix the version number AGAIN diff --git a/pypy/doc/conf.py b/pypy/doc/conf.py --- a/pypy/doc/conf.py +++ b/pypy/doc/conf.py @@ -71,9 +71,9 @@ # module/cpyext/include/patchlevel.h # # The short X.Y version. -version = '7.1' +version = '7.0' # The full version, including alpha/beta/rc tags. -release = '7.1.0' +release = '7.0.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. 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 @@ -32,8 +32,8 @@ * module/sys/version.py * doc/conf.py */ -#define PYPY_VERSION "7.1.0-alpha0" -#define PYPY_VERSION_NUM 0x07010000 +#define PYPY_VERSION "7.0.0" +#define PYPY_VERSION_NUM 0x07000000 /* Defined to mean a PyPy where cpyext holds more regular references to PyObjects, e.g. staying alive as long as the internal PyPy object stays alive. */ 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 @@ -13,7 +13,7 @@ # make sure to keep PYPY_VERSION in sync with: # module/cpyext/include/patchlevel.h # doc/conf.py -PYPY_VERSION = (7, 1, 0, "alpha", 0) +PYPY_VERSION = (7, 0, 0, "final", 0) import pypy From pypy.commits at gmail.com Fri Feb 8 05:35:36 2019 From: pypy.commits at gmail.com (antocuni) Date: Fri, 08 Feb 2019 02:35:36 -0800 (PST) Subject: [pypy-commit] pypy release-pypy3.6-7.x: fix the version :( Message-ID: <5c5d5b78.1c69fb81.f65fd.da7c@mx.google.com> Author: Antonio Cuni Branch: release-pypy3.6-7.x Changeset: r95891:e83543812df5 Date: 2019-02-08 11:32 +0100 http://bitbucket.org/pypy/pypy/changeset/e83543812df5/ Log: fix the version :( diff --git a/pypy/doc/conf.py b/pypy/doc/conf.py --- a/pypy/doc/conf.py +++ b/pypy/doc/conf.py @@ -71,9 +71,9 @@ # module/cpyext/include/patchlevel.h # # The short X.Y version. -version = '7.1' +version = '7.0' # The full version, including alpha/beta/rc tags. -release = '7.1.0' +release = '7.0.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From pypy.commits at gmail.com Fri Feb 8 05:35:37 2019 From: pypy.commits at gmail.com (arigo) Date: Fri, 08 Feb 2019 02:35:37 -0800 (PST) Subject: [pypy-commit] pypy release-pypy2.7-7.x: Crashes with too-old versions of hypothesis. Turn crashes into warnings Message-ID: <5c5d5b79.1c69fb81.73aa1.49c8@mx.google.com> Author: Armin Rigo Branch: release-pypy2.7-7.x Changeset: r95892:c8805ee6d784 Date: 2019-02-08 00:54 +0100 http://bitbucket.org/pypy/pypy/changeset/c8805ee6d784/ Log: Crashes with too-old versions of hypothesis. Turn crashes into warnings diff --git a/rpython/conftest.py b/rpython/conftest.py --- a/rpython/conftest.py +++ b/rpython/conftest.py @@ -6,15 +6,16 @@ option = None try: - from hypothesis import settings, __version__ + from hypothesis import settings except ImportError: pass else: - if __version__[:2] < '3.6': - s = settings(deadline=None) - settings.register_profile('default', s) - else: + try: settings.register_profile('default', deadline=None) + except Exception: + import warnings + warnings.warn("Version of hypothesis too old, " + "cannot set the deadline to None") settings.load_profile('default') def braindead_deindent(self): From pypy.commits at gmail.com Fri Feb 8 05:35:39 2019 From: pypy.commits at gmail.com (arigo) Date: Fri, 08 Feb 2019 02:35:39 -0800 (PST) Subject: [pypy-commit] pypy release-pypy3.5-7.x: Crashes with too-old versions of hypothesis. Turn crashes into warnings Message-ID: <5c5d5b7b.1c69fb81.aa8cf.5275@mx.google.com> Author: Armin Rigo Branch: release-pypy3.5-7.x Changeset: r95893:928a4f70d3de Date: 2019-02-08 00:54 +0100 http://bitbucket.org/pypy/pypy/changeset/928a4f70d3de/ Log: Crashes with too-old versions of hypothesis. Turn crashes into warnings diff --git a/rpython/conftest.py b/rpython/conftest.py --- a/rpython/conftest.py +++ b/rpython/conftest.py @@ -6,15 +6,16 @@ option = None try: - from hypothesis import settings, __version__ + from hypothesis import settings except ImportError: pass else: - if __version__[:2] < '3.6': - s = settings(deadline=None) - settings.register_profile('default', s) - else: + try: settings.register_profile('default', deadline=None) + except Exception: + import warnings + warnings.warn("Version of hypothesis too old, " + "cannot set the deadline to None") settings.load_profile('default') def braindead_deindent(self): From pypy.commits at gmail.com Fri Feb 8 05:35:41 2019 From: pypy.commits at gmail.com (arigo) Date: Fri, 08 Feb 2019 02:35:41 -0800 (PST) Subject: [pypy-commit] pypy release-pypy3.6-7.x: Crashes with too-old versions of hypothesis. Turn crashes into warnings Message-ID: <5c5d5b7d.1c69fb81.8fbd0.c4a3@mx.google.com> Author: Armin Rigo Branch: release-pypy3.6-7.x Changeset: r95894:fb40f7a5524c Date: 2019-02-08 00:54 +0100 http://bitbucket.org/pypy/pypy/changeset/fb40f7a5524c/ Log: Crashes with too-old versions of hypothesis. Turn crashes into warnings diff --git a/rpython/conftest.py b/rpython/conftest.py --- a/rpython/conftest.py +++ b/rpython/conftest.py @@ -6,15 +6,16 @@ option = None try: - from hypothesis import settings, __version__ + from hypothesis import settings except ImportError: pass else: - if __version__[:2] < '3.6': - s = settings(deadline=None) - settings.register_profile('default', s) - else: + try: settings.register_profile('default', deadline=None) + except Exception: + import warnings + warnings.warn("Version of hypothesis too old, " + "cannot set the deadline to None") settings.load_profile('default') def braindead_deindent(self): From pypy.commits at gmail.com Fri Feb 8 05:57:35 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 08 Feb 2019 02:57:35 -0800 (PST) Subject: [pypy-commit] pypy default: merge math-improvements Message-ID: <5c5d609f.1c69fb81.4360f.ab33@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r95895:7a4d0769c63d Date: 2019-02-08 11:01 +0100 http://bitbucket.org/pypy/pypy/changeset/7a4d0769c63d/ Log: merge math-improvements diff too long, truncating to 2000 out of 2276 lines 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 @@ -13,3 +13,9 @@ The zlib module's compressobj and decompressobj now expose copy methods as they do on CPython. + + +.. math-improvements + +Improve performance of long operations where one of the operands fits into +an int. \ No newline at end of file diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py --- a/pypy/objspace/std/intobject.py +++ b/pypy/objspace/std/intobject.py @@ -299,7 +299,7 @@ return ix -def _pow_ovf2long(space, iv, iw, w_modulus): +def _pow_ovf2long(space, iv, w_iv, iw, w_iw, w_modulus): if space.is_none(w_modulus) and _recover_with_smalllong(space): from pypy.objspace.std.smalllongobject import _pow as _pow_small try: @@ -308,9 +308,12 @@ return _pow_small(space, r_longlong(iv), iw, r_longlong(0)) except (OverflowError, ValueError): pass - from pypy.objspace.std.longobject import W_LongObject - w_iv = W_LongObject.fromint(space, iv) - w_iw = W_LongObject.fromint(space, iw) + from pypy.objspace.std.longobject import W_LongObject, W_AbstractLongObject + if w_iv is None or not isinstance(w_iv, W_AbstractLongObject): + w_iv = W_LongObject.fromint(space, iv) + if w_iw is None or not isinstance(w_iw, W_AbstractLongObject): + w_iw = W_LongObject.fromint(space, iw) + return w_iv.descr_pow(space, w_iw, w_modulus) @@ -318,7 +321,7 @@ op = getattr(operator, opname, None) assert op or ovf2small - def ovf2long(space, x, y): + def ovf2long(space, x, w_x, y, w_y): """Handle overflowing to smalllong or long""" if _recover_with_smalllong(space): if ovf2small: @@ -330,9 +333,12 @@ b = r_longlong(y) return W_SmallLongObject(op(a, b)) - from pypy.objspace.std.longobject import W_LongObject - w_x = W_LongObject.fromint(space, x) - w_y = W_LongObject.fromint(space, y) + from pypy.objspace.std.longobject import W_LongObject, W_AbstractLongObject + if w_x is None or not isinstance(w_x, W_AbstractLongObject): + w_x = W_LongObject.fromint(space, x) + if w_y is None or not isinstance(w_y, W_AbstractLongObject): + w_y = W_LongObject.fromint(space, y) + return getattr(w_x, 'descr_' + opname)(space, w_y) return ovf2long @@ -496,12 +502,18 @@ # can't return NotImplemented (space.pow doesn't do full # ternary, i.e. w_modulus.__zpow__(self, w_exponent)), so # handle it ourselves - return _pow_ovf2long(space, x, y, w_modulus) + return _pow_ovf2long(space, x, self, y, w_exponent, w_modulus) try: result = _pow(space, x, y, z) - except (OverflowError, ValueError): - return _pow_ovf2long(space, x, y, w_modulus) + except OverflowError: + return _pow_ovf2long(space, x, self, y, w_exponent, w_modulus) + except ValueError: + # float result, so let avoid a roundtrip in rbigint. + self = self.descr_float(space) + w_exponent = w_exponent.descr_float(space) + return space.pow(self, w_exponent, space.w_None) + return space.newint(result) @unwrap_spec(w_modulus=WrappedDefault(None)) @@ -546,7 +558,7 @@ try: z = ovfcheck(op(x, y)) except OverflowError: - return ovf2long(space, x, y) + return ovf2long(space, x, self, y, w_other) else: z = op(x, y) return wrapint(space, z) @@ -568,7 +580,7 @@ try: z = ovfcheck(op(y, x)) except OverflowError: - return ovf2long(space, y, x) + return ovf2long(space, y, w_other, x, self) # XXX write a test else: z = op(y, x) return wrapint(space, z) @@ -599,7 +611,7 @@ try: return func(space, x, y) except OverflowError: - return ovf2long(space, x, y) + return ovf2long(space, x, self, y, w_other) else: return func(space, x, y) @@ -614,7 +626,7 @@ try: return func(space, y, x) except OverflowError: - return ovf2long(space, y, x) + return ovf2long(space, y, w_other, x, self) else: return func(space, y, x) diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py --- a/pypy/objspace/std/longobject.py +++ b/pypy/objspace/std/longobject.py @@ -308,28 +308,47 @@ @unwrap_spec(w_modulus=WrappedDefault(None)) def descr_pow(self, space, w_exponent, w_modulus=None): + exp_int = 0 + exp_bigint = None + sign = 0 + if isinstance(w_exponent, W_AbstractIntObject): - w_exponent = w_exponent.descr_long(space) + exp_int = w_exponent.int_w(space) + if exp_int > 0: + sign = 1 + elif exp_int < 0: + sign = -1 elif not isinstance(w_exponent, W_AbstractLongObject): return space.w_NotImplemented + else: + exp_bigint = w_exponent.asbigint() + sign = exp_bigint.sign if space.is_none(w_modulus): - if w_exponent.asbigint().sign < 0: + if sign < 0: self = self.descr_float(space) w_exponent = w_exponent.descr_float(space) return space.pow(self, w_exponent, space.w_None) - return W_LongObject(self.num.pow(w_exponent.asbigint())) + if not exp_bigint: + return W_LongObject(self.num.int_pow(exp_int)) + else: + return W_LongObject(self.num.pow(exp_bigint)) + elif isinstance(w_modulus, W_AbstractIntObject): w_modulus = w_modulus.descr_long(space) + elif not isinstance(w_modulus, W_AbstractLongObject): return space.w_NotImplemented - if w_exponent.asbigint().sign < 0: + if sign < 0: raise oefmt(space.w_TypeError, "pow() 2nd argument cannot be negative when 3rd " "argument specified") try: - result = self.num.pow(w_exponent.asbigint(), w_modulus.asbigint()) + if not exp_bigint: + result = self.num.int_pow(exp_int, w_modulus.asbigint()) + else: + result = self.num.pow(exp_bigint, w_modulus.asbigint()) except ValueError: raise oefmt(space.w_ValueError, "pow 3rd argument cannot be 0") return W_LongObject(result) @@ -372,22 +391,16 @@ descr_gt = _make_descr_cmp('gt') descr_ge = _make_descr_cmp('ge') - def _make_generic_descr_binop_noncommutative(opname): - methname = opname + '_' if opname in ('and', 'or') else opname - descr_rname = 'descr_r' + opname - op = getattr(rbigint, methname) + def descr_sub(self, space, w_other): + if isinstance(w_other, W_AbstractIntObject): + return W_LongObject(self.num.int_sub(w_other.int_w(space))) + elif not isinstance(w_other, W_AbstractLongObject): + return space.w_NotImplemented + return W_LongObject(self.num.sub(w_other.asbigint())) - @func_renamer('descr_' + opname) - @delegate_other - def descr_binop(self, space, w_other): - return W_LongObject(op(self.num, w_other.asbigint())) - - @func_renamer(descr_rname) - @delegate_other - def descr_rbinop(self, space, w_other): - return W_LongObject(op(w_other.asbigint(), self.num)) - - return descr_binop, descr_rbinop + @delegate_other + def descr_rsub(self, space, w_other): + return W_LongObject(w_other.asbigint().sub(self.num)) def _make_generic_descr_binop(opname): if opname not in COMMUTATIVE_OPS: @@ -419,28 +432,23 @@ return descr_binop, descr_rbinop descr_add, descr_radd = _make_generic_descr_binop('add') - descr_sub, descr_rsub = _make_generic_descr_binop_noncommutative('sub') + descr_mul, descr_rmul = _make_generic_descr_binop('mul') descr_and, descr_rand = _make_generic_descr_binop('and') descr_or, descr_ror = _make_generic_descr_binop('or') descr_xor, descr_rxor = _make_generic_descr_binop('xor') - def _make_descr_binop(func, int_func=None): + def _make_descr_binop(func, int_func): opname = func.__name__[1:] - if int_func: - @func_renamer('descr_' + opname) - def descr_binop(self, space, w_other): - if isinstance(w_other, W_AbstractIntObject): - return int_func(self, space, w_other.int_w(space)) - elif not isinstance(w_other, W_AbstractLongObject): - return space.w_NotImplemented - return func(self, space, w_other) - else: - @delegate_other - @func_renamer('descr_' + opname) - def descr_binop(self, space, w_other): - return func(self, space, w_other) + @func_renamer('descr_' + opname) + def descr_binop(self, space, w_other): + if isinstance(w_other, W_AbstractIntObject): + return int_func(self, space, w_other.int_w(space)) + elif not isinstance(w_other, W_AbstractLongObject): + return space.w_NotImplemented + return func(self, space, w_other) + @delegate_other @func_renamer('descr_r' + opname) def descr_rbinop(self, space, w_other): @@ -460,10 +468,10 @@ raise oefmt(space.w_OverflowError, "shift count too large") return W_LongObject(self.num.lshift(shift)) - def _int_lshift(self, space, w_other): - if w_other < 0: + def _int_lshift(self, space, other): + if other < 0: raise oefmt(space.w_ValueError, "negative shift count") - return W_LongObject(self.num.lshift(w_other)) + return W_LongObject(self.num.lshift(other)) descr_lshift, descr_rlshift = _make_descr_binop(_lshift, _int_lshift) @@ -476,11 +484,11 @@ raise oefmt(space.w_OverflowError, "shift count too large") return newlong(space, self.num.rshift(shift)) - def _int_rshift(self, space, w_other): - if w_other < 0: + def _int_rshift(self, space, other): + if other < 0: raise oefmt(space.w_ValueError, "negative shift count") - return newlong(space, self.num.rshift(w_other)) + return newlong(space, self.num.rshift(other)) descr_rshift, descr_rrshift = _make_descr_binop(_rshift, _int_rshift) def _floordiv(self, space, w_other): @@ -491,17 +499,18 @@ "long division or modulo by zero") return newlong(space, z) - def _floordiv(self, space, w_other): + def _int_floordiv(self, space, other): try: - z = self.num.floordiv(w_other.asbigint()) + z = self.num.int_floordiv(other) except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "long division or modulo by zero") return newlong(space, z) - descr_floordiv, descr_rfloordiv = _make_descr_binop(_floordiv) + descr_floordiv, descr_rfloordiv = _make_descr_binop(_floordiv, _int_floordiv) _div = func_with_new_name(_floordiv, '_div') - descr_div, descr_rdiv = _make_descr_binop(_div) + _int_div = func_with_new_name(_int_floordiv, '_int_div') + descr_div, descr_rdiv = _make_descr_binop(_div, _int_div) def _mod(self, space, w_other): try: @@ -511,9 +520,9 @@ "long division or modulo by zero") return newlong(space, z) - def _int_mod(self, space, w_other): + def _int_mod(self, space, other): try: - z = self.num.int_mod(w_other) + z = self.num.int_mod(other) except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "long division or modulo by zero") @@ -527,7 +536,16 @@ raise oefmt(space.w_ZeroDivisionError, "long division or modulo by zero") return space.newtuple([newlong(space, div), newlong(space, mod)]) - descr_divmod, descr_rdivmod = _make_descr_binop(_divmod) + + def _int_divmod(self, space, other): + try: + div, mod = self.num.int_divmod(other) + except ZeroDivisionError: + raise oefmt(space.w_ZeroDivisionError, + "long division or modulo by zero") + return space.newtuple([newlong(space, div), newlong(space, mod)]) + + descr_divmod, descr_rdivmod = _make_descr_binop(_divmod, _int_divmod) def newlong(space, bigint): diff --git a/pypy/objspace/std/test/test_intobject.py b/pypy/objspace/std/test/test_intobject.py --- a/pypy/objspace/std/test/test_intobject.py +++ b/pypy/objspace/std/test/test_intobject.py @@ -679,6 +679,11 @@ x = int(321) assert x.__rlshift__(333) == 1422567365923326114875084456308921708325401211889530744784729710809598337369906606315292749899759616L + def test_some_rops(self): + import sys + x = int(-sys.maxint) + assert x.__rsub__(2) == (2 + sys.maxint) + class AppTestIntShortcut(AppTestInt): spaceconfig = {"objspace.std.intshortcut": True} diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py --- a/rpython/rlib/rarithmetic.py +++ b/rpython/rlib/rarithmetic.py @@ -612,6 +612,7 @@ r_ulonglong = build_int('r_ulonglong', False, 64) r_longlonglong = build_int('r_longlonglong', True, 128) +r_ulonglonglong = build_int('r_ulonglonglong', False, 128) longlongmax = r_longlong(LONGLONG_TEST - 1) if r_longlong is not r_int: diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py --- a/rpython/rlib/rbigint.py +++ b/rpython/rlib/rbigint.py @@ -27,6 +27,7 @@ else: UDIGIT_MASK = longlongmask LONG_TYPE = rffi.__INT128_T + ULONG_TYPE = rffi.__UINT128_T if LONG_BIT > SHIFT: STORE_TYPE = lltype.Signed UNSIGNED_TYPE = lltype.Unsigned @@ -40,6 +41,7 @@ STORE_TYPE = lltype.Signed UNSIGNED_TYPE = lltype.Unsigned LONG_TYPE = rffi.LONGLONG + ULONG_TYPE = rffi.ULONGLONG MASK = int((1 << SHIFT) - 1) FLOAT_MULTIPLIER = float(1 << SHIFT) @@ -97,6 +99,9 @@ def _widen_digit(x): return rffi.cast(LONG_TYPE, x) +def _unsigned_widen_digit(x): + return rffi.cast(ULONG_TYPE, x) + @specialize.argtype(0) def _store_digit(x): return rffi.cast(STORE_TYPE, x) @@ -108,6 +113,7 @@ NULLDIGIT = _store_digit(0) ONEDIGIT = _store_digit(1) +NULLDIGITS = [NULLDIGIT] def _check_digits(l): for x in l: @@ -133,22 +139,26 @@ def specialize_call(self, hop): hop.exception_cannot_occur() +def intsign(i): + return -1 if i < 0 else 1 class rbigint(object): """This is a reimplementation of longs using a list of digits.""" _immutable_ = True - _immutable_fields_ = ["_digits"] - - def __init__(self, digits=[NULLDIGIT], sign=0, size=0): + _immutable_fields_ = ["_digits[*]", "size", "sign"] + + def __init__(self, digits=NULLDIGITS, sign=0, size=0): if not we_are_translated(): _check_digits(digits) make_sure_not_resized(digits) self._digits = digits + assert size >= 0 self.size = size or len(digits) + self.sign = sign - # __eq__ and __ne__ method exist for testingl only, they are not RPython! + # __eq__ and __ne__ method exist for testing only, they are not RPython! @not_rpython def __eq__(self, other): if not isinstance(other, rbigint): @@ -159,6 +169,7 @@ def __ne__(self, other): return not (self == other) + @specialize.argtype(1) def digit(self, x): """Return the x'th digit, as an int.""" return self._digits[x] @@ -170,6 +181,12 @@ return _widen_digit(self._digits[x]) widedigit._always_inline_ = True + def uwidedigit(self, x): + """Return the x'th digit, as a long long int if needed + to have enough room to contain two digits.""" + return _unsigned_widen_digit(self._digits[x]) + uwidedigit._always_inline_ = True + def udigit(self, x): """Return the x'th digit, as an unsigned int.""" return _load_unsigned_digit(self._digits[x]) @@ -183,7 +200,9 @@ setdigit._always_inline_ = True def numdigits(self): - return self.size + w = self.size + assert w > 0 + return w numdigits._always_inline_ = True @staticmethod @@ -196,13 +215,15 @@ if intval < 0: sign = -1 ival = -r_uint(intval) + carry = ival >> SHIFT elif intval > 0: sign = 1 ival = r_uint(intval) + carry = 0 else: return NULLRBIGINT - carry = ival >> SHIFT + if carry: return rbigint([_store_digit(ival & MASK), _store_digit(carry)], sign, 2) @@ -509,23 +530,22 @@ return True @jit.elidable - def int_eq(self, other): + def int_eq(self, iother): """ eq with int """ - - if not int_in_valid_range(other): - # Fallback to Long. - return self.eq(rbigint.fromint(other)) + if not int_in_valid_range(iother): + # Fallback to Long. + return self.eq(rbigint.fromint(iother)) if self.numdigits() > 1: return False - return (self.sign * self.digit(0)) == other + return (self.sign * self.digit(0)) == iother def ne(self, other): return not self.eq(other) - def int_ne(self, other): - return not self.int_eq(other) + def int_ne(self, iother): + return not self.int_eq(iother) @jit.elidable def lt(self, other): @@ -563,59 +583,38 @@ return False @jit.elidable - def int_lt(self, other): + def int_lt(self, iother): """ lt where other is an int """ - if not int_in_valid_range(other): + if not int_in_valid_range(iother): # Fallback to Long. - return self.lt(rbigint.fromint(other)) - - osign = 1 - if other == 0: - osign = 0 - elif other < 0: - osign = -1 - - if self.sign > osign: - return False - elif self.sign < osign: - return True - - digits = self.numdigits() - - if digits > 1: - if osign == 1: - return False - else: - return True - - d1 = self.sign * self.digit(0) - if d1 < other: - return True - return False + return self.lt(rbigint.fromint(iother)) + + return _x_int_lt(self, iother, False) def le(self, other): return not other.lt(self) - def int_le(self, other): - # Alternative that might be faster, reimplant this. as a check with other + 1. But we got to check for overflow - # or reduce valid range. - - if self.int_eq(other): - return True - return self.int_lt(other) + def int_le(self, iother): + """ le where iother is an int """ + + if not int_in_valid_range(iother): + # Fallback to Long. + return self.le(rbigint.fromint(iother)) + + return _x_int_lt(self, iother, True) def gt(self, other): return other.lt(self) - def int_gt(self, other): - return not self.int_le(other) + def int_gt(self, iother): + return not self.int_le(iother) def ge(self, other): return not self.lt(other) - def int_ge(self, other): - return not self.int_lt(other) + def int_ge(self, iother): + return not self.int_lt(iother) @jit.elidable def hash(self): @@ -635,20 +634,20 @@ return result @jit.elidable - def int_add(self, other): - if not int_in_valid_range(other): + def int_add(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.add(rbigint.fromint(other)) + return self.add(rbigint.fromint(iother)) elif self.sign == 0: - return rbigint.fromint(other) - elif other == 0: + return rbigint.fromint(iother) + elif iother == 0: return self - sign = -1 if other < 0 else 1 + sign = intsign(iother) if self.sign == sign: - result = _x_int_add(self, other) + result = _x_int_add(self, iother) else: - result = _x_int_sub(self, other) + result = _x_int_sub(self, iother) result.sign *= -1 result.sign *= sign return result @@ -658,7 +657,7 @@ if other.sign == 0: return self elif self.sign == 0: - return rbigint(other._digits[:other.size], -other.sign, other.size) + return rbigint(other._digits[:other.numdigits()], -other.sign, other.numdigits()) elif self.sign == other.sign: result = _x_sub(self, other) else: @@ -667,93 +666,94 @@ return result @jit.elidable - def int_sub(self, other): - if not int_in_valid_range(other): + def int_sub(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.sub(rbigint.fromint(other)) - elif other == 0: + return self.sub(rbigint.fromint(iother)) + elif iother == 0: return self elif self.sign == 0: - return rbigint.fromint(-other) - elif self.sign == (-1 if other < 0 else 1): - result = _x_int_sub(self, other) + return rbigint.fromint(-iother) + elif self.sign == intsign(iother): + result = _x_int_sub(self, iother) else: - result = _x_int_add(self, other) + result = _x_int_add(self, iother) result.sign *= self.sign return result @jit.elidable - def mul(self, b): - asize = self.numdigits() - bsize = b.numdigits() - - a = self - - if asize > bsize: - a, b, asize, bsize = b, a, bsize, asize - - if a.sign == 0 or b.sign == 0: + def mul(self, other): + selfsize = self.numdigits() + othersize = other.numdigits() + + if selfsize > othersize: + self, other, selfsize, othersize = other, self, othersize, selfsize + + if self.sign == 0 or other.sign == 0: return NULLRBIGINT - if asize == 1: - if a._digits[0] == ONEDIGIT: - return rbigint(b._digits[:b.size], a.sign * b.sign, b.size) - elif bsize == 1: - res = b.widedigit(0) * a.widedigit(0) + if selfsize == 1: + if self._digits[0] == ONEDIGIT: + return rbigint(other._digits[:othersize], self.sign * other.sign, othersize) + elif othersize == 1: + res = other.uwidedigit(0) * self.udigit(0) carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], a.sign * b.sign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * other.sign, 2) else: - return rbigint([_store_digit(res & MASK)], a.sign * b.sign, 1) - - result = _x_mul(a, b, a.digit(0)) + return rbigint([_store_digit(res & MASK)], self.sign * other.sign, 1) + + result = _x_mul(self, other, self.digit(0)) elif USE_KARATSUBA: - if a is b: + if self is other: i = KARATSUBA_SQUARE_CUTOFF else: i = KARATSUBA_CUTOFF - if asize <= i: - result = _x_mul(a, b) - """elif 2 * asize <= bsize: - result = _k_lopsided_mul(a, b)""" + if selfsize <= i: + result = _x_mul(self, other) + """elif 2 * selfsize <= othersize: + result = _k_lopsided_mul(self, other)""" else: - result = _k_mul(a, b) + result = _k_mul(self, other) else: - result = _x_mul(a, b) - - result.sign = a.sign * b.sign + result = _x_mul(self, other) + + result.sign = self.sign * other.sign return result @jit.elidable - def int_mul(self, b): - if not int_in_valid_range(b): + def int_mul(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.mul(rbigint.fromint(b)) - - if self.sign == 0 or b == 0: + return self.mul(rbigint.fromint(iother)) + + if self.sign == 0 or iother == 0: return NULLRBIGINT asize = self.numdigits() - digit = abs(b) - bsign = -1 if b < 0 else 1 + digit = abs(iother) + + othersign = intsign(iother) if digit == 1: - return rbigint(self._digits[:self.size], self.sign * bsign, asize) + if othersign == 1: + return self + return rbigint(self._digits[:asize], self.sign * othersign, asize) elif asize == 1: - res = self.widedigit(0) * digit + udigit = r_uint(digit) + res = self.uwidedigit(0) * udigit carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * bsign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * othersign, 2) else: - return rbigint([_store_digit(res & MASK)], self.sign * bsign, 1) - + return rbigint([_store_digit(res & MASK)], self.sign * othersign, 1) elif digit & (digit - 1) == 0: result = self.lqshift(ptwotable[digit]) else: result = _muladd1(self, digit) - result.sign = self.sign * bsign + result.sign = self.sign * othersign return result @jit.elidable @@ -763,12 +763,10 @@ @jit.elidable def floordiv(self, other): - if self.sign == 1 and other.numdigits() == 1 and other.sign == 1: - digit = other.digit(0) - if digit == 1: - return rbigint(self._digits[:self.size], 1, self.size) - elif digit and digit & (digit - 1) == 0: - return self.rshift(ptwotable[digit]) + if other.numdigits() == 1: + otherint = other.digit(0) * other.sign + assert int_in_valid_range(otherint) + return self.int_floordiv(otherint) div, mod = _divrem(self, other) if mod.sign * other.sign == -1: @@ -782,6 +780,37 @@ return self.floordiv(other) @jit.elidable + def int_floordiv(self, iother): + if not int_in_valid_range(iother): + # Fallback to long. + return self.floordiv(rbigint.fromint(iother)) + + if iother == 0: + raise ZeroDivisionError("long division by zero") + + digit = abs(iother) + assert digit > 0 + + if self.sign == 1 and iother > 0: + if digit == 1: + return self + elif digit & (digit - 1) == 0: + return self.rqshift(ptwotable[digit]) + + div, mod = _divrem1(self, digit) + + if mod != 0 and self.sign * intsign(iother) == -1: + if div.sign == 0: + return ONENEGATIVERBIGINT + div = div.int_add(1) + div.sign = self.sign * intsign(iother) + div._normalize() + return div + + def int_div(self, iother): + return self.int_floordiv(iother) + + @jit.elidable def mod(self, other): if other.sign == 0: raise ZeroDivisionError("long division or modulo by zero") @@ -799,50 +828,50 @@ return mod @jit.elidable - def int_mod(self, other): - if other == 0: + def int_mod(self, iother): + if iother == 0: raise ZeroDivisionError("long division or modulo by zero") if self.sign == 0: return NULLRBIGINT - elif not int_in_valid_range(other): + elif not int_in_valid_range(iother): # Fallback to long. - return self.mod(rbigint.fromint(other)) + return self.mod(rbigint.fromint(iother)) if 1: # preserve indentation to preserve history - digit = abs(other) + digit = abs(iother) if digit == 1: return NULLRBIGINT elif digit == 2: modm = self.digit(0) & 1 if modm: - return ONENEGATIVERBIGINT if other < 0 else ONERBIGINT + return ONENEGATIVERBIGINT if iother < 0 else ONERBIGINT return NULLRBIGINT elif digit & (digit - 1) == 0: mod = self.int_and_(digit - 1) else: # Perform - size = self.numdigits() - 1 + size = UDIGIT_TYPE(self.numdigits() - 1) if size > 0: - rem = self.widedigit(size) - size -= 1 - while size >= 0: - rem = ((rem << SHIFT) + self.widedigit(size)) % digit + wrem = self.widedigit(size) + while size > 0: size -= 1 + wrem = ((wrem << SHIFT) | self.digit(size)) % digit + rem = _store_digit(wrem) else: - rem = self.digit(0) % digit + rem = _store_digit(self.digit(0) % digit) if rem == 0: return NULLRBIGINT - mod = rbigint([_store_digit(rem)], -1 if self.sign < 0 else 1, 1) - - if mod.sign * (-1 if other < 0 else 1) == -1: - mod = mod.int_add(other) + mod = rbigint([rem], -1 if self.sign < 0 else 1, 1) + + if mod.sign * intsign(iother) == -1: + mod = mod.int_add(iother) return mod @jit.elidable - def divmod(v, w): + def divmod(self, other): """ The / and % operators are now defined in terms of divmod(). The expression a mod b has the value a - b*floor(a/b). @@ -859,46 +888,78 @@ have different signs. We then subtract one from the 'div' part of the outcome to keep the invariant intact. """ - div, mod = _divrem(v, w) - if mod.sign * w.sign == -1: - mod = mod.add(w) + div, mod = _divrem(self, other) + if mod.sign * other.sign == -1: + mod = mod.add(other) if div.sign == 0: return ONENEGATIVERBIGINT, mod div = div.int_sub(1) return div, mod @jit.elidable - def pow(a, b, c=None): + def int_divmod(self, iother): + """ Divmod with int """ + + if iother == 0: + raise ZeroDivisionError("long division or modulo by zero") + + wsign = intsign(iother) + if not int_in_valid_range(iother) or (wsign == -1 and self.sign != wsign): + # Just fallback. + return self.divmod(rbigint.fromint(iother)) + + digit = abs(iother) + assert digit > 0 + + div, mod = _divrem1(self, digit) + # _divrem1 doesn't fix the sign + if div.size == 1 and div._digits[0] == NULLDIGIT: + div.sign = 0 + else: + div.sign = self.sign * wsign + if self.sign < 0: + mod = -mod + if mod and self.sign * wsign == -1: + mod += iother + if div.sign == 0: + div = ONENEGATIVERBIGINT + else: + div = div.int_sub(1) + mod = rbigint.fromint(mod) + return div, mod + + @jit.elidable + def pow(self, other, modulus=None): negativeOutput = False # if x<0 return negative output # 5-ary values. If the exponent is large enough, table is - # precomputed so that table[i] == a**i % c for i in range(32). + # precomputed so that table[i] == self**i % modulus for i in range(32). # python translation: the table is computed when needed. - if b.sign < 0: # if exponent is negative - if c is not None: + if other.sign < 0: # if exponent is negative + if modulus is not None: raise TypeError( "pow() 2nd argument " "cannot be negative when 3rd argument specified") # XXX failed to implement raise ValueError("bigint pow() too negative") - size_b = b.numdigits() - - if c is not None: - if c.sign == 0: + size_b = UDIGIT_TYPE(other.numdigits()) + + if modulus is not None: + if modulus.sign == 0: raise ValueError("pow() 3rd argument cannot be 0") # if modulus < 0: # negativeOutput = True # modulus = -modulus - if c.sign < 0: + if modulus.sign < 0: negativeOutput = True - c = c.neg() + modulus = modulus.neg() # if modulus == 1: # return 0 - if c.numdigits() == 1 and c._digits[0] == ONEDIGIT: + if modulus.numdigits() == 1 and modulus._digits[0] == ONEDIGIT: return NULLRBIGINT # Reduce base by modulus in some cases: @@ -910,63 +971,61 @@ # base % modulus instead. # We could _always_ do this reduction, but mod() isn't cheap, # so we only do it when it buys something. - if a.sign < 0 or a.numdigits() > c.numdigits(): - a = a.mod(c) - - elif b.sign == 0: + if self.sign < 0 or self.numdigits() > modulus.numdigits(): + self = self.mod(modulus) + elif other.sign == 0: return ONERBIGINT - elif a.sign == 0: + elif self.sign == 0: return NULLRBIGINT elif size_b == 1: - if b._digits[0] == NULLDIGIT: - return ONERBIGINT if a.sign == 1 else ONENEGATIVERBIGINT - elif b._digits[0] == ONEDIGIT: - return a - elif a.numdigits() == 1: - adigit = a.digit(0) - digit = b.digit(0) + if other._digits[0] == ONEDIGIT: + return self + elif self.numdigits() == 1 and modulus is None: + adigit = self.digit(0) + digit = other.digit(0) if adigit == 1: - if a.sign == -1 and digit % 2: + if self.sign == -1 and digit % 2: return ONENEGATIVERBIGINT return ONERBIGINT elif adigit & (adigit - 1) == 0: - ret = a.lshift(((digit-1)*(ptwotable[adigit]-1)) + digit-1) - if a.sign == -1 and not digit % 2: + ret = self.lshift(((digit-1)*(ptwotable[adigit]-1)) + digit-1) + if self.sign == -1 and not digit % 2: ret.sign = 1 return ret - # At this point a, b, and c are guaranteed non-negative UNLESS - # c is NULL, in which case a may be negative. */ - - z = rbigint([ONEDIGIT], 1, 1) + # At this point self, other, and modulus are guaranteed non-negative UNLESS + # modulus is NULL, in which case self may be negative. */ + + z = ONERBIGINT # python adaptation: moved macros REDUCE(X) and MULT(X, Y, result) # into helper function result = _help_mult(x, y, c) if size_b <= FIVEARY_CUTOFF: # Left-to-right binary exponentiation (HAC Algorithm 14.79) # http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf - size_b -= 1 - while size_b >= 0: - bi = b.digit(size_b) + + while size_b > 0: + size_b -= 1 + bi = other.digit(size_b) j = 1 << (SHIFT-1) while j != 0: - z = _help_mult(z, z, c) + z = _help_mult(z, z, modulus) if bi & j: - z = _help_mult(z, a, c) + z = _help_mult(z, self, modulus) j >>= 1 - size_b -= 1 + else: # Left-to-right 5-ary exponentiation (HAC Algorithm 14.82) - # This is only useful in the case where c != None. + # This is only useful in the case where modulus != None. # z still holds 1L table = [z] * 32 table[0] = z for i in range(1, 32): - table[i] = _help_mult(table[i-1], a, c) + table[i] = _help_mult(table[i-1], self, modulus) # Note that here SHIFT is not a multiple of 5. The difficulty - # is to extract 5 bits at a time from 'b', starting from the + # is to extract 5 bits at a time from 'other', starting from the # most significant digits, so that at the end of the algorithm # it falls exactly to zero. # m = max number of bits = i * SHIFT @@ -985,37 +1044,120 @@ index = (accum >> j) & 0x1f else: # 'accum' does not have enough digit. - # must get the next digit from 'b' in order to complete + # must get the next digit from 'other' in order to complete if size_b == 0: break # Done size_b -= 1 assert size_b >= 0 - bi = b.udigit(size_b) + bi = other.udigit(size_b) index = ((accum << (-j)) | (bi >> (j+SHIFT))) & 0x1f accum = bi j += SHIFT # for k in range(5): - z = _help_mult(z, z, c) + z = _help_mult(z, z, modulus) if index: - z = _help_mult(z, table[index], c) + z = _help_mult(z, table[index], modulus) # assert j == -5 if negativeOutput and z.sign != 0: - z = z.sub(c) + z = z.sub(modulus) + return z + + @jit.elidable + def int_pow(self, iother, modulus=None): + negativeOutput = False # if x<0 return negative output + + # 5-ary values. If the exponent is large enough, table is + # precomputed so that table[i] == self**i % modulus for i in range(32). + # python translation: the table is computed when needed. + + if iother < 0: # if exponent is negative + if modulus is not None: + raise TypeError( + "pow() 2nd argument " + "cannot be negative when 3rd argument specified") + # XXX failed to implement + raise ValueError("bigint pow() too negative") + + assert iother >= 0 + if modulus is not None: + if modulus.sign == 0: + raise ValueError("pow() 3rd argument cannot be 0") + + # if modulus < 0: + # negativeOutput = True + # modulus = -modulus + if modulus.sign < 0: + negativeOutput = True + modulus = modulus.neg() + + # if modulus == 1: + # return 0 + if modulus.numdigits() == 1 and modulus._digits[0] == ONEDIGIT: + return NULLRBIGINT + + # Reduce base by modulus in some cases: + # 1. If base < 0. Forcing the base non-neg makes things easier. + # 2. If base is obviously larger than the modulus. The "small + # exponent" case later can multiply directly by base repeatedly, + # while the "large exponent" case multiplies directly by base 31 + # times. It can be unboundedly faster to multiply by + # base % modulus instead. + # We could _always_ do this reduction, but mod() isn't cheap, + # so we only do it when it buys something. + if self.sign < 0 or self.numdigits() > modulus.numdigits(): + self = self.mod(modulus) + elif iother == 0: + return ONERBIGINT + elif self.sign == 0: + return NULLRBIGINT + elif iother == 1: + return self + elif self.numdigits() == 1: + adigit = self.digit(0) + if adigit == 1: + if self.sign == -1 and iother % 2: + return ONENEGATIVERBIGINT + return ONERBIGINT + elif adigit & (adigit - 1) == 0: + ret = self.lshift(((iother-1)*(ptwotable[adigit]-1)) + iother-1) + if self.sign == -1 and not iother % 2: + ret.sign = 1 + return ret + + # At this point self, iother, and modulus are guaranteed non-negative UNLESS + # modulus is NULL, in which case self may be negative. */ + + z = ONERBIGINT + + # python adaptation: moved macros REDUCE(X) and MULT(X, Y, result) + # into helper function result = _help_mult(x, y, modulus) + # Left-to-right binary exponentiation (HAC Algorithm 14.79) + # http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf + j = 1 << (SHIFT-1) + + while j != 0: + z = _help_mult(z, z, modulus) + if iother & j: + z = _help_mult(z, self, modulus) + j >>= 1 + + if negativeOutput and z.sign != 0: + z = z.sub(modulus) return z @jit.elidable def neg(self): - return rbigint(self._digits, -self.sign, self.size) + return rbigint(self._digits, -self.sign, self.numdigits()) @jit.elidable def abs(self): if self.sign != -1: return self - return rbigint(self._digits, 1, self.size) + return rbigint(self._digits, 1, self.numdigits()) @jit.elidable def invert(self): #Implement ~x as -(x + 1) @@ -1041,15 +1183,15 @@ # So we can avoid problems with eq, AND avoid the need for normalize. if self.sign == 0: return self - return rbigint([NULLDIGIT] * wordshift + self._digits, self.sign, self.size + wordshift) + return rbigint([NULLDIGIT] * wordshift + self._digits, self.sign, self.numdigits() + wordshift) oldsize = self.numdigits() newsize = oldsize + wordshift + 1 z = rbigint([NULLDIGIT] * newsize, self.sign, newsize) - accum = _widen_digit(0) + accum = _unsigned_widen_digit(0) j = 0 while j < oldsize: - accum += self.widedigit(j) << remshift + accum += self.uwidedigit(j) << remshift z.setdigit(wordshift, accum) accum >>= SHIFT wordshift += 1 @@ -1061,7 +1203,7 @@ z._normalize() return z - lshift._always_inline_ = True # It's so fast that it's always benefitial. + lshift._always_inline_ = True # It's so fast that it's always beneficial. @jit.elidable def lqshift(self, int_other): @@ -1071,17 +1213,17 @@ oldsize = self.numdigits() z = rbigint([NULLDIGIT] * (oldsize + 1), self.sign, (oldsize + 1)) - accum = _widen_digit(0) + accum = _unsigned_widen_digit(0) i = 0 while i < oldsize: - accum += self.widedigit(i) << int_other + accum += self.uwidedigit(i) << int_other z.setdigit(i, accum) accum >>= SHIFT i += 1 z.setdigit(oldsize, accum) z._normalize() return z - lqshift._always_inline_ = True # It's so fast that it's always benefitial. + lqshift._always_inline_ = True # It's so fast that it's always beneficial. @jit.elidable def rshift(self, int_other, dont_invert=False): @@ -1112,6 +1254,31 @@ z._normalize() return z rshift._always_inline_ = 'try' # It's so fast that it's always benefitial. + + @jit.elidable + def rqshift(self, int_other): + wordshift = int_other / SHIFT + loshift = int_other % SHIFT + newsize = self.numdigits() - wordshift + + if newsize <= 0: + return NULLRBIGINT + + hishift = SHIFT - loshift + z = rbigint([NULLDIGIT] * newsize, self.sign, newsize) + i = 0 + + while i < newsize: + digit = self.udigit(wordshift) + newdigit = (digit >> loshift) + if i+1 < newsize: + newdigit |= (self.udigit(wordshift+1) << hishift) + z.setdigit(i, newdigit) + i += 1 + wordshift += 1 + z._normalize() + return z + rshift._always_inline_ = 'try' # It's so fast that it's always beneficial. @jit.elidable def abs_rshift_and_mask(self, bigshiftcount, mask): @@ -1167,24 +1334,24 @@ return _bitwise(self, '&', other) @jit.elidable - def int_and_(self, other): - return _int_bitwise(self, '&', other) + def int_and_(self, iother): + return _int_bitwise(self, '&', iother) @jit.elidable def xor(self, other): return _bitwise(self, '^', other) @jit.elidable - def int_xor(self, other): - return _int_bitwise(self, '^', other) + def int_xor(self, iother): + return _int_bitwise(self, '^', iother) @jit.elidable def or_(self, other): return _bitwise(self, '|', other) @jit.elidable - def int_or_(self, other): - return _int_bitwise(self, '|', other) + def int_or_(self, iother): + return _int_bitwise(self, '|', iother) @jit.elidable def oct(self): @@ -1218,7 +1385,10 @@ for d in digits: l = l << SHIFT l += intmask(d) - return l * self.sign + result = l * self.sign + if result == 0: + assert self.sign == 0 + return result def _normalize(self): i = self.numdigits() @@ -1227,11 +1397,10 @@ i -= 1 assert i > 0 - if i != self.numdigits(): - self.size = i - if self.numdigits() == 1 and self._digits[0] == NULLDIGIT: + self.size = i + if i == 1 and self._digits[0] == NULLDIGIT: self.sign = 0 - self._digits = [NULLDIGIT] + self._digits = NULLDIGITS _normalize._always_inline_ = True @@ -1256,8 +1425,8 @@ def __repr__(self): return "" % (self._digits, - self.sign, self.size, len(self._digits), - self.str()) + self.sign, self.numdigits(), len(self._digits), + self.tolong()) ONERBIGINT = rbigint([ONEDIGIT], 1, 1) ONENEGATIVERBIGINT = rbigint([ONEDIGIT], -1, 1) @@ -1322,7 +1491,7 @@ if x > 0: return digits_from_nonneg_long(x), 1 elif x == 0: - return [NULLDIGIT], 0 + return NULLDIGITS, 0 elif x != most_neg_value_of_same_type(x): # normal case return digits_from_nonneg_long(-x), -1 @@ -1340,7 +1509,7 @@ def args_from_long(x): if x >= 0: if x == 0: - return [NULLDIGIT], 0 + return NULLDIGITS, 0 else: return digits_from_nonneg_long(x), 1 else: @@ -1450,7 +1619,7 @@ if adigit == bdigit: return NULLRBIGINT - + return rbigint.fromint(adigit - bdigit) z = rbigint([NULLDIGIT] * size_a, 1, size_a) @@ -1497,11 +1666,11 @@ z = rbigint([NULLDIGIT] * (size_a + size_b), 1) i = UDIGIT_TYPE(0) while i < size_a: - f = a.widedigit(i) + f = a.uwidedigit(i) pz = i << 1 pa = i + 1 - carry = z.widedigit(pz) + f * f + carry = z.uwidedigit(pz) + f * f z.setdigit(pz, carry) pz += 1 carry >>= SHIFT @@ -1511,18 +1680,18 @@ # pyramid it appears. Same as adding f<<1 once. f <<= 1 while pa < size_a: - carry += z.widedigit(pz) + a.widedigit(pa) * f + carry += z.uwidedigit(pz) + a.uwidedigit(pa) * f pa += 1 z.setdigit(pz, carry) pz += 1 carry >>= SHIFT if carry: - carry += z.widedigit(pz) + carry += z.udigit(pz) z.setdigit(pz, carry) pz += 1 carry >>= SHIFT if carry: - z.setdigit(pz, z.widedigit(pz) + carry) + z.setdigit(pz, z.udigit(pz) + carry) assert (carry >> SHIFT) == 0 i += 1 z._normalize() @@ -1543,29 +1712,29 @@ size_a1 = UDIGIT_TYPE(size_a - 1) size_b1 = UDIGIT_TYPE(size_b - 1) while i < size_a1: - f0 = a.widedigit(i) - f1 = a.widedigit(i + 1) + f0 = a.uwidedigit(i) + f1 = a.uwidedigit(i + 1) pz = i - carry = z.widedigit(pz) + b.widedigit(0) * f0 + carry = z.uwidedigit(pz) + b.uwidedigit(0) * f0 z.setdigit(pz, carry) pz += 1 carry >>= SHIFT j = UDIGIT_TYPE(0) while j < size_b1: - # this operation does not overflow using + # this operation does not overflow using # SHIFT = (LONG_BIT // 2) - 1 = B - 1; in fact before it # carry and z.widedigit(pz) are less than 2**(B - 1); # b.widedigit(j + 1) * f0 < (2**(B-1) - 1)**2; so # carry + z.widedigit(pz) + b.widedigit(j + 1) * f0 + # b.widedigit(j) * f1 < 2**(2*B - 1) - 2**B < 2**LONG)BIT - 1 - carry += z.widedigit(pz) + b.widedigit(j + 1) * f0 + \ - b.widedigit(j) * f1 + carry += z.uwidedigit(pz) + b.uwidedigit(j + 1) * f0 + \ + b.uwidedigit(j) * f1 z.setdigit(pz, carry) pz += 1 carry >>= SHIFT j += 1 # carry < 2**(B + 1) - 2 - carry += z.widedigit(pz) + b.widedigit(size_b1) * f1 + carry += z.uwidedigit(pz) + b.uwidedigit(size_b1) * f1 z.setdigit(pz, carry) pz += 1 carry >>= SHIFT @@ -1576,17 +1745,17 @@ i += 2 if size_a & 1: pz = size_a1 - f = a.widedigit(pz) + f = a.uwidedigit(pz) pb = 0 - carry = _widen_digit(0) + carry = _unsigned_widen_digit(0) while pb < size_b: - carry += z.widedigit(pz) + b.widedigit(pb) * f + carry += z.uwidedigit(pz) + b.uwidedigit(pb) * f pb += 1 z.setdigit(pz, carry) pz += 1 carry >>= SHIFT if carry: - z.setdigit(pz, z.widedigit(pz) + carry) + z.setdigit(pz, z.udigit(pz) + carry) z._normalize() return z @@ -1602,8 +1771,8 @@ size_lo = min(size_n, size) # We use "or" her to avoid having a check where list can be empty in _normalize. - lo = rbigint(n._digits[:size_lo] or [NULLDIGIT], 1) - hi = rbigint(n._digits[size_lo:n.size] or [NULLDIGIT], 1) + lo = rbigint(n._digits[:size_lo] or NULLDIGITS, 1) + hi = rbigint(n._digits[size_lo:size_n] or NULLDIGITS, 1) lo._normalize() hi._normalize() return hi, lo @@ -1708,113 +1877,16 @@ ret._normalize() return ret -""" (*) Why adding t3 can't "run out of room" above. - -Let f(x) mean the floor of x and c(x) mean the ceiling of x. Some facts -to start with: - -1. For any integer i, i = c(i/2) + f(i/2). In particular, - bsize = c(bsize/2) + f(bsize/2). -2. shift = f(bsize/2) -3. asize <= bsize -4. Since we call k_lopsided_mul if asize*2 <= bsize, asize*2 > bsize in this - routine, so asize > bsize/2 >= f(bsize/2) in this routine. - -We allocated asize + bsize result digits, and add t3 into them at an offset -of shift. This leaves asize+bsize-shift allocated digit positions for t3 -to fit into, = (by #1 and #2) asize + f(bsize/2) + c(bsize/2) - f(bsize/2) = -asize + c(bsize/2) available digit positions. - -bh has c(bsize/2) digits, and bl at most f(size/2) digits. So bh+hl has -at most c(bsize/2) digits + 1 bit. - -If asize == bsize, ah has c(bsize/2) digits, else ah has at most f(bsize/2) -digits, and al has at most f(bsize/2) digits in any case. So ah+al has at -most (asize == bsize ? c(bsize/2) : f(bsize/2)) digits + 1 bit. - -The product (ah+al)*(bh+bl) therefore has at most - - c(bsize/2) + (asize == bsize ? c(bsize/2) : f(bsize/2)) digits + 2 bits - -and we have asize + c(bsize/2) available digit positions. We need to show -this is always enough. An instance of c(bsize/2) cancels out in both, so -the question reduces to whether asize digits is enough to hold -(asize == bsize ? c(bsize/2) : f(bsize/2)) digits + 2 bits. If asize < bsize, -then we're asking whether asize digits >= f(bsize/2) digits + 2 bits. By #4, -asize is at least f(bsize/2)+1 digits, so this in turn reduces to whether 1 -digit is enough to hold 2 bits. This is so since SHIFT=15 >= 2. If -asize == bsize, then we're asking whether bsize digits is enough to hold -c(bsize/2) digits + 2 bits, or equivalently (by #1) whether f(bsize/2) digits -is enough to hold 2 bits. This is so if bsize >= 2, which holds because -bsize >= KARATSUBA_CUTOFF >= 2. - -Note that since there's always enough room for (ah+al)*(bh+bl), and that's -clearly >= each of ah*bh and al*bl, there's always enough room to subtract -ah*bh and al*bl too. -""" - -def _k_lopsided_mul(a, b): - # Not in use anymore, only account for like 1% performance. Perhaps if we - # Got rid of the extra list allocation this would be more effective. - """ - b has at least twice the digits of a, and a is big enough that Karatsuba - would pay off *if* the inputs had balanced sizes. View b as a sequence - of slices, each with a->ob_size digits, and multiply the slices by a, - one at a time. This gives k_mul balanced inputs to work with, and is - also cache-friendly (we compute one double-width slice of the result - at a time, then move on, never bactracking except for the helpful - single-width slice overlap between successive partial sums). - """ - asize = a.numdigits() - bsize = b.numdigits() - # nbdone is # of b digits already multiplied - - assert asize > KARATSUBA_CUTOFF - assert 2 * asize <= bsize - - # Allocate result space, and zero it out. - ret = rbigint([NULLDIGIT] * (asize + bsize), 1) - - # Successive slices of b are copied into bslice. - #bslice = rbigint([0] * asize, 1) - # XXX we cannot pre-allocate, see comments below! - # XXX prevent one list from being created. - bslice = rbigint(sign=1) - - nbdone = 0 - while bsize > 0: - nbtouse = min(bsize, asize) - - # Multiply the next slice of b by a. - - #bslice.digits[:nbtouse] = b.digits[nbdone : nbdone + nbtouse] - # XXX: this would be more efficient if we adopted CPython's - # way to store the size, instead of resizing the list! - # XXX change the implementation, encoding length via the sign. - bslice._digits = b._digits[nbdone : nbdone + nbtouse] - bslice.size = nbtouse - product = _k_mul(a, bslice) - - # Add into result. - _v_iadd(ret, nbdone, ret.numdigits() - nbdone, - product, product.numdigits()) - - bsize -= nbtouse - nbdone += nbtouse - - ret._normalize() - return ret - def _inplace_divrem1(pout, pin, n): """ Divide bigint pin by non-zero digit n, storing quotient in pout, and returning the remainder. It's OK for pin == pout on entry. """ - rem = _widen_digit(0) + rem = _unsigned_widen_digit(0) assert n > 0 and n <= MASK size = pin.numdigits() - 1 while size >= 0: - rem = (rem << SHIFT) | pin.widedigit(size) + rem = (rem << SHIFT) | pin.udigit(size) hi = rem // n pout.setdigit(size, hi) rem -= hi * n @@ -1891,14 +1963,15 @@ def _muladd1(a, n, extra=0): """Multiply by a single digit and add a single digit, ignoring the sign. """ + assert n > 0 size_a = a.numdigits() z = rbigint([NULLDIGIT] * (size_a+1), 1) assert extra & MASK == extra - carry = _widen_digit(extra) + carry = _unsigned_widen_digit(extra) i = 0 while i < size_a: - carry += a.widedigit(i) * n + carry += a.uwidedigit(i) * n z.setdigit(i, carry) carry >>= SHIFT i += 1 @@ -1912,10 +1985,10 @@ """ carry = 0 - assert 0 <= d and d < SHIFT + #assert 0 <= d and d < SHIFT i = 0 while i < m: - acc = a.widedigit(i) << d | carry + acc = a.uwidedigit(i) << d | carry z.setdigit(i, acc) carry = acc >> SHIFT i += 1 @@ -1927,14 +2000,14 @@ * result in z[0:m], and return the d bits shifted out of the bottom. """ - carry = _widen_digit(0) - acc = _widen_digit(0) + carry = _unsigned_widen_digit(0) + acc = _unsigned_widen_digit(0) mask = (1 << d) - 1 - assert 0 <= d and d < SHIFT + #assert 0 <= d and d < SHIFT i = m-1 while i >= 0: - acc = (carry << SHIFT) | a.widedigit(i) + acc = (carry << SHIFT) | a.udigit(i) carry = acc & mask z.setdigit(i, acc >> d) i -= 1 @@ -1989,10 +2062,17 @@ else: vtop = v.widedigit(j) assert vtop <= wm1 + vv = (vtop << SHIFT) | v.widedigit(abs(j-1)) + + # Hints to make division just as fast as doing it unsigned. But avoids casting to get correct results. + assert vv >= 0 + assert wm1 >= 1 + q = vv / wm1 - r = vv - wm1 * q - while wm2 * q > ((r << SHIFT) | v.widedigit(abs(j-2))): + r = vv % wm1 # This seems to be slightly faster on widen digits than vv - wm1 * q. + vj2 = v.digit(abs(j-2)) + while wm2 * q > ((r << SHIFT) | vj2): q -= 1 r += wm1 @@ -2059,6 +2139,36 @@ rem.sign = - rem.sign return z, rem +def _x_int_lt(a, b, eq=False): + """ Compare bigint a with int b for less than or less than or equal """ + osign = 1 + if b == 0: + osign = 0 + elif b < 0: + osign = -1 + + if a.sign > osign: + return False + elif a.sign < osign: + return True + + digits = a.numdigits() + + if digits > 1: + if osign == 1: + return False + else: + return True + + d1 = a.sign * a.digit(0) + if eq: + if d1 <= b: + return True + else: + if d1 < b: + return True + return False + # ______________ conversions to double _______________ def _AsScaledDouble(v): @@ -2764,7 +2874,7 @@ elif s[p] == '+': p += 1 - a = rbigint() + a = NULLRBIGINT tens = 1 dig = 0 ord0 = ord('0') @@ -2785,7 +2895,7 @@ base = parser.base if (base & (base - 1)) == 0 and base >= 2: return parse_string_from_binary_base(parser) - a = rbigint() + a = NULLRBIGINT digitmax = BASE_MAX[base] tens, dig = 1, 0 while True: diff --git a/rpython/rlib/test/test_rbigint.py b/rpython/rlib/test/test_rbigint.py --- a/rpython/rlib/test/test_rbigint.py +++ b/rpython/rlib/test/test_rbigint.py @@ -95,6 +95,46 @@ r2 = op1 // op2 assert r1.tolong() == r2 + def test_int_floordiv(self): + x = 1000L + r = rbigint.fromlong(x) + r2 = r.int_floordiv(10) + assert r2.tolong() == 100L + + for op1 in gen_signs(long_vals): + for op2 in signed_int_vals: + if not op2: + continue + rl_op1 = rbigint.fromlong(op1) + r1 = rl_op1.int_floordiv(op2) + r2 = op1 // op2 + assert r1.tolong() == r2 + + assert pytest.raises(ZeroDivisionError, r.int_floordiv, 0) + + # Error pointed out by Armin Rigo + n = sys.maxint+1 + r = rbigint.fromlong(n) + assert r.int_floordiv(int(-n)).tolong() == -1L + + for x in int_vals: + if not x: + continue + r = rbigint.fromlong(x) + rn = rbigint.fromlong(-x) + res = r.int_floordiv(x) + res2 = r.int_floordiv(-x) + res3 = rn.int_floordiv(x) + assert res.tolong() == 1L + assert res2.tolong() == -1L + assert res3.tolong() == -1L + + def test_floordiv2(self): + n1 = rbigint.fromlong(sys.maxint + 1) + n2 = rbigint.fromlong(-(sys.maxint + 1)) + assert n1.floordiv(n2).tolong() == -1L + assert n2.floordiv(n1).tolong() == -1L + def test_truediv(self): for op1 in gen_signs(long_vals_not_too_big): rl_op1 = rbigint.fromlong(op1) @@ -185,9 +225,26 @@ r4 = pow(op1, op2, op3) assert r3.tolong() == r4 + def test_int_pow(self): + for op1 in gen_signs(long_vals_not_too_big): + rl_op1 = rbigint.fromlong(op1) + for op2 in [0, 1, 2, 8, 9, 10, 11, 127, 128, 129]: + r1 = rl_op1.int_pow(op2) + r2 = op1 ** op2 + assert r1.tolong() == r2 + + for op3 in gen_signs(long_vals_not_too_big): + if not op3: + continue + r3 = rl_op1.int_pow(op2, rbigint.fromlong(op3)) + r4 = pow(op1, op2, op3) + print op1, op2, op3 + assert r3.tolong() == r4 + def test_pow_raises(self): r1 = rbigint.fromint(2) r0 = rbigint.fromint(0) + py.test.raises(ValueError, r1.int_pow, 2, r0) py.test.raises(ValueError, r1.pow, r1, r0) def test_touint(self): @@ -601,6 +658,9 @@ # test special optimization case in rshift: assert rbigint.fromlong(-(1 << 100)).rshift(5).tolong() == -(1 << 100) >> 5 + # Chek value accuracy. + assert rbigint.fromlong(18446744073709551615L).rshift(1).tolong() == 18446744073709551615L >> 1 + def test_qshift(self): for x in range(10): for y in range(1, 161, 16): @@ -610,11 +670,18 @@ for z in range(1, 31): res1 = f1.lqshift(z).tolong() + res2 = f1.rqshift(z).tolong() res3 = nf1.lqshift(z).tolong() assert res1 == num << z + assert res2 == num >> z assert res3 == -num << z + # Large digit + for x in range((1 << SHIFT) - 10, (1 << SHIFT) + 10): + f1 = rbigint.fromlong(x) + assert f1.rqshift(SHIFT).tolong() == x >> SHIFT + assert f1.rqshift(SHIFT+1).tolong() == x >> (SHIFT+1) def test_from_list_n_bits(self): for x in ([3L ** 30L, 5L ** 20L, 7 ** 300] + @@ -864,6 +931,27 @@ assert rem.tolong() == _rem + def test_int_divmod(self): + for x in long_vals: + for y in int_vals + [-sys.maxint-1]: + if not y: + continue + for sx, sy in (1, 1), (1, -1), (-1, -1), (-1, 1): + sx *= x + sy *= y + if sy == sys.maxint + 1: + continue + f1 = rbigint.fromlong(sx) + div, rem = f1.int_divmod(sy) + div1, rem1 = f1.divmod(rbigint.fromlong(sy)) + _div, _rem = divmod(sx, sy) + print sx, sy, " | ", div.tolong(), rem.tolong() + assert div1.tolong() == _div + assert rem1.tolong() == _rem + assert div.tolong() == _div + assert rem.tolong() == _rem + py.test.raises(ZeroDivisionError, rbigint.fromlong(x).int_divmod, 0) + # testing Karatsuba stuff def test__v_iadd(self): f1 = bigint([lobj.MASK] * 10, 1) @@ -1067,8 +1155,14 @@ except Exception as e: pytest.raises(type(e), f1.pow, f2, f3) else: - v = f1.pow(f2, f3) - assert v.tolong() == res + v1 = f1.pow(f2, f3) + try: + v2 = f1.int_pow(f2.toint(), f3) + except OverflowError: + pass + else: + assert v2.tolong() == res + assert v1.tolong() == res @given(biglongs, biglongs) @example(510439143470502793407446782273075179618477362188870662225920, @@ -1088,6 +1182,18 @@ a, b = f1.divmod(f2) assert (a.tolong(), b.tolong()) == res + @given(biglongs, ints) + def test_int_divmod(self, x, iy): + f1 = rbigint.fromlong(x) + try: + res = divmod(x, iy) + except Exception as e: + pytest.raises(type(e), f1.int_divmod, iy) + else: + print x, iy + a, b = f1.int_divmod(iy) + assert (a.tolong(), b.tolong()) == res + @given(longs) def test_hash(self, x): # hash of large integers: should be equal to the hash of the @@ -1118,10 +1224,34 @@ assert ra.truediv(rb) == a / b @given(longs, longs) - def test_bitwise(self, x, y): + def test_bitwise_and_mul(self, x, y): lx = rbigint.fromlong(x) ly = rbigint.fromlong(y) - for mod in "xor and_ or_".split(): - res1 = getattr(lx, mod)(ly).tolong() + for mod in "xor and_ or_ mul".split(): + res1a = getattr(lx, mod)(ly).tolong() + res1b = getattr(ly, mod)(lx).tolong() + res2 = getattr(operator, mod)(x, y) + assert res1a == res2 + + @given(longs, ints) + def test_int_bitwise_and_mul(self, x, y): + lx = rbigint.fromlong(x) + for mod in "xor and_ or_ mul".split(): + res1 = getattr(lx, 'int_' + mod)(y).tolong() res2 = getattr(operator, mod)(x, y) assert res1 == res2 + + @given(longs, ints) + def test_int_comparison(self, x, y): + lx = rbigint.fromlong(x) + assert lx.int_lt(y) == (x < y) + assert lx.int_eq(y) == (x == y) + assert lx.int_le(y) == (x <= y) + + @given(longs, longs) + def test_int_comparison(self, x, y): + lx = rbigint.fromlong(x) + ly = rbigint.fromlong(y) + assert lx.lt(ly) == (x < y) + assert lx.eq(ly) == (x == y) + assert lx.le(ly) == (x <= y) diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py --- a/rpython/rtyper/lltypesystem/ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/ll2ctypes.py @@ -175,7 +175,16 @@ if res >= (1 << 127): res -= 1 << 128 return res + class c_uint128(ctypes.Array): # based on 2 ulongs + _type_ = ctypes.c_uint64 + _length_ = 2 + @property + def value(self): + res = self[0] | (self[1] << 64) + return res + _ctypes_cache[rffi.__INT128_T] = c_int128 + _ctypes_cache[rffi.__UINT128_T] = c_uint128 # for unicode strings, do not use ctypes.c_wchar because ctypes # automatically converts arrays into unicode strings. diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py --- a/rpython/rtyper/lltypesystem/lloperation.py +++ b/rpython/rtyper/lltypesystem/lloperation.py @@ -324,6 +324,26 @@ 'lllong_rshift': LLOp(canfold=True), # args (r_longlonglong, int) 'lllong_xor': LLOp(canfold=True), + 'ulllong_is_true': LLOp(canfold=True), + 'ulllong_invert': LLOp(canfold=True), + + 'ulllong_add': LLOp(canfold=True), + 'ulllong_sub': LLOp(canfold=True), + 'ulllong_mul': LLOp(canfold=True), + 'ulllong_floordiv': LLOp(canfold=True), + 'ulllong_mod': LLOp(canfold=True), + 'ulllong_lt': LLOp(canfold=True), + 'ulllong_le': LLOp(canfold=True), + 'ulllong_eq': LLOp(canfold=True), + 'ulllong_ne': LLOp(canfold=True), + 'ulllong_gt': LLOp(canfold=True), + 'ulllong_ge': LLOp(canfold=True), + 'ulllong_and': LLOp(canfold=True), + 'ulllong_or': LLOp(canfold=True), + 'ulllong_lshift': LLOp(canfold=True), # args (r_ulonglonglong, int) + 'ulllong_rshift': LLOp(canfold=True), # args (r_ulonglonglong, int) + 'ulllong_xor': LLOp(canfold=True), + 'cast_primitive': LLOp(canfold=True), 'cast_bool_to_int': LLOp(canfold=True), 'cast_bool_to_uint': LLOp(canfold=True), diff --git a/rpython/rtyper/lltypesystem/lltype.py b/rpython/rtyper/lltypesystem/lltype.py --- a/rpython/rtyper/lltypesystem/lltype.py +++ b/rpython/rtyper/lltypesystem/lltype.py @@ -8,7 +8,7 @@ from rpython.rlib.rarithmetic import ( base_int, intmask, is_emulated_long, is_valid_int, longlonglongmask, longlongmask, maxint, normalizedinttype, r_int, r_longfloat, r_longlong, - r_longlonglong, r_singlefloat, r_uint, r_ulonglong) + r_longlonglong, r_singlefloat, r_uint, r_ulonglong, r_ulonglonglong) from rpython.rtyper.extregistry import ExtRegistryEntry from rpython.tool import leakfinder from rpython.tool.identity_dict import identity_dict @@ -676,6 +676,7 @@ _numbertypes[r_int] = _numbertypes[int] _numbertypes[r_longlonglong] = Number("SignedLongLongLong", r_longlonglong, longlonglongmask) + From pypy.commits at gmail.com Fri Feb 8 05:57:37 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 08 Feb 2019 02:57:37 -0800 (PST) Subject: [pypy-commit] pypy py3.5: merge default Message-ID: <5c5d60a1.1c69fb81.65c40.6fb5@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.5 Changeset: r95896:8d7a3fd5c328 Date: 2019-02-08 11:56 +0100 http://bitbucket.org/pypy/pypy/changeset/8d7a3fd5c328/ Log: merge default diff too long, truncating to 2000 out of 2357 lines diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -58,3 +58,6 @@ 3f6eaa010fce78cc7973bdc1dfdb95970f08fed2 release-pypy3.5-v5.10.1 ab0b9caf307db6592905a80b8faffd69b39005b8 release-pypy2.7-v6.0.0 fdd60ed87e941677e8ea11acf9f1819466521bf2 release-pypy3.5-v6.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 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 @@ -13,3 +13,9 @@ The zlib module's compressobj and decompressobj now expose copy methods as they do on CPython. + + +.. math-improvements + +Improve performance of long operations where one of the operands fits into +an int. \ No newline at end of file diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -28,6 +28,8 @@ compression and decompression tests have a little real data to assert against. """ + cls.w_runappdirect = cls.space.wrap(cls.runappdirect) + cls.w_zlib = cls.space.getbuiltinmodule('zlib') expanded = b'some bytes which will be compressed' cls.w_expanded = cls.space.newbytes(expanded) diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py --- a/pypy/objspace/std/intobject.py +++ b/pypy/objspace/std/intobject.py @@ -437,7 +437,7 @@ return ix -def _pow_ovf2long(space, iv, iw, w_modulus): +def _pow_ovf2long(space, iv, w_iv, iw, w_iw, w_modulus): if space.is_none(w_modulus) and _recover_with_smalllong(space): from pypy.objspace.std.smalllongobject import _pow as _pow_small try: @@ -446,9 +446,12 @@ return _pow_small(space, r_longlong(iv), iw, r_longlong(0)) except (OverflowError, ValueError): pass - from pypy.objspace.std.longobject import W_LongObject - w_iv = W_LongObject.fromint(space, iv) - w_iw = W_LongObject.fromint(space, iw) + from pypy.objspace.std.longobject import W_LongObject, W_AbstractLongObject + if w_iv is None or not isinstance(w_iv, W_AbstractLongObject): + w_iv = W_LongObject.fromint(space, iv) + if w_iw is None or not isinstance(w_iw, W_AbstractLongObject): + w_iw = W_LongObject.fromint(space, iw) + return w_iv.descr_pow(space, w_iw, w_modulus) @@ -456,7 +459,7 @@ op = getattr(operator, opname, None) assert op or ovf2small - def ovf2long(space, x, y): + def ovf2long(space, x, w_x, y, w_y): """Handle overflowing to smalllong or long""" if _recover_with_smalllong(space): if ovf2small: @@ -468,9 +471,12 @@ b = r_longlong(y) return W_SmallLongObject(op(a, b)) - from pypy.objspace.std.longobject import W_LongObject - w_x = W_LongObject.fromint(space, x) - w_y = W_LongObject.fromint(space, y) + from pypy.objspace.std.longobject import W_LongObject, W_AbstractLongObject + if w_x is None or not isinstance(w_x, W_AbstractLongObject): + w_x = W_LongObject.fromint(space, x) + if w_y is None or not isinstance(w_y, W_AbstractLongObject): + w_y = W_LongObject.fromint(space, y) + return getattr(w_x, 'descr_' + opname)(space, w_y) return ovf2long @@ -630,12 +636,18 @@ # can't return NotImplemented (space.pow doesn't do full # ternary, i.e. w_modulus.__zpow__(self, w_exponent)), so # handle it ourselves - return _pow_ovf2long(space, x, y, w_modulus) + return _pow_ovf2long(space, x, self, y, w_exponent, w_modulus) try: result = _pow(space, x, y, z) - except (OverflowError, ValueError): - return _pow_ovf2long(space, x, y, w_modulus) + except OverflowError: + return _pow_ovf2long(space, x, self, y, w_exponent, w_modulus) + except ValueError: + # float result, so let avoid a roundtrip in rbigint. + self = self.descr_float(space) + w_exponent = w_exponent.descr_float(space) + return space.pow(self, w_exponent, space.w_None) + return space.newint(result) @unwrap_spec(w_modulus=WrappedDefault(None)) @@ -685,7 +697,7 @@ try: z = ovfcheck(op(x, y)) except OverflowError: - return ovf2long(space, x, y) + return ovf2long(space, x, self, y, w_other) else: z = op(x, y) return wrapint(space, z) @@ -709,7 +721,7 @@ try: z = ovfcheck(op(y, x)) except OverflowError: - return ovf2long(space, y, x) + return ovf2long(space, y, w_other, x, self) # XXX write a test else: z = op(y, x) return wrapint(space, z) @@ -743,7 +755,7 @@ try: return func(space, x, y) except OverflowError: - return ovf2long(space, x, y) + return ovf2long(space, x, self, y, w_other) else: return func(space, x, y) elif isinstance(w_other, W_AbstractIntObject): @@ -760,7 +772,7 @@ try: return func(space, y, x) except OverflowError: - return ovf2long(space, y, x) + return ovf2long(space, y, w_other, x, self) else: return func(space, y, x) elif isinstance(w_other, W_AbstractIntObject): diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py --- a/pypy/objspace/std/longobject.py +++ b/pypy/objspace/std/longobject.py @@ -234,22 +234,16 @@ descr_gt = _make_descr_cmp('gt') descr_ge = _make_descr_cmp('ge') - def _make_generic_descr_binop_noncommutative(opname): - methname = opname + '_' if opname in ('and', 'or') else opname - descr_rname = 'descr_r' + opname - op = getattr(rbigint, methname) + def descr_sub(self, space, w_other): + if isinstance(w_other, W_IntObject): + return W_LongObject(self.num.int_sub(w_other.int_w(space))) + elif not isinstance(w_other, W_AbstractLongObject): + return space.w_NotImplemented + return W_LongObject(self.num.sub(w_other.asbigint())) - @func_renamer('descr_' + opname) - @delegate_other - def descr_binop(self, space, w_other): - return W_LongObject(op(self.num, w_other.asbigint())) - - @func_renamer(descr_rname) - @delegate_other - def descr_rbinop(self, space, w_other): - return W_LongObject(op(w_other.asbigint(), self.num)) - - return descr_binop, descr_rbinop + @delegate_other + def descr_rsub(self, space, w_other): + return W_LongObject(w_other.asbigint().sub(self.num)) def _make_generic_descr_binop(opname): if opname not in COMMUTATIVE_OPS: @@ -281,28 +275,23 @@ return descr_binop, descr_rbinop descr_add, descr_radd = _make_generic_descr_binop('add') - descr_sub, descr_rsub = _make_generic_descr_binop_noncommutative('sub') + descr_mul, descr_rmul = _make_generic_descr_binop('mul') descr_and, descr_rand = _make_generic_descr_binop('and') descr_or, descr_ror = _make_generic_descr_binop('or') descr_xor, descr_rxor = _make_generic_descr_binop('xor') - def _make_descr_binop(func, int_func=None): + def _make_descr_binop(func, int_func): opname = func.__name__[1:] - if int_func: - @func_renamer('descr_' + opname) - def descr_binop(self, space, w_other): - if isinstance(w_other, W_IntObject): - return int_func(self, space, w_other.int_w(space)) - elif not isinstance(w_other, W_AbstractLongObject): - return space.w_NotImplemented - return func(self, space, w_other) - else: - @delegate_other - @func_renamer('descr_' + opname) - def descr_binop(self, space, w_other): - return func(self, space, w_other) + @func_renamer('descr_' + opname) + def descr_binop(self, space, w_other): + if isinstance(w_other, W_IntObject): + return int_func(self, space, w_other.int_w(space)) + elif not isinstance(w_other, W_AbstractLongObject): + return space.w_NotImplemented + return func(self, space, w_other) + @delegate_other @func_renamer('descr_r' + opname) def descr_rbinop(self, space, w_other): @@ -322,10 +311,10 @@ raise oefmt(space.w_OverflowError, "shift count too large") return W_LongObject(self.num.lshift(shift)) - def _int_lshift(self, space, w_other): - if w_other < 0: + def _int_lshift(self, space, other): + if other < 0: raise oefmt(space.w_ValueError, "negative shift count") - return W_LongObject(self.num.lshift(w_other)) + return W_LongObject(self.num.lshift(other)) descr_lshift, descr_rlshift = _make_descr_binop(_lshift, _int_lshift) @@ -338,11 +327,11 @@ raise oefmt(space.w_OverflowError, "shift count too large") return newlong(space, self.num.rshift(shift)) - def _int_rshift(self, space, w_other): - if w_other < 0: + def _int_rshift(self, space, other): + if other < 0: raise oefmt(space.w_ValueError, "negative shift count") - return newlong(space, self.num.rshift(w_other)) + return newlong(space, self.num.rshift(other)) descr_rshift, descr_rrshift = _make_descr_binop(_rshift, _int_rshift) def _floordiv(self, space, w_other): @@ -353,14 +342,14 @@ "long division or modulo by zero") return newlong(space, z) - def _floordiv(self, space, w_other): + def _int_floordiv(self, space, other): try: - z = self.num.floordiv(w_other.asbigint()) + z = self.num.int_floordiv(other) except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "integer division or modulo by zero") return newlong(space, z) - descr_floordiv, descr_rfloordiv = _make_descr_binop(_floordiv) + descr_floordiv, descr_rfloordiv = _make_descr_binop(_floordiv, _int_floordiv) def _mod(self, space, w_other): try: @@ -370,9 +359,9 @@ "integer division or modulo by zero") return newlong(space, z) - def _int_mod(self, space, w_other): + def _int_mod(self, space, other): try: - z = self.num.int_mod(w_other) + z = self.num.int_mod(other) except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "long division or modulo by zero") @@ -386,7 +375,16 @@ raise oefmt(space.w_ZeroDivisionError, "integer division or modulo by zero") return space.newtuple([newlong(space, div), newlong(space, mod)]) - descr_divmod, descr_rdivmod = _make_descr_binop(_divmod) + + def _int_divmod(self, space, other): + try: + div, mod = self.num.int_divmod(other) + except ZeroDivisionError: + raise oefmt(space.w_ZeroDivisionError, + "long division or modulo by zero") + return space.newtuple([newlong(space, div), newlong(space, mod)]) + + descr_divmod, descr_rdivmod = _make_descr_binop(_divmod, _int_divmod) def _hash_long(space, v): diff --git a/pypy/objspace/std/test/test_intobject.py b/pypy/objspace/std/test/test_intobject.py --- a/pypy/objspace/std/test/test_intobject.py +++ b/pypy/objspace/std/test/test_intobject.py @@ -738,6 +738,11 @@ raises(ValueError, int, '00000000000000000000000000000000000007', 0) raises(ValueError, int, '00000000000000000077777777777777777777', 0) + def test_some_rops(self): + b = 2 ** 31 + x = -b + assert x.__rsub__(2) == (2 + b) + class AppTestIntShortcut(AppTestInt): spaceconfig = {"objspace.std.intshortcut": True} 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 pmaj=2 # python main version pmin=7 # python minor version -maj=6 +maj=7 min=0 rev=0 branchname=release-pypy$pmaj.$pmin-$maj.x # ==OR== release-$maj.x # ==OR== release-$maj.$min.x @@ -13,7 +13,7 @@ hg log -r $tagname || exit 1 hgrev=`hg id -r $tagname -i` -rel=pypy$pmaj-v$maj.$min.$rev +rel=pypy$pmaj.$pmin-v$maj.$min.$rev # The script should be run in an empty in the pypy tree, i.e. pypy/tmp if [ "`ls . | wc -l`" != "0" ] then @@ -23,7 +23,7 @@ # Download latest builds from the buildmaster, rename the top # level directory, and repackage ready to be uploaded to bitbucket -for plat in linux linux64 linux-armhf-raspbian linux-armel osx64 s390x +for plat in linux linux64 osx64 s390x # linux-armhf-raspbian linux-armel do echo downloading package for $plat if wget -q --show-progress http://buildbot.pypy.org/nightly/$branchname/pypy-c-jit-latest-$plat.tar.bz2 diff --git a/rpython/conftest.py b/rpython/conftest.py --- a/rpython/conftest.py +++ b/rpython/conftest.py @@ -6,15 +6,16 @@ option = None try: - from hypothesis import settings, __version__ + from hypothesis import settings except ImportError: pass else: - if __version__[:2] < '3.6': - s = settings(deadline=None) - settings.register_profile('default', s) - else: + try: settings.register_profile('default', deadline=None) + except Exception: + import warnings + warnings.warn("Version of hypothesis too old, " + "cannot set the deadline to None") settings.load_profile('default') def braindead_deindent(self): diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py --- a/rpython/rlib/_rsocket_rffi.py +++ b/rpython/rlib/_rsocket_rffi.py @@ -1191,14 +1191,14 @@ inet_ntoa = external('inet_ntoa', [in_addr], rffi.CCHARP) -if _POSIX: - inet_pton = external('inet_pton', [rffi.INT, rffi.CCHARP, - rffi.VOIDP], rffi.INT, - save_err=SAVE_ERR) - inet_ntop = external('inet_ntop', [rffi.INT, rffi.VOIDP, CCHARP, - socklen_t], CCHARP, - save_err=SAVE_ERR) +inet_pton = external('inet_pton', [rffi.INT, rffi.CCHARP, + rffi.VOIDP], rffi.INT, + save_err=SAVE_ERR) + +inet_ntop = external('inet_ntop', [rffi.INT, rffi.VOIDP, CCHARP, + socklen_t], CCHARP, + save_err=SAVE_ERR) inet_addr = external('inet_addr', [rffi.CCHARP], rffi.UINT) socklen_t_ptr = lltype.Ptr(rffi.CFixedArray(socklen_t, 1)) diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py --- a/rpython/rlib/rarithmetic.py +++ b/rpython/rlib/rarithmetic.py @@ -612,6 +612,7 @@ r_ulonglong = build_int('r_ulonglong', False, 64) r_longlonglong = build_int('r_longlonglong', True, 128) +r_ulonglonglong = build_int('r_ulonglonglong', False, 128) longlongmax = r_longlong(LONGLONG_TEST - 1) if r_longlong is not r_int: diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py --- a/rpython/rlib/rbigint.py +++ b/rpython/rlib/rbigint.py @@ -27,6 +27,7 @@ else: UDIGIT_MASK = longlongmask LONG_TYPE = rffi.__INT128_T + ULONG_TYPE = rffi.__UINT128_T if LONG_BIT > SHIFT: STORE_TYPE = lltype.Signed UNSIGNED_TYPE = lltype.Unsigned @@ -40,6 +41,7 @@ STORE_TYPE = lltype.Signed UNSIGNED_TYPE = lltype.Unsigned LONG_TYPE = rffi.LONGLONG + ULONG_TYPE = rffi.ULONGLONG MASK = int((1 << SHIFT) - 1) FLOAT_MULTIPLIER = float(1 << SHIFT) @@ -97,6 +99,9 @@ def _widen_digit(x): return rffi.cast(LONG_TYPE, x) +def _unsigned_widen_digit(x): + return rffi.cast(ULONG_TYPE, x) + @specialize.argtype(0) def _store_digit(x): return rffi.cast(STORE_TYPE, x) @@ -108,6 +113,7 @@ NULLDIGIT = _store_digit(0) ONEDIGIT = _store_digit(1) +NULLDIGITS = [NULLDIGIT] def _check_digits(l): for x in l: @@ -133,22 +139,26 @@ def specialize_call(self, hop): hop.exception_cannot_occur() +def intsign(i): + return -1 if i < 0 else 1 class rbigint(object): """This is a reimplementation of longs using a list of digits.""" _immutable_ = True - _immutable_fields_ = ["_digits"] - - def __init__(self, digits=[NULLDIGIT], sign=0, size=0): + _immutable_fields_ = ["_digits[*]", "size", "sign"] + + def __init__(self, digits=NULLDIGITS, sign=0, size=0): if not we_are_translated(): _check_digits(digits) make_sure_not_resized(digits) self._digits = digits + assert size >= 0 self.size = size or len(digits) + self.sign = sign - # __eq__ and __ne__ method exist for testingl only, they are not RPython! + # __eq__ and __ne__ method exist for testing only, they are not RPython! @not_rpython def __eq__(self, other): if not isinstance(other, rbigint): @@ -159,6 +169,7 @@ def __ne__(self, other): return not (self == other) + @specialize.argtype(1) def digit(self, x): """Return the x'th digit, as an int.""" return self._digits[x] @@ -170,6 +181,12 @@ return _widen_digit(self._digits[x]) widedigit._always_inline_ = True + def uwidedigit(self, x): + """Return the x'th digit, as a long long int if needed + to have enough room to contain two digits.""" + return _unsigned_widen_digit(self._digits[x]) + uwidedigit._always_inline_ = True + def udigit(self, x): """Return the x'th digit, as an unsigned int.""" return _load_unsigned_digit(self._digits[x]) @@ -183,7 +200,9 @@ setdigit._always_inline_ = True def numdigits(self): - return self.size + w = self.size + assert w > 0 + return w numdigits._always_inline_ = True @staticmethod @@ -196,13 +215,15 @@ if intval < 0: sign = -1 ival = -r_uint(intval) + carry = ival >> SHIFT elif intval > 0: sign = 1 ival = r_uint(intval) + carry = 0 else: return NULLRBIGINT - carry = ival >> SHIFT + if carry: return rbigint([_store_digit(ival & MASK), _store_digit(carry)], sign, 2) @@ -509,23 +530,22 @@ return True @jit.elidable - def int_eq(self, other): + def int_eq(self, iother): """ eq with int """ - - if not int_in_valid_range(other): - # Fallback to Long. - return self.eq(rbigint.fromint(other)) + if not int_in_valid_range(iother): + # Fallback to Long. + return self.eq(rbigint.fromint(iother)) if self.numdigits() > 1: return False - return (self.sign * self.digit(0)) == other + return (self.sign * self.digit(0)) == iother def ne(self, other): return not self.eq(other) - def int_ne(self, other): - return not self.int_eq(other) + def int_ne(self, iother): + return not self.int_eq(iother) @jit.elidable def lt(self, other): @@ -563,59 +583,38 @@ return False @jit.elidable - def int_lt(self, other): + def int_lt(self, iother): """ lt where other is an int """ - if not int_in_valid_range(other): + if not int_in_valid_range(iother): # Fallback to Long. - return self.lt(rbigint.fromint(other)) - - osign = 1 - if other == 0: - osign = 0 - elif other < 0: - osign = -1 - - if self.sign > osign: - return False - elif self.sign < osign: - return True - - digits = self.numdigits() - - if digits > 1: - if osign == 1: - return False - else: - return True - - d1 = self.sign * self.digit(0) - if d1 < other: - return True - return False + return self.lt(rbigint.fromint(iother)) + + return _x_int_lt(self, iother, False) def le(self, other): return not other.lt(self) - def int_le(self, other): - # Alternative that might be faster, reimplant this. as a check with other + 1. But we got to check for overflow - # or reduce valid range. - - if self.int_eq(other): - return True - return self.int_lt(other) + def int_le(self, iother): + """ le where iother is an int """ + + if not int_in_valid_range(iother): + # Fallback to Long. + return self.le(rbigint.fromint(iother)) + + return _x_int_lt(self, iother, True) def gt(self, other): return other.lt(self) - def int_gt(self, other): - return not self.int_le(other) + def int_gt(self, iother): + return not self.int_le(iother) def ge(self, other): return not self.lt(other) - def int_ge(self, other): - return not self.int_lt(other) + def int_ge(self, iother): + return not self.int_lt(iother) @jit.elidable def hash(self): @@ -635,20 +634,20 @@ return result @jit.elidable - def int_add(self, other): - if not int_in_valid_range(other): + def int_add(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.add(rbigint.fromint(other)) + return self.add(rbigint.fromint(iother)) elif self.sign == 0: - return rbigint.fromint(other) - elif other == 0: + return rbigint.fromint(iother) + elif iother == 0: return self - sign = -1 if other < 0 else 1 + sign = intsign(iother) if self.sign == sign: - result = _x_int_add(self, other) + result = _x_int_add(self, iother) else: - result = _x_int_sub(self, other) + result = _x_int_sub(self, iother) result.sign *= -1 result.sign *= sign return result @@ -658,7 +657,7 @@ if other.sign == 0: return self elif self.sign == 0: - return rbigint(other._digits[:other.size], -other.sign, other.size) + return rbigint(other._digits[:other.numdigits()], -other.sign, other.numdigits()) elif self.sign == other.sign: result = _x_sub(self, other) else: @@ -667,93 +666,94 @@ return result @jit.elidable - def int_sub(self, other): - if not int_in_valid_range(other): + def int_sub(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.sub(rbigint.fromint(other)) - elif other == 0: + return self.sub(rbigint.fromint(iother)) + elif iother == 0: return self elif self.sign == 0: - return rbigint.fromint(-other) - elif self.sign == (-1 if other < 0 else 1): - result = _x_int_sub(self, other) + return rbigint.fromint(-iother) + elif self.sign == intsign(iother): + result = _x_int_sub(self, iother) else: - result = _x_int_add(self, other) + result = _x_int_add(self, iother) result.sign *= self.sign return result @jit.elidable - def mul(self, b): - asize = self.numdigits() - bsize = b.numdigits() - - a = self - - if asize > bsize: - a, b, asize, bsize = b, a, bsize, asize - - if a.sign == 0 or b.sign == 0: + def mul(self, other): + selfsize = self.numdigits() + othersize = other.numdigits() + + if selfsize > othersize: + self, other, selfsize, othersize = other, self, othersize, selfsize + + if self.sign == 0 or other.sign == 0: return NULLRBIGINT - if asize == 1: - if a._digits[0] == ONEDIGIT: - return rbigint(b._digits[:b.size], a.sign * b.sign, b.size) - elif bsize == 1: - res = b.widedigit(0) * a.widedigit(0) + if selfsize == 1: + if self._digits[0] == ONEDIGIT: + return rbigint(other._digits[:othersize], self.sign * other.sign, othersize) + elif othersize == 1: + res = other.uwidedigit(0) * self.udigit(0) carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], a.sign * b.sign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * other.sign, 2) else: - return rbigint([_store_digit(res & MASK)], a.sign * b.sign, 1) - - result = _x_mul(a, b, a.digit(0)) + return rbigint([_store_digit(res & MASK)], self.sign * other.sign, 1) + + result = _x_mul(self, other, self.digit(0)) elif USE_KARATSUBA: - if a is b: + if self is other: i = KARATSUBA_SQUARE_CUTOFF else: i = KARATSUBA_CUTOFF - if asize <= i: - result = _x_mul(a, b) - """elif 2 * asize <= bsize: - result = _k_lopsided_mul(a, b)""" + if selfsize <= i: + result = _x_mul(self, other) + """elif 2 * selfsize <= othersize: + result = _k_lopsided_mul(self, other)""" else: - result = _k_mul(a, b) + result = _k_mul(self, other) else: - result = _x_mul(a, b) - - result.sign = a.sign * b.sign + result = _x_mul(self, other) + + result.sign = self.sign * other.sign return result @jit.elidable - def int_mul(self, b): - if not int_in_valid_range(b): + def int_mul(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.mul(rbigint.fromint(b)) - - if self.sign == 0 or b == 0: + return self.mul(rbigint.fromint(iother)) + + if self.sign == 0 or iother == 0: return NULLRBIGINT asize = self.numdigits() - digit = abs(b) - bsign = -1 if b < 0 else 1 + digit = abs(iother) + + othersign = intsign(iother) if digit == 1: - return rbigint(self._digits[:self.size], self.sign * bsign, asize) + if othersign == 1: + return self + return rbigint(self._digits[:asize], self.sign * othersign, asize) elif asize == 1: - res = self.widedigit(0) * digit + udigit = r_uint(digit) + res = self.uwidedigit(0) * udigit carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * bsign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * othersign, 2) else: - return rbigint([_store_digit(res & MASK)], self.sign * bsign, 1) - + return rbigint([_store_digit(res & MASK)], self.sign * othersign, 1) elif digit & (digit - 1) == 0: result = self.lqshift(ptwotable[digit]) else: result = _muladd1(self, digit) - result.sign = self.sign * bsign + result.sign = self.sign * othersign return result @jit.elidable @@ -763,12 +763,10 @@ @jit.elidable def floordiv(self, other): - if self.sign == 1 and other.numdigits() == 1 and other.sign == 1: - digit = other.digit(0) - if digit == 1: - return rbigint(self._digits[:self.size], 1, self.size) - elif digit and digit & (digit - 1) == 0: - return self.rshift(ptwotable[digit]) + if other.numdigits() == 1: + otherint = other.digit(0) * other.sign + assert int_in_valid_range(otherint) + return self.int_floordiv(otherint) div, mod = _divrem(self, other) if mod.sign * other.sign == -1: @@ -782,6 +780,37 @@ return self.floordiv(other) @jit.elidable + def int_floordiv(self, iother): + if not int_in_valid_range(iother): + # Fallback to long. + return self.floordiv(rbigint.fromint(iother)) + + if iother == 0: + raise ZeroDivisionError("long division by zero") + + digit = abs(iother) + assert digit > 0 + + if self.sign == 1 and iother > 0: + if digit == 1: + return self + elif digit & (digit - 1) == 0: + return self.rqshift(ptwotable[digit]) + + div, mod = _divrem1(self, digit) + + if mod != 0 and self.sign * intsign(iother) == -1: + if div.sign == 0: + return ONENEGATIVERBIGINT + div = div.int_add(1) + div.sign = self.sign * intsign(iother) + div._normalize() + return div + + def int_div(self, iother): + return self.int_floordiv(iother) + + @jit.elidable def mod(self, other): if other.sign == 0: raise ZeroDivisionError("long division or modulo by zero") @@ -799,50 +828,50 @@ return mod @jit.elidable - def int_mod(self, other): - if other == 0: + def int_mod(self, iother): + if iother == 0: raise ZeroDivisionError("long division or modulo by zero") if self.sign == 0: return NULLRBIGINT - elif not int_in_valid_range(other): + elif not int_in_valid_range(iother): # Fallback to long. - return self.mod(rbigint.fromint(other)) + return self.mod(rbigint.fromint(iother)) if 1: # preserve indentation to preserve history - digit = abs(other) + digit = abs(iother) if digit == 1: return NULLRBIGINT elif digit == 2: modm = self.digit(0) & 1 if modm: - return ONENEGATIVERBIGINT if other < 0 else ONERBIGINT + return ONENEGATIVERBIGINT if iother < 0 else ONERBIGINT return NULLRBIGINT elif digit & (digit - 1) == 0: mod = self.int_and_(digit - 1) else: # Perform - size = self.numdigits() - 1 + size = UDIGIT_TYPE(self.numdigits() - 1) if size > 0: - rem = self.widedigit(size) - size -= 1 - while size >= 0: - rem = ((rem << SHIFT) + self.widedigit(size)) % digit + wrem = self.widedigit(size) + while size > 0: size -= 1 + wrem = ((wrem << SHIFT) | self.digit(size)) % digit + rem = _store_digit(wrem) else: - rem = self.digit(0) % digit + rem = _store_digit(self.digit(0) % digit) if rem == 0: return NULLRBIGINT - mod = rbigint([_store_digit(rem)], -1 if self.sign < 0 else 1, 1) - - if mod.sign * (-1 if other < 0 else 1) == -1: - mod = mod.int_add(other) + mod = rbigint([rem], -1 if self.sign < 0 else 1, 1) + + if mod.sign * intsign(iother) == -1: + mod = mod.int_add(iother) return mod @jit.elidable - def divmod(v, w): + def divmod(self, other): """ The / and % operators are now defined in terms of divmod(). The expression a mod b has the value a - b*floor(a/b). @@ -859,46 +888,78 @@ have different signs. We then subtract one from the 'div' part of the outcome to keep the invariant intact. """ - div, mod = _divrem(v, w) - if mod.sign * w.sign == -1: - mod = mod.add(w) + div, mod = _divrem(self, other) + if mod.sign * other.sign == -1: + mod = mod.add(other) if div.sign == 0: return ONENEGATIVERBIGINT, mod div = div.int_sub(1) return div, mod @jit.elidable - def pow(a, b, c=None): + def int_divmod(self, iother): + """ Divmod with int """ + + if iother == 0: + raise ZeroDivisionError("long division or modulo by zero") + + wsign = intsign(iother) + if not int_in_valid_range(iother) or (wsign == -1 and self.sign != wsign): + # Just fallback. + return self.divmod(rbigint.fromint(iother)) + + digit = abs(iother) + assert digit > 0 + + div, mod = _divrem1(self, digit) + # _divrem1 doesn't fix the sign + if div.size == 1 and div._digits[0] == NULLDIGIT: + div.sign = 0 + else: + div.sign = self.sign * wsign + if self.sign < 0: + mod = -mod + if mod and self.sign * wsign == -1: + mod += iother + if div.sign == 0: + div = ONENEGATIVERBIGINT + else: + div = div.int_sub(1) + mod = rbigint.fromint(mod) + return div, mod + + @jit.elidable + def pow(self, other, modulus=None): negativeOutput = False # if x<0 return negative output # 5-ary values. If the exponent is large enough, table is - # precomputed so that table[i] == a**i % c for i in range(32). + # precomputed so that table[i] == self**i % modulus for i in range(32). # python translation: the table is computed when needed. - if b.sign < 0: # if exponent is negative - if c is not None: + if other.sign < 0: # if exponent is negative + if modulus is not None: raise TypeError( "pow() 2nd argument " "cannot be negative when 3rd argument specified") # XXX failed to implement raise ValueError("bigint pow() too negative") - size_b = b.numdigits() - - if c is not None: - if c.sign == 0: + size_b = UDIGIT_TYPE(other.numdigits()) + + if modulus is not None: + if modulus.sign == 0: raise ValueError("pow() 3rd argument cannot be 0") # if modulus < 0: # negativeOutput = True # modulus = -modulus - if c.sign < 0: + if modulus.sign < 0: negativeOutput = True - c = c.neg() + modulus = modulus.neg() # if modulus == 1: # return 0 - if c.numdigits() == 1 and c._digits[0] == ONEDIGIT: + if modulus.numdigits() == 1 and modulus._digits[0] == ONEDIGIT: return NULLRBIGINT # Reduce base by modulus in some cases: @@ -910,63 +971,61 @@ # base % modulus instead. # We could _always_ do this reduction, but mod() isn't cheap, # so we only do it when it buys something. - if a.sign < 0 or a.numdigits() > c.numdigits(): - a = a.mod(c) - - elif b.sign == 0: + if self.sign < 0 or self.numdigits() > modulus.numdigits(): + self = self.mod(modulus) + elif other.sign == 0: return ONERBIGINT - elif a.sign == 0: + elif self.sign == 0: return NULLRBIGINT elif size_b == 1: - if b._digits[0] == NULLDIGIT: - return ONERBIGINT if a.sign == 1 else ONENEGATIVERBIGINT - elif b._digits[0] == ONEDIGIT: - return a - elif a.numdigits() == 1: - adigit = a.digit(0) - digit = b.digit(0) + if other._digits[0] == ONEDIGIT: + return self + elif self.numdigits() == 1 and modulus is None: + adigit = self.digit(0) + digit = other.digit(0) if adigit == 1: - if a.sign == -1 and digit % 2: + if self.sign == -1 and digit % 2: return ONENEGATIVERBIGINT return ONERBIGINT elif adigit & (adigit - 1) == 0: - ret = a.lshift(((digit-1)*(ptwotable[adigit]-1)) + digit-1) - if a.sign == -1 and not digit % 2: + ret = self.lshift(((digit-1)*(ptwotable[adigit]-1)) + digit-1) + if self.sign == -1 and not digit % 2: ret.sign = 1 return ret - # At this point a, b, and c are guaranteed non-negative UNLESS - # c is NULL, in which case a may be negative. */ - - z = rbigint([ONEDIGIT], 1, 1) + # At this point self, other, and modulus are guaranteed non-negative UNLESS + # modulus is NULL, in which case self may be negative. */ + + z = ONERBIGINT # python adaptation: moved macros REDUCE(X) and MULT(X, Y, result) # into helper function result = _help_mult(x, y, c) if size_b <= FIVEARY_CUTOFF: # Left-to-right binary exponentiation (HAC Algorithm 14.79) # http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf - size_b -= 1 - while size_b >= 0: - bi = b.digit(size_b) + + while size_b > 0: + size_b -= 1 + bi = other.digit(size_b) j = 1 << (SHIFT-1) while j != 0: - z = _help_mult(z, z, c) + z = _help_mult(z, z, modulus) if bi & j: - z = _help_mult(z, a, c) + z = _help_mult(z, self, modulus) j >>= 1 - size_b -= 1 + else: # Left-to-right 5-ary exponentiation (HAC Algorithm 14.82) - # This is only useful in the case where c != None. + # This is only useful in the case where modulus != None. # z still holds 1L table = [z] * 32 table[0] = z for i in range(1, 32): - table[i] = _help_mult(table[i-1], a, c) + table[i] = _help_mult(table[i-1], self, modulus) # Note that here SHIFT is not a multiple of 5. The difficulty - # is to extract 5 bits at a time from 'b', starting from the + # is to extract 5 bits at a time from 'other', starting from the # most significant digits, so that at the end of the algorithm # it falls exactly to zero. # m = max number of bits = i * SHIFT @@ -985,37 +1044,120 @@ index = (accum >> j) & 0x1f else: # 'accum' does not have enough digit. - # must get the next digit from 'b' in order to complete + # must get the next digit from 'other' in order to complete if size_b == 0: break # Done size_b -= 1 assert size_b >= 0 - bi = b.udigit(size_b) + bi = other.udigit(size_b) index = ((accum << (-j)) | (bi >> (j+SHIFT))) & 0x1f accum = bi j += SHIFT # for k in range(5): - z = _help_mult(z, z, c) + z = _help_mult(z, z, modulus) if index: - z = _help_mult(z, table[index], c) + z = _help_mult(z, table[index], modulus) # assert j == -5 if negativeOutput and z.sign != 0: - z = z.sub(c) + z = z.sub(modulus) + return z + + @jit.elidable + def int_pow(self, iother, modulus=None): + negativeOutput = False # if x<0 return negative output + + # 5-ary values. If the exponent is large enough, table is + # precomputed so that table[i] == self**i % modulus for i in range(32). + # python translation: the table is computed when needed. + + if iother < 0: # if exponent is negative + if modulus is not None: + raise TypeError( + "pow() 2nd argument " + "cannot be negative when 3rd argument specified") + # XXX failed to implement + raise ValueError("bigint pow() too negative") + + assert iother >= 0 + if modulus is not None: + if modulus.sign == 0: + raise ValueError("pow() 3rd argument cannot be 0") + + # if modulus < 0: + # negativeOutput = True + # modulus = -modulus + if modulus.sign < 0: + negativeOutput = True + modulus = modulus.neg() + + # if modulus == 1: + # return 0 + if modulus.numdigits() == 1 and modulus._digits[0] == ONEDIGIT: + return NULLRBIGINT + + # Reduce base by modulus in some cases: + # 1. If base < 0. Forcing the base non-neg makes things easier. + # 2. If base is obviously larger than the modulus. The "small + # exponent" case later can multiply directly by base repeatedly, + # while the "large exponent" case multiplies directly by base 31 + # times. It can be unboundedly faster to multiply by + # base % modulus instead. + # We could _always_ do this reduction, but mod() isn't cheap, + # so we only do it when it buys something. + if self.sign < 0 or self.numdigits() > modulus.numdigits(): + self = self.mod(modulus) + elif iother == 0: + return ONERBIGINT + elif self.sign == 0: + return NULLRBIGINT + elif iother == 1: + return self + elif self.numdigits() == 1: + adigit = self.digit(0) + if adigit == 1: + if self.sign == -1 and iother % 2: + return ONENEGATIVERBIGINT + return ONERBIGINT + elif adigit & (adigit - 1) == 0: + ret = self.lshift(((iother-1)*(ptwotable[adigit]-1)) + iother-1) + if self.sign == -1 and not iother % 2: + ret.sign = 1 + return ret + + # At this point self, iother, and modulus are guaranteed non-negative UNLESS + # modulus is NULL, in which case self may be negative. */ + + z = ONERBIGINT + + # python adaptation: moved macros REDUCE(X) and MULT(X, Y, result) + # into helper function result = _help_mult(x, y, modulus) + # Left-to-right binary exponentiation (HAC Algorithm 14.79) + # http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf + j = 1 << (SHIFT-1) + + while j != 0: + z = _help_mult(z, z, modulus) + if iother & j: + z = _help_mult(z, self, modulus) + j >>= 1 + + if negativeOutput and z.sign != 0: + z = z.sub(modulus) return z @jit.elidable def neg(self): - return rbigint(self._digits, -self.sign, self.size) + return rbigint(self._digits, -self.sign, self.numdigits()) @jit.elidable def abs(self): if self.sign != -1: return self - return rbigint(self._digits, 1, self.size) + return rbigint(self._digits, 1, self.numdigits()) @jit.elidable def invert(self): #Implement ~x as -(x + 1) @@ -1041,15 +1183,15 @@ # So we can avoid problems with eq, AND avoid the need for normalize. if self.sign == 0: return self - return rbigint([NULLDIGIT] * wordshift + self._digits, self.sign, self.size + wordshift) + return rbigint([NULLDIGIT] * wordshift + self._digits, self.sign, self.numdigits() + wordshift) oldsize = self.numdigits() newsize = oldsize + wordshift + 1 z = rbigint([NULLDIGIT] * newsize, self.sign, newsize) - accum = _widen_digit(0) + accum = _unsigned_widen_digit(0) j = 0 while j < oldsize: - accum += self.widedigit(j) << remshift + accum += self.uwidedigit(j) << remshift z.setdigit(wordshift, accum) accum >>= SHIFT wordshift += 1 @@ -1061,7 +1203,7 @@ z._normalize() return z - lshift._always_inline_ = True # It's so fast that it's always benefitial. + lshift._always_inline_ = True # It's so fast that it's always beneficial. @jit.elidable def lqshift(self, int_other): @@ -1071,17 +1213,17 @@ oldsize = self.numdigits() z = rbigint([NULLDIGIT] * (oldsize + 1), self.sign, (oldsize + 1)) - accum = _widen_digit(0) + accum = _unsigned_widen_digit(0) i = 0 while i < oldsize: - accum += self.widedigit(i) << int_other + accum += self.uwidedigit(i) << int_other z.setdigit(i, accum) accum >>= SHIFT i += 1 z.setdigit(oldsize, accum) z._normalize() return z - lqshift._always_inline_ = True # It's so fast that it's always benefitial. + lqshift._always_inline_ = True # It's so fast that it's always beneficial. @jit.elidable def rshift(self, int_other, dont_invert=False): @@ -1112,6 +1254,31 @@ z._normalize() return z rshift._always_inline_ = 'try' # It's so fast that it's always benefitial. + + @jit.elidable + def rqshift(self, int_other): + wordshift = int_other / SHIFT + loshift = int_other % SHIFT + newsize = self.numdigits() - wordshift + + if newsize <= 0: + return NULLRBIGINT + + hishift = SHIFT - loshift + z = rbigint([NULLDIGIT] * newsize, self.sign, newsize) + i = 0 + + while i < newsize: + digit = self.udigit(wordshift) + newdigit = (digit >> loshift) + if i+1 < newsize: + newdigit |= (self.udigit(wordshift+1) << hishift) + z.setdigit(i, newdigit) + i += 1 + wordshift += 1 + z._normalize() + return z + rshift._always_inline_ = 'try' # It's so fast that it's always beneficial. @jit.elidable def abs_rshift_and_mask(self, bigshiftcount, mask): @@ -1167,24 +1334,24 @@ return _bitwise(self, '&', other) @jit.elidable - def int_and_(self, other): - return _int_bitwise(self, '&', other) + def int_and_(self, iother): + return _int_bitwise(self, '&', iother) @jit.elidable def xor(self, other): return _bitwise(self, '^', other) @jit.elidable - def int_xor(self, other): - return _int_bitwise(self, '^', other) + def int_xor(self, iother): + return _int_bitwise(self, '^', iother) @jit.elidable def or_(self, other): return _bitwise(self, '|', other) @jit.elidable - def int_or_(self, other): - return _int_bitwise(self, '|', other) + def int_or_(self, iother): + return _int_bitwise(self, '|', iother) @jit.elidable def oct(self): @@ -1218,7 +1385,10 @@ for d in digits: l = l << SHIFT l += intmask(d) - return l * self.sign + result = l * self.sign + if result == 0: + assert self.sign == 0 + return result def _normalize(self): i = self.numdigits() @@ -1227,11 +1397,10 @@ i -= 1 assert i > 0 - if i != self.numdigits(): - self.size = i - if self.numdigits() == 1 and self._digits[0] == NULLDIGIT: + self.size = i + if i == 1 and self._digits[0] == NULLDIGIT: self.sign = 0 - self._digits = [NULLDIGIT] + self._digits = NULLDIGITS _normalize._always_inline_ = True @@ -1256,8 +1425,8 @@ def __repr__(self): return "" % (self._digits, - self.sign, self.size, len(self._digits), - self.str()) + self.sign, self.numdigits(), len(self._digits), + self.tolong()) ONERBIGINT = rbigint([ONEDIGIT], 1, 1) ONENEGATIVERBIGINT = rbigint([ONEDIGIT], -1, 1) @@ -1322,7 +1491,7 @@ if x > 0: return digits_from_nonneg_long(x), 1 elif x == 0: - return [NULLDIGIT], 0 + return NULLDIGITS, 0 elif x != most_neg_value_of_same_type(x): # normal case return digits_from_nonneg_long(-x), -1 @@ -1340,7 +1509,7 @@ def args_from_long(x): if x >= 0: if x == 0: - return [NULLDIGIT], 0 + return NULLDIGITS, 0 else: return digits_from_nonneg_long(x), 1 else: @@ -1450,7 +1619,7 @@ if adigit == bdigit: return NULLRBIGINT - + return rbigint.fromint(adigit - bdigit) z = rbigint([NULLDIGIT] * size_a, 1, size_a) @@ -1497,11 +1666,11 @@ z = rbigint([NULLDIGIT] * (size_a + size_b), 1) i = UDIGIT_TYPE(0) while i < size_a: - f = a.widedigit(i) + f = a.uwidedigit(i) pz = i << 1 pa = i + 1 - carry = z.widedigit(pz) + f * f + carry = z.uwidedigit(pz) + f * f z.setdigit(pz, carry) pz += 1 carry >>= SHIFT @@ -1511,18 +1680,18 @@ # pyramid it appears. Same as adding f<<1 once. f <<= 1 while pa < size_a: - carry += z.widedigit(pz) + a.widedigit(pa) * f + carry += z.uwidedigit(pz) + a.uwidedigit(pa) * f pa += 1 z.setdigit(pz, carry) pz += 1 carry >>= SHIFT if carry: - carry += z.widedigit(pz) + carry += z.udigit(pz) z.setdigit(pz, carry) pz += 1 carry >>= SHIFT if carry: - z.setdigit(pz, z.widedigit(pz) + carry) + z.setdigit(pz, z.udigit(pz) + carry) assert (carry >> SHIFT) == 0 i += 1 z._normalize() @@ -1543,29 +1712,29 @@ size_a1 = UDIGIT_TYPE(size_a - 1) size_b1 = UDIGIT_TYPE(size_b - 1) while i < size_a1: - f0 = a.widedigit(i) - f1 = a.widedigit(i + 1) + f0 = a.uwidedigit(i) + f1 = a.uwidedigit(i + 1) pz = i - carry = z.widedigit(pz) + b.widedigit(0) * f0 + carry = z.uwidedigit(pz) + b.uwidedigit(0) * f0 z.setdigit(pz, carry) pz += 1 carry >>= SHIFT j = UDIGIT_TYPE(0) while j < size_b1: - # this operation does not overflow using + # this operation does not overflow using # SHIFT = (LONG_BIT // 2) - 1 = B - 1; in fact before it # carry and z.widedigit(pz) are less than 2**(B - 1); # b.widedigit(j + 1) * f0 < (2**(B-1) - 1)**2; so # carry + z.widedigit(pz) + b.widedigit(j + 1) * f0 + # b.widedigit(j) * f1 < 2**(2*B - 1) - 2**B < 2**LONG)BIT - 1 - carry += z.widedigit(pz) + b.widedigit(j + 1) * f0 + \ - b.widedigit(j) * f1 + carry += z.uwidedigit(pz) + b.uwidedigit(j + 1) * f0 + \ + b.uwidedigit(j) * f1 z.setdigit(pz, carry) pz += 1 carry >>= SHIFT j += 1 # carry < 2**(B + 1) - 2 - carry += z.widedigit(pz) + b.widedigit(size_b1) * f1 + carry += z.uwidedigit(pz) + b.uwidedigit(size_b1) * f1 z.setdigit(pz, carry) pz += 1 carry >>= SHIFT @@ -1576,17 +1745,17 @@ i += 2 if size_a & 1: pz = size_a1 - f = a.widedigit(pz) + f = a.uwidedigit(pz) pb = 0 - carry = _widen_digit(0) + carry = _unsigned_widen_digit(0) while pb < size_b: - carry += z.widedigit(pz) + b.widedigit(pb) * f + carry += z.uwidedigit(pz) + b.uwidedigit(pb) * f pb += 1 z.setdigit(pz, carry) pz += 1 carry >>= SHIFT if carry: - z.setdigit(pz, z.widedigit(pz) + carry) + z.setdigit(pz, z.udigit(pz) + carry) z._normalize() return z @@ -1602,8 +1771,8 @@ size_lo = min(size_n, size) # We use "or" her to avoid having a check where list can be empty in _normalize. - lo = rbigint(n._digits[:size_lo] or [NULLDIGIT], 1) - hi = rbigint(n._digits[size_lo:n.size] or [NULLDIGIT], 1) + lo = rbigint(n._digits[:size_lo] or NULLDIGITS, 1) + hi = rbigint(n._digits[size_lo:size_n] or NULLDIGITS, 1) lo._normalize() hi._normalize() return hi, lo @@ -1708,113 +1877,16 @@ ret._normalize() return ret -""" (*) Why adding t3 can't "run out of room" above. - -Let f(x) mean the floor of x and c(x) mean the ceiling of x. Some facts -to start with: - -1. For any integer i, i = c(i/2) + f(i/2). In particular, - bsize = c(bsize/2) + f(bsize/2). -2. shift = f(bsize/2) -3. asize <= bsize -4. Since we call k_lopsided_mul if asize*2 <= bsize, asize*2 > bsize in this - routine, so asize > bsize/2 >= f(bsize/2) in this routine. - -We allocated asize + bsize result digits, and add t3 into them at an offset -of shift. This leaves asize+bsize-shift allocated digit positions for t3 -to fit into, = (by #1 and #2) asize + f(bsize/2) + c(bsize/2) - f(bsize/2) = -asize + c(bsize/2) available digit positions. - -bh has c(bsize/2) digits, and bl at most f(size/2) digits. So bh+hl has -at most c(bsize/2) digits + 1 bit. - -If asize == bsize, ah has c(bsize/2) digits, else ah has at most f(bsize/2) -digits, and al has at most f(bsize/2) digits in any case. So ah+al has at -most (asize == bsize ? c(bsize/2) : f(bsize/2)) digits + 1 bit. - -The product (ah+al)*(bh+bl) therefore has at most - - c(bsize/2) + (asize == bsize ? c(bsize/2) : f(bsize/2)) digits + 2 bits - -and we have asize + c(bsize/2) available digit positions. We need to show -this is always enough. An instance of c(bsize/2) cancels out in both, so -the question reduces to whether asize digits is enough to hold -(asize == bsize ? c(bsize/2) : f(bsize/2)) digits + 2 bits. If asize < bsize, -then we're asking whether asize digits >= f(bsize/2) digits + 2 bits. By #4, -asize is at least f(bsize/2)+1 digits, so this in turn reduces to whether 1 -digit is enough to hold 2 bits. This is so since SHIFT=15 >= 2. If -asize == bsize, then we're asking whether bsize digits is enough to hold -c(bsize/2) digits + 2 bits, or equivalently (by #1) whether f(bsize/2) digits -is enough to hold 2 bits. This is so if bsize >= 2, which holds because -bsize >= KARATSUBA_CUTOFF >= 2. - -Note that since there's always enough room for (ah+al)*(bh+bl), and that's -clearly >= each of ah*bh and al*bl, there's always enough room to subtract -ah*bh and al*bl too. -""" - -def _k_lopsided_mul(a, b): - # Not in use anymore, only account for like 1% performance. Perhaps if we - # Got rid of the extra list allocation this would be more effective. - """ - b has at least twice the digits of a, and a is big enough that Karatsuba - would pay off *if* the inputs had balanced sizes. View b as a sequence - of slices, each with a->ob_size digits, and multiply the slices by a, - one at a time. This gives k_mul balanced inputs to work with, and is - also cache-friendly (we compute one double-width slice of the result - at a time, then move on, never bactracking except for the helpful - single-width slice overlap between successive partial sums). - """ - asize = a.numdigits() - bsize = b.numdigits() - # nbdone is # of b digits already multiplied - - assert asize > KARATSUBA_CUTOFF - assert 2 * asize <= bsize - - # Allocate result space, and zero it out. - ret = rbigint([NULLDIGIT] * (asize + bsize), 1) - - # Successive slices of b are copied into bslice. - #bslice = rbigint([0] * asize, 1) - # XXX we cannot pre-allocate, see comments below! - # XXX prevent one list from being created. - bslice = rbigint(sign=1) - - nbdone = 0 - while bsize > 0: - nbtouse = min(bsize, asize) - - # Multiply the next slice of b by a. - - #bslice.digits[:nbtouse] = b.digits[nbdone : nbdone + nbtouse] - # XXX: this would be more efficient if we adopted CPython's - # way to store the size, instead of resizing the list! - # XXX change the implementation, encoding length via the sign. - bslice._digits = b._digits[nbdone : nbdone + nbtouse] - bslice.size = nbtouse - product = _k_mul(a, bslice) - - # Add into result. - _v_iadd(ret, nbdone, ret.numdigits() - nbdone, - product, product.numdigits()) - - bsize -= nbtouse - nbdone += nbtouse - - ret._normalize() - return ret - def _inplace_divrem1(pout, pin, n): """ Divide bigint pin by non-zero digit n, storing quotient in pout, and returning the remainder. It's OK for pin == pout on entry. """ - rem = _widen_digit(0) + rem = _unsigned_widen_digit(0) assert n > 0 and n <= MASK size = pin.numdigits() - 1 while size >= 0: - rem = (rem << SHIFT) | pin.widedigit(size) + rem = (rem << SHIFT) | pin.udigit(size) hi = rem // n pout.setdigit(size, hi) rem -= hi * n @@ -1891,14 +1963,15 @@ def _muladd1(a, n, extra=0): """Multiply by a single digit and add a single digit, ignoring the sign. """ + assert n > 0 size_a = a.numdigits() z = rbigint([NULLDIGIT] * (size_a+1), 1) assert extra & MASK == extra - carry = _widen_digit(extra) + carry = _unsigned_widen_digit(extra) i = 0 while i < size_a: - carry += a.widedigit(i) * n + carry += a.uwidedigit(i) * n z.setdigit(i, carry) carry >>= SHIFT i += 1 @@ -1912,10 +1985,10 @@ """ carry = 0 - assert 0 <= d and d < SHIFT + #assert 0 <= d and d < SHIFT i = 0 while i < m: - acc = a.widedigit(i) << d | carry + acc = a.uwidedigit(i) << d | carry z.setdigit(i, acc) carry = acc >> SHIFT i += 1 @@ -1927,14 +2000,14 @@ * result in z[0:m], and return the d bits shifted out of the bottom. """ - carry = _widen_digit(0) - acc = _widen_digit(0) + carry = _unsigned_widen_digit(0) + acc = _unsigned_widen_digit(0) mask = (1 << d) - 1 - assert 0 <= d and d < SHIFT + #assert 0 <= d and d < SHIFT i = m-1 while i >= 0: - acc = (carry << SHIFT) | a.widedigit(i) + acc = (carry << SHIFT) | a.udigit(i) carry = acc & mask z.setdigit(i, acc >> d) i -= 1 @@ -1989,10 +2062,17 @@ else: vtop = v.widedigit(j) assert vtop <= wm1 + vv = (vtop << SHIFT) | v.widedigit(abs(j-1)) + + # Hints to make division just as fast as doing it unsigned. But avoids casting to get correct results. + assert vv >= 0 + assert wm1 >= 1 + q = vv / wm1 - r = vv - wm1 * q - while wm2 * q > ((r << SHIFT) | v.widedigit(abs(j-2))): + r = vv % wm1 # This seems to be slightly faster on widen digits than vv - wm1 * q. + vj2 = v.digit(abs(j-2)) + while wm2 * q > ((r << SHIFT) | vj2): q -= 1 r += wm1 @@ -2059,6 +2139,36 @@ rem.sign = - rem.sign return z, rem +def _x_int_lt(a, b, eq=False): + """ Compare bigint a with int b for less than or less than or equal """ + osign = 1 + if b == 0: + osign = 0 + elif b < 0: + osign = -1 + + if a.sign > osign: + return False + elif a.sign < osign: + return True + + digits = a.numdigits() + + if digits > 1: + if osign == 1: + return False + else: + return True + + d1 = a.sign * a.digit(0) + if eq: + if d1 <= b: + return True + else: + if d1 < b: + return True + return False + # ______________ conversions to double _______________ def _AsScaledDouble(v): @@ -2764,7 +2874,7 @@ elif s[p] == '+': p += 1 - a = rbigint() + a = NULLRBIGINT tens = 1 dig = 0 ord0 = ord('0') @@ -2785,7 +2895,7 @@ base = parser.base if (base & (base - 1)) == 0 and base >= 2: return parse_string_from_binary_base(parser) - a = rbigint() + a = NULLRBIGINT digitmax = BASE_MAX[base] tens, dig = 1, 0 while True: diff --git a/rpython/rlib/test/test_rbigint.py b/rpython/rlib/test/test_rbigint.py --- a/rpython/rlib/test/test_rbigint.py +++ b/rpython/rlib/test/test_rbigint.py @@ -95,6 +95,46 @@ r2 = op1 // op2 assert r1.tolong() == r2 + def test_int_floordiv(self): + x = 1000L + r = rbigint.fromlong(x) + r2 = r.int_floordiv(10) + assert r2.tolong() == 100L + + for op1 in gen_signs(long_vals): + for op2 in signed_int_vals: + if not op2: + continue + rl_op1 = rbigint.fromlong(op1) + r1 = rl_op1.int_floordiv(op2) + r2 = op1 // op2 + assert r1.tolong() == r2 + + assert pytest.raises(ZeroDivisionError, r.int_floordiv, 0) + + # Error pointed out by Armin Rigo + n = sys.maxint+1 + r = rbigint.fromlong(n) + assert r.int_floordiv(int(-n)).tolong() == -1L + + for x in int_vals: + if not x: + continue + r = rbigint.fromlong(x) + rn = rbigint.fromlong(-x) + res = r.int_floordiv(x) + res2 = r.int_floordiv(-x) + res3 = rn.int_floordiv(x) + assert res.tolong() == 1L + assert res2.tolong() == -1L + assert res3.tolong() == -1L + + def test_floordiv2(self): + n1 = rbigint.fromlong(sys.maxint + 1) + n2 = rbigint.fromlong(-(sys.maxint + 1)) + assert n1.floordiv(n2).tolong() == -1L + assert n2.floordiv(n1).tolong() == -1L + def test_truediv(self): for op1 in gen_signs(long_vals_not_too_big): rl_op1 = rbigint.fromlong(op1) @@ -185,9 +225,26 @@ r4 = pow(op1, op2, op3) assert r3.tolong() == r4 + def test_int_pow(self): + for op1 in gen_signs(long_vals_not_too_big): + rl_op1 = rbigint.fromlong(op1) + for op2 in [0, 1, 2, 8, 9, 10, 11, 127, 128, 129]: + r1 = rl_op1.int_pow(op2) + r2 = op1 ** op2 + assert r1.tolong() == r2 + + for op3 in gen_signs(long_vals_not_too_big): + if not op3: + continue + r3 = rl_op1.int_pow(op2, rbigint.fromlong(op3)) + r4 = pow(op1, op2, op3) + print op1, op2, op3 + assert r3.tolong() == r4 + def test_pow_raises(self): r1 = rbigint.fromint(2) r0 = rbigint.fromint(0) + py.test.raises(ValueError, r1.int_pow, 2, r0) py.test.raises(ValueError, r1.pow, r1, r0) def test_touint(self): @@ -601,6 +658,9 @@ # test special optimization case in rshift: assert rbigint.fromlong(-(1 << 100)).rshift(5).tolong() == -(1 << 100) >> 5 + # Chek value accuracy. + assert rbigint.fromlong(18446744073709551615L).rshift(1).tolong() == 18446744073709551615L >> 1 + def test_qshift(self): for x in range(10): for y in range(1, 161, 16): @@ -610,11 +670,18 @@ for z in range(1, 31): res1 = f1.lqshift(z).tolong() + res2 = f1.rqshift(z).tolong() res3 = nf1.lqshift(z).tolong() assert res1 == num << z + assert res2 == num >> z assert res3 == -num << z + # Large digit + for x in range((1 << SHIFT) - 10, (1 << SHIFT) + 10): + f1 = rbigint.fromlong(x) + assert f1.rqshift(SHIFT).tolong() == x >> SHIFT + assert f1.rqshift(SHIFT+1).tolong() == x >> (SHIFT+1) def test_from_list_n_bits(self): for x in ([3L ** 30L, 5L ** 20L, 7 ** 300] + @@ -864,6 +931,27 @@ assert rem.tolong() == _rem + def test_int_divmod(self): + for x in long_vals: + for y in int_vals + [-sys.maxint-1]: + if not y: + continue + for sx, sy in (1, 1), (1, -1), (-1, -1), (-1, 1): + sx *= x + sy *= y + if sy == sys.maxint + 1: + continue + f1 = rbigint.fromlong(sx) + div, rem = f1.int_divmod(sy) + div1, rem1 = f1.divmod(rbigint.fromlong(sy)) + _div, _rem = divmod(sx, sy) + print sx, sy, " | ", div.tolong(), rem.tolong() + assert div1.tolong() == _div + assert rem1.tolong() == _rem + assert div.tolong() == _div + assert rem.tolong() == _rem + py.test.raises(ZeroDivisionError, rbigint.fromlong(x).int_divmod, 0) + # testing Karatsuba stuff def test__v_iadd(self): f1 = bigint([lobj.MASK] * 10, 1) @@ -1067,8 +1155,14 @@ except Exception as e: pytest.raises(type(e), f1.pow, f2, f3) else: - v = f1.pow(f2, f3) - assert v.tolong() == res + v1 = f1.pow(f2, f3) + try: + v2 = f1.int_pow(f2.toint(), f3) + except OverflowError: + pass + else: + assert v2.tolong() == res + assert v1.tolong() == res @given(biglongs, biglongs) @example(510439143470502793407446782273075179618477362188870662225920, @@ -1088,6 +1182,18 @@ a, b = f1.divmod(f2) assert (a.tolong(), b.tolong()) == res + @given(biglongs, ints) + def test_int_divmod(self, x, iy): + f1 = rbigint.fromlong(x) + try: + res = divmod(x, iy) + except Exception as e: + pytest.raises(type(e), f1.int_divmod, iy) + else: + print x, iy + a, b = f1.int_divmod(iy) + assert (a.tolong(), b.tolong()) == res + @given(longs) def test_hash(self, x): # hash of large integers: should be equal to the hash of the @@ -1118,10 +1224,34 @@ assert ra.truediv(rb) == a / b @given(longs, longs) - def test_bitwise(self, x, y): + def test_bitwise_and_mul(self, x, y): lx = rbigint.fromlong(x) ly = rbigint.fromlong(y) - for mod in "xor and_ or_".split(): - res1 = getattr(lx, mod)(ly).tolong() + for mod in "xor and_ or_ mul".split(): + res1a = getattr(lx, mod)(ly).tolong() + res1b = getattr(ly, mod)(lx).tolong() + res2 = getattr(operator, mod)(x, y) + assert res1a == res2 + + @given(longs, ints) + def test_int_bitwise_and_mul(self, x, y): + lx = rbigint.fromlong(x) + for mod in "xor and_ or_ mul".split(): + res1 = getattr(lx, 'int_' + mod)(y).tolong() res2 = getattr(operator, mod)(x, y) assert res1 == res2 + + @given(longs, ints) + def test_int_comparison(self, x, y): + lx = rbigint.fromlong(x) + assert lx.int_lt(y) == (x < y) + assert lx.int_eq(y) == (x == y) + assert lx.int_le(y) == (x <= y) + + @given(longs, longs) + def test_int_comparison(self, x, y): + lx = rbigint.fromlong(x) + ly = rbigint.fromlong(y) + assert lx.lt(ly) == (x < y) + assert lx.eq(ly) == (x == y) + assert lx.le(ly) == (x <= y) diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py --- a/rpython/rtyper/lltypesystem/ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/ll2ctypes.py @@ -175,7 +175,16 @@ if res >= (1 << 127): res -= 1 << 128 return res + class c_uint128(ctypes.Array): # based on 2 ulongs + _type_ = ctypes.c_uint64 + _length_ = 2 + @property + def value(self): + res = self[0] | (self[1] << 64) + return res + _ctypes_cache[rffi.__INT128_T] = c_int128 + _ctypes_cache[rffi.__UINT128_T] = c_uint128 # for unicode strings, do not use ctypes.c_wchar because ctypes # automatically converts arrays into unicode strings. diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py --- a/rpython/rtyper/lltypesystem/lloperation.py +++ b/rpython/rtyper/lltypesystem/lloperation.py From pypy.commits at gmail.com Fri Feb 8 06:12:57 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 08 Feb 2019 03:12:57 -0800 (PST) Subject: [pypy-commit] pypy regalloc-playground: add whatsnew entry Message-ID: <5c5d6439.1c69fb81.88204.79b8@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: regalloc-playground Changeset: r95898:57127989bf1e Date: 2019-02-08 12:12 +0100 http://bitbucket.org/pypy/pypy/changeset/57127989bf1e/ Log: add whatsnew entry 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 @@ -18,4 +18,8 @@ .. math-improvements Improve performance of long operations where one of the operands fits into -an int. \ No newline at end of file +an int. + +.. regalloc-playgrounds + +Improve register allocation in the JIT. From pypy.commits at gmail.com Fri Feb 8 06:12:55 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 08 Feb 2019 03:12:55 -0800 (PST) Subject: [pypy-commit] pypy regalloc-playground: merge default Message-ID: <5c5d6437.1c69fb81.9189b.faed@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: regalloc-playground Changeset: r95897:cba2a76ece6b Date: 2019-02-08 12:10 +0100 http://bitbucket.org/pypy/pypy/changeset/cba2a76ece6b/ Log: merge default diff too long, truncating to 2000 out of 5796 lines diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -4,8 +4,10 @@ *~ .*.swp .idea +.mypy_cache .project .pydevproject +.vscode __pycache__ .cache/ diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -58,3 +58,6 @@ 3f6eaa010fce78cc7973bdc1dfdb95970f08fed2 release-pypy3.5-v5.10.1 ab0b9caf307db6592905a80b8faffd69b39005b8 release-pypy2.7-v6.0.0 fdd60ed87e941677e8ea11acf9f1819466521bf2 release-pypy3.5-v6.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -40,16 +40,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -59,8 +59,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -70,10 +70,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -114,12 +114,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -134,8 +134,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -144,10 +145,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -163,6 +164,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -176,6 +178,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -187,7 +190,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -198,7 +200,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -210,6 +211,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -217,12 +219,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -242,7 +246,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -270,12 +273,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -290,10 +296,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -301,28 +309,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -332,6 +338,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -340,6 +347,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -349,8 +357,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -364,7 +373,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -384,12 +392,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz diff --git a/extra_tests/cffi_tests/cffi0/test_function.py b/extra_tests/cffi_tests/cffi0/test_function.py --- a/extra_tests/cffi_tests/cffi0/test_function.py +++ b/extra_tests/cffi_tests/cffi0/test_function.py @@ -46,14 +46,15 @@ assert x != math.sin(1.23) # rounding effects assert abs(x - math.sin(1.23)) < 1E-6 - def test_lround_no_return_value(self): + def test_getenv_no_return_value(self): # check that 'void'-returning functions work too ffi = FFI(backend=self.Backend()) ffi.cdef(""" - void lround(double x); + void getenv(char *); """) - m = ffi.dlopen(lib_m) - x = m.lround(1.23) + needs_dlopen_none() + m = ffi.dlopen(None) + x = m.getenv(b"FOO") assert x is None def test_dlopen_filename(self): diff --git a/extra_tests/cffi_tests/cffi1/test_pkgconfig.py b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py new file mode 100644 --- /dev/null +++ b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py @@ -0,0 +1,95 @@ +# Generated by pypy/tool/import_cffi.py +import sys +import subprocess +import py +import cffi.pkgconfig as pkgconfig +from cffi import PkgConfigError + + +def mock_call(libname, flag): + assert libname=="foobarbaz" + flags = { + "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", + "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", + } + return flags[flag] + + +def test_merge_flags(): + d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} + d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} + + pkgconfig.merge_flags(d1, d2) + assert d1 == { + "ham": [1, 2, 3], + "spam" : ["a", "b", "c", "spam", "spam", "spam"], + "bar" : ["b", "a", "z"], + "foo" : []} + + +def test_pkgconfig(): + assert pkgconfig.flags_from_pkgconfig([]) == {} + + saved = pkgconfig.call + try: + pkgconfig.call = mock_call + flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) + finally: + pkgconfig.call = saved + assert flags == { + 'include_dirs': ['/usr/include/python3.6m'], + 'library_dirs': ['/usr/lib64'], + 'libraries': ['python3.6'], + 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], + 'extra_compile_args': ['-O42'], + 'extra_link_args': ['-shared'] + } + +class mock_subprocess: + PIPE = Ellipsis + class Popen: + def __init__(self, cmd, stdout, stderr): + if mock_subprocess.RESULT is None: + raise OSError("oops can't run") + assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] + def communicate(self): + bout, berr, rc = mock_subprocess.RESULT + self.returncode = rc + return bout, berr + +def test_call(): + saved = pkgconfig.subprocess + try: + pkgconfig.subprocess = mock_subprocess + + mock_subprocess.RESULT = None + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "cannot run pkg-config: oops can't run" + + mock_subprocess.RESULT = b"", "Foo error!\n", 1 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "Foo error!" + + mock_subprocess.RESULT = b"abc\\def\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value).startswith("pkg-config --cflags libfoo returned an " + "unsupported backslash-escaped output:") + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + if sys.version_info >= (3,): + mock_subprocess.RESULT = b"\xff\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, + "libfoo", "--cflags", encoding="utf-8") + assert str(e.value) == ( + "pkg-config --cflags libfoo returned bytes that cannot be " + "decoded with encoding 'utf-8':\nb'\\xff\\n'") + + finally: + pkgconfig.subprocess = saved diff --git a/lib_pypy/_csv.py b/lib_pypy/_csv.py deleted file mode 100644 --- a/lib_pypy/_csv.py +++ /dev/null @@ -1,573 +0,0 @@ -"""CSV parsing and writing. - -This module provides classes that assist in the reading and writing -of Comma Separated Value (CSV) files, and implements the interface -described by PEP 305. Although many CSV files are simple to parse, -the format is not formally defined by a stable specification and -is subtle enough that parsing lines of a CSV file with something -like line.split(\",\") is bound to fail. The module supports three -basic APIs: reading, writing, and registration of dialects. - - -DIALECT REGISTRATION: - -Readers and writers support a dialect argument, which is a convenient -handle on a group of settings. When the dialect argument is a string, -it identifies one of the dialects previously registered with the module. -If it is a class or instance, the attributes of the argument are used as -the settings for the reader or writer: - - class excel: - delimiter = ',' - quotechar = '\"' - escapechar = None - doublequote = True - skipinitialspace = False - lineterminator = '\\r\\n' - quoting = QUOTE_MINIMAL - -SETTINGS: - - * quotechar - specifies a one-character string to use as the - quoting character. It defaults to '\"'. - * delimiter - specifies a one-character string to use as the - field separator. It defaults to ','. - * skipinitialspace - specifies how to interpret whitespace which - immediately follows a delimiter. It defaults to False, which - means that whitespace immediately following a delimiter is part - of the following field. - * lineterminator - specifies the character sequence which should - terminate rows. - * quoting - controls when quotes should be generated by the writer. - It can take on any of the following module constants: - - csv.QUOTE_MINIMAL means only when required, for example, when a - field contains either the quotechar or the delimiter - csv.QUOTE_ALL means that quotes are always placed around fields. - csv.QUOTE_NONNUMERIC means that quotes are always placed around - fields which do not parse as integers or floating point - numbers. - csv.QUOTE_NONE means that quotes are never placed around fields. - * escapechar - specifies a one-character string used to escape - the delimiter when quoting is set to QUOTE_NONE. - * doublequote - controls the handling of quotes inside fields. When - True, two consecutive quotes are interpreted as one during read, - and when writing, each quote character embedded in the data is - written as two quotes. -""" - -__version__ = "1.0" - -QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE = range(4) -_dialects = {} -_field_limit = 128 * 1024 # max parsed field size - -class Error(Exception): - pass - -class Dialect(object): - """CSV dialect - - The Dialect type records CSV parsing and generation options.""" - - __slots__ = ["_delimiter", "_doublequote", "_escapechar", - "_lineterminator", "_quotechar", "_quoting", - "_skipinitialspace", "_strict"] - - def __new__(cls, dialect, **kwargs): - - for name in kwargs: - if '_' + name not in Dialect.__slots__: - raise TypeError("unexpected keyword argument '%s'" % - (name,)) - - if dialect is not None: - if isinstance(dialect, basestring): - dialect = get_dialect(dialect) - - # Can we reuse this instance? - if (isinstance(dialect, Dialect) - and all(value is None for value in kwargs.itervalues())): - return dialect - - self = object.__new__(cls) - - - def set_char(x): - if x is None: - return None - if isinstance(x, str) and len(x) <= 1: - return x - raise TypeError("%r must be a 1-character string" % (name,)) - def set_str(x): - if isinstance(x, str): - return x - raise TypeError("%r must be a string" % (name,)) - def set_quoting(x): - if x in range(4): - return x - raise TypeError("bad 'quoting' value") - - attributes = {"delimiter": (',', set_char), - "doublequote": (True, bool), - "escapechar": (None, set_char), - "lineterminator": ("\r\n", set_str), - "quotechar": ('"', set_char), - "quoting": (QUOTE_MINIMAL, set_quoting), - "skipinitialspace": (False, bool), - "strict": (False, bool), - } - - # Copy attributes - notset = object() - for name in Dialect.__slots__: - name = name[1:] - value = notset - if name in kwargs: - value = kwargs[name] - elif dialect is not None: - value = getattr(dialect, name, notset) - - # mapping by name: (default, converter) - if value is notset: - value = attributes[name][0] - if name == 'quoting' and not self.quotechar: - value = QUOTE_NONE - else: - converter = attributes[name][1] - if converter: - value = converter(value) - - setattr(self, '_' + name, value) - - if not self.delimiter: - raise TypeError("delimiter must be set") - - if self.quoting != QUOTE_NONE and not self.quotechar: - raise TypeError("quotechar must be set if quoting enabled") - - if not self.lineterminator: - raise TypeError("lineterminator must be set") - - return self - - delimiter = property(lambda self: self._delimiter) - doublequote = property(lambda self: self._doublequote) - escapechar = property(lambda self: self._escapechar) - lineterminator = property(lambda self: self._lineterminator) - quotechar = property(lambda self: self._quotechar) - quoting = property(lambda self: self._quoting) - skipinitialspace = property(lambda self: self._skipinitialspace) - strict = property(lambda self: self._strict) - - -def _call_dialect(dialect_inst, kwargs): - return Dialect(dialect_inst, **kwargs) - -def register_dialect(name, dialect=None, **kwargs): - """Create a mapping from a string name to a dialect class. - dialect = csv.register_dialect(name, dialect)""" - if not isinstance(name, basestring): - raise TypeError("dialect name must be a string or unicode") - - dialect = _call_dialect(dialect, kwargs) - _dialects[name] = dialect - -def unregister_dialect(name): - """Delete the name/dialect mapping associated with a string name.\n - csv.unregister_dialect(name)""" - try: - del _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def get_dialect(name): - """Return the dialect instance associated with name. - dialect = csv.get_dialect(name)""" - try: - return _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def list_dialects(): - """Return a list of all know dialect names - names = csv.list_dialects()""" - return list(_dialects) - -class Reader(object): - """CSV reader - - Reader objects are responsible for reading and parsing tabular data - in CSV format.""" - - - (START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD, - IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD, - EAT_CRNL) = range(8) - - def __init__(self, iterator, dialect=None, **kwargs): - self.dialect = _call_dialect(dialect, kwargs) - self.input_iter = iter(iterator) - self.line_num = 0 - - self._parse_reset() - - def _parse_reset(self): - self.field = '' - self.fields = [] - self.state = self.START_RECORD - self.numeric_field = False - - def __iter__(self): - return self - - def next(self): - self._parse_reset() - while True: - try: - line = next(self.input_iter) - except StopIteration: - # End of input OR exception - if len(self.field) > 0: - raise Error("newline inside string") - raise - - self.line_num += 1 - - if '\0' in line: - raise Error("line contains NULL byte") - pos = 0 - while pos < len(line): - pos = self._parse_process_char(line, pos) - self._parse_eol() - - if self.state == self.START_RECORD: - break - - fields = self.fields - self.fields = [] - return fields - - def _parse_process_char(self, line, pos): - c = line[pos] - if self.state == self.IN_FIELD: - # in unquoted field - pos2 = pos - while True: - if c in '\n\r': - # end of line - return [fields] - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.EAT_CRNL - elif c == self.dialect.escapechar: - # possible escaped character - pos2 -= 1 - self.state = self.ESCAPED_CHAR - elif c == self.dialect.delimiter: - # save field - wait for new field - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.START_FIELD - else: - # normal character - save in field - pos2 += 1 - if pos2 < len(line): - c = line[pos2] - continue - break - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - 1 - - elif self.state == self.START_RECORD: - if c in '\n\r': - self.state = self.EAT_CRNL - else: - self.state = self.START_FIELD - # restart process - self._parse_process_char(line, pos) - - elif self.state == self.START_FIELD: - if c in '\n\r': - # save empty field - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # start quoted field - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.escapechar: - # possible escaped character - self.state = self.ESCAPED_CHAR - elif c == ' ' and self.dialect.skipinitialspace: - # ignore space at start of field - pass - elif c == self.dialect.delimiter: - # save empty field - self._parse_save_field() - else: - # begin new unquoted field - if self.dialect.quoting == QUOTE_NONNUMERIC: - self.numeric_field = True - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.IN_QUOTED_FIELD: - if c == self.dialect.escapechar: - # possible escape character - self.state = self.ESCAPE_IN_QUOTED_FIELD - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - if self.dialect.doublequote: - # doublequote; " represented by "" - self.state = self.QUOTE_IN_QUOTED_FIELD - else: - #end of quote part of field - self.state = self.IN_FIELD - else: - # normal character - save in field - self._parse_add_char(c) - - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # doublequote - seen a quote in a quoted field - if (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # save "" as " - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.delimiter: - # save field - wait for new field - self._parse_save_field() - self.state = self.START_FIELD - elif c in '\r\n': - # end of line - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif not self.dialect.strict: - self._parse_add_char(c) - self.state = self.IN_FIELD - else: - raise Error("'%c' expected after '%c'" % - (self.dialect.delimiter, self.dialect.quotechar)) - - elif self.state == self.EAT_CRNL: - if c not in '\r\n': - raise Error("new-line character seen in unquoted field - " - "do you need to open the file " - "in universal-newline mode?") - - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - return pos + 1 - - def _parse_eol(self): - if self.state == self.EAT_CRNL: - self.state = self.START_RECORD - elif self.state == self.START_RECORD: - # empty line - return [] - pass - elif self.state == self.IN_FIELD: - # in unquoted field - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.START_FIELD: - # save empty field - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char('\n') - self.state = self.IN_FIELD - elif self.state == self.IN_QUOTED_FIELD: - pass - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char('\n') - self.state = self.IN_QUOTED_FIELD - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - def _parse_save_field(self): - field, self.field = self.field, '' - if self.numeric_field: - self.numeric_field = False - field = float(field) - self.fields.append(field) - - def _parse_add_char(self, c): - if len(self.field) + len(c) > _field_limit: - raise Error("field larger than field limit (%d)" % (_field_limit)) - self.field += c - - -class Writer(object): - """CSV writer - - Writer objects are responsible for generating tabular data - in CSV format from sequence input.""" - - def __init__(self, file, dialect=None, **kwargs): - if not (hasattr(file, 'write') and callable(file.write)): - raise TypeError("argument 1 must have a 'write' method") - self.writeline = file.write - self.dialect = _call_dialect(dialect, kwargs) - - def _join_reset(self): - self.rec = [] - self.num_fields = 0 - - def _join_append(self, field, quoted, quote_empty): - dialect = self.dialect - # If this is not the first field we need a field separator - if self.num_fields > 0: - self.rec.append(dialect.delimiter) - - if dialect.quoting == QUOTE_NONE: - need_escape = tuple(dialect.lineterminator) + ( - dialect.escapechar, # escapechar always first - dialect.delimiter, dialect.quotechar) - - else: - for c in tuple(dialect.lineterminator) + ( - dialect.delimiter, dialect.escapechar): - if c and c in field: - quoted = True - - need_escape = () - if dialect.quotechar in field: - if dialect.doublequote: - field = field.replace(dialect.quotechar, - dialect.quotechar * 2) - quoted = True - else: - need_escape = (dialect.quotechar,) - - - for c in need_escape: - if c and c in field: - if not dialect.escapechar: - raise Error("need to escape, but no escapechar set") - field = field.replace(c, dialect.escapechar + c) - - # If field is empty check if it needs to be quoted - if field == '' and quote_empty: - if dialect.quoting == QUOTE_NONE: - raise Error("single empty field record must be quoted") - quoted = 1 - - if quoted: - field = dialect.quotechar + field + dialect.quotechar - - self.rec.append(field) - self.num_fields += 1 - - - - def writerow(self, row): - dialect = self.dialect - try: - rowlen = len(row) - except TypeError: - raise Error("sequence expected") - - # join all fields in internal buffer - self._join_reset() - - for field in row: - quoted = False - if dialect.quoting == QUOTE_NONNUMERIC: - try: - float(field) - except: - quoted = True - # This changed since 2.5: - # quoted = not isinstance(field, (int, long, float)) - elif dialect.quoting == QUOTE_ALL: - quoted = True - - if field is None: - value = "" - elif isinstance(field, float): - value = repr(field) - else: - value = str(field) - self._join_append(value, quoted, rowlen == 1) - - # add line terminator - self.rec.append(dialect.lineterminator) - - self.writeline(''.join(self.rec)) - - def writerows(self, rows): - for row in rows: - self.writerow(row) - -def reader(*args, **kwargs): - """ - csv_reader = reader(iterable [, dialect='excel'] - [optional keyword args]) - for row in csv_reader: - process(row) - - The "iterable" argument can be any object that returns a line - of input for each iteration, such as a file object or a list. The - optional \"dialect\" parameter is discussed below. The function - also accepts optional keyword arguments which override settings - provided by the dialect. - - The returned object is an iterator. Each iteration returns a row - of the CSV file (which can span multiple input lines)""" - - return Reader(*args, **kwargs) - -def writer(*args, **kwargs): - """ - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - for row in sequence: - csv_writer.writerow(row) - - [or] - - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - csv_writer.writerows(rows) - - The \"fileobj\" argument can be any object that supports the file API.""" - return Writer(*args, **kwargs) - - -undefined = object() -def field_size_limit(limit=undefined): - """Sets an upper limit on parsed fields. - csv.field_size_limit([limit]) - - Returns old limit. If limit is not given, no new limit is set and - the old limit is returned""" - - global _field_limit - old_limit = _field_limit - - if limit is not undefined: - if not isinstance(limit, (int, long)): - raise TypeError("int expected, got %s" % - (limit.__class__.__name__,)) - _field_limit = limit - - return old_limit 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 @@ -3,6 +3,7 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing +from .error import PkgConfigError __version__ = "1.12.0" __version_info__ = (1, 12, 0) 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 @@ -592,7 +592,7 @@ if sys.platform == "win32": # we need 'libpypy-c.lib'. Current distributions of # pypy (>= 4.1) contain it as 'libs/python27.lib'. - pythonlib = "python27" + pythonlib = "python{0[0]}{0[1]}".format(sys.version_info) if hasattr(sys, 'prefix'): ensure('library_dirs', os.path.join(sys.prefix, 'libs')) else: @@ -643,6 +643,16 @@ self._assigned_source = (str(module_name), source, source_extension, kwds) + def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, + source_extension='.c', **kwds): + from . import pkgconfig + if not isinstance(pkgconfig_libs, list): + raise TypeError("the pkgconfig_libs argument must be a list " + "of package names") + kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) + pkgconfig.merge_flags(kwds, kwds2) + self.set_source(module_name, source, source_extension, **kwds) + def distutils_extension(self, tmpdir='build', verbose=True): from distutils.dir_util import mkpath from .recompiler import recompile diff --git a/lib_pypy/cffi/error.py b/lib_pypy/cffi/error.py --- a/lib_pypy/cffi/error.py +++ b/lib_pypy/cffi/error.py @@ -1,8 +1,9 @@ class FFIError(Exception): - pass + __module__ = 'cffi' class CDefError(Exception): + __module__ = 'cffi' def __str__(self): try: current_decl = self.args[1] @@ -16,8 +17,15 @@ class VerificationError(Exception): """ An error raised when verification fails """ + __module__ = 'cffi' class VerificationMissing(Exception): """ An error raised when incomplete structures are passed into cdef, but no verification has been done """ + __module__ = 'cffi' + +class PkgConfigError(Exception): + """ An error raised for missing modules in pkg-config + """ + __module__ = 'cffi' diff --git a/lib_pypy/cffi/pkgconfig.py b/lib_pypy/cffi/pkgconfig.py new file mode 100644 --- /dev/null +++ b/lib_pypy/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except EnvironmentError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret diff --git a/lib_pypy/pwd.py b/lib_pypy/pwd.py deleted file mode 100644 --- a/lib_pypy/pwd.py +++ /dev/null @@ -1,114 +0,0 @@ -# indirectly based on ctypes implementation: Victor Stinner, 2008-05-08 -""" -This module provides access to the Unix password database. -It is available on all Unix versions. - -Password database entries are reported as 7-tuples containing the following -items from the password database (see `'), in order: -pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell. -The uid and gid items are integers, all others are strings. An -exception is raised if the entry asked for cannot be found. -""" - -from _pwdgrp_cffi import ffi, lib -import _structseq -import thread -_lock = thread.allocate_lock() - -try: from __pypy__ import builtinify -except ImportError: builtinify = lambda f: f - - -class struct_passwd: - """ - pwd.struct_passwd: Results from getpw*() routines. - - This object may be accessed either as a tuple of - (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell) - or via the object attributes as named in the above tuple. - """ - __metaclass__ = _structseq.structseqtype - name = "pwd.struct_passwd" - - pw_name = _structseq.structseqfield(0) - pw_passwd = _structseq.structseqfield(1) - pw_uid = _structseq.structseqfield(2) - pw_gid = _structseq.structseqfield(3) - pw_gecos = _structseq.structseqfield(4) - pw_dir = _structseq.structseqfield(5) - pw_shell = _structseq.structseqfield(6) - - -def _mkpwent(pw): - return struct_passwd([ - ffi.string(pw.pw_name), - ffi.string(pw.pw_passwd), - pw.pw_uid, - pw.pw_gid, - ffi.string(pw.pw_gecos), - ffi.string(pw.pw_dir), - ffi.string(pw.pw_shell)]) - - at builtinify -def getpwuid(uid): - """ - getpwuid(uid) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given numeric user ID. - See pwd.__doc__ for more on password database entries. - """ - with _lock: - pw = lib.getpwuid(uid) - if not pw: - raise KeyError("getpwuid(): uid not found: %s" % uid) - return _mkpwent(pw) - - at builtinify -def getpwnam(name): - """ - getpwnam(name) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given user name. - See pwd.__doc__ for more on password database entries. - """ - if not isinstance(name, basestring): - raise TypeError("expected string") - name = str(name) - with _lock: - pw = lib.getpwnam(name) - if not pw: - raise KeyError("getpwname(): name not found: %s" % name) - return _mkpwent(pw) - - at builtinify -def getpwall(): - """ - getpwall() -> list_of_entries - Return a list of all available password database entries, in arbitrary order. - See pwd.__doc__ for more on password database entries. - """ - users = [] - with _lock: - lib.setpwent() - while True: - pw = lib.getpwent() - if not pw: - break - users.append(_mkpwent(pw)) - lib.endpwent() - return users - -__all__ = ('struct_passwd', 'getpwuid', 'getpwnam', 'getpwall') - -if __name__ == "__main__": -# Uncomment next line to test CPython implementation -# from pwd import getpwuid, getpwnam, getpwall - from os import getuid - uid = getuid() - pw = getpwuid(uid) - print("uid %s: %s" % (pw.pw_uid, pw)) - name = pw.pw_name - print("name %r: %s" % (name, getpwnam(name))) - print("All:") - for pw in getpwall(): - print(pw) diff --git a/pypy/conftest.py b/pypy/conftest.py --- a/pypy/conftest.py +++ b/pypy/conftest.py @@ -5,6 +5,18 @@ rsyncdirs = ['.', '../lib-python', '../lib_pypy', '../demo'] rsyncignore = ['_cache'] +try: + from hypothesis import settings, __version__ +except ImportError: + pass +else: + if __version__[:2] < '3.6': + s = settings(deadline=None) + settings.register_profile('default', s) + else: + settings.register_profile('default', deadline=None) + settings.load_profile('default') + # PyPy's command line extra options (these are added # to py.test's standard options) # diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst --- a/pypy/doc/build.rst +++ b/pypy/doc/build.rst @@ -220,11 +220,12 @@ Making a debug build of PyPy ---------------------------- -If the Makefile is rerun with the lldebug or lldebug0 target, appropriate -compilation flags are added to add debug info and reduce compiler optimizations -to ``-O0`` respectively. If you stop in a debugger, you will see the -very wordy machine-generated C code from the rpython translation step, which -takes a little bit of reading to relate back to the rpython code. +Rerun the ``Makefile`` with the ``make lldebug`` or ``make lldebug0`` target, +which will build in a way that running under a debugger makes sense. +Appropriate compilation flags are added to add debug info, and for ``lldebug0`` +compiler optimizations are fully disabled. If you stop in a debugger, you will +see the very wordy machine-generated C code from the rpython translation step, +which takes a little bit of reading to relate back to the rpython code. Build cffi import libraries for the stdlib ------------------------------------------ diff --git a/pypy/doc/conf.py b/pypy/doc/conf.py --- a/pypy/doc/conf.py +++ b/pypy/doc/conf.py @@ -65,10 +65,15 @@ # |version| and |release|, also used in various other places throughout the # built documents. # + +# Make sure to keep this in sync with: +# module/sys/version.py +# module/cpyext/include/patchlevel.h +# # The short X.Y version. -version = '6.0' +version = '7.1' # The full version, including alpha/beta/rc tags. -release = '6.0.0' +release = '7.1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/pypy/doc/contributor.rst b/pypy/doc/contributor.rst --- a/pypy/doc/contributor.rst +++ b/pypy/doc/contributor.rst @@ -7,16 +7,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -26,8 +26,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -37,10 +37,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -81,12 +81,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -101,8 +101,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -111,10 +112,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -130,6 +131,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -143,6 +145,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -154,7 +157,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -165,7 +167,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -177,6 +178,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -184,12 +186,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -209,7 +213,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -237,12 +240,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -257,10 +263,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -268,28 +276,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -299,6 +305,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -307,6 +314,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -316,8 +324,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -331,7 +340,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -351,12 +359,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz diff --git a/pypy/doc/how-to-release.rst b/pypy/doc/how-to-release.rst --- a/pypy/doc/how-to-release.rst +++ b/pypy/doc/how-to-release.rst @@ -16,9 +16,6 @@ How to Create a PyPy Release ++++++++++++++++++++++++++++ -Overview --------- - As a meta rule setting up issues in the tracker for items here may help not forgetting things. A set of todo files may also work. @@ -28,17 +25,54 @@ Release Steps -------------- +++++++++++++++ -* If needed, make a release branch -* Bump the - pypy version number in module/sys/version.py and in - module/cpyext/include/patchlevel.h and in doc/conf.py. The branch - will capture the revision number of this change for the release. +Make the release branch +------------------------ - Some of the next updates may be done before or after branching; make - sure things are ported back to the trunk and to the branch as - necessary. +This is needed only in case you are doing a new major version; if not, you can +probably reuse the existing release branch. + +We want to be able to freely merge default into the branch and vice-versa; +thus we need to do a complicate dance to avoid to patch the version number +when we do a merge:: + + $ hg up -r default + $ # edit the version to e.g. 7.0.0-final + $ hg ci + $ hg branch release-pypy2.7-7.x && hg ci + $ hg up -r default + $ # edit the version to 7.1.0-alpha0 + $ hg ci + $ hg up -r release-pypy2.7-7.x + $ hg merge default + $ # edit the version to AGAIN 7.0.0-final + $ hg ci + +Then, we need to do the same for the 3.x branch:: + + $ hg up -r py3.5 + $ hg merge default # this brings the version fo 7.1.0-alpha0 + $ hg branch release-pypy3.5-7.x + $ # edit the version to 7.0.0-final + $ hg ci + $ hg up -r py3.5 + $ hg merge release-pypy3.5-7.x + $ # edit the version to 7.1.0-alpha0 + $ hg ci + +To change the version, you need to edit three files: + + - ``module/sys/version.py`` + + - ``module/cpyext/include/patchlevel.h`` + + - ``doc/conf.py`` + + +Other steps +----------- + * Make sure the RPython builds on the buildbot pass with no failures 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-v7.0.0.rst release-v6.0.0.rst release-v5.10.1.rst release-v5.10.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-7.0.0.rst whatsnew-pypy2-6.0.0.rst whatsnew-pypy2-5.10.0.rst whatsnew-pypy2-5.10.0.rst @@ -41,6 +42,7 @@ .. toctree:: whatsnew-pypy3-head.rst + whatsnew-pypy3-7.0.0.rst whatsnew-pypy3-5.9.0.rst whatsnew-pypy3-5.8.0.rst whatsnew-pypy3-5.7.0.rst diff --git a/pypy/doc/interpreter.rst b/pypy/doc/interpreter.rst --- a/pypy/doc/interpreter.rst +++ b/pypy/doc/interpreter.rst @@ -156,7 +156,7 @@ environment found in `Frames`. Frames and Functions have references to a code object. Here is a list of Code attributes: -* ``co_flags`` flags if this code object has nested scopes/generators +* ``co_flags`` flags if this code object has nested scopes/generators/etc. * ``co_stacksize`` the maximum depth the stack can reach while executing the code * ``co_code`` the actual bytecode string diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-v7.0.0.rst @@ -0,0 +1,151 @@ +====================================================== +PyPy v7.0.0: triple release of 2.7, 3.5 and 3.6-alpha +====================================================== + +The PyPy team is proud to release the version 7.0.0 of PyPy, which includes +three different interpreters: + + - PyPy2.7, which is an interpreter supporting the syntax and the features of + Python 2.7 + + - PyPy3.5, which supports Python 3.5 + + - PyPy3.6-alpha: this is the first official release of PyPy to support 3.6 + features, although it is still considered alpha quality. + +All the interpreters are based on much the same codebase, thus the triple +release. + +Until we can work with downstream providers to distribute builds with PyPy, we +have made packages for some common packages `available as wheels`_. + +The GC `hooks`_ , which can be used to gain more insights into its +performance, has been improved and it is now possible to manually manage the +GC by using a combination of ``gc.disable`` and ``gc.collect_step``. See the +`GC blog post`_. + + +We updated the `cffi`_ module included in PyPy to version 1.12, and the +`cppyy`_ backend to 1.4. Please use these to wrap your C and C++ code, +respectively, for a JIT friendly experience. + +As always, this release is 100% compatible with the previous one and fixed +several issues and bugs raised by the growing community of PyPy users. +We strongly recommend updating. + +The PyPy3.6 release and the Windows PyPy3.5 release are still not production +quality so your mileage may vary. There are open issues with incomplete +compatibility and c-extension support. + +The utf8 branch that changes internal representation of unicode to utf8 did not +make it into the release, so there is still more goodness coming. +You can download the v6.0 releases here: + + http://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project. If PyPy is not quite good enough for your needs, we are available for +direct consulting work. + +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. + +.. _`PyPy`: index.html +.. _`RPython`: https://rpython.readthedocs.org +.. _`help`: project-ideas.html +.. _`cffi`: http://cffi.readthedocs.io +.. _`cppyy`: https://cppyy.readthedocs.io +.. _`available as wheels`: https://github.com/antocuni/pypy-wheels +.. _`GC blog post`: https://morepypy.blogspot.com/2019/01/pypy-for-low-latency-systems.html + + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7, 3.5 and 3.6. 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 release supports: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS X 64 bits, Windows 32 bits, OpenBSD, FreeBSD) + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +Unfortunately at the moment of writing our ARM buildbots are out of service, +so for now we are **not** releasing any binary for the ARM architecture. + +.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://rpython.readthedocs.io/en/latest/examples.html + + +Changelog +========= + +If not specified, the changes are shared across versions + +* Support ``__set_name__``, ``__init_subclass__`` (Py3.6) +* Support ``cppyy`` in Py3.5 and Py3.6 +* Use implementation-specific site directories in ``sysconfig`` (Py3.5, Py3.6) +* Adding detection of gcc to ``sysconfig`` (Py3.5, Py3.6) +* Fix multiprocessing regression on newer glibcs +* Make sure 'blocking-ness' of socket is set along with default timeout +* Include ``crypt.h`` for ``crypt()`` on Linux +* Improve and re-organize the contributing_ documentation +* Make the ``__module__`` attribute writable, fixing an incompatibility with + NumPy 1.16 +* Implement ``Py_ReprEnter``, ``Py_ReprLeave(), ``PyMarshal_ReadObjectFromString``, + ``PyMarshal_WriteObjectToString``, ``PyObject_DelItemString``, + ``PyMapping_DelItem``, ``PyMapping_DelItemString``, ``PyEval_GetFrame``, + ``PyOS_InputHook``, ``PyErr_FormatFromCause`` (Py3.6), +* Implement new wordcode instruction encoding (Py3.6) +* Log additional gc-minor and gc-collect-step info in the PYPYLOG +* The ``reverse-debugger`` (revdb) branch has been merged to the default + branch, so it should always be up-to-date. You still need a special pypy + build, but you can compile it from the same source as the one we distribute + for the v7.0.0 release. For more information, see + https://bitbucket.org/pypy/revdb +* Support underscores in numerical literals like ``'4_2'`` (Py3.6) +* Pre-emptively raise MemoryError if the size of dequeue in ``_collections.deque`` + is too large (Py3.5) +* Fix multithreading issues in calls to ``os.setenv`` +* Add missing defines and typedefs for numpy and pandas on MSVC +* Add CPython macros like ``Py_NAN`` to header files +* Rename the ``MethodType`` to ``instancemethod``, like CPython +* Better support for `async with` in generators (Py3.5, Py3.6) +* Improve the performance of ``pow(a, b, c)`` if ``c`` is a large integer +* Now ``vmprof`` works on FreeBSD +* Support GNU Hurd, fixes for FreeBSD +* Add deprecation warning if type of result of ``__float__`` is float inherited + class (Py3.6) +* Fix async generator bug when yielding a ``StopIteration`` (Py3.6) +* Speed up ``max(list-of-int)`` from non-jitted code +* Fix Windows ``os.listdir()`` for some cases (see CPython #32539) +* Add ``select.PIPE_BUF`` +* Use ``subprocess`` to avoid shell injection in ``shutil`` module - backport + of https://bugs.python.org/issue34540 +* Rename ``_Py_ZeroStruct`` to ``_Py_FalseStruct`` (Py3.5, Py3.6) +* Remove some cpyext names for Py3.5, Py3.6 +* Enable use of unicode file names in ``dlopen`` +* Backport CPython fix for ``thread.RLock`` +* Make GC hooks measure time in seconds (as opposed to an opaque unit) +* Refactor and reorganize tests in ``test_lib_pypy`` +* Check error values in ``socket.setblocking`` (Py3.6) +* Add support for FsPath to os.unlink() (Py3.6) +* Fix freezing builtin modules at translation +* Tweak ``W_UnicodeDictionaryStrategy`` which speeds up dictionaries with only + unicode keys + +We also refactored many parts of the JIT bridge optimizations, as well as cpyext +internals, and together with new contributors fixed issues, added new +documentation, and cleaned up the codebase. + +.. _contributing: http://doc.pypy.org/en/latest/contributing.html 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,69 +1,21 @@ ========================== -What's new in PyPy2.7 6.0+ +What's new in PyPy2.7 7.0+ ========================== -.. this is a revision shortly after release-pypy-6.0.0 -.. startrev: e50e11af23f1 +.. this is a revision shortly after release-pypy-7.0.0 +.. startrev: 481c69f7d81f -.. branch: cppyy-packaging +.. branch: zlib-copying-redux -Main items: vastly better template resolution and improved performance. In -detail: upgrade to backend 1.4, improved handling of templated methods and -functions (in particular automatic deduction of types), improved pythonization -interface, range of compatibility fixes for Python3, free functions now take -fast libffi path when possible, moves for strings (incl. from Python str), -easier/faster handling of std::vector by numpy, improved and faster object -identity preservation +Fix calling copy on already-flushed compressobjs. -.. branch: socket_default_timeout_blockingness +.. branch: zlib-copying -Make sure 'blocking-ness' of socket is set along with default timeout +The zlib module's compressobj and decompressobj now expose copy methods +as they do on CPython. -.. branch: crypt_h -Include crypt.h for crypt() on Linux +.. math-improvements -.. branch: gc-more-logging - -Log additional gc-minor and gc-collect-step info in the PYPYLOG - -.. branch: reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb - - -.. branch: pyparser-improvements-3 - -Small refactorings in the Python parser. - -.. branch: fix-readme-typo - -.. branch: avoid_shell_injection_in_shutil - -Backport CPython fix for possible shell injection issue in `distutils.spawn`, -https://bugs.python.org/issue34540 - -.. branch: cffi_dlopen_unicode - -Enable use of unicode file names in `dlopen` - -.. branch: rlock-in-rpython - -Backport CPython fix for `thread.RLock` - - -.. branch: expose-gc-time - -Make GC hooks measure time in seconds (as opposed to an opaque unit). - -.. branch: cleanup-test_lib_pypy - -Update most test_lib_pypy/ tests and move them to extra_tests/. - -.. branch: gc-disable - -Make it possible to manually manage the GC by using a combination of -gc.disable() and gc.collect_step(). Make sure to write a proper release -announcement in which we explain that existing programs could leak memory if -they run for too much time between a gc.disable()/gc.enable() +Improve performance of long operations where one of the operands fits into +an int. \ No newline at end of file diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-pypy2-7.0.0.rst copy from pypy/doc/whatsnew-head.rst copy to pypy/doc/whatsnew-pypy2-7.0.0.rst diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-7.0.0.rst copy from pypy/doc/whatsnew-pypy3-head.rst copy to pypy/doc/whatsnew-pypy3-7.0.0.rst --- a/pypy/doc/whatsnew-pypy3-head.rst +++ b/pypy/doc/whatsnew-pypy3-7.0.0.rst @@ -1,7 +1,19 @@ -========================= -What's new in PyPy3 5.9+ -========================= - -.. this is the revision after release-pypy3.5-5.9 -.. startrev: be41e3ac0a29 - +======================== +What's new in PyPy3 6.0+ +======================== + +.. this is the revision after release-pypy3.5-v6.0 +.. startrev: 580e3e26cd32 From pypy.commits at gmail.com Fri Feb 8 06:13:36 2019 From: pypy.commits at gmail.com (antocuni) Date: Fri, 08 Feb 2019 03:13:36 -0800 (PST) Subject: [pypy-commit] pypy default: fix version in the release announcement Message-ID: <5c5d6460.1c69fb81.e606d.e493@mx.google.com> Author: Antonio Cuni Branch: Changeset: r95899:7ba58d0d1973 Date: 2019-02-08 12:10 +0100 http://bitbucket.org/pypy/pypy/changeset/7ba58d0d1973/ Log: fix version in the release announcement diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst --- a/pypy/doc/release-v7.0.0.rst +++ b/pypy/doc/release-v7.0.0.rst @@ -39,7 +39,7 @@ The utf8 branch that changes internal representation of unicode to utf8 did not make it into the release, so there is still more goodness coming. -You can download the v6.0 releases here: +You can download the v7.0 releases here: http://pypy.org/download.html From pypy.commits at gmail.com Fri Feb 8 07:30:34 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Fri, 08 Feb 2019 04:30:34 -0800 (PST) Subject: [pypy-commit] pypy zlib-copying-third-time-a-charm: Close to-be-merged. Message-ID: <5c5d766a.1c69fb81.45b91.4df1@mx.google.com> Author: Julian Berman Branch: zlib-copying-third-time-a-charm Changeset: r95900:ec4721a48f93 Date: 2019-02-08 07:27 -0500 http://bitbucket.org/pypy/pypy/changeset/ec4721a48f93/ Log: Close to-be-merged. From pypy.commits at gmail.com Fri Feb 8 07:30:36 2019 From: pypy.commits at gmail.com (Julian Berman) Date: Fri, 08 Feb 2019 04:30:36 -0800 (PST) Subject: [pypy-commit] pypy default: Merge zlib-copying-third-time-a-charm. Message-ID: <5c5d766c.1c69fb81.c84d9.742b@mx.google.com> Author: Julian Berman Branch: Changeset: r95901:887c215ee39f Date: 2019-02-08 07:29 -0500 http://bitbucket.org/pypy/pypy/changeset/887c215ee39f/ Log: Merge zlib-copying-third-time-a-charm. 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 @@ -5,6 +5,11 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f +.. branch: zlib-copying-third-time-a-charm + +Make sure zlib decompressobjs have their streams deallocated immediately +on flush. + .. branch: zlib-copying-redux Fix calling copy on already-flushed compressobjs. diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -323,12 +323,16 @@ try: self.lock() try: + if not self.stream: + raise oefmt( + space.w_ValueError, + "Decompressor was already flushed", + ) copied = rzlib.inflateCopy(self.stream) finally: self.unlock() except rzlib.RZlibError as e: raise zlib_error(space, e.msg) - return Decompress( space=space, stream=copied, @@ -359,6 +363,9 @@ else: string, finished, unused_len = result self._save_unconsumed_input(data, finished, unused_len) + if finished: + rzlib.inflateEnd(self.stream) + self.stream = rzlib.null_stream return space.newbytes(string) From pypy.commits at gmail.com Fri Feb 8 07:48:41 2019 From: pypy.commits at gmail.com (antocuni) Date: Fri, 08 Feb 2019 04:48:41 -0800 (PST) Subject: [pypy-commit] pypy default: Added tag release-pypy2.7-v7.0.0 for changeset c8805ee6d784 Message-ID: <5c5d7aa9.1c69fb81.296cd.49c2@mx.google.com> Author: Antonio Cuni Branch: Changeset: r95902:194ef7383743 Date: 2019-02-08 13:47 +0100 http://bitbucket.org/pypy/pypy/changeset/194ef7383743/ Log: Added tag release-pypy2.7-v7.0.0 for changeset c8805ee6d784 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -61,3 +61,5 @@ 9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +c8805ee6d7846ca2722b106eeaa2f128c699aba3 release-pypy2.7-v7.0.0 From pypy.commits at gmail.com Fri Feb 8 07:48:43 2019 From: pypy.commits at gmail.com (antocuni) Date: Fri, 08 Feb 2019 04:48:43 -0800 (PST) Subject: [pypy-commit] pypy default: Added tag release-pypy3.5-v7.0.0 for changeset 928a4f70d3de Message-ID: <5c5d7aab.1c69fb81.e1dc.58c3@mx.google.com> Author: Antonio Cuni Branch: Changeset: r95903:f7df2fcb0792 Date: 2019-02-08 13:47 +0100 http://bitbucket.org/pypy/pypy/changeset/f7df2fcb0792/ Log: Added tag release-pypy3.5-v7.0.0 for changeset 928a4f70d3de diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -63,3 +63,5 @@ dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 c8805ee6d7846ca2722b106eeaa2f128c699aba3 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +928a4f70d3de7d17449456946154c5da6e600162 release-pypy3.5-v7.0.0 From pypy.commits at gmail.com Fri Feb 8 07:48:46 2019 From: pypy.commits at gmail.com (antocuni) Date: Fri, 08 Feb 2019 04:48:46 -0800 (PST) Subject: [pypy-commit] pypy default: Added tag release-pypy3.6-v7.0.0 for changeset fb40f7a5524c Message-ID: <5c5d7aae.1c69fb81.e5829.a768@mx.google.com> Author: Antonio Cuni Branch: Changeset: r95904:18e56676c281 Date: 2019-02-08 13:47 +0100 http://bitbucket.org/pypy/pypy/changeset/18e56676c281/ Log: Added tag release-pypy3.6-v7.0.0 for changeset fb40f7a5524c diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -65,3 +65,5 @@ c8805ee6d7846ca2722b106eeaa2f128c699aba3 release-pypy2.7-v7.0.0 1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 928a4f70d3de7d17449456946154c5da6e600162 release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +fb40f7a5524c77b80e6c468e087d621610137261 release-pypy3.6-v7.0.0 From pypy.commits at gmail.com Fri Feb 8 09:38:48 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 08 Feb 2019 06:38:48 -0800 (PST) Subject: [pypy-commit] pypy default: merge regalloc-playground Message-ID: <5c5d9478.1c69fb81.6ec08.d319@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r95905:cfad18a6fd4b Date: 2019-02-08 15:38 +0100 http://bitbucket.org/pypy/pypy/changeset/cfad18a6fd4b/ Log: merge regalloc-playground improve register allocation by using better heuristics. diff too long, truncating to 2000 out of 3847 lines 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 @@ -23,4 +23,8 @@ .. math-improvements Improve performance of long operations where one of the operands fits into -an int. \ No newline at end of file +an int. + +.. regalloc-playgrounds + +Improve register allocation in the JIT. diff --git a/pypy/doc/whatsnew-pypy2-5.10.0.rst b/pypy/doc/whatsnew-pypy2-5.10.0.rst --- a/pypy/doc/whatsnew-pypy2-5.10.0.rst +++ b/pypy/doc/whatsnew-pypy2-5.10.0.rst @@ -1,42 +1,42 @@ -========================== -What's new in PyPy2.7 5.10 -========================== - -.. this is a revision shortly after release-pypy2.7-v5.9.0 -.. startrev:d56dadcef996 - - -.. branch: cppyy-packaging - -Cleanup and improve cppyy packaging - -.. branch: docs-osx-brew-openssl - -.. branch: keep-debug-symbols - -Add a smartstrip tool, which can optionally keep the debug symbols in a -separate file, instead of just stripping them away. Use it in packaging - -.. branch: bsd-patches - -Fix failures on FreeBSD, contributed by David Naylor as patches on the issue -tracker (issues 2694, 2695, 2696, 2697) - -.. branch: run-extra-tests - -Run extra_tests/ in buildbot - -.. branch: vmprof-0.4.10 - -Upgrade the _vmprof backend to vmprof 0.4.10 - -.. branch: fix-vmprof-stacklet-switch -.. branch: fix-vmprof-stacklet-switch-2 - -Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) - -.. branch: win32-vcvars - -.. branch: rdict-fast-hash - -Make it possible to declare that the hash function of an r_dict is fast in RPython. +========================== +What's new in PyPy2.7 5.10 +========================== + +.. this is a revision shortly after release-pypy2.7-v5.9.0 +.. startrev:d56dadcef996 + + +.. branch: cppyy-packaging + +Cleanup and improve cppyy packaging + +.. branch: docs-osx-brew-openssl + +.. branch: keep-debug-symbols + +Add a smartstrip tool, which can optionally keep the debug symbols in a +separate file, instead of just stripping them away. Use it in packaging + +.. branch: bsd-patches + +Fix failures on FreeBSD, contributed by David Naylor as patches on the issue +tracker (issues 2694, 2695, 2696, 2697) + +.. branch: run-extra-tests + +Run extra_tests/ in buildbot + +.. branch: vmprof-0.4.10 + +Upgrade the _vmprof backend to vmprof 0.4.10 + +.. branch: fix-vmprof-stacklet-switch +.. branch: fix-vmprof-stacklet-switch-2 + +Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) + +.. branch: win32-vcvars + +.. branch: rdict-fast-hash + +Make it possible to declare that the hash function of an r_dict is fast in RPython. diff --git a/pypy/doc/whatsnew-pypy2-6.0.0.rst b/pypy/doc/whatsnew-pypy2-6.0.0.rst --- a/pypy/doc/whatsnew-pypy2-6.0.0.rst +++ b/pypy/doc/whatsnew-pypy2-6.0.0.rst @@ -1,128 +1,128 @@ -=========================== -What's new in PyPy2.7 5.10+ -=========================== - -.. this is a revision shortly after release-pypy2.7-v5.10.0 -.. startrev: 6b024edd9d12 - -.. branch: cpyext-avoid-roundtrip - -Big refactoring of some cpyext code, which avoids a lot of nonsense when -calling C from Python and vice-versa: the result is a big speedup in -function/method calls, up to 6 times faster. - -.. branch: cpyext-datetime2 - -Support ``tzinfo`` field on C-API datetime objects, fixes latest pandas HEAD - - -.. branch: mapdict-size-limit - -Fix a corner case of mapdict: When an instance is used like a dict (using -``setattr`` and ``getattr``, or ``.__dict__``) and a lot of attributes are -added, then the performance using mapdict is linear in the number of -attributes. This is now fixed (by switching to a regular dict after 80 -attributes). - - -.. branch: cpyext-faster-arg-passing - -When using cpyext, improve the speed of passing certain objects from PyPy to C -code, most notably None, True, False, types, all instances of C-defined types. -Before, a dict lookup was needed every time such an object crossed over, now it -is just a field read. - - -.. branch: 2634_datetime_timedelta_performance - -Improve datetime + timedelta performance. - -.. branch: memory-accounting - -Improve way to describe memory - -.. branch: msvc14 - -Allow compilaiton with Visual Studio 2017 compiler suite on windows - -.. branch: refactor-slots - -Refactor cpyext slots. - - -.. branch: call-loopinvariant-into-bridges - -Speed up branchy code that does a lot of function inlining by saving one call -to read the TLS in most bridges. - -.. branch: rpython-sprint - -Refactor in rpython signatures - -.. branch: cpyext-tls-operror2 - -Store error state thread-locally in executioncontext, fixes issue #2764 - -.. branch: cpyext-fast-typecheck - -Optimize `Py*_Check` for `Bool`, `Float`, `Set`. Also refactor and simplify -`W_PyCWrapperObject` which is used to call slots from the C-API, greatly -improving microbenchmarks in https://github.com/antocuni/cpyext-benchmarks - - -.. branch: fix-sre-problems - -Fix two (unrelated) JIT bugs manifesting in the re module: - -- green fields are broken and were thus disabled, plus their usage removed from - the _sre implementation - -- in rare "trace is too long" situations, the JIT could break behaviour - arbitrarily. - -.. branch: jit-hooks-can-be-disabled - -Be more efficient about JIT hooks. Make it possible for the frontend to declare -that jit hooks are currently not enabled at all. in that case, the list of ops -does not have to be created in the case of the on_abort hook (which is -expensive). - - -.. branch: pyparser-improvements - -Improve speed of Python parser, improve ParseError messages slightly. - -.. branch: ioctl-arg-size - -Work around possible bugs in upstream ioctl users, like CPython allocate at -least 1024 bytes for the arg in calls to ``ioctl(fd, request, arg)``. Fixes -issue #2776 - -.. branch: cpyext-subclass-setattr - -Fix for python-level classes that inherit from C-API types, previously the -`w_obj` was not necessarily preserved throughout the lifetime of the `pyobj` -which led to cases where instance attributes were lost. Fixes issue #2793 - - -.. branch: pyparser-improvements-2 - -Improve line offsets that are reported by SyntaxError. Improve error messages -for a few situations, including mismatched parenthesis. - -.. branch: issue2752 - -Fix a rare GC bug that was introduced more than one year ago, but was -not diagnosed before issue #2752. - -.. branch: gc-hooks - -Introduce GC hooks, as documented in doc/gc_info.rst - -.. branch: gc-hook-better-timestamp - -Improve GC hooks - -.. branch: cppyy-packaging - -Update backend to 0.6.0 and support exceptions through wrappers +=========================== +What's new in PyPy2.7 5.10+ +=========================== + +.. this is a revision shortly after release-pypy2.7-v5.10.0 +.. startrev: 6b024edd9d12 + +.. branch: cpyext-avoid-roundtrip + +Big refactoring of some cpyext code, which avoids a lot of nonsense when +calling C from Python and vice-versa: the result is a big speedup in +function/method calls, up to 6 times faster. + +.. branch: cpyext-datetime2 + +Support ``tzinfo`` field on C-API datetime objects, fixes latest pandas HEAD + + +.. branch: mapdict-size-limit + +Fix a corner case of mapdict: When an instance is used like a dict (using +``setattr`` and ``getattr``, or ``.__dict__``) and a lot of attributes are +added, then the performance using mapdict is linear in the number of +attributes. This is now fixed (by switching to a regular dict after 80 +attributes). + + +.. branch: cpyext-faster-arg-passing + +When using cpyext, improve the speed of passing certain objects from PyPy to C +code, most notably None, True, False, types, all instances of C-defined types. +Before, a dict lookup was needed every time such an object crossed over, now it +is just a field read. + + +.. branch: 2634_datetime_timedelta_performance + +Improve datetime + timedelta performance. + +.. branch: memory-accounting + +Improve way to describe memory + +.. branch: msvc14 + +Allow compilaiton with Visual Studio 2017 compiler suite on windows + +.. branch: refactor-slots + +Refactor cpyext slots. + + +.. branch: call-loopinvariant-into-bridges + +Speed up branchy code that does a lot of function inlining by saving one call +to read the TLS in most bridges. + +.. branch: rpython-sprint + +Refactor in rpython signatures + +.. branch: cpyext-tls-operror2 + +Store error state thread-locally in executioncontext, fixes issue #2764 + +.. branch: cpyext-fast-typecheck + +Optimize `Py*_Check` for `Bool`, `Float`, `Set`. Also refactor and simplify +`W_PyCWrapperObject` which is used to call slots from the C-API, greatly +improving microbenchmarks in https://github.com/antocuni/cpyext-benchmarks + + +.. branch: fix-sre-problems + +Fix two (unrelated) JIT bugs manifesting in the re module: + +- green fields are broken and were thus disabled, plus their usage removed from + the _sre implementation + +- in rare "trace is too long" situations, the JIT could break behaviour + arbitrarily. + +.. branch: jit-hooks-can-be-disabled + +Be more efficient about JIT hooks. Make it possible for the frontend to declare +that jit hooks are currently not enabled at all. in that case, the list of ops +does not have to be created in the case of the on_abort hook (which is +expensive). + + +.. branch: pyparser-improvements + +Improve speed of Python parser, improve ParseError messages slightly. + +.. branch: ioctl-arg-size + +Work around possible bugs in upstream ioctl users, like CPython allocate at +least 1024 bytes for the arg in calls to ``ioctl(fd, request, arg)``. Fixes +issue #2776 + +.. branch: cpyext-subclass-setattr + +Fix for python-level classes that inherit from C-API types, previously the +`w_obj` was not necessarily preserved throughout the lifetime of the `pyobj` +which led to cases where instance attributes were lost. Fixes issue #2793 + + +.. branch: pyparser-improvements-2 + +Improve line offsets that are reported by SyntaxError. Improve error messages +for a few situations, including mismatched parenthesis. + +.. branch: issue2752 + +Fix a rare GC bug that was introduced more than one year ago, but was +not diagnosed before issue #2752. + +.. branch: gc-hooks + +Introduce GC hooks, as documented in doc/gc_info.rst + +.. branch: gc-hook-better-timestamp + +Improve GC hooks + +.. branch: cppyy-packaging + +Update backend to 0.6.0 and support exceptions through wrappers diff --git a/pypy/doc/whatsnew-pypy2-7.0.0.rst b/pypy/doc/whatsnew-pypy2-7.0.0.rst --- a/pypy/doc/whatsnew-pypy2-7.0.0.rst +++ b/pypy/doc/whatsnew-pypy2-7.0.0.rst @@ -1,69 +1,69 @@ -========================== -What's new in PyPy2.7 6.0+ -========================== - -.. this is a revision shortly after release-pypy-6.0.0 -.. startrev: e50e11af23f1 - -.. branch: cppyy-packaging - -Main items: vastly better template resolution and improved performance. In -detail: upgrade to backend 1.4, improved handling of templated methods and -functions (in particular automatic deduction of types), improved pythonization -interface, range of compatibility fixes for Python3, free functions now take -fast libffi path when possible, moves for strings (incl. from Python str), -easier/faster handling of std::vector by numpy, improved and faster object -identity preservation - -.. branch: socket_default_timeout_blockingness - -Make sure 'blocking-ness' of socket is set along with default timeout - -.. branch: crypt_h - -Include crypt.h for crypt() on Linux - -.. branch: gc-more-logging - -Log additional gc-minor and gc-collect-step info in the PYPYLOG - -.. branch: reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb - - -.. branch: pyparser-improvements-3 - -Small refactorings in the Python parser. - -.. branch: fix-readme-typo - -.. branch: avoid_shell_injection_in_shutil - -Backport CPython fix for possible shell injection issue in `distutils.spawn`, -https://bugs.python.org/issue34540 - -.. branch: cffi_dlopen_unicode - -Enable use of unicode file names in `dlopen` - -.. branch: rlock-in-rpython - -Backport CPython fix for `thread.RLock` - - -.. branch: expose-gc-time - -Make GC hooks measure time in seconds (as opposed to an opaque unit). - -.. branch: cleanup-test_lib_pypy - -Update most test_lib_pypy/ tests and move them to extra_tests/. - -.. branch: gc-disable - -Make it possible to manually manage the GC by using a combination of -gc.disable() and gc.collect_step(). Make sure to write a proper release -announcement in which we explain that existing programs could leak memory if -they run for too much time between a gc.disable()/gc.enable() +========================== +What's new in PyPy2.7 6.0+ +========================== + +.. this is a revision shortly after release-pypy-6.0.0 +.. startrev: e50e11af23f1 + +.. branch: cppyy-packaging + +Main items: vastly better template resolution and improved performance. In +detail: upgrade to backend 1.4, improved handling of templated methods and +functions (in particular automatic deduction of types), improved pythonization +interface, range of compatibility fixes for Python3, free functions now take +fast libffi path when possible, moves for strings (incl. from Python str), +easier/faster handling of std::vector by numpy, improved and faster object +identity preservation + +.. branch: socket_default_timeout_blockingness + +Make sure 'blocking-ness' of socket is set along with default timeout + +.. branch: crypt_h + +Include crypt.h for crypt() on Linux + +.. branch: gc-more-logging + +Log additional gc-minor and gc-collect-step info in the PYPYLOG + +.. branch: reverse-debugger + +The reverse-debugger branch has been merged. For more information, see +https://bitbucket.org/pypy/revdb + + +.. branch: pyparser-improvements-3 + +Small refactorings in the Python parser. + +.. branch: fix-readme-typo + +.. branch: avoid_shell_injection_in_shutil + +Backport CPython fix for possible shell injection issue in `distutils.spawn`, +https://bugs.python.org/issue34540 + +.. branch: cffi_dlopen_unicode + +Enable use of unicode file names in `dlopen` + +.. branch: rlock-in-rpython + +Backport CPython fix for `thread.RLock` + + +.. branch: expose-gc-time + +Make GC hooks measure time in seconds (as opposed to an opaque unit). + +.. branch: cleanup-test_lib_pypy + +Update most test_lib_pypy/ tests and move them to extra_tests/. + +.. branch: gc-disable + +Make it possible to manually manage the GC by using a combination of +gc.disable() and gc.collect_step(). Make sure to write a proper release +announcement in which we explain that existing programs could leak memory if +they run for too much time between a gc.disable()/gc.enable() diff --git a/rpython/annotator/binaryop.py b/rpython/annotator/binaryop.py --- a/rpython/annotator/binaryop.py +++ b/rpython/annotator/binaryop.py @@ -727,6 +727,10 @@ return [get_setitem, op.simple_call(get_setitem.result, v_idx, v_value)] + at op.contains.register_transform(SomeInstance) +def contains_SomeInstance(annotator, v_ins, v_idx): + get_contains = op.getattr(v_ins, const('__contains__')) + return [get_contains, op.simple_call(get_contains.result, v_idx)] class __extend__(pairtype(SomeIterator, SomeIterator)): diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py --- a/rpython/annotator/test/test_annrpython.py +++ b/rpython/annotator/test/test_annrpython.py @@ -4065,6 +4065,20 @@ assert len(a.translator.graphs) == 2 # fn, __setitem__ assert isinstance(s, annmodel.SomeInteger) + def test_instance_contains(self): + class A(object): + def __contains__(self, i): + return i & 1 == 0 + + def fn(i): + a = A() + return 0 in a and 1 not in a + + a = self.RPythonAnnotator() + s = a.build_types(fn, [int]) + assert len(a.translator.graphs) == 2 # fn, __contains__ + assert isinstance(s, annmodel.SomeBool) + def test_instance_getslice(self): class A(object): def __getslice__(self, stop, start): diff --git a/rpython/jit/backend/llsupport/regalloc.py b/rpython/jit/backend/llsupport/regalloc.py --- a/rpython/jit/backend/llsupport/regalloc.py +++ b/rpython/jit/backend/llsupport/regalloc.py @@ -1,4 +1,4 @@ -import os +import sys from rpython.jit.metainterp.history import Const, REF, JitCellToken from rpython.rlib.objectmodel import we_are_translated, specialize from rpython.jit.metainterp.resoperation import rop, AbstractValue @@ -67,6 +67,7 @@ @specialize.arg(1) def foreach(self, function, arg): + # XXX unused? node = self.master_node while node is not None: function(arg, node.val) @@ -297,12 +298,12 @@ def is_still_alive(self, v): # Check if 'v' is alive at the current position. # Return False if the last usage is strictly before. - return self.longevity[v][1] >= self.position + return self.longevity[v].last_usage >= self.position def stays_alive(self, v): # Check if 'v' stays alive after the current position. # Return False if the last usage is before or at position. - return self.longevity[v][1] > self.position + return self.longevity[v].last_usage > self.position def next_instruction(self, incr=1): self.position += incr @@ -319,7 +320,7 @@ self._check_type(v) if isinstance(v, Const): return - if v not in self.longevity or self.longevity[v][1] <= self.position: + if v not in self.longevity or self.longevity[v].last_usage <= self.position: if v in self.reg_bindings: self.free_regs.append(self.reg_bindings[v]) del self.reg_bindings[v] @@ -355,7 +356,7 @@ for v in self.reg_bindings: if v not in self.longevity: llop.debug_print(lltype.Void, "variable %s not in longevity\n" % v.repr({})) - assert self.longevity[v][1] > self.position + assert self.longevity[v].last_usage > self.position def try_allocate_reg(self, v, selected_reg=None, need_lower_byte=False): """ Try to allocate a register, if we have one free. @@ -366,6 +367,9 @@ returns allocated register or None, if not possible. """ self._check_type(v) + if isinstance(v, TempVar): + self.longevity[v] = Lifetime(self.position, self.position) + # YYY all subtly similar code assert not isinstance(v, Const) if selected_reg is not None: res = self.reg_bindings.get(v, None) @@ -385,42 +389,55 @@ loc = self.reg_bindings.get(v, None) if loc is not None and loc not in self.no_lower_byte_regs: return loc - for i in range(len(self.free_regs) - 1, -1, -1): - reg = self.free_regs[i] - if reg not in self.no_lower_byte_regs: - if loc is not None: - self.free_regs[i] = loc - else: - del self.free_regs[i] - self.reg_bindings[v] = reg - return reg - return None + free_regs = [reg for reg in self.free_regs + if reg not in self.no_lower_byte_regs] + newloc = self.longevity.try_pick_free_reg( + self.position, v, free_regs) + if newloc is None: + return None + self.free_regs.remove(newloc) + if loc is not None: + self.free_regs.append(loc) + self.reg_bindings[v] = newloc + return newloc try: return self.reg_bindings[v] except KeyError: - if self.free_regs: - loc = self.free_regs.pop() - self.reg_bindings[v] = loc - return loc + loc = self.longevity.try_pick_free_reg( + self.position, v, self.free_regs) + if loc is None: + return None + self.reg_bindings[v] = loc + self.free_regs.remove(loc) + return loc - def _spill_var(self, v, forbidden_vars, selected_reg, + def _spill_var(self, forbidden_vars, selected_reg, need_lower_byte=False): - v_to_spill = self._pick_variable_to_spill(v, forbidden_vars, + v_to_spill = self._pick_variable_to_spill(forbidden_vars, selected_reg, need_lower_byte=need_lower_byte) loc = self.reg_bindings[v_to_spill] + self._sync_var_to_stack(v_to_spill) del self.reg_bindings[v_to_spill] - if self.frame_manager.get(v_to_spill) is None: - newloc = self.frame_manager.loc(v_to_spill) - self.assembler.regalloc_mov(loc, newloc) return loc - def _pick_variable_to_spill(self, v, forbidden_vars, selected_reg=None, - need_lower_byte=False): - """ Slightly less silly algorithm. - """ - cur_max_age = -1 + def _pick_variable_to_spill(self, forbidden_vars, selected_reg=None, + need_lower_byte=False, regs=None): + + # try to spill a variable that has no further real usages, ie that only + # appears in failargs or in a jump + # if that doesn't exist, spill the variable that has a real_usage that + # is the furthest away from the current position + + # YYY check for fixed variable usages + if regs is None: + regs = self.reg_bindings.keys() + + cur_max_use_distance = -1 + position = self.position candidate = None - for next in self.reg_bindings: + cur_max_age_failargs = -1 + candidate_from_failargs = None + for next in regs: reg = self.reg_bindings[next] if next in forbidden_vars: continue @@ -431,13 +448,25 @@ continue if need_lower_byte and reg in self.no_lower_byte_regs: continue - max_age = self.longevity[next][1] - if cur_max_age < max_age: - cur_max_age = max_age - candidate = next - if candidate is None: - raise NoVariableToSpill - return candidate + lifetime = self.longevity[next] + if lifetime.is_last_real_use_before(position): + # this variable has no "real" use as an argument to an op left + # it is only used in failargs, and maybe in a jump. spilling is + # fine + max_age = lifetime.last_usage + if cur_max_age_failargs < max_age: + cur_max_age_failargs = max_age + candidate_from_failargs = next + else: + use_distance = lifetime.next_real_usage(position) - position + if cur_max_use_distance < use_distance: + cur_max_use_distance = use_distance + candidate = next + if candidate_from_failargs is not None: + return candidate_from_failargs + if candidate is not None: + return candidate + raise NoVariableToSpill def force_allocate_reg(self, v, forbidden_vars=[], selected_reg=None, need_lower_byte=False): @@ -450,12 +479,12 @@ """ self._check_type(v) if isinstance(v, TempVar): - self.longevity[v] = (self.position, self.position) + self.longevity[v] = Lifetime(self.position, self.position) loc = self.try_allocate_reg(v, selected_reg, need_lower_byte=need_lower_byte) if loc: return loc - loc = self._spill_var(v, forbidden_vars, selected_reg, + loc = self._spill_var(forbidden_vars, selected_reg, need_lower_byte=need_lower_byte) prev_loc = self.reg_bindings.get(v, None) if prev_loc is not None: @@ -468,7 +497,7 @@ self.bindings_to_frame_reg[v] = None def force_spill_var(self, var): - self._sync_var(var) + self._sync_var_to_stack(var) try: loc = self.reg_bindings[var] del self.reg_bindings[var] @@ -502,7 +531,7 @@ if selected_reg in self.free_regs: self.assembler.regalloc_mov(immloc, selected_reg) return selected_reg - loc = self._spill_var(v, forbidden_vars, selected_reg) + loc = self._spill_var(forbidden_vars, selected_reg) self.free_regs.append(loc) self.assembler.regalloc_mov(immloc, loc) return loc @@ -523,6 +552,7 @@ loc = self.force_allocate_reg(v, forbidden_vars, selected_reg, need_lower_byte=need_lower_byte) if prev_loc is not loc: + self.assembler.num_reloads += 1 self.assembler.regalloc_mov(prev_loc, loc) return loc @@ -530,15 +560,7 @@ reg = self.reg_bindings[from_v] del self.reg_bindings[from_v] self.reg_bindings[to_v] = reg - - def _move_variable_away(self, v, prev_loc): - if self.free_regs: - loc = self.free_regs.pop() - self.reg_bindings[v] = loc - self.assembler.regalloc_mov(prev_loc, loc) - else: - loc = self.frame_manager.loc(v) - self.assembler.regalloc_mov(prev_loc, loc) + return reg def force_result_in_reg(self, result_v, v, forbidden_vars=[]): """ Make sure that result is in the same register as v. @@ -548,41 +570,39 @@ self._check_type(result_v) self._check_type(v) if isinstance(v, Const): - if self.free_regs: - loc = self.free_regs.pop() - else: - loc = self._spill_var(v, forbidden_vars, None) - self.assembler.regalloc_mov(self.convert_to_imm(v), loc) - self.reg_bindings[result_v] = loc - return loc - if v not in self.reg_bindings: - # v not in a register. allocate one for result_v and move v there - prev_loc = self.frame_manager.loc(v) - loc = self.force_allocate_reg(result_v, forbidden_vars) - self.assembler.regalloc_mov(prev_loc, loc) - return loc - if self.longevity[v][1] > self.position: - # we need to find a new place for variable v and - # store result in the same place - loc = self.reg_bindings[v] - del self.reg_bindings[v] - if self.frame_manager.get(v) is None: - self._move_variable_away(v, loc) - self.reg_bindings[result_v] = loc - else: - self._reallocate_from_to(v, result_v) - loc = self.reg_bindings[result_v] - return loc + result_loc = self.force_allocate_reg(result_v, forbidden_vars) + self.assembler.regalloc_mov(self.convert_to_imm(v), result_loc) + return result_loc + v_keeps_living = self.longevity[v].last_usage > self.position + # there are two cases where we should allocate a new register for + # result: + # 1) v is itself not in a register + # 2) v keeps on being live. if there is a free register, we need a move + # anyway, so we can use force_allocate_reg on result_v to make sure any + # fixed registers are used + if (v not in self.reg_bindings or (v_keeps_living and self.free_regs)): + v_loc = self.loc(v) + result_loc = self.force_allocate_reg(result_v, forbidden_vars) + self.assembler.regalloc_mov(v_loc, result_loc) + return result_loc + if v_keeps_living: + # since there are no free registers, v needs to go to the stack. + # sync it there. + self._sync_var_to_stack(v) + return self._reallocate_from_to(v, result_v) - def _sync_var(self, v): + def _sync_var_to_stack(self, v): + self.assembler.num_spills += 1 if not self.frame_manager.get(v): reg = self.reg_bindings[v] to = self.frame_manager.loc(v) self.assembler.regalloc_mov(reg, to) + else: + self.assembler.num_spills_to_existing += 1 # otherwise it's clean def _bc_spill(self, v, new_free_regs): - self._sync_var(v) + self._sync_var_to_stack(v) new_free_regs.append(self.reg_bindings.pop(v)) def before_call(self, force_store=[], save_all_regs=0): @@ -650,7 +670,7 @@ move_or_spill = [] for v, reg in self.reg_bindings.items(): - max_age = self.longevity[v][1] + max_age = self.longevity[v].last_usage if v not in force_store and max_age <= self.position: # variable dies del self.reg_bindings[v] @@ -671,45 +691,32 @@ else: # this is a register like eax/rax, which needs either # spilling or moving. - move_or_spill.append((v, max_age)) + move_or_spill.append(v) if len(move_or_spill) > 0: - while len(self.free_regs) > 0: - new_reg = self.free_regs.pop() - if new_reg in self.save_around_call_regs: - new_free_regs.append(new_reg) # not this register... - continue - # This 'new_reg' is suitable for moving a candidate to. - # Pick the one with the smallest max_age. (This - # is one step of a naive sorting algo, slow in theory, - # but the list should always be very small so it - # doesn't matter.) - best_i = 0 - smallest_max_age = move_or_spill[0][1] - for i in range(1, len(move_or_spill)): - max_age = move_or_spill[i][1] - if max_age < smallest_max_age: - best_i = i - smallest_max_age = max_age - v, max_age = move_or_spill.pop(best_i) - # move from 'reg' to 'new_reg' + free_regs = [reg for reg in self.free_regs + if reg not in self.save_around_call_regs] + # chose which to spill using the usual spill heuristics + while len(move_or_spill) > len(free_regs): + v = self._pick_variable_to_spill([], regs=move_or_spill) + self._bc_spill(v, new_free_regs) + move_or_spill.remove(v) + assert len(move_or_spill) <= len(free_regs) + for v in move_or_spill: + # search next good reg + new_reg = None + while True: + new_reg = self.free_regs.pop() + if new_reg in self.save_around_call_regs: + new_free_regs.append(new_reg) # not this register... + continue + break + assert new_reg is not None # must succeed reg = self.reg_bindings[v] - if not we_are_translated(): - if move_or_spill: - assert max_age <= min([_a for _, _a in move_or_spill]) - assert reg in save_sublist - assert reg in self.save_around_call_regs - assert new_reg not in self.save_around_call_regs + self.assembler.num_moves_calls += 1 self.assembler.regalloc_mov(reg, new_reg) self.reg_bindings[v] = new_reg # change the binding new_free_regs.append(reg) - # - if len(move_or_spill) == 0: - break - else: - # no more free registers to move to, spill the rest - for v, max_age in move_or_spill: - self._bc_spill(v, new_free_regs) # re-add registers in 'new_free_regs', but in reverse order, # so that the last ones (added just above, from @@ -772,7 +779,7 @@ # of COND_CALL don't accept a cc as input if next_op.getarg(0) is not op: return False - if self.longevity[op][1] > i + 1: + if self.longevity[op].last_usage > i + 1: return False if opnum != rop.COND_CALL: if op in operations[i + 1].getfailargs(): @@ -786,61 +793,295 @@ descr = op.getdescr() assert isinstance(descr, JitCellToken) if op.numargs() == 2: - self.rm._sync_var(op.getarg(1)) + self.rm._sync_var_to_stack(op.getarg(1)) return [self.loc(op.getarg(0)), self.fm.loc(op.getarg(1))] else: assert op.numargs() == 1 return [self.loc(op.getarg(0))] +# ____________________________________________________________ + + + +UNDEF_POS = -42 + +class Lifetime(object): + def __init__(self, definition_pos=UNDEF_POS, last_usage=UNDEF_POS): + # all positions are indexes into the operations list + + # the position where the variable is defined + self.definition_pos = definition_pos + # the position where the variable is last used. this includes failargs + # and jumps + self.last_usage = last_usage + + # *real* usages, ie as an argument to an operation (as opposed to jump + # arguments or in failargs) + self.real_usages = None + + # fixed registers are positions where the variable *needs* to be in a + # specific register + self.fixed_positions = None + + # another Lifetime that lives after the current one that would like to + # share a register with this variable + self.share_with = None + + # the other lifetime will have this variable set to self.definition_pos + self._definition_pos_shared = UNDEF_POS + + def last_usage_including_sharing(self): + while self.share_with is not None: + self = self.share_with + return self.last_usage + + def is_last_real_use_before(self, position): + if self.real_usages is None: + return True + return self.real_usages[-1] <= position + + def next_real_usage(self, position): + assert position >= self.definition_pos + # binary search + l = self.real_usages + low = 0 + high = len(l) + if position >= l[-1]: + return -1 + while low < high: + mid = low + (high - low) // 2 # no overflow ;-) + if position < l[mid]: + high = mid + else: + low = mid + 1 + return l[low] + + def definition_pos_shared(self): + if self._definition_pos_shared != UNDEF_POS: + return self._definition_pos_shared + else: + return self.definition_pos + + def fixed_register(self, position, reg): + """ registers a fixed register use for the variable at position in + register reg. returns the position from where on the register should be + held free. """ + assert self.definition_pos <= position <= self.last_usage + if self.fixed_positions is None: + self.fixed_positions = [] + res = self.definition_pos_shared() + else: + assert position > self.fixed_positions[-1][0] + res = self.fixed_positions[-1][0] + self.fixed_positions.append((position, reg)) + return res + + def find_fixed_register(self, opindex): + # XXX could use binary search + if self.fixed_positions is not None: + for (index, reg) in self.fixed_positions: + if opindex <= index: + return reg + if self.share_with is not None: + return self.share_with.find_fixed_register(opindex) + + def _check_invariants(self): + assert self.definition_pos <= self.last_usage + if self.real_usages is not None: + assert sorted(self.real_usages) == self.real_usages + assert self.last_usage >= max(self.real_usages) + assert self.definition_pos < min(self.real_usages) + + def __repr__(self): + if self.fixed_positions: + s = " " + ", ".join("@%s in %s" % (index, reg) for (index, reg) in self.fixed_positions) + else: + s = "" + return "%s:%s(%s)%s" % (self.definition_pos, self.real_usages, self.last_usage, s) + + +class FixedRegisterPositions(object): + def __init__(self, register): + self.register = register + + self.index_lifetimes = [] + + def fixed_register(self, opindex, definition_pos): + if self.index_lifetimes: + assert opindex > self.index_lifetimes[-1][0] + self.index_lifetimes.append((opindex, definition_pos)) + + def free_until_pos(self, opindex): + # XXX could use binary search + for (index, definition_pos) in self.index_lifetimes: + if opindex <= index: + if definition_pos >= opindex: + return definition_pos + else: + # the variable doesn't exist or didn't make it into the + # register despite being defined already. so we don't care + # too much, and can say that the variable is free until + # index + return index + return sys.maxint + + def __repr__(self): + return "%s: fixed at %s" % (self.register, self.index_lifetimes) + + +class LifetimeManager(object): + def __init__(self, longevity): + self.longevity = longevity + + # dictionary maps register to FixedRegisterPositions + self.fixed_register_use = {} + + def fixed_register(self, opindex, register, var=None): + """ Tell the LifetimeManager that variable var *must* be in register at + operation opindex. var can be None, if no variable at all can be in + that register at the point.""" + if var is None: + definition_pos = opindex + else: + varlifetime = self.longevity[var] + definition_pos = varlifetime.fixed_register(opindex, register) + if register not in self.fixed_register_use: + self.fixed_register_use[register] = FixedRegisterPositions(register) + self.fixed_register_use[register].fixed_register(opindex, definition_pos) + + def try_use_same_register(self, v0, v1): + """ Try to arrange things to put v0 and v1 into the same register. + v0 must be defined before v1""" + # only works in limited situations now + longevityvar0 = self[v0] + longevityvar1 = self[v1] + assert longevityvar0.definition_pos < longevityvar1.definition_pos + if longevityvar0.last_usage != longevityvar1.definition_pos: + return # not supported for now + longevityvar0.share_with = longevityvar1 + longevityvar1._definition_pos_shared = longevityvar0.definition_pos_shared() + + def longest_free_reg(self, position, free_regs): + """ for every register in free_regs, compute how far into the future + that register can remain free, according to the constraints of the + fixed registers. Find the register that is free the longest. Return a + tuple (reg, free_until_pos). """ + free_until_pos = {} + max_free_pos = position + best_reg = None + # reverse for compatibility with old code + for i in range(len(free_regs) - 1, -1, -1): + reg = free_regs[i] + fixed_reg_pos = self.fixed_register_use.get(reg, None) + if fixed_reg_pos is None: + return reg, sys.maxint + else: + free_until_pos = fixed_reg_pos.free_until_pos(position) + if free_until_pos > max_free_pos: + best_reg = reg + max_free_pos = free_until_pos + return best_reg, max_free_pos + + def free_reg_whole_lifetime(self, position, v, free_regs): + """ try to find a register from free_regs for v at position that's + free for the whole lifetime of v. pick the one that is blocked first + *after* the lifetime of v. """ + longevityvar = self[v] + min_fixed_use_after = sys.maxint + best_reg = None + unfixed_reg = None + for reg in free_regs: + fixed_reg_pos = self.fixed_register_use.get(reg, None) + if fixed_reg_pos is None: + unfixed_reg = reg + continue + use_after = fixed_reg_pos.free_until_pos(position) + if use_after <= longevityvar.last_usage_including_sharing(): + # can't fit + continue + assert use_after >= longevityvar.last_usage_including_sharing() + if use_after < min_fixed_use_after: + best_reg = reg + min_fixed_use_after = use_after + if best_reg is not None: + return best_reg + + # no fitting fixed registers. pick a non-fixed one + return unfixed_reg + + def try_pick_free_reg(self, position, v, free_regs): + if not free_regs: + return None + longevityvar = self[v] + # check whether there is a fixed register and whether it's free + reg = longevityvar.find_fixed_register(position) + if reg is not None and reg in free_regs: + return reg + + # try to find a register that's free for the whole lifetime of v + # pick the one that is blocked first *after* the lifetime of v + loc = self.free_reg_whole_lifetime(position, v, free_regs) + if loc is not None: + return loc + + # can't fit v completely, so pick the register that's free the longest + loc, free_until = self.longest_free_reg(position, free_regs) + if loc is not None: + return loc + # YYY could check whether it's best to spill v here, but hard + # to do in the current system + return None + + def __contains__(self, var): + return var in self.longevity + + def __getitem__(self, var): + return self.longevity[var] + + def __setitem__(self, var, val): + self.longevity[var] = val + def compute_vars_longevity(inputargs, operations): - # compute a dictionary that maps variables to index in - # operations that is a "last-time-seen" - - # returns a pair longevity/useful. Non-useful variables are ones that - # never appear in the assembler or it does not matter if they appear on - # stack or in registers. Main example is loop arguments that go - # only to guard operations or to jump or to finish - last_used = {} - last_real_usage = {} + # compute a dictionary that maps variables to Lifetime information + # if a variable is not in the dictionary, it's operation is dead because + # it's side-effect-free and the result is unused + longevity = {} for i in range(len(operations)-1, -1, -1): op = operations[i] - if op.type != 'v': - if op not in last_used and rop.has_no_side_effect(op.opnum): + opnum = op.getopnum() + if op not in longevity: + if op.type != 'v' and rop.has_no_side_effect(opnum): + # result not used, operation has no side-effect, it can be + # removed continue - opnum = op.getopnum() + longevity[op] = Lifetime(definition_pos=i, last_usage=i) + else: + longevity[op].definition_pos = i for j in range(op.numargs()): arg = op.getarg(j) if isinstance(arg, Const): continue - if arg not in last_used: - last_used[arg] = i + if arg not in longevity: + lifetime = longevity[arg] = Lifetime(last_usage=i) + else: + lifetime = longevity[arg] if opnum != rop.JUMP and opnum != rop.LABEL: - if arg not in last_real_usage: - last_real_usage[arg] = i + if lifetime.real_usages is None: + lifetime.real_usages = [] + lifetime.real_usages.append(i) if rop.is_guard(op.opnum): for arg in op.getfailargs(): if arg is None: # hole continue assert not isinstance(arg, Const) - if arg not in last_used: - last_used[arg] = i + if arg not in longevity: + longevity[arg] = Lifetime(last_usage=i) # - longevity = {} - for i, arg in enumerate(operations): - if arg.type != 'v' and arg in last_used: - assert not isinstance(arg, Const) - assert i < last_used[arg] - longevity[arg] = (i, last_used[arg]) - del last_used[arg] for arg in inputargs: assert not isinstance(arg, Const) - if arg not in last_used: - longevity[arg] = (-1, -1) - else: - longevity[arg] = (0, last_used[arg]) - del last_used[arg] - assert len(last_used) == 0 + if arg not in longevity: + longevity[arg] = Lifetime(-1, -1) if not we_are_translated(): produced = {} @@ -851,9 +1092,15 @@ if not isinstance(arg, Const): assert arg in produced produced[op] = None - - return longevity, last_real_usage + for lifetime in longevity.itervalues(): + if lifetime.real_usages is not None: + lifetime.real_usages.reverse() + if not we_are_translated(): + lifetime._check_invariants() + return LifetimeManager(longevity) + +# YYY unused? def is_comparison_or_ovf_op(opnum): return rop.is_comparison(opnum) or rop.is_ovf(opnum) diff --git a/rpython/jit/backend/llsupport/test/test_regalloc.py b/rpython/jit/backend/llsupport/test/test_regalloc.py --- a/rpython/jit/backend/llsupport/test/test_regalloc.py +++ b/rpython/jit/backend/llsupport/test/test_regalloc.py @@ -1,9 +1,19 @@ import py +import sys from rpython.jit.metainterp.history import ConstInt, INT, FLOAT -from rpython.jit.backend.llsupport.regalloc import FrameManager, LinkedList -from rpython.jit.backend.llsupport.regalloc import RegisterManager as BaseRegMan +from rpython.jit.metainterp.history import BasicFailDescr, TargetToken +from rpython.jit.metainterp.resoperation import rop from rpython.jit.metainterp.resoperation import InputArgInt, InputArgRef,\ InputArgFloat +from rpython.jit.backend.detect_cpu import getcpuclass +from rpython.jit.backend.llsupport.regalloc import FrameManager, LinkedList +from rpython.jit.backend.llsupport.regalloc import RegisterManager as BaseRegMan,\ + Lifetime as RealLifetime, UNDEF_POS, BaseRegalloc, compute_vars_longevity,\ + LifetimeManager +from rpython.jit.tool.oparser import parse +from rpython.jit.codewriter.effectinfo import EffectInfo +from rpython.rtyper.lltypesystem import lltype +from rpython.rtyper.annlowlevel import llhelper def newboxes(*values): return [InputArgInt(v) for v in values] @@ -11,33 +21,61 @@ def newrefboxes(count): return [InputArgRef() for _ in range(count)] +def Lifetime(definition_pos=UNDEF_POS, last_usage=UNDEF_POS, + real_usages=UNDEF_POS): + if real_usages == UNDEF_POS: + real_usages = last_usage + lifetime = RealLifetime(definition_pos, last_usage) + if isinstance(real_usages, int): + real_usages = [real_usages] + lifetime.real_usages = real_usages + return lifetime + + def boxes_and_longevity(num): res = [] longevity = {} for i in range(num): box = InputArgInt(0) res.append(box) - longevity[box] = (0, 1) + longevity[box] = Lifetime(0, 1) return res, longevity class FakeReg(object): def __init__(self, i): self.n = i + def _getregkey(self): + return self.n + def is_memory_reference(self): + return False def __repr__(self): return 'r%d' % self.n r0, r1, r2, r3 = [FakeReg(i) for i in range(4)] +r4, r5, r6, r7, r8, r9 = [FakeReg(i) for i in range(4, 10)] + regs = [r0, r1, r2, r3] class RegisterManager(BaseRegMan): all_regs = regs + + def __init__(self, longevity, frame_manager=None, assembler=None): + if isinstance(longevity, dict): + longevity = LifetimeManager(longevity) + BaseRegMan.__init__(self, longevity, frame_manager, assembler) + def convert_to_imm(self, v): return v class FakeFramePos(object): def __init__(self, pos, box_type): self.pos = pos + self.value = pos self.box_type = box_type + def _getregkey(self): + return ~self.value + def is_memory_reference(self): + return True def __repr__(self): return 'FramePos<%d,%s>' % (self.pos, self.box_type) def __eq__(self, other): @@ -66,17 +104,311 @@ assert isinstance(loc, FakeFramePos) return loc.pos +class FakeCPU(object): + def get_baseofs_of_frame_field(self): + return 0 + class MockAsm(object): def __init__(self): self.moves = [] - + self.emitted = [] + self.cpu = FakeCPU() + + # XXX register allocation statistics to be removed later + self.num_moves_calls = 0 + self.num_moves_jump = 0 + self.num_spills = 0 + self.num_spills_to_existing = 0 + self.num_reloads = 0 + + self.preamble_num_moves_calls = 0 + self.preamble_num_moves_jump = 0 + self.preamble_num_spills = 0 + self.preamble_num_spills_to_existing = 0 + self.preamble_num_reloads = 0 + def regalloc_mov(self, from_loc, to_loc): self.moves.append((from_loc, to_loc)) + self.emitted.append(("move", to_loc, from_loc)) + + +def test_lifetime_next_real_usage(): + lt = RealLifetime(0, 1000) + lt.real_usages = [0, 1, 5, 10, 24, 35, 55, 56, 57, 90, 92, 100] + for i in range(100): + next = lt.next_real_usage(i) + assert next in lt.real_usages + assert next > i + assert lt.real_usages[lt.real_usages.index(next) - 1] <= i + assert lt.next_real_usage(100) == -1 + assert lt.next_real_usage(101) == -1 + +def test_fixed_position(): + b0, b1, b2 = newboxes(0, 0, 0) + l0 = Lifetime(0, 5) + l1 = Lifetime(2, 9) + l2 = Lifetime(0, 9) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(1, r0, b0) + longevity.fixed_register(4, r2, b0) + longevity.fixed_register(5, r1, b1) + longevity.fixed_register(8, r1, b1) + + assert l0.fixed_positions == [(1, r0), (4, r2)] + assert l1.fixed_positions == [(5, r1), (8, r1)] + assert l2.fixed_positions is None + + fpr0 = longevity.fixed_register_use[r0] + fpr1 = longevity.fixed_register_use[r1] + fpr2 = longevity.fixed_register_use[r2] + assert r3 not in longevity.fixed_register_use + assert fpr0.index_lifetimes == [(1, 0)] + assert fpr1.index_lifetimes == [(5, 2), (8, 5)] + assert fpr2.index_lifetimes == [(4, 1)] + +def test_fixed_position_none(): + b0, b1, b2 = newboxes(0, 0, 0) + l0 = Lifetime(0, 5) + l1 = Lifetime(2, 9) + l2 = Lifetime(0, 9) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(1, r0) + longevity.fixed_register(4, r2) + longevity.fixed_register(5, r1) + longevity.fixed_register(8, r1) + + fpr0 = longevity.fixed_register_use[r0] + fpr1 = longevity.fixed_register_use[r1] + fpr2 = longevity.fixed_register_use[r2] + assert r3 not in longevity.fixed_register_use + assert fpr0.index_lifetimes == [(1, 1)] + assert fpr1.index_lifetimes == [(5, 5), (8, 8)] + assert fpr2.index_lifetimes == [(4, 4)] + + +def test_free_until_pos_none(): + longevity = LifetimeManager({}) + longevity.fixed_register(5, r1, None) + longevity.fixed_register(8, r1, None) + longevity.fixed_register(35, r1, None) + + fpr1 = longevity.fixed_register_use[r1] + + assert fpr1.free_until_pos(0) == 5 + assert fpr1.free_until_pos(1) == 5 + assert fpr1.free_until_pos(2) == 5 + assert fpr1.free_until_pos(3) == 5 + assert fpr1.free_until_pos(4) == 5 + assert fpr1.free_until_pos(5) == 5 + assert fpr1.free_until_pos(10) == 35 + assert fpr1.free_until_pos(20) == 35 + assert fpr1.free_until_pos(30) == 35 + assert fpr1.free_until_pos(36) == sys.maxint + +def test_free_until_pos(): + b0, b1, b2 = newboxes(0, 0, 0) + l0 = Lifetime(0, 5) + l1 = Lifetime(2, 9) + l2 = Lifetime(30, 40) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(5, r1, b1) + longevity.fixed_register(8, r1, b1) + longevity.fixed_register(35, r1, b2) + + fpr1 = longevity.fixed_register_use[r1] + + # simple cases: we are before the beginning of the lifetime of the variable + # in the fixed register, then it's free until the definition of the + # variable + assert fpr1.free_until_pos(0) == 2 + assert fpr1.free_until_pos(1) == 2 + assert fpr1.free_until_pos(2) == 2 + assert fpr1.free_until_pos(10) == 30 + assert fpr1.free_until_pos(20) == 30 + assert fpr1.free_until_pos(30) == 30 + + # after the fixed use, we are fine anyway + assert fpr1.free_until_pos(36) == sys.maxint + assert fpr1.free_until_pos(50) == sys.maxint + + # asking for a position *after* the definition of the variable in the fixed + # register means the variable didn't make it into the fixed register, but + # at the latest by the use point it will have to go there + assert fpr1.free_until_pos(3) == 5 + assert fpr1.free_until_pos(4) == 5 + assert fpr1.free_until_pos(5) == 5 + assert fpr1.free_until_pos(6) == 8 + assert fpr1.free_until_pos(7) == 8 + assert fpr1.free_until_pos(8) == 8 + assert fpr1.free_until_pos(31) == 35 + assert fpr1.free_until_pos(32) == 35 + assert fpr1.free_until_pos(33) == 35 + assert fpr1.free_until_pos(34) == 35 + assert fpr1.free_until_pos(35) == 35 + +def test_free_until_pos_different_regs(): + b0, b1, b2 = newboxes(0, 0, 0) + l0 = Lifetime(0, 5) + l1 = Lifetime(2, 9) + l2 = Lifetime(30, 40) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(1, r0, b0) + longevity.fixed_register(4, r2, b0) + fpr2 = longevity.fixed_register_use[r2] + # the definition of b0 is before the other fixed register use of r0, so the + # earliest b0 can be in r2 is that use point at index 1 + assert fpr2.free_until_pos(0) == 1 + + +def test_longest_free_reg(): + b0, b1, b2 = newboxes(0, 0, 0) + l0 = Lifetime(0, 5) + l1 = Lifetime(2, 9) + l2 = Lifetime(30, 40) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(1, r0, b0) + longevity.fixed_register(4, r2, b0) + longevity.fixed_register(5, r1, b1) + longevity.fixed_register(8, r1, b1) + longevity.fixed_register(35, r1, b2) + + assert longevity.longest_free_reg(0, [r0, r1, r2]) == (r1, 2) + +def test_try_pick_free_reg(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(0, 4) + l1 = Lifetime(2, 20) + l2 = Lifetime(6, 20) + l3 = Lifetime(8, 20) + l4 = Lifetime(0, 10) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2, b3: l3, b4: l4}) + longevity.fixed_register(3, r1, b1) + longevity.fixed_register(7, r2, b2) + longevity.fixed_register(9, r3, b3) + + # a best fit + loc = longevity.try_pick_free_reg(0, b0, [r1, r2, r3, r4, r5]) + assert loc is r2 + + # does not fit into any of the fixed regs, use a non-fixed one + loc = longevity.try_pick_free_reg(0, b4, [r5, r2, r3, r4, r1]) + assert loc in [r4, r5] + + # all available are fixed but var doesn't fit completely into any of these. + # pick the biggest interval + loc = longevity.try_pick_free_reg(0, b4, [r1, r2, r3]) + assert loc is r3 + +def test_try_pick_free_reg_bug(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(10, 30) + l1 = Lifetime(0, 15) + longevity = LifetimeManager({b0: l0, b1: l1}) + longevity.fixed_register(20, r0, b0) + + # does not fit into r0, use r1 + loc = longevity.try_pick_free_reg(0, b1, [r0, r1]) + assert loc == r1 + +def test_try_pick_free_reg_bug2(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(1, 2) + l1 = Lifetime(2, 4) + longevity = LifetimeManager({b0: l0, b1: l1}) + longevity.fixed_register(4, r1, b1) + + # does not fit into r0, use r1 + loc = longevity.try_pick_free_reg(0, b0, [r0, r1]) + assert loc == r0 + +def test_simple_coalescing(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(0, 4) + l1 = Lifetime(4, 20) + l2 = Lifetime(4, 20) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(10, r1, b1) + longevity.fixed_register(10, r2, b2) + longevity.try_use_same_register(b0, b2) + + loc = longevity.try_pick_free_reg(0, b0, [r0, r1, r2, r3, r4]) + assert loc is r2 + +def test_coalescing_blocks_regs_correctly(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(10, 30) + l1 = Lifetime(30, 40) + l2 = Lifetime(30, 40) + l3 = Lifetime(0, 15) + l4 = Lifetime(0, 5) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2, b3: l3, b4: l4}) + longevity.try_use_same_register(b0, b1) + longevity.fixed_register(35, r1, b1) + longevity.fixed_register(35, r2, b2) + + loc = longevity.try_pick_free_reg(0, b3, [r1, r2]) + # r2 is picked, otherwise b0 can't end up in r1 + assert loc is r2 + + loc = longevity.try_pick_free_reg(0, b4, [r1, r2]) + # r1 is picked, because b4 fits before b0 + assert loc is r1 + +def test_coalescing_non_fixed_regs(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(0, 10) + l1 = Lifetime(10, 20) + l2 = Lifetime(25, 40) + l3 = Lifetime(15, 40) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2, b3: l3}) + longevity.try_use_same_register(b0, b1) + longevity.fixed_register(35, r2, b2) + longevity.fixed_register(35, r3, b3) + + loc = longevity.try_pick_free_reg(0, b0, [r1, r2, r3]) + # r2 is picked, otherwise b1 can't end up in the same reg as b0 + assert loc is r2 + + +def test_chained_coalescing(): + # 5 + b4 + # | + # 10 + b0 | + # | | + # | 15 + + # | + # + + # 20 + # + b1 + # | + # | + # | + # + + # 30 + # + b2 + # | + # r1 * + # | + # + + # 40 + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(10, 20) + l1 = Lifetime(20, 30) + l2 = Lifetime(30, 40) + l4 = Lifetime(5, 15) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2, b4: l4}) + longevity.try_use_same_register(b0, b1) + longevity.try_use_same_register(b1, b2) + longevity.fixed_register(35, r1, b2) + + loc = longevity.try_pick_free_reg(5, b4, [r0, r1]) + assert loc is r0 + class TestRegalloc(object): def test_freeing_vars(self): b0, b1, b2 = newboxes(0, 0, 0) - longevity = {b0: (0, 1), b1: (0, 2), b2: (0, 2)} + longevity = {b0: Lifetime(0, 1), b1: Lifetime(0, 2), b2: Lifetime(0, 2)} rm = RegisterManager(longevity) rm.next_instruction() for b in b0, b1, b2: @@ -99,7 +431,7 @@ rm._check_invariants() assert len(rm.free_regs) == 4 assert len(rm.reg_bindings) == 0 - + def test_register_exhaustion(self): boxes, longevity = boxes_and_longevity(5) rm = RegisterManager(longevity) @@ -115,7 +447,7 @@ class XRegisterManager(RegisterManager): no_lower_byte_regs = [r2, r3] - + rm = XRegisterManager(longevity) rm.next_instruction() loc0 = rm.try_allocate_reg(b0, need_lower_byte=True) @@ -150,7 +482,7 @@ class XRegisterManager(RegisterManager): no_lower_byte_regs = [r2, r3] - + rm = XRegisterManager(longevity, frame_manager=fm, assembler=MockAsm()) @@ -173,7 +505,7 @@ assert isinstance(loc, FakeReg) assert loc not in [r2, r3] rm._check_invariants() - + def test_make_sure_var_in_reg(self): boxes, longevity = boxes_and_longevity(5) fm = TFrameManager() @@ -187,74 +519,10 @@ loc = rm.make_sure_var_in_reg(b0) assert isinstance(loc, FakeReg) rm._check_invariants() - - def test_force_result_in_reg_1(self): - b0, b1 = newboxes(0, 0) - longevity = {b0: (0, 1), b1: (1, 3)} - fm = TFrameManager() - asm = MockAsm() - rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) - rm.next_instruction() - # first path, var is already in reg and dies - loc0 = rm.force_allocate_reg(b0) - rm._check_invariants() - rm.next_instruction() - loc = rm.force_result_in_reg(b1, b0) - assert loc is loc0 - assert len(asm.moves) == 0 - rm._check_invariants() - - def test_force_result_in_reg_2(self): - b0, b1 = newboxes(0, 0) - longevity = {b0: (0, 2), b1: (1, 3)} - fm = TFrameManager() - asm = MockAsm() - rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) - rm.next_instruction() - loc0 = rm.force_allocate_reg(b0) - rm._check_invariants() - rm.next_instruction() - loc = rm.force_result_in_reg(b1, b0) - assert loc is loc0 - assert rm.loc(b0) is not loc0 - assert len(asm.moves) == 1 - rm._check_invariants() - - def test_force_result_in_reg_3(self): - b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) - longevity = {b0: (0, 2), b1: (0, 2), b3: (0, 2), b2: (0, 2), b4: (1, 3)} - fm = TFrameManager() - asm = MockAsm() - rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) - rm.next_instruction() - for b in b0, b1, b2, b3: - rm.force_allocate_reg(b) - assert not len(rm.free_regs) - rm._check_invariants() - rm.next_instruction() - rm.force_result_in_reg(b4, b0) - rm._check_invariants() - assert len(asm.moves) == 1 - - def test_force_result_in_reg_4(self): - b0, b1 = newboxes(0, 0) - longevity = {b0: (0, 1), b1: (0, 1)} - fm = TFrameManager() - asm = MockAsm() - rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) - rm.next_instruction() - fm.loc(b0) - rm.force_result_in_reg(b1, b0) - rm._check_invariants() - loc = rm.loc(b1) - assert isinstance(loc, FakeReg) - loc = rm.loc(b0) - assert isinstance(loc, FakeFramePos) - assert len(asm.moves) == 1 def test_bogus_make_sure_var_in_reg(self): b0, = newboxes(0) - longevity = {b0: (0, 1)} + longevity = {b0: Lifetime(0, 1)} fm = TFrameManager() asm = MockAsm() rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) @@ -281,17 +549,6 @@ assert len(rm.reg_bindings) == 4 rm._check_invariants() - def test_force_result_in_reg_const(self): - boxes, longevity = boxes_and_longevity(2) - fm = TFrameManager() - asm = MockAsm() - rm = RegisterManager(longevity, frame_manager=fm, - assembler=asm) - rm.next_instruction() - c = ConstInt(0) - rm.force_result_in_reg(boxes[0], c) - rm._check_invariants() - def test_loc_of_const(self): rm = RegisterManager({}) rm.next_instruction() @@ -311,6 +568,7 @@ assembler=asm) for b in boxes[:-1]: rm.force_allocate_reg(b) + rm.position = 0 rm.before_call() assert len(rm.reg_bindings) == 2 assert fm.get_frame_depth() == 2 @@ -342,7 +600,7 @@ rm.after_call(boxes[-1]) assert len(rm.reg_bindings) == 1 rm._check_invariants() - + def test_different_frame_width(self): class XRegisterManager(RegisterManager): @@ -350,19 +608,21 @@ fm = TFrameManager() b0 = InputArgInt() - longevity = {b0: (0, 1)} + longevity = {b0: Lifetime(0, 1)} asm = MockAsm() rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) f0 = InputArgFloat() - longevity = {f0: (0, 1)} + longevity = {f0: Lifetime(0, 1)} xrm = XRegisterManager(longevity, frame_manager=fm, assembler=asm) xrm.loc(f0) rm.loc(b0) assert fm.get_frame_depth() == 3 - + def test_spilling(self): b0, b1, b2, b3, b4, b5 = newboxes(0, 1, 2, 3, 4, 5) - longevity = {b0: (0, 3), b1: (0, 3), b3: (0, 5), b2: (0, 2), b4: (1, 4), b5: (1, 3)} + longevity = {b0: Lifetime(0, 3), b1: Lifetime(0, 3), + b3: Lifetime(0, 5), b2: Lifetime(0, 2), + b4: Lifetime(1, 4), b5: Lifetime(1, 3)} fm = TFrameManager() asm = MockAsm() rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) @@ -378,6 +638,48 @@ assert spilled2 is loc rm._check_invariants() + def test_spilling_furthest_next_real_use(self): + b0, b1, b2, b3, b4, b5 = newboxes(0, 1, 2, 3, 4, 5) + longevity = {b0: Lifetime(0, 3, [1, 2, 3]), b1: Lifetime(0, 3, [3]), + b3: Lifetime(0, 4, [1, 2, 3, 4]), b2: Lifetime(0, 2), + b4: Lifetime(1, 4), b5: Lifetime(1, 3)} + fm = TFrameManager() + asm = MockAsm() + rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) + rm.next_instruction() + for b in b0, b1, b2, b3: + rm.force_allocate_reg(b) + assert len(rm.free_regs) == 0 + rm.next_instruction() + loc = rm.loc(b1) + spilled = rm.force_allocate_reg(b4) + assert spilled is loc + spilled2 = rm.force_allocate_reg(b5) + assert spilled2 is loc + rm._check_invariants() + + + def test_spill_useless_vars_first(self): + b0, b1, b2, b3, b4, b5 = newboxes(0, 1, 2, 3, 4, 5) + longevity = {b0: Lifetime(0, 5), b1: Lifetime(0, 10), + # b2 and b3 become useless but b3 lives longer + b3: Lifetime(0, 7, 3), b2: Lifetime(0, 6, 3), + b4: Lifetime(4, 5), b5: Lifetime(4, 7)} + fm = TFrameManager() + asm = MockAsm() + rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) + rm.next_instruction() + for b in b0, b1, b2, b3: + rm.force_allocate_reg(b) + rm.position = 4 + assert len(rm.free_regs) == 0 + loc = rm.loc(b3) + spilled = rm.force_allocate_reg(b4) + assert spilled is loc + loc = rm.loc(b2) + spilled2 = rm.force_allocate_reg(b5) + assert spilled2 is loc + rm._check_invariants() def test_hint_frame_locations_1(self): for hint_value in range(11): @@ -568,3 +870,503 @@ assert fm.get_loc_index(floc) == 0 for box in fm.bindings.keys(): fm.mark_as_free(box) + + +class TestForceResultInReg(object): + # use it's own class since there are so many cases + + def test_force_result_in_reg_1(self): + # var in reg, dies + b0, b1 = newboxes(0, 0) + longevity = {b0: Lifetime(0, 1), b1: Lifetime(1, 3)} + fm = TFrameManager() + asm = MockAsm() + rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) + rm.next_instruction() + loc0 = rm.force_allocate_reg(b0) + rm._check_invariants() + rm.next_instruction() + loc = rm.force_result_in_reg(b1, b0) + assert loc is loc0 + assert len(asm.moves) == 0 + rm._check_invariants() + + def test_force_result_in_reg_2(self): + # var in reg, survives + b0, b1 = newboxes(0, 0) + longevity = {b0: Lifetime(0, 2), b1: Lifetime(1, 3)} + fm = TFrameManager() + asm = MockAsm() + rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) + rm.next_instruction() + loc0 = rm.force_allocate_reg(b0) + rm._check_invariants() + rm.next_instruction() + loc = rm.force_result_in_reg(b1, b0) + assert loc is not loc0 + assert rm.loc(b0) is loc0 + assert len(asm.moves) == 1 + rm._check_invariants() + + def test_force_result_in_reg_3(self): + # var in reg, survives, no free registers + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + longevity = {b0: Lifetime(0, 2), b1: Lifetime(0, 2), + b3: Lifetime(0, 2), b2: Lifetime(0, 2), + b4: Lifetime(1, 3)} + fm = TFrameManager() + asm = MockAsm() + rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) + rm.next_instruction() + for b in b0, b1, b2, b3: + rm.force_allocate_reg(b) + assert not len(rm.free_regs) + rm._check_invariants() + rm.next_instruction() + rm.force_result_in_reg(b4, b0) + rm._check_invariants() + assert len(asm.moves) == 1 + + def test_force_result_in_reg_4(self): + b0, b1 = newboxes(0, 0) + longevity = {b0: Lifetime(0, 1), b1: Lifetime(0, 1)} + fm = TFrameManager() + asm = MockAsm() + rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) + rm.next_instruction() + fm.loc(b0) + rm.force_result_in_reg(b1, b0) + rm._check_invariants() + loc = rm.loc(b1) + assert isinstance(loc, FakeReg) + loc = rm.loc(b0) + assert isinstance(loc, FakeFramePos) + assert len(asm.moves) == 1 + + def test_force_result_in_reg_const(self): + # const + boxes, longevity = boxes_and_longevity(2) + fm = TFrameManager() + asm = MockAsm() + rm = RegisterManager(longevity, frame_manager=fm, + assembler=asm) + rm.next_instruction() + c = ConstInt(0) + rm.force_result_in_reg(boxes[0], c) + rm._check_invariants() + + # some tests where the result is supposed to go in a fixed register + + def test_force_result_in_reg_fixed_reg_1(self): + # var in reg, dies + b0, b1 = newboxes(0, 0) + longevity = LifetimeManager({b0: Lifetime(0, 1), b1: Lifetime(1, 3)}) + longevity.try_use_same_register(b0, b1) + longevity.fixed_register(1, r1, b1) + fm = TFrameManager() + asm = MockAsm() + rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) + rm.next_instruction() + loc0 = rm.force_allocate_reg(b0) + rm._check_invariants() + rm.next_instruction() + loc = rm.force_result_in_reg(b1, b0) From pypy.commits at gmail.com Fri Feb 8 11:40:35 2019 From: pypy.commits at gmail.com (ambv) Date: Fri, 08 Feb 2019 08:40:35 -0800 (PST) Subject: [pypy-commit] pypy py3.6: Support for os.PathLike in the posix module Message-ID: <5c5db103.1c69fb81.f101a.d1f8@mx.google.com> Author: Łukasz Langa Branch: py3.6 Changeset: r95906:075d529ca03f Date: 2019-02-08 17:39 +0100 http://bitbucket.org/pypy/pypy/changeset/075d529ca03f/ Log: Support for os.PathLike in the posix module diff --git a/lib-python/3/test/test_os.py b/lib-python/3/test/test_os.py --- a/lib-python/3/test/test_os.py +++ b/lib-python/3/test/test_os.py @@ -3041,8 +3041,11 @@ if cleanup_fn is not None: cleanup_fn(result) + # custom PyPy error message: see BPO-35942 with self.assertRaisesRegex( - TypeError, 'should be string, bytes'): + TypeError, + r'(should be string, bytes|' + r'__fspath__\(\) to return str or bytes)'): fn(int_fspath, *extra_args) if allow_fd: diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -1740,9 +1740,19 @@ from pypy.interpreter.unicodehelper import fsdecode return fsdecode(space, w_obj) - def fsencode_w(self, w_obj): - if self.isinstance_w(w_obj, self.w_unicode): - w_obj = self.fsencode(w_obj) + def fsencode_w(self, w_obj, allowed_types="string, bytes, or os.PathLike"): + try: + self._try_buffer_w(w_obj, self.BUF_FULL_RO) + if not self.isinstance_w(w_obj, self.w_bytes): + tp = self.type(w_obj).name + self.warn(self.newtext( + "path should be %s, not %s" % (allowed_types, tp,)), + self.w_DeprecationWarning) + except BufferInterfaceNotFound: + from pypy.module.posix.interp_posix import fspath + w_obj = fspath(self, w_obj) + if self.isinstance_w(w_obj, self.w_unicode): + w_obj = self.fsencode(w_obj) return self.bytesbuf0_w(w_obj) def bytesbuf0_w(self, w_obj): @@ -1760,7 +1770,12 @@ return rstring.assert_str0(result) def fsdecode_w(self, w_obj): - if self.isinstance_w(w_obj, self.w_bytes): + try: + self._try_buffer_w(w_obj, self.BUF_FULL_RO) + except BufferInterfaceNotFound: + from pypy.module.posix.interp_posix import fspath + w_obj = fspath(self, w_obj) + else: w_obj = self.fsdecode(w_obj) return self.unicode0_w(w_obj) diff --git a/pypy/module/posix/interp_posix.py b/pypy/module/posix/interp_posix.py --- a/pypy/module/posix/interp_posix.py +++ b/pypy/module/posix/interp_posix.py @@ -56,7 +56,7 @@ return self.space.fsencode_w(self.w_obj) def as_unicode(self): - return self.space.unicode0_w(self.w_obj) + return self.space.fsdecode_w(self.w_obj) class FileDecoder(object): is_unicode = False @@ -66,7 +66,7 @@ self.w_obj = w_obj def as_bytes(self): - return self.space.bytesbuf0_w(self.w_obj) + return self.space.fsencode_w(self.w_obj) def as_unicode(self): return self.space.fsdecode_w(self.w_obj) @@ -85,7 +85,7 @@ fname = FileEncoder(space, w_fname) return func(fname, *args) else: - fname = space.bytesbuf0_w(w_fname) + fname = FileDecoder(space, w_fname) return func(fname, *args) return dispatch @@ -136,9 +136,11 @@ @specialize.arg(2) def _unwrap_path(space, w_value, allow_fd=True): - if space.is_none(w_value): - raise oefmt(space.w_TypeError, - "can't specify None for path argument") + # equivalent of posixmodule.c:path_converter() in CPython + if allow_fd: + allowed_types = "string, bytes, os.PathLike or integer" + else: + allowed_types = "string, bytes or os.PathLike" if _WIN32: try: path_u = space.unicode0_w(w_value) @@ -146,36 +148,20 @@ except OperationError: pass try: - path_b = space.fsencode_w(w_value) + path_b = space.fsencode_w(w_value, allowed_types=allowed_types) return Path(-1, path_b, None, w_value) except OperationError as e: - if not e.match(space, space.w_TypeError): + if not allow_fd or not e.match(space, space.w_TypeError): raise - if allow_fd: + # File descriptor case try: space.index(w_value) except OperationError: - pass - else: - fd = unwrap_fd(space, w_value, "string, bytes or integer") - return Path(fd, None, None, w_value) - - # Inline fspath() for better error messages. - w_fspath_method = space.lookup(w_value, '__fspath__') - if w_fspath_method: - w_result = space.get_and_call_function(w_fspath_method, w_value) - if (space.isinstance_w(w_result, space.w_text) or - space.isinstance_w(w_result, space.w_bytes)): - return _unwrap_path(space, w_result, allow_fd=False) - - if allow_fd: - raise oefmt(space.w_TypeError, - "illegal type for path parameter (should be " - "string, bytes, os.PathLike or integer, not %T)", w_value) - else: - raise oefmt(space.w_TypeError, - "illegal type for path parameter (should be " - "string, bytes or os.PathLike, not %T)", w_value) + raise oefmt(space.w_TypeError, + "illegal type for path parameter (should be " + "%s, not %T)", allowed_types, w_value) + fd = unwrap_fd(space, w_value, allowed_types) + return Path(fd, None, None, w_value) class _PathOrFd(Unwrapper): def unwrap(self, space, w_value): @@ -211,7 +197,7 @@ if space.is_none(w_value): return DEFAULT_DIR_FD else: - return unwrap_fd(space, w_value) + return unwrap_fd(space, w_value, allowed_types="integer or None") class _DirFD(Unwrapper): def unwrap(self, space, w_value): diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -212,7 +212,7 @@ assert exc.value.filename == "nonexistentdir/nonexistentfile" excinfo = raises(TypeError, self.posix.stat, None) - assert "can't specify None" in str(excinfo.value) + assert "should be string, bytes, os.PathLike or integer, not None" in str(excinfo.value) excinfo = raises(TypeError, self.posix.stat, 2.) assert "should be string, bytes, os.PathLike or integer, not float" in str(excinfo.value) raises(ValueError, self.posix.stat, -1) @@ -1189,15 +1189,19 @@ skip("encoding not good enough") dest = bytes_dir + b"/file.txt" posix.symlink(bytes_dir + b"/somefile", dest) - with open(dest) as f: - data = f.read() - assert data == "who cares?" - # - posix.unlink(dest) + try: + with open(dest) as f: + data = f.read() + assert data == "who cares?" + finally: + posix.unlink(dest) posix.symlink(memoryview(bytes_dir + b"/somefile"), dest) - with open(dest) as f: - data = f.read() - assert data == "who cares?" + try: + with open(dest) as f: + data = f.read() + assert data == "who cares?" + finally: + posix.unlink(dest) # XXX skip test if dir_fd is unsupported def test_symlink_fd(self): @@ -1211,6 +1215,25 @@ finally: posix.close(f) posix.unlink(bytes_dir + '/somelink'.encode()) + + def test_symlink_fspath(self): + class Path: + def __init__(self, b): + self.path = b + def __fspath__(self): + return self.path + posix = self.posix + bytes_dir = self.bytes_dir + if bytes_dir is None: + skip("encoding not good enough") + dest = Path(bytes_dir + b"/file.txt") + posix.symlink(Path(bytes_dir + b"/somefile"), dest) + try: + with open(dest) as f: + data = f.read() + assert data == "who cares?" + finally: + posix.unlink(dest) else: def test_symlink(self): posix = self.posix From pypy.commits at gmail.com Fri Feb 8 11:44:37 2019 From: pypy.commits at gmail.com (antocuni) Date: Fri, 08 Feb 2019 08:44:37 -0800 (PST) Subject: [pypy-commit] pypy.org extradoc: checksums for new/updated packages which I have built+uploaded so far Message-ID: <5c5db1f5.1c69fb81.2ae75.d5ec@mx.google.com> Author: Antonio Cuni Branch: extradoc Changeset: r932:128fdd22c9b6 Date: 2019-02-08 17:44 +0100 http://bitbucket.org/pypy/pypy.org/changeset/128fdd22c9b6/ Log: checksums for new/updated packages which I have built+uploaded so far diff --git a/source/download.txt b/source/download.txt --- a/source/download.txt +++ b/source/download.txt @@ -451,7 +451,44 @@ Checksums --------- -Here are the checksums for each of the downloads +Here are the checksums for each of the downloads of PyPy 7.0.0 and the older 6.0.0. + +pypy2.7-7.0.0 sha256:: + + 446fc208dd77a0048368da830564e6e4180bcd786e524b5369c61785af5c903a pypy2.7-v7.0.0-linux32.tar.bz2 + 971b1909f9fe960c4c643a6940d3f8a60d9a7a2937119535ab0cfaf83498ecd7 pypy2.7-v7.0.0-linux64.tar.bz2 + e7ecb029d9c7a59388838fc4820a50a2f5bee6536010031060e3dfa882730dc8 pypy2.7-v7.0.0-osx64.tar.bz2 + 2ce390d93fa57ba912066a8b6439588bd9cf6aa9cef44d892b8e3e6dba64615e pypy2.7-v7.0.0-s390x.tar.bz2 + 04477a41194240cd71e485c3f41dec35a787d1b3bc030f9aa59e5e81bcf4118b pypy2.7-v7.0.0-win32.zip + 165ffdf49a04c3ebdc966f76e67dd1767ad699657215dd83ca6996ab8ed87f52 pypy2.7-v7.0.0-ppc64.tar.bz2 + cfb0e2e9b1434e94ea559548c7486c8e7b4319a397309e8ed3783d9beadf1c6c pypy2.7-v7.0.0-ppc64le.tar.bz2 + f51d8bbfc4e73a8a01820b7871a45d13c59f1399822cdf8a19388c69eb20c18c pypy2.7-v7.0.0-src.tar.bz2 + 77c8c02cf412a5f8182ffe8845877cffa506e5a5ce3a7cd835483fdc1202afd4 pypy2.7-v7.0.0-src.zip + + +pypy 3.5-v7.0.0 sha256:: + + b8db8fbca9621de8ea8cd7184b322f2dddb2f385e8e5a63dfb75bb3fea4b2e3f pypy3.5-v7.0.0-linux32.tar.bz2 + 729e3c54325969c98bd3658c6342b9f5987b96bad1d6def04250a08401b54c4b pypy3.5-v7.0.0-linux64.tar.bz2 + 7c6d71653d9b1a7946d1eeebbf24b454fe934fba8b0c39f648bdc545fb2895ce pypy3.5-v7.0.0-osx64.tar.bz2 + d588b045cc0d3a75c31fce54c1d181b1206ad9a5dd272fe79160a6268401605f pypy3.5-v7.0.0-s390x.tar.bz2 + + TODO win32 + + b2ddb0f45cb4e0384fb498ef7fcca2ac96c730b9000affcf8d730169397f017f pypy3.5-v7.0.0-src.tar.bz2 + 3aa3a921c163667761165dbd2070e56d6715979fe9cc1f135d58ea0692a05a1e pypy3.5-v7.0.0-src.zip + + + +pypy 3.6-v7.0.0-alpha sha256:: + + 8576bde0760c239040706cf4952995eb0e77938b175885392a465a0d1616173d pypy3.6-v7.0.0-linux64.tar.bz2 + 4a95ffd61fd2d626a9c099db6e44889c2a7eecee9cb1cbc29e06603c218ba8e2 pypy3.6-v7.0.0-osx64.tar.bz2 + 645d81472d16922fd592e9261da449cb19847ff7d5eaa89bcf05d9214b6b2698 pypy3.6-v7.0.0-win32.zip + 7ccbf81db5c647fa0c27636c7d18d059d2570fff7eaffc03857c67bee84b8a26 pypy3.6-v7.0.0-src.tar.bz2 + 867dce40a63caccde161d90a0792e69f2a510a1f3147b694731052be52fafe5c pypy3.6-v7.0.0-src.zip + + pypy2.7-6.0.0 sha256:: @@ -465,17 +502,6 @@ 3553b19447cdb627919cc37d76979e15dc755b085e979f5ffa9b25933ec343b3 pypy2-v6.0.0-src.zip 6e2210dae1ae721ed4eb9cba19f15453514b64111511c84f24843c4fdefdaf7f pypy2-v6.0.0-win32.zip -pypy2.7-7.0.0 sha256:: - - 446fc208dd77a0048368da830564e6e4180bcd786e524b5369c61785af5c903a pypy2.7-v7.0.0-linux32.tar.bz2 - 971b1909f9fe960c4c643a6940d3f8a60d9a7a2937119535ab0cfaf83498ecd7 pypy2.7-v7.0.0-linux64.tar.bz2 - e7ecb029d9c7a59388838fc4820a50a2f5bee6536010031060e3dfa882730dc8 pypy2.7-v7.0.0-osx64.tar.bz2 - 2ce390d93fa57ba912066a8b6439588bd9cf6aa9cef44d892b8e3e6dba64615e pypy2.7-v7.0.0-s390x.tar.bz2 - 8e60c3fc261d4e42fc59d1f46c88dd1f6d952d0ce6b9cb197d62652ed03df3f9 pypy2.7-v7.0.0-src.tar.bz2 - 7819066fe4b1eaba961ba166434263683cf9184137ca238b26e1fa44906db24b pypy2.7-v7.0.0-src.zip - 04477a41194240cd71e485c3f41dec35a787d1b3bc030f9aa59e5e81bcf4118b pypy2.7-v7.0.0-win32.zip - - pypy 3.5-v6.0.0 sha256:: b04eeee5160e6cb5f8962de80f077ea1dc7be34e77d74bf075519c23603f5ff9 pypy3-v6.0.0-linux32.tar.bz2 @@ -489,21 +515,4 @@ 72dddb3746a51f7672c77d619c818e27efe899e08ae82762448e50dbfdc2f5f3 pypy3-v6.0.0-win32.zip -pypy 3.5-v7.0.0 sha256:: - 41eb18fe7deb44df9359666c9e659aa7eadf62fa08688ae80a8f6d168111f45b pypy3.5-v7.0.0-linux32.tar.bz2 - c5bc684cfc2ce1545b9b98bc461aea2c185466d142d5dd48fbf245b1beb9279e pypy3.5-v7.0.0-linux64.tar.bz2 - 74b7aca83690f9578250a65a2a565d1ff4aba57c5821eed94b7bca96ec64ba88 pypy3.5-v7.0.0-osx64.tar.bz2 - 641c3e75a987d12d8a685d68e27e57f19627434b389f6f3512a7bcc8c0fc2f60 pypy3.5-v7.0.0-s390x.tar.bz2 - ef62eb73f416e2de8c9fec974c79434f7a650a6aaf9a640d5a2bec12984b0a45 pypy3.5-v7.0.0-src.tar.bz2 - dfecdc6a51a1cda963b3e7582da28b197460e72af23adb7b3b65138ae223171e pypy3.5-v7.0.0-src.zip - d8a1d3fb3fb40c45a6ce5c7ca1f4c072dd850612b7702d70673a23e96781334b pypy3.5-v7.0.0-win32.zip - - -pypy 3.6-v7.0.0-alpha sha256:: - - 8576bde0760c239040706cf4952995eb0e77938b175885392a465a0d1616173d pypy3.6-v7.0.0-linux64.tar.bz2 - 4a95ffd61fd2d626a9c099db6e44889c2a7eecee9cb1cbc29e06603c218ba8e2 pypy3.6-v7.0.0-osx64.tar.bz2 - e1d441789473896d08d51958503ea24a570b9e4a97611edb0c95f69788363a24 pypy3.6-v7.0.0-src.tar.bz2 - 7450e9ecb91b627fec707fd74249f39cb92b96e8889e82faefc5f9494fcf5560 pypy3.6-v7.0.0-src.zip - 645d81472d16922fd592e9261da449cb19847ff7d5eaa89bcf05d9214b6b2698 pypy3.6-v7.0.0-win32.zip From pypy.commits at gmail.com Fri Feb 8 16:16:40 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Fri, 08 Feb 2019 13:16:40 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: Fixing asyncio - work in progress Message-ID: <5c5df1b8.1c69fb81.7041b.c696@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r95907:a7c5ca35f519 Date: 2019-02-08 12:36 +0000 http://bitbucket.org/pypy/pypy/changeset/a7c5ca35f519/ Log: Fixing asyncio - work in progress diff --git a/lib_pypy/_overlapped.py b/lib_pypy/_overlapped.py --- a/lib_pypy/_overlapped.py +++ b/lib_pypy/_overlapped.py @@ -30,6 +30,7 @@ self.writebuffer = None self.overlapped[0].hEvent = \ _kernel32.CreateEventW(NULL, True, False, NULL) + self.address = _ffi.addressof(self.overlapped) def __del__(self): # do this somehow else @@ -113,17 +114,23 @@ return None -def post_to_queue_callback(lpparameter, timerorwaitfired) - pdata = _ffi.cast("PostCallbackData", lpparameter) + at _ffi.callback("void(void*, bool)") +def post_to_queue_callback(lpparameter, timerorwaitfired): + pdata = _ffi.cast("PostCallbackData *", lpparameter) _kernel32.PostQueuedCompletionStatus(pdata.hCompletionPort, timeorwaitfired, 0, pdata.Overlapped) -def RegisterWaitWithQueue(object, completionport, ovaddress, miliseconds) +def RegisterWaitWithQueue(object, completionport, ovaddress, miliseconds): data = _ffi.new('PostCallbackData[1]') newwaitobject = _ffi.new("HANDLE[1]") - data.hCompletionPort = completionport - data.Overlapped = ovaddress - success = _kernel32.RegisterWaitForSingleObject(newwaitobject, object, data, _ffi.post_to_queue_callback, + data[0].hCompletionPort = _Z(completionport) + data[0].Overlapped = ovaddress[0] + success = _kernel32.RegisterWaitForSingleObject(newwaitobject, + object, + _ffi.cast("WAITORTIMERCALLBACK",post_to_queue_callback), + data, + miliseconds, + _kernel32.WT_EXECUTEINWAITTHREAD | _kernel32.WT_EXECUTEONLYONCE) return None diff --git a/lib_pypy/_pypy_winbase_build.py b/lib_pypy/_pypy_winbase_build.py --- a/lib_pypy/_pypy_winbase_build.py +++ b/lib_pypy/_pypy_winbase_build.py @@ -91,11 +91,6 @@ } OVERLAPPED, *LPOVERLAPPED; -typedef struct _PostCallbackData { - HANDLE hCompletionPort; - LPOVERLAPPED Overlapped; -} PostCallbackData, *LPPostCallbackData; - DWORD WINAPI GetVersion(void); BOOL WINAPI CreatePipe(PHANDLE, PHANDLE, void *, DWORD); HANDLE WINAPI CreateNamedPipeA(LPCSTR, DWORD, DWORD, DWORD, DWORD, DWORD, @@ -115,7 +110,6 @@ BOOL WINAPI CloseHandle(HANDLE); DWORD WINAPI GetLastError(VOID); BOOL WINAPI GetOverlappedResult(HANDLE, LPOVERLAPPED, LPDWORD, BOOL); - HANDLE WINAPI GetCurrentProcess(void); BOOL WINAPI DuplicateHandle(HANDLE, HANDLE, HANDLE, LPHANDLE, DWORD, BOOL, DWORD); @@ -130,12 +124,25 @@ BOOL WINAPI TerminateProcess(HANDLE, UINT); HANDLE WINAPI GetStdHandle(DWORD); DWORD WINAPI GetModuleFileNameW(HANDLE, wchar_t *, DWORD); - UINT WINAPI SetErrorMode(UINT); #define SEM_FAILCRITICALERRORS 0x0001 #define SEM_NOGPFAULTERRORBOX 0x0002 #define SEM_NOALIGNMENTFAULTEXCEPT 0x0004 #define SEM_NOOPENFILEERRORBOX 0x8000 + + +typedef struct _PostCallbackData { + HANDLE hCompletionPort; + LPOVERLAPPED Overlapped; +} PostCallbackData, *LPPostCallbackData; + + +typedef VOID (WINAPI *WAITORTIMERCALLBACK) ( PVOID, BOOL); +BOOL WINAPI RegisterWaitForSingleObject(PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, ULONG, ULONG); +BOOL WINAPI PostQueuedCompletionStatus(HANDLE, DWORD, ULONG_PTR, LPOVERLAPPED); + +#define WT_EXECUTEINWAITTHREAD 0x00000004 +#define WT_EXECUTEONLYONCE 0x00000008 """) # -------------------- diff --git a/lib_pypy/_pypy_winbase_cffi.py b/lib_pypy/_pypy_winbase_cffi.py --- a/lib_pypy/_pypy_winbase_cffi.py +++ b/lib_pypy/_pypy_winbase_cffi.py @@ -3,8 +3,8 @@ ffi = _cffi_backend.FFI('_pypy_winbase_cffi', _version = 0x2601, - _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xAD\x03\x00\x00\x13\x11\x00\x00\xB2\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xAC\x03\x00\x00\xA8\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xA7\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x29\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x2E\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x2E\x11\x00\x00\x2E\x11\x00\x00\x2E\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x6B\x03\x00\x00\x49\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x49\x11\x00\x00\x49\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x33\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x49\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x66\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x66\x0D\x00\x00\x00\x0F\x00\x00\x66\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xAB\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xAD\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x6E\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x6B\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x71\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x71\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x49\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x77\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x6E\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xB2\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xAA\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x02\x01\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xB1\x03\x00\x00\x04\x01\x00\x00\x00\x01', - _globals = (b'\x00\x00\x27\x23CancelIoEx',0,b'\x00\x00\x24\x23CloseHandle',0,b'\x00\x00\x27\x23ConnectNamedPipe',0,b'\x00\x00\x6D\x23CreateEventA',0,b'\x00\x00\x73\x23CreateEventW',0,b'\x00\x00\x79\x23CreateFileA',0,b'\x00\x00\x9B\x23CreateFileW',0,b'\x00\x00\x82\x23CreateNamedPipeA',0,b'\x00\x00\x91\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x48\x23CreateProcessW',0,b'\x00\x00\x3F\x23DuplicateHandle',0,b'\x00\x00\x8F\x23GetCurrentProcess',0,b'\x00\x00\x35\x23GetExitCodeProcess',0,b'\x00\x00\x63\x23GetLastError',0,b'\x00\x00\x5E\x23GetModuleFileNameW',0,b'\x00\x00\x2B\x23GetOverlappedResult',0,b'\x00\x00\x8C\x23GetStdHandle',0,b'\x00\x00\x63\x23GetVersion',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x57\x23SetErrorMode',0,b'\x00\x00\xA4\x23SetEvent',0,b'\x00\x00\x39\x23SetNamedPipeHandleState',0,b'\x00\x00\x31\x23TerminateProcess',0,b'\x00\x00\x5A\x23WaitForSingleObject',0,b'\x00\x00\x54\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x68\x23_getwch',0,b'\x00\x00\x68\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x6A\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x65\x23_ungetwch',0), - _struct_unions = ((b'\x00\x00\x00\xAF\x00\x00\x00\x03$1',b'\x00\x00\xAE\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xAE\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xA8\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xAC\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x66\x11wShowWindow',b'\x00\x00\x66\x11cbReserved2',b'\x00\x00\xB0\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xA7\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xAF\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xAA\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x29\x11Overlapped'),(b'\x00\x00\x00\xAB\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle')), - _typenames = (b'\x00\x00\x00\x29LPOVERLAPPED',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xA9LPPostCallbackData',b'\x00\x00\x00\x6ELPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\xA7OVERLAPPED',b'\x00\x00\x00\xA8PROCESS_INFORMATION',b'\x00\x00\x00\x6EPSECURITY_ATTRIBUTES',b'\x00\x00\x00\xAAPostCallbackData',b'\x00\x00\x00\xABSECURITY_ATTRIBUTES',b'\x00\x00\x00\xACSTARTUPINFO',b'\x00\x00\x00\x66wint_t'), + _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xBF\x03\x00\x00\x13\x11\x00\x00\xC4\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xBE\x03\x00\x00\xBA\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xB5\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xB9\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x79\x03\x00\x00\x57\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x57\x11\x00\x00\x57\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x3B\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x57\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x74\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x74\x0D\x00\x00\x00\x0F\x00\x00\x74\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xBD\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xBF\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x7C\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x79\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x7F\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x7F\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x57\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x85\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xC4\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xC4\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xBC\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x02\x01\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xC3\x03\x00\x00\x04\x01\x00\x00\x00\x01', + _globals = (b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x7B\x23CreateEventA',0,b'\x00\x00\x81\x23CreateEventW',0,b'\x00\x00\x87\x23CreateFileA',0,b'\x00\x00\xA9\x23CreateFileW',0,b'\x00\x00\x90\x23CreateNamedPipeA',0,b'\x00\x00\x9F\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x56\x23CreateProcessW',0,b'\x00\x00\x4D\x23DuplicateHandle',0,b'\x00\x00\x9D\x23GetCurrentProcess',0,b'\x00\x00\x3D\x23GetExitCodeProcess',0,b'\x00\x00\x71\x23GetLastError',0,b'\x00\x00\x6C\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x9A\x23GetStdHandle',0,b'\x00\x00\x71\x23GetVersion',0,b'\x00\x00\x47\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x65\x23SetErrorMode',0,b'\x00\x00\xB2\x23SetEvent',0,b'\x00\x00\x41\x23SetNamedPipeHandleState',0,b'\x00\x00\x39\x23TerminateProcess',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x68\x23WaitForSingleObject',0,b'\x00\x00\x62\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x76\x23_getwch',0,b'\x00\x00\x76\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x78\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x73\x23_ungetwch',0), + _struct_unions = ((b'\x00\x00\x00\xC1\x00\x00\x00\x03$1',b'\x00\x00\xC0\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xC0\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xBA\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xBE\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x74\x11wShowWindow',b'\x00\x00\x74\x11cbReserved2',b'\x00\x00\xC2\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xB9\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xC1\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xBC\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x31\x11Overlapped'),(b'\x00\x00\x00\xBD\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle')), + _typenames = (b'\x00\x00\x00\x31LPOVERLAPPED',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xBBLPPostCallbackData',b'\x00\x00\x00\x7CLPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\xB9OVERLAPPED',b'\x00\x00\x00\xBAPROCESS_INFORMATION',b'\x00\x00\x00\x7CPSECURITY_ATTRIBUTES',b'\x00\x00\x00\xBCPostCallbackData',b'\x00\x00\x00\xBDSECURITY_ATTRIBUTES',b'\x00\x00\x00\xBESTARTUPINFO',b'\x00\x00\x00\x27WAITORTIMERCALLBACK',b'\x00\x00\x00\x74wint_t'), ) From pypy.commits at gmail.com Fri Feb 8 16:16:42 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Fri, 08 Feb 2019 13:16:42 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: added overlapped type and implemented overlapped cancel method Message-ID: <5c5df1ba.1c69fb81.59fb6.aa7d@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r95908:4f3f9908aea9 Date: 2019-02-08 13:39 +0000 http://bitbucket.org/pypy/pypy/changeset/4f3f9908aea9/ Log: added overlapped type and implemented overlapped cancel method diff --git a/lib_pypy/_overlapped.py b/lib_pypy/_overlapped.py --- a/lib_pypy/_overlapped.py +++ b/lib_pypy/_overlapped.py @@ -20,6 +20,21 @@ from _winapi import INVALID_HANDLE_VALUE, _MAX_PATH , _Z import _winapi +from enum import Enum +class OverlappedType(Enum): + TYPE_NONE = 0 + TYPE_NOT_STARTED = 1 + TYPE_READ = 2 + TYPE_READINTO = 3 + TYPE_WRITE = 4 + TYPE_ACCEPT = 5 + TYPE_CONNECT = 6 + TYPE_DISCONNECT = 7 + TYPE_CONNECT_NAMED_PIPE = 8 + TYPE_WAIT_NAMED_PIPE_AND_CONNECT = 9 + TYPE_TRANSMIT_FILE = 10 + + class Overlapped(object): def __init__(self, handle): self.overlapped = _ffi.new('OVERLAPPED[1]') @@ -28,6 +43,7 @@ self.pending = 0 self.completed = 0 self.writebuffer = None + self.type = OverlappedType.TYPE_NONE self.overlapped[0].hEvent = \ _kernel32.CreateEventW(NULL, True, False, NULL) self.address = _ffi.addressof(self.overlapped) @@ -48,7 +64,7 @@ @property def event(self): - return None + return self.overlapped[0].hEvent def GetOverlappedResult(self, wait): transferred = _ffi.new('DWORD[1]', [0]) @@ -75,8 +91,13 @@ return None def cancel(self): - xxx - return None + result = true + if self.type == OverlappedType.TYPE_NOT_STARTED or OverlappedType.TYPE_WAIT_NAMED_PIPE_AND_CONNECT: + return None + if not _kernel32.HasOverlappedIoCompleted(self.overlapped): + ### If we are to support xp we will need to dynamically load the below method + _kernel32.CancelIoEx(self.handle, self.overlapped) + return result def CreateEvent(eventattributes, manualreset, initialstate, name): @@ -117,7 +138,7 @@ @_ffi.callback("void(void*, bool)") def post_to_queue_callback(lpparameter, timerorwaitfired): pdata = _ffi.cast("PostCallbackData *", lpparameter) - _kernel32.PostQueuedCompletionStatus(pdata.hCompletionPort, timeorwaitfired, 0, pdata.Overlapped) + _kernel32.PostQueuedCompletionStatus(pdata.hCompletionPort, timerorwaitfired, 0, pdata.Overlapped) def RegisterWaitWithQueue(object, completionport, ovaddress, miliseconds): @@ -133,4 +154,4 @@ miliseconds, _kernel32.WT_EXECUTEINWAITTHREAD | _kernel32.WT_EXECUTEONLYONCE) - return None + return newwaitobject diff --git a/lib_pypy/_pypy_winbase_build.py b/lib_pypy/_pypy_winbase_build.py --- a/lib_pypy/_pypy_winbase_build.py +++ b/lib_pypy/_pypy_winbase_build.py @@ -106,6 +106,7 @@ HANDLE WINAPI CreateEventA(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCSTR); HANDLE WINAPI CreateEventW(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR); VOID WINAPI SetEvent(HANDLE); +BOOL WINAPI CancelIo(HANDLE); BOOL WINAPI CancelIoEx(HANDLE, LPOVERLAPPED); BOOL WINAPI CloseHandle(HANDLE); DWORD WINAPI GetLastError(VOID); @@ -130,19 +131,18 @@ #define SEM_NOALIGNMENTFAULTEXCEPT 0x0004 #define SEM_NOOPENFILEERRORBOX 0x8000 - typedef struct _PostCallbackData { HANDLE hCompletionPort; LPOVERLAPPED Overlapped; } PostCallbackData, *LPPostCallbackData; - typedef VOID (WINAPI *WAITORTIMERCALLBACK) ( PVOID, BOOL); BOOL WINAPI RegisterWaitForSingleObject(PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, ULONG, ULONG); BOOL WINAPI PostQueuedCompletionStatus(HANDLE, DWORD, ULONG_PTR, LPOVERLAPPED); #define WT_EXECUTEINWAITTHREAD 0x00000004 #define WT_EXECUTEONLYONCE 0x00000008 + """) # -------------------- diff --git a/lib_pypy/_pypy_winbase_cffi.py b/lib_pypy/_pypy_winbase_cffi.py --- a/lib_pypy/_pypy_winbase_cffi.py +++ b/lib_pypy/_pypy_winbase_cffi.py @@ -4,7 +4,7 @@ ffi = _cffi_backend.FFI('_pypy_winbase_cffi', _version = 0x2601, _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xBF\x03\x00\x00\x13\x11\x00\x00\xC4\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xBE\x03\x00\x00\xBA\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xB5\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xB9\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x79\x03\x00\x00\x57\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x57\x11\x00\x00\x57\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x3B\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x57\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x74\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x74\x0D\x00\x00\x00\x0F\x00\x00\x74\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xBD\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xBF\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x7C\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x79\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x7F\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x7F\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x57\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x85\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xC4\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xC4\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xBC\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x02\x01\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xC3\x03\x00\x00\x04\x01\x00\x00\x00\x01', - _globals = (b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x7B\x23CreateEventA',0,b'\x00\x00\x81\x23CreateEventW',0,b'\x00\x00\x87\x23CreateFileA',0,b'\x00\x00\xA9\x23CreateFileW',0,b'\x00\x00\x90\x23CreateNamedPipeA',0,b'\x00\x00\x9F\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x56\x23CreateProcessW',0,b'\x00\x00\x4D\x23DuplicateHandle',0,b'\x00\x00\x9D\x23GetCurrentProcess',0,b'\x00\x00\x3D\x23GetExitCodeProcess',0,b'\x00\x00\x71\x23GetLastError',0,b'\x00\x00\x6C\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x9A\x23GetStdHandle',0,b'\x00\x00\x71\x23GetVersion',0,b'\x00\x00\x47\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x65\x23SetErrorMode',0,b'\x00\x00\xB2\x23SetEvent',0,b'\x00\x00\x41\x23SetNamedPipeHandleState',0,b'\x00\x00\x39\x23TerminateProcess',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x68\x23WaitForSingleObject',0,b'\x00\x00\x62\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x76\x23_getwch',0,b'\x00\x00\x76\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x78\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x73\x23_ungetwch',0), + _globals = (b'\x00\x00\x2C\x23CancelIo',0,b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x7B\x23CreateEventA',0,b'\x00\x00\x81\x23CreateEventW',0,b'\x00\x00\x87\x23CreateFileA',0,b'\x00\x00\xA9\x23CreateFileW',0,b'\x00\x00\x90\x23CreateNamedPipeA',0,b'\x00\x00\x9F\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x56\x23CreateProcessW',0,b'\x00\x00\x4D\x23DuplicateHandle',0,b'\x00\x00\x9D\x23GetCurrentProcess',0,b'\x00\x00\x3D\x23GetExitCodeProcess',0,b'\x00\x00\x71\x23GetLastError',0,b'\x00\x00\x6C\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x9A\x23GetStdHandle',0,b'\x00\x00\x71\x23GetVersion',0,b'\x00\x00\x47\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x65\x23SetErrorMode',0,b'\x00\x00\xB2\x23SetEvent',0,b'\x00\x00\x41\x23SetNamedPipeHandleState',0,b'\x00\x00\x39\x23TerminateProcess',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x68\x23WaitForSingleObject',0,b'\x00\x00\x62\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x76\x23_getwch',0,b'\x00\x00\x76\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x78\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x73\x23_ungetwch',0), _struct_unions = ((b'\x00\x00\x00\xC1\x00\x00\x00\x03$1',b'\x00\x00\xC0\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xC0\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xBA\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xBE\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x74\x11wShowWindow',b'\x00\x00\x74\x11cbReserved2',b'\x00\x00\xC2\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xB9\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xC1\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xBC\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x31\x11Overlapped'),(b'\x00\x00\x00\xBD\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle')), _typenames = (b'\x00\x00\x00\x31LPOVERLAPPED',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xBBLPPostCallbackData',b'\x00\x00\x00\x7CLPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\xB9OVERLAPPED',b'\x00\x00\x00\xBAPROCESS_INFORMATION',b'\x00\x00\x00\x7CPSECURITY_ATTRIBUTES',b'\x00\x00\x00\xBCPostCallbackData',b'\x00\x00\x00\xBDSECURITY_ATTRIBUTES',b'\x00\x00\x00\xBESTARTUPINFO',b'\x00\x00\x00\x27WAITORTIMERCALLBACK',b'\x00\x00\x00\x74wint_t'), ) From pypy.commits at gmail.com Fri Feb 8 16:16:44 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Fri, 08 Feb 2019 13:16:44 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: Work in progress. Added GetQueueCompletionStatus. Message-ID: <5c5df1bc.1c69fb81.832d3.074e@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r95909:846255c51472 Date: 2019-02-08 14:31 +0000 http://bitbucket.org/pypy/pypy/changeset/846255c51472/ Log: Work in progress. Added GetQueueCompletionStatus. diff --git a/lib_pypy/_overlapped.py b/lib_pypy/_overlapped.py --- a/lib_pypy/_overlapped.py +++ b/lib_pypy/_overlapped.py @@ -131,8 +131,20 @@ return None -def GetQueuedCompletionStatus(handle, milliseconds): - return None +def GetQueuedCompletionStatus(completionport, milliseconds): + numberofbytes = _ffi.new('DWORD[1]', [0]) + completionkey = _ffi.new('ULONG *', 0) + + if completionport is None: + raise _winapi._WinError() + overlapped = _ffi.new('OVERLAPPED*') + result = _kernel32.GetQueuedCompletionStatus(_Z(completionport), + numberofbytes, + completionkey, + overlapped, + milliseconds) + err = _kernel32.GetLastError() + return (err, numberofbytes, completionkey, overlapped) @_ffi.callback("void(void*, bool)") diff --git a/lib_pypy/_pypy_winbase_build.py b/lib_pypy/_pypy_winbase_build.py --- a/lib_pypy/_pypy_winbase_build.py +++ b/lib_pypy/_pypy_winbase_build.py @@ -140,6 +140,8 @@ BOOL WINAPI RegisterWaitForSingleObject(PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, ULONG, ULONG); BOOL WINAPI PostQueuedCompletionStatus(HANDLE, DWORD, ULONG_PTR, LPOVERLAPPED); +BOOL WINAPI GetQueuedCompletionStatus(HANDLE, LPDWORD, PULONG_PTR, LPOVERLAPPED, DWORD); + #define WT_EXECUTEINWAITTHREAD 0x00000004 #define WT_EXECUTEONLYONCE 0x00000008 diff --git a/lib_pypy/_pypy_winbase_cffi.py b/lib_pypy/_pypy_winbase_cffi.py --- a/lib_pypy/_pypy_winbase_cffi.py +++ b/lib_pypy/_pypy_winbase_cffi.py @@ -3,8 +3,8 @@ ffi = _cffi_backend.FFI('_pypy_winbase_cffi', _version = 0x2601, - _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xBF\x03\x00\x00\x13\x11\x00\x00\xC4\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xBE\x03\x00\x00\xBA\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xB5\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xB9\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x79\x03\x00\x00\x57\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x57\x11\x00\x00\x57\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x3B\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x57\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x74\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x74\x0D\x00\x00\x00\x0F\x00\x00\x74\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xBD\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xBF\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x7C\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x79\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x7F\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x7F\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x57\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x85\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x7C\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xC4\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xC4\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xBC\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x02\x01\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xC3\x03\x00\x00\x04\x01\x00\x00\x00\x01', - _globals = (b'\x00\x00\x2C\x23CancelIo',0,b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x7B\x23CreateEventA',0,b'\x00\x00\x81\x23CreateEventW',0,b'\x00\x00\x87\x23CreateFileA',0,b'\x00\x00\xA9\x23CreateFileW',0,b'\x00\x00\x90\x23CreateNamedPipeA',0,b'\x00\x00\x9F\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x56\x23CreateProcessW',0,b'\x00\x00\x4D\x23DuplicateHandle',0,b'\x00\x00\x9D\x23GetCurrentProcess',0,b'\x00\x00\x3D\x23GetExitCodeProcess',0,b'\x00\x00\x71\x23GetLastError',0,b'\x00\x00\x6C\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x9A\x23GetStdHandle',0,b'\x00\x00\x71\x23GetVersion',0,b'\x00\x00\x47\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x65\x23SetErrorMode',0,b'\x00\x00\xB2\x23SetEvent',0,b'\x00\x00\x41\x23SetNamedPipeHandleState',0,b'\x00\x00\x39\x23TerminateProcess',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x68\x23WaitForSingleObject',0,b'\x00\x00\x62\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x76\x23_getwch',0,b'\x00\x00\x76\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x78\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x73\x23_ungetwch',0), - _struct_unions = ((b'\x00\x00\x00\xC1\x00\x00\x00\x03$1',b'\x00\x00\xC0\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xC0\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xBA\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xBE\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x74\x11wShowWindow',b'\x00\x00\x74\x11cbReserved2',b'\x00\x00\xC2\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xB9\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xC1\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xBC\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x31\x11Overlapped'),(b'\x00\x00\x00\xBD\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle')), - _typenames = (b'\x00\x00\x00\x31LPOVERLAPPED',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xBBLPPostCallbackData',b'\x00\x00\x00\x7CLPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\xB9OVERLAPPED',b'\x00\x00\x00\xBAPROCESS_INFORMATION',b'\x00\x00\x00\x7CPSECURITY_ATTRIBUTES',b'\x00\x00\x00\xBCPostCallbackData',b'\x00\x00\x00\xBDSECURITY_ATTRIBUTES',b'\x00\x00\x00\xBESTARTUPINFO',b'\x00\x00\x00\x27WAITORTIMERCALLBACK',b'\x00\x00\x00\x74wint_t'), + _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xC6\x03\x00\x00\x13\x11\x00\x00\xCB\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xC5\x03\x00\x00\xC1\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xBC\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xC0\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x31\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x80\x03\x00\x00\x5E\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x5E\x11\x00\x00\x5E\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x3B\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x5E\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x7B\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x7B\x0D\x00\x00\x00\x0F\x00\x00\x7B\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xC4\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xC6\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x83\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x80\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x86\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x86\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x5E\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x8C\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xCB\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xCB\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xC3\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x02\x01\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xCA\x03\x00\x00\x04\x01\x00\x00\x00\x01', + _globals = (b'\x00\x00\x2C\x23CancelIo',0,b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x82\x23CreateEventA',0,b'\x00\x00\x88\x23CreateEventW',0,b'\x00\x00\x8E\x23CreateFileA',0,b'\x00\x00\xB0\x23CreateFileW',0,b'\x00\x00\x97\x23CreateNamedPipeA',0,b'\x00\x00\xA6\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x5D\x23CreateProcessW',0,b'\x00\x00\x54\x23DuplicateHandle',0,b'\x00\x00\xA4\x23GetCurrentProcess',0,b'\x00\x00\x3D\x23GetExitCodeProcess',0,b'\x00\x00\x78\x23GetLastError',0,b'\x00\x00\x73\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x41\x23GetQueuedCompletionStatus',0,b'\x00\x00\xA1\x23GetStdHandle',0,b'\x00\x00\x78\x23GetVersion',0,b'\x00\x00\x4E\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x6C\x23SetErrorMode',0,b'\x00\x00\xB9\x23SetEvent',0,b'\x00\x00\x48\x23SetNamedPipeHandleState',0,b'\x00\x00\x39\x23TerminateProcess',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x6F\x23WaitForSingleObject',0,b'\x00\x00\x69\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x7D\x23_getwch',0,b'\x00\x00\x7D\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x7F\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x7A\x23_ungetwch',0), + _struct_unions = ((b'\x00\x00\x00\xC8\x00\x00\x00\x03$1',b'\x00\x00\xC7\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xC7\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xC1\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xC5\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x7B\x11wShowWindow',b'\x00\x00\x7B\x11cbReserved2',b'\x00\x00\xC9\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xC0\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xC8\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xC3\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x31\x11Overlapped'),(b'\x00\x00\x00\xC4\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle')), + _typenames = (b'\x00\x00\x00\x31LPOVERLAPPED',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xC2LPPostCallbackData',b'\x00\x00\x00\x83LPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\xC0OVERLAPPED',b'\x00\x00\x00\xC1PROCESS_INFORMATION',b'\x00\x00\x00\x83PSECURITY_ATTRIBUTES',b'\x00\x00\x00\xC3PostCallbackData',b'\x00\x00\x00\xC4SECURITY_ATTRIBUTES',b'\x00\x00\x00\xC5STARTUPINFO',b'\x00\x00\x00\x27WAITORTIMERCALLBACK',b'\x00\x00\x00\x7Bwint_t'), ) diff --git a/lib_pypy/_winapi.py b/lib_pypy/_winapi.py --- a/lib_pypy/_winapi.py +++ b/lib_pypy/_winapi.py @@ -66,84 +66,6 @@ if not ret: raise _WinError() -class Overlapped(object): - def __init__(self, handle): - self.overlapped = _ffi.new('OVERLAPPED[1]') - self.handle = handle - self.readbuffer = None - self.pending = 0 - self.completed = 0 - self.writebuffer = None - self.overlapped[0].hEvent = \ - _kernel32.CreateEventW(NULL, True, False, NULL) - - def __del__(self): - # do this somehow else - xxx - err = _kernel32.GetLastError() - bytes = _ffi.new('DWORD[1]') - o = overlapped[0] - if overlapped[0].pending: - if _kernel32.CancelIoEx(o.handle, o.overlapped) & \ - self.GetOverlappedResult(o.handle, o.overlapped, _ffi.addressof(bytes), True): - # The operation is no longer pending, nothing to do - pass - else: - raise RuntimeError('deleting an overlapped struct with a pending operation not supported') - - @property - def event(self): - return None - - def GetOverlappedResult(self, wait): - transferred = _ffi.new('DWORD[1]', [0]) - res = _kernel32.GetOverlappedResult(self.handle, self.overlapped, transferred, wait != 0) - if res: - err = ERROR_SUCCESS - else: - err = GetLastError() - if err in (ERROR_SUCCESS, ERROR_MORE_DATA, ERROR_OPERATION_ABORTED): - self.completed = 1 - self.pending = 0 - elif res == ERROR_IO_INCOMPLETE: - pass - else: - self.pending = 0 - raise _WinError() - if self.completed and self.read_buffer: - if transferred != len(self.read_buffer): - raise _WinError() - return transferred[0], err - - def getbuffer(self): - xxx - return None - - def cancel(self): - xxx - return None - - -def ConnectNamedPipe(handle, overlapped=False): - if overlapped: - ov = Overlapped(handle) - else: - ov = Overlapped(None) - success = _kernel32.ConnectNamedPipe(handle, ov.overlapped) - if overlapped: - # Overlapped ConnectNamedPipe never returns a success code - assert success == 0 - err = _kernel32.GetLastError() - if err == ERROR_IO_PENDING: - ov.pending = 1 - elif err == ERROR_PIPE_CONNECTED: - _kernel32.SetEvent(ov.overlapped[0].hEvent) - else: - del ov - raise _WinError() - return ov - elif not success: - raise _WinError() def GetCurrentProcess(): return _handle2int(_kernel32.GetCurrentProcess()) From pypy.commits at gmail.com Fri Feb 8 16:16:46 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Fri, 08 Feb 2019 13:16:46 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: Started implementing CreateIoCompletionPort Message-ID: <5c5df1be.1c69fb81.512f9.39ed@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r95910:ba8adec396ef Date: 2019-02-08 15:55 +0000 http://bitbucket.org/pypy/pypy/changeset/ba8adec396ef/ Log: Started implementing CreateIoCompletionPort diff --git a/lib_pypy/_overlapped.py b/lib_pypy/_overlapped.py --- a/lib_pypy/_overlapped.py +++ b/lib_pypy/_overlapped.py @@ -17,7 +17,7 @@ NULL = _ffi.NULL -from _winapi import INVALID_HANDLE_VALUE, _MAX_PATH , _Z +from _winapi import INVALID_HANDLE_VALUE, _MAX_PATH , _Z, _int2handle import _winapi from enum import Enum @@ -100,6 +100,12 @@ return result +def _int2intptr(int2cast): + return _ffi.cast("ULONG *", int2cast) + +def _int2dword(int2cast): + return _ffi.new("DWORD[1]", [int2cast]) + def CreateEvent(eventattributes, manualreset, initialstate, name): event = _kernel32.CreateEventW(NULL, manualreset, initialstate, _Z(name)) if not event: @@ -128,8 +134,19 @@ raise _winapi._WinError() def CreateIoCompletionPort(handle, existingcompletionport, completionkey, numberofconcurrentthreads): - return None + ##completionkey = _int2intptr(completionkey) + existingcompletionport = _int2handle(existingcompletionport) + ##numberofconcurrentthreads = _int2dword(numberofconcurrentthreads) + #import pdb; pdb.set_trace() + result = _kernel32.CreateIoCompletionPort(handle, + existingcompletionport, + completionkey, + numberofconcurrentthreads) + if not result: + raise _winapi._WinError() + + return result def GetQueuedCompletionStatus(completionport, milliseconds): numberofbytes = _ffi.new('DWORD[1]', [0]) @@ -138,7 +155,7 @@ if completionport is None: raise _winapi._WinError() overlapped = _ffi.new('OVERLAPPED*') - result = _kernel32.GetQueuedCompletionStatus(_Z(completionport), + result = _kernel32.GetQueuedCompletionStatus(completionport, numberofbytes, completionkey, overlapped, @@ -146,7 +163,6 @@ err = _kernel32.GetLastError() return (err, numberofbytes, completionkey, overlapped) - @_ffi.callback("void(void*, bool)") def post_to_queue_callback(lpparameter, timerorwaitfired): pdata = _ffi.cast("PostCallbackData *", lpparameter) @@ -157,7 +173,7 @@ data = _ffi.new('PostCallbackData[1]') newwaitobject = _ffi.new("HANDLE[1]") - data[0].hCompletionPort = _Z(completionport) + data[0].hCompletionPort = completionport data[0].Overlapped = ovaddress[0] success = _kernel32.RegisterWaitForSingleObject(newwaitobject, object, diff --git a/lib_pypy/_pypy_winbase_build.py b/lib_pypy/_pypy_winbase_build.py --- a/lib_pypy/_pypy_winbase_build.py +++ b/lib_pypy/_pypy_winbase_build.py @@ -141,6 +141,7 @@ BOOL WINAPI PostQueuedCompletionStatus(HANDLE, DWORD, ULONG_PTR, LPOVERLAPPED); BOOL WINAPI GetQueuedCompletionStatus(HANDLE, LPDWORD, PULONG_PTR, LPOVERLAPPED, DWORD); +HANDLE WINAPI CreateIoCompletionPort(HANDLE, HANDLE, ULONG_PTR, DWORD); #define WT_EXECUTEINWAITTHREAD 0x00000004 #define WT_EXECUTEONLYONCE 0x00000008 diff --git a/lib_pypy/_pypy_winbase_cffi.py b/lib_pypy/_pypy_winbase_cffi.py --- a/lib_pypy/_pypy_winbase_cffi.py +++ b/lib_pypy/_pypy_winbase_cffi.py @@ -3,8 +3,8 @@ ffi = _cffi_backend.FFI('_pypy_winbase_cffi', _version = 0x2601, - _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xC6\x03\x00\x00\x13\x11\x00\x00\xCB\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xC5\x03\x00\x00\xC1\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xBC\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xC0\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x31\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x80\x03\x00\x00\x5E\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x5E\x11\x00\x00\x5E\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x3B\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x5E\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x7B\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x7B\x0D\x00\x00\x00\x0F\x00\x00\x7B\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xC4\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xC6\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x83\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x80\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x86\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x86\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x5E\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x8C\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xCB\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xCB\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xC3\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x02\x01\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xCA\x03\x00\x00\x04\x01\x00\x00\x00\x01', - _globals = (b'\x00\x00\x2C\x23CancelIo',0,b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x82\x23CreateEventA',0,b'\x00\x00\x88\x23CreateEventW',0,b'\x00\x00\x8E\x23CreateFileA',0,b'\x00\x00\xB0\x23CreateFileW',0,b'\x00\x00\x97\x23CreateNamedPipeA',0,b'\x00\x00\xA6\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x5D\x23CreateProcessW',0,b'\x00\x00\x54\x23DuplicateHandle',0,b'\x00\x00\xA4\x23GetCurrentProcess',0,b'\x00\x00\x3D\x23GetExitCodeProcess',0,b'\x00\x00\x78\x23GetLastError',0,b'\x00\x00\x73\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x41\x23GetQueuedCompletionStatus',0,b'\x00\x00\xA1\x23GetStdHandle',0,b'\x00\x00\x78\x23GetVersion',0,b'\x00\x00\x4E\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x6C\x23SetErrorMode',0,b'\x00\x00\xB9\x23SetEvent',0,b'\x00\x00\x48\x23SetNamedPipeHandleState',0,b'\x00\x00\x39\x23TerminateProcess',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x6F\x23WaitForSingleObject',0,b'\x00\x00\x69\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x7D\x23_getwch',0,b'\x00\x00\x7D\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x7F\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x7A\x23_ungetwch',0), - _struct_unions = ((b'\x00\x00\x00\xC8\x00\x00\x00\x03$1',b'\x00\x00\xC7\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xC7\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xC1\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xC5\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x7B\x11wShowWindow',b'\x00\x00\x7B\x11cbReserved2',b'\x00\x00\xC9\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xC0\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xC8\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xC3\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x31\x11Overlapped'),(b'\x00\x00\x00\xC4\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle')), - _typenames = (b'\x00\x00\x00\x31LPOVERLAPPED',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xC2LPPostCallbackData',b'\x00\x00\x00\x83LPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\xC0OVERLAPPED',b'\x00\x00\x00\xC1PROCESS_INFORMATION',b'\x00\x00\x00\x83PSECURITY_ATTRIBUTES',b'\x00\x00\x00\xC3PostCallbackData',b'\x00\x00\x00\xC4SECURITY_ATTRIBUTES',b'\x00\x00\x00\xC5STARTUPINFO',b'\x00\x00\x00\x27WAITORTIMERCALLBACK',b'\x00\x00\x00\x7Bwint_t'), + _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xCC\x03\x00\x00\x13\x11\x00\x00\xD1\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xCB\x03\x00\x00\xC7\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xC2\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xC6\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x31\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x80\x03\x00\x00\x5E\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x5E\x11\x00\x00\x5E\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x3B\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x5E\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x7B\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x7B\x0D\x00\x00\x00\x0F\x00\x00\x7B\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xCA\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xCC\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x83\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x80\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x86\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x86\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x5E\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x8C\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xD1\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xD1\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xC9\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x02\x01\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xD0\x03\x00\x00\x04\x01\x00\x00\x00\x01', + _globals = (b'\x00\x00\x2C\x23CancelIo',0,b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x82\x23CreateEventA',0,b'\x00\x00\x88\x23CreateEventW',0,b'\x00\x00\x8E\x23CreateFileA',0,b'\x00\x00\xB6\x23CreateFileW',0,b'\x00\x00\xA4\x23CreateIoCompletionPort',0,b'\x00\x00\x97\x23CreateNamedPipeA',0,b'\x00\x00\xAC\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x5D\x23CreateProcessW',0,b'\x00\x00\x54\x23DuplicateHandle',0,b'\x00\x00\xAA\x23GetCurrentProcess',0,b'\x00\x00\x3D\x23GetExitCodeProcess',0,b'\x00\x00\x78\x23GetLastError',0,b'\x00\x00\x73\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x41\x23GetQueuedCompletionStatus',0,b'\x00\x00\xA1\x23GetStdHandle',0,b'\x00\x00\x78\x23GetVersion',0,b'\x00\x00\x4E\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x6C\x23SetErrorMode',0,b'\x00\x00\xBF\x23SetEvent',0,b'\x00\x00\x48\x23SetNamedPipeHandleState',0,b'\x00\x00\x39\x23TerminateProcess',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x6F\x23WaitForSingleObject',0,b'\x00\x00\x69\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x7D\x23_getwch',0,b'\x00\x00\x7D\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x7F\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x7A\x23_ungetwch',0), + _struct_unions = ((b'\x00\x00\x00\xCE\x00\x00\x00\x03$1',b'\x00\x00\xCD\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xCD\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xC7\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xCB\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x7B\x11wShowWindow',b'\x00\x00\x7B\x11cbReserved2',b'\x00\x00\xCF\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xC6\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xCE\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xC9\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x31\x11Overlapped'),(b'\x00\x00\x00\xCA\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle')), + _typenames = (b'\x00\x00\x00\x31LPOVERLAPPED',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xC8LPPostCallbackData',b'\x00\x00\x00\x83LPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\xC6OVERLAPPED',b'\x00\x00\x00\xC7PROCESS_INFORMATION',b'\x00\x00\x00\x83PSECURITY_ATTRIBUTES',b'\x00\x00\x00\xC9PostCallbackData',b'\x00\x00\x00\xCASECURITY_ATTRIBUTES',b'\x00\x00\x00\xCBSTARTUPINFO',b'\x00\x00\x00\x27WAITORTIMERCALLBACK',b'\x00\x00\x00\x7Bwint_t'), ) diff --git a/lib_pypy/_winapi.py b/lib_pypy/_winapi.py --- a/lib_pypy/_winapi.py +++ b/lib_pypy/_winapi.py @@ -225,7 +225,7 @@ GENERIC_WRITE = 0x40000000 GENERIC_EXECUTE= 0x20000000 GENERIC_ALL = 0x10000000 -INVALID_HANDLE_VALUE = -1 +INVALID_HANDLE_VALUE = _int2handle(-1) FILE_FLAG_WRITE_THROUGH = 0x80000000 FILE_FLAG_OVERLAPPED = 0x40000000 FILE_FLAG_NO_BUFFERING = 0x20000000 From pypy.commits at gmail.com Fri Feb 8 16:16:47 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Fri, 08 Feb 2019 13:16:47 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: Work in progress - fixed casting issues Message-ID: <5c5df1bf.1c69fb81.74e16.891a@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r95911:d599f3b0c6cb Date: 2019-02-08 21:14 +0000 http://bitbucket.org/pypy/pypy/changeset/d599f3b0c6cb/ Log: Work in progress - fixed casting issues diff --git a/lib_pypy/_overlapped.py b/lib_pypy/_overlapped.py --- a/lib_pypy/_overlapped.py +++ b/lib_pypy/_overlapped.py @@ -17,7 +17,7 @@ NULL = _ffi.NULL -from _winapi import INVALID_HANDLE_VALUE, _MAX_PATH , _Z, _int2handle +from _winapi import INVALID_HANDLE_VALUE, _MAX_PATH , _Z import _winapi from enum import Enum @@ -101,10 +101,14 @@ def _int2intptr(int2cast): - return _ffi.cast("ULONG *", int2cast) + return _ffi.cast("ULONG_PTR", int2cast) def _int2dword(int2cast): - return _ffi.new("DWORD[1]", [int2cast]) + return _ffi.cast("DWORD", int2cast) + +def _int2handle(val): + return _ffi.cast("HANDLE", val) + def CreateEvent(eventattributes, manualreset, initialstate, name): event = _kernel32.CreateEventW(NULL, manualreset, initialstate, _Z(name)) @@ -134,11 +138,11 @@ raise _winapi._WinError() def CreateIoCompletionPort(handle, existingcompletionport, completionkey, numberofconcurrentthreads): - ##completionkey = _int2intptr(completionkey) + completionkey = _int2intptr(completionkey) existingcompletionport = _int2handle(existingcompletionport) - ##numberofconcurrentthreads = _int2dword(numberofconcurrentthreads) - - #import pdb; pdb.set_trace() + numberofconcurrentthreads = _int2dword(numberofconcurrentthreads) + handle = _int2handle(handle) + result = _kernel32.CreateIoCompletionPort(handle, existingcompletionport, completionkey, @@ -166,7 +170,8 @@ @_ffi.callback("void(void*, bool)") def post_to_queue_callback(lpparameter, timerorwaitfired): pdata = _ffi.cast("PostCallbackData *", lpparameter) - _kernel32.PostQueuedCompletionStatus(pdata.hCompletionPort, timerorwaitfired, 0, pdata.Overlapped) + + _kernel32.PostQueuedCompletionStatus(pdata.hCompletionPort, timerorwaitfired, _ffi.cast("ULONG_PTR",0), pdata.Overlapped) def RegisterWaitWithQueue(object, completionport, ovaddress, miliseconds): diff --git a/lib_pypy/_winapi.py b/lib_pypy/_winapi.py --- a/lib_pypy/_winapi.py +++ b/lib_pypy/_winapi.py @@ -23,6 +23,7 @@ raise WindowsError(code, message) def _int2handle(val): + return _ffi.cast("HANDLE", val) def _handle2int(handle): From pypy.commits at gmail.com Fri Feb 8 16:44:39 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Fri, 08 Feb 2019 13:44:39 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: Started implementing WSARecv Message-ID: <5c5df847.1c69fb81.64b80.e144@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r95912:eb3c85f76e9d Date: 2019-02-08 21:43 +0000 http://bitbucket.org/pypy/pypy/changeset/eb3c85f76e9d/ Log: Started implementing WSARecv diff --git a/lib_pypy/_overlapped.py b/lib_pypy/_overlapped.py --- a/lib_pypy/_overlapped.py +++ b/lib_pypy/_overlapped.py @@ -39,10 +39,15 @@ def __init__(self, handle): self.overlapped = _ffi.new('OVERLAPPED[1]') self.handle = handle + ### I can't see these buffers being used. + ### it is possible that we could delete them. self.readbuffer = None + self.writebuffer = None + + self.allocated_buffer = None + self.user_buffer = None self.pending = 0 self.completed = 0 - self.writebuffer = None self.type = OverlappedType.TYPE_NONE self.overlapped[0].hEvent = \ _kernel32.CreateEventW(NULL, True, False, NULL) @@ -98,6 +103,15 @@ ### If we are to support xp we will need to dynamically load the below method _kernel32.CancelIoEx(self.handle, self.overlapped) return result + + def WSARecv(self ,handle, size, flags): + if self.type != OverlappedType.TYPE_NONE: + raise _winapi._WinError() + + self.type = OverlappedType.TYPE_READ + self.handle = handle + self.allocated_buffer = _ffi.buffer(max(1, size)) + return do_WSARecv(self, handle, self.allocated_buffer[:], size, flags) def _int2intptr(int2cast): @@ -188,3 +202,6 @@ _kernel32.WT_EXECUTEINWAITTHREAD | _kernel32.WT_EXECUTEONLYONCE) return newwaitobject + + + \ No newline at end of file From pypy.commits at gmail.com Sat Feb 9 06:00:46 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sat, 09 Feb 2019 03:00:46 -0800 (PST) Subject: [pypy-commit] pypy default: tests are good Message-ID: <5c5eb2de.1c69fb81.bbab5.9a48@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r95913:a362dbfc3e89 Date: 2019-02-09 12:00 +0100 http://bitbucket.org/pypy/pypy/changeset/a362dbfc3e89/ Log: tests are good 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 @@ -20,11 +20,11 @@ as they do on CPython. -.. math-improvements +.. branch: math-improvements Improve performance of long operations where one of the operands fits into an int. -.. regalloc-playgrounds +.. branch: regalloc-playgrounds Improve register allocation in the JIT. From pypy.commits at gmail.com Sat Feb 9 07:50:04 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 09 Feb 2019 04:50:04 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8: Add a conditional_call_elidable here Message-ID: <5c5ecc7c.1c69fb81.5a676.d81b@mx.google.com> Author: Armin Rigo Branch: unicode-utf8 Changeset: r95917:fb5c9143c946 Date: 2019-02-09 13:50 +0100 http://bitbucket.org/pypy/pypy/changeset/fb5c9143c946/ Log: Add a conditional_call_elidable here diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -847,13 +847,12 @@ descr_rmul = descr_mul def _get_index_storage(self): - # XXX write the correct jit.elidable - if self._index_storage == rutf8.null_storage(): - storage = rutf8.create_utf8_index_storage(self._utf8, self._length) - else: - storage = self._index_storage - if not jit.isconstant(self): - self._index_storage = storage + return jit.conditional_call_elidable(self._index_storage, + W_UnicodeObject._compute_index_storage, self) + + def _compute_index_storage(self): + storage = rutf8.create_utf8_index_storage(self._utf8, self._length) + self._index_storage = storage return storage def _getitem_result(self, space, index): From pypy.commits at gmail.com Sat Feb 9 09:26:35 2019 From: pypy.commits at gmail.com (fijal) Date: Sat, 09 Feb 2019 06:26:35 -0800 (PST) Subject: [pypy-commit] pypy arm64: (arigo, fijal, rodolph, bivab) fight until the test nearly passes Message-ID: <5c5ee31b.1c69fb81.f0fcd.0df6@mx.google.com> Author: Maciej Fijalkowski Branch: arm64 Changeset: r95918:dba5b910fb11 Date: 2019-02-09 14:26 +0000 http://bitbucket.org/pypy/pypy/changeset/dba5b910fb11/ Log: (arigo, fijal, rodolph, bivab) fight until the test nearly passes diff --git a/rpython/jit/backend/aarch64/assembler.py b/rpython/jit/backend/aarch64/assembler.py --- a/rpython/jit/backend/aarch64/assembler.py +++ b/rpython/jit/backend/aarch64/assembler.py @@ -4,9 +4,9 @@ #from rpython.jit.backend.arm.locations import imm, StackLocation, get_fp_offset #from rpython.jit.backend.arm.helper.regalloc import VMEM_imm_size from rpython.jit.backend.aarch64.opassembler import ResOpAssembler -from rpython.jit.backend.aarch64.regalloc import Regalloc +from rpython.jit.backend.aarch64.regalloc import (Regalloc, # CoreRegisterManager, check_imm_arg, VFPRegisterManager, -# operations as regalloc_operations) + operations as regalloc_operations) #from rpython.jit.backend.arm import callbuilder from rpython.jit.backend.aarch64 import registers as r from rpython.jit.backend.llsupport import jitframe @@ -30,6 +30,7 @@ def assemble_loop(self, jd_id, unique_id, logger, loopname, inputargs, operations, looptoken, log): clt = CompiledLoopToken(self.cpu, looptoken.number) + clt._debug_nbargs = len(inputargs) looptoken.compiled_loop_token = clt if not we_are_translated(): @@ -127,6 +128,12 @@ self.target_tokens_currently_compiling = {} self.frame_depth_to_patch = [] + def teardown(self): + self.current_clt = None + self._regalloc = None + self.mc = None + self.pending_guards = None + def _build_failure_recovery(self, exc, withfloats=False): pass # XXX @@ -148,9 +155,38 @@ def _check_frame_depth_debug(self, mc): pass + def update_frame_depth(self, frame_depth): + baseofs = self.cpu.get_baseofs_of_frame_field() + self.current_clt.frame_info.update_frame_depth(baseofs, frame_depth) + + def write_pending_failure_recoveries(self): + pass # XXX + def reserve_gcref_table(self, allgcrefs): pass + def materialize_loop(self, looptoken): + self.datablockwrapper.done() # finish using cpu.asmmemmgr + self.datablockwrapper = None + allblocks = self.get_asmmemmgr_blocks(looptoken) + size = self.mc.get_relative_pos() + res = self.mc.materialize(self.cpu, allblocks, + self.cpu.gc_ll_descr.gcrootmap) + #self.cpu.codemap.register_codemap( + # self.codemap.get_final_bytecode(res, size)) + return res + + def patch_gcref_table(self, looptoken, rawstart): + pass + + def process_pending_guards(self, rawstart): + pass + + def fixup_target_tokens(self, rawstart): + for targettoken in self.target_tokens_currently_compiling: + targettoken._ll_loop_code += rawstart + self.target_tokens_currently_compiling = None + def _call_header_with_stack_check(self): self._call_header() if self.stack_check_slowpath == 0: @@ -192,3 +228,160 @@ gcrootmap = self.cpu.gc_ll_descr.gcrootmap if gcrootmap and gcrootmap.is_shadow_stack: self.gen_shadowstack_header(gcrootmap) + + def _assemble(self, regalloc, inputargs, operations): + #self.guard_success_cc = c.cond_none + regalloc.compute_hint_frame_locations(operations) + self._walk_operations(inputargs, operations, regalloc) + #assert self.guard_success_cc == c.cond_none + frame_depth = regalloc.get_final_frame_depth() + jump_target_descr = regalloc.jump_target_descr + if jump_target_descr is not None: + tgt_depth = jump_target_descr._arm_clt.frame_info.jfi_frame_depth + target_frame_depth = tgt_depth - JITFRAME_FIXED_SIZE + frame_depth = max(frame_depth, target_frame_depth) + return frame_depth + + def _walk_operations(self, inputargs, operations, regalloc): + self._regalloc = regalloc + regalloc.operations = operations + while regalloc.position() < len(operations) - 1: + regalloc.next_instruction() + i = regalloc.position() + op = operations[i] + self.mc.mark_op(op) + opnum = op.getopnum() + if rop.has_no_side_effect(opnum) and op not in regalloc.longevity: + regalloc.possibly_free_vars_for_op(op) + elif not we_are_translated() and op.getopnum() == rop.FORCE_SPILL: + regalloc.prepare_force_spill(op) + else: + arglocs = regalloc_operations[opnum](regalloc, op) + if arglocs is not None: + asm_operations[opnum](self, op, arglocs) + if rop.is_guard(opnum): + regalloc.possibly_free_vars(op.getfailargs()) + if op.type != 'v': + regalloc.possibly_free_var(op) + regalloc.possibly_free_vars_for_op(op) + regalloc.free_temp_vars() + regalloc._check_invariants() + if not we_are_translated(): + self.mc.BRK() + self.mc.mark_op(None) # end of the loop + regalloc.operations = None + + # regalloc support + def load(self, loc, value): + """load an immediate value into a register""" + assert (loc.is_core_reg() and value.is_imm() + or loc.is_vfp_reg() and value.is_imm_float()) + if value.is_imm(): + self.mc.gen_load_int(loc.value, value.getint()) + elif value.is_imm_float(): + self.mc.gen_load_int(r.ip.value, value.getint()) + self.mc.VLDR(loc.value, r.ip.value) + + def _mov_stack_to_loc(self, prev_loc, loc): + offset = prev_loc.value + if loc.is_core_reg(): + assert prev_loc.type != FLOAT, 'trying to load from an \ + incompatible location into a core register' + # unspill a core register + assert 0 <= offset <= (1<<15) - 1 + self.mc.LDR_ri(loc.value, r.fp.value, offset) + return + xxx + # elif loc.is_vfp_reg(): + # assert prev_loc.type == FLOAT, 'trying to load from an \ + # incompatible location into a float register' + # # load spilled value into vfp reg + # is_imm = check_imm_arg(offset) + # helper, save = self.get_tmp_reg() + # save_helper = not is_imm and save + # elif loc.is_raw_sp(): + # assert (loc.type == prev_loc.type == FLOAT + # or (loc.type != FLOAT and prev_loc.type != FLOAT)) + # tmp = loc + # if loc.is_float(): + # loc = r.vfp_ip + # else: + # loc, save_helper = self.get_tmp_reg() + # assert not save_helper + # helper, save_helper = self.get_tmp_reg([loc]) + # assert not save_helper + # else: + # assert 0, 'unsupported case' + + # if save_helper: + # self.mc.PUSH([helper.value], cond=cond) + # self.load_reg(self.mc, loc, r.fp, offset, cond=cond, helper=helper) + # if save_helper: + # self.mc.POP([helper.value], cond=cond) + + def regalloc_mov(self, prev_loc, loc): + """Moves a value from a previous location to some other location""" + if prev_loc.is_imm(): + return self._mov_imm_to_loc(prev_loc, loc) + elif prev_loc.is_core_reg(): + self._mov_reg_to_loc(prev_loc, loc) + elif prev_loc.is_stack(): + self._mov_stack_to_loc(prev_loc, loc) + elif prev_loc.is_imm_float(): + self._mov_imm_float_to_loc(prev_loc, loc) + elif prev_loc.is_vfp_reg(): + self._mov_vfp_reg_to_loc(prev_loc, loc) + elif prev_loc.is_raw_sp(): + self._mov_raw_sp_to_loc(prev_loc, loc) + else: + assert 0, 'unsupported case' + mov_loc_loc = regalloc_mov + + def gen_func_epilog(self, mc=None): + gcrootmap = self.cpu.gc_ll_descr.gcrootmap + if mc is None: + mc = self.mc + if gcrootmap and gcrootmap.is_shadow_stack: + self.gen_footer_shadowstack(gcrootmap, mc) + if self.cpu.supports_floats: + XXX + # mc.VPOP([reg.value for reg in r.callee_saved_vfp_registers]) + + # pop all callee saved registers + + stack_size = (len(r.callee_saved_registers) + 2) * WORD + for i in range(0, len(r.callee_saved_registers), 2): + mc.LDP_rri(r.callee_saved_registers[i].value, + r.callee_saved_registers[i + 1].value, + r.sp.value, + (i + 2) * WORD) + mc.LDP_rr_postindex(r.fp.value, r.lr.value, r.sp.value, stack_size) + + mc.RET_r(r.lr.value) + + + +def not_implemented(msg): + msg = '[ARM/asm] %s\n' % msg + if we_are_translated(): + llop.debug_print(lltype.Void, msg) + raise NotImplementedError(msg) + + +def notimplemented_op(self, op, arglocs, regalloc): + print "[ARM/asm] %s not implemented" % op.getopname() + raise NotImplementedError(op) + + +asm_operations = [notimplemented_op] * (rop._LAST + 1) +asm_extra_operations = {} + +for name, value in ResOpAssembler.__dict__.iteritems(): + if name.startswith('emit_opx_'): + opname = name[len('emit_opx_'):] + num = getattr(EffectInfo, 'OS_' + opname.upper()) + asm_extra_operations[num] = value + elif name.startswith('emit_op_'): + opname = name[len('emit_op_'):] + num = getattr(rop, opname.upper()) + asm_operations[num] = value diff --git a/rpython/jit/backend/aarch64/codebuilder.py b/rpython/jit/backend/aarch64/codebuilder.py --- a/rpython/jit/backend/aarch64/codebuilder.py +++ b/rpython/jit/backend/aarch64/codebuilder.py @@ -1,7 +1,11 @@ +from rpython.rlib.objectmodel import we_are_translated from rpython.jit.backend.llsupport.asmmemmgr import BlockBuilderMixin from rpython.jit.backend.aarch64.locations import RegisterLocation from rpython.jit.backend.aarch64 import registers as r +from rpython.rlib.rarithmetic import intmask +from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.tool.udir import udir class AbstractAarch64Builder(object): @@ -28,6 +32,9 @@ self.write32((base << 22) | ((0x7F & (offset >> 3)) << 15) | (reg2 << 10) | (rn << 5) | reg1) + def MOV_r_u16(self, rd, immed, shift): # u16 is an unsigned 16-bit + self.MOVK_r_u16(rd, immed, shift) + def MOV_rr(self, rd, rn): self.ORR_rr(rd, r.xzr.value, rn) @@ -36,12 +43,68 @@ self.write32((base << 21) | (rm << 16) | (rn << 5) | rd) + def MOVK_r_u16(self, rd, immed, shift): + base = 0b111100101 + assert 0 <= immed < 1 << 16 + assert shift in (0, 16, 32, 48) + self.write32((base << 23) | (shift >> 4 << 21) | (immed << 5) | rd) + + def MOVN_r_u16(self, rd, immed): + base = 0b10010010100 + assert 0 <= immed < 1 << 16 + self.write32((base << 21) | (immed << 5) | rd) + def ADD_ri(self, rd, rn, constant): base = 0b1001000100 assert 0 <= constant < 4096 self.write32((base << 22) | (constant << 10) | (rn << 5) | rd) + def LDP_rri(self, reg1, reg2, rn, offset): + base = 0b1010100101 + assert -512 <= offset < 512 + assert offset & 0x7 == 0 + self.write32((base << 22) | ((0x7F & (offset >> 3)) << 15) | + (reg2 << 10) | (rn << 5) | reg1) + + def LDP_rr_postindex(self, reg1, reg2, rn, offset): + base = 0b1010100011 + assert -512 <= offset < 512 + assert offset & 0x7 == 0 + self.write32((base << 22) | ((0x7F & (offset >> 3)) << 15) | + (reg2 << 10) | (rn << 5) | reg1) + + def LDR_ri(self, rt, rn, immed): + base = 0b1111100101 + assert 0 <= immed <= 1<<15 + assert immed & 0x7 == 0 + immed >>= 3 + self.write32((base << 22) | (immed << 10) | (rn << 5) | rt) + + def ADD_rr(self, rd, rn, rm): + base = 0b10001011000 + self.write32((base << 21) | (rm << 16) | (rn << 5) | (rd)) + + def BRK(self): + self.write32(0b11010100001 << 21) + + def gen_load_int(self, r, value): + """r is the register number, value is the value to be loaded to the + register""" + shift = 0 + if value < 0: + value = ~value + nxt = intmask(value & 0xFFFF) + self.MOVN_r_u16(r, nxt) + value >>= 16 + shift += 16 + while value: + nxt = intmask(value & 0xFFFF) + self.MOV_r_u16(r, nxt, shift) + value >>= 16 + shift += 16 + + class InstrBuilder(BlockBuilderMixin, AbstractAarch64Builder): def __init__(self, arch_version=7): diff --git a/rpython/jit/backend/aarch64/locations.py b/rpython/jit/backend/aarch64/locations.py --- a/rpython/jit/backend/aarch64/locations.py +++ b/rpython/jit/backend/aarch64/locations.py @@ -69,6 +69,21 @@ def is_float(self): return True +class ImmLocation(AssemblerLocation): + _immutable_ = True + + def __init__(self, value): + self.value = value + + def getint(self): + return self.value + + def __repr__(self): + return "imm(%d)" % (self.value) + + def is_imm(self): + return True + class StackLocation(AssemblerLocation): _immutable_ = True diff --git a/rpython/jit/backend/aarch64/opassembler.py b/rpython/jit/backend/aarch64/opassembler.py --- a/rpython/jit/backend/aarch64/opassembler.py +++ b/rpython/jit/backend/aarch64/opassembler.py @@ -1,5 +1,35 @@ +from rpython.jit.backend.aarch64 import registers as r from rpython.jit.backend.llsupport.assembler import GuardToken, BaseAssembler class ResOpAssembler(BaseAssembler): - pass + def emit_op_int_add(self, op, arglocs): + return self.int_add_impl(op, arglocs) + + emit_op_nursery_ptr_increment = emit_op_int_add + + def int_add_impl(self, op, arglocs, ovfcheck=False): + l0, l1, res = arglocs + if ovfcheck: + XXX + s = 1 + else: + s = 0 + if l0.is_imm(): + self.mc.ADD_ri(res.value, l1.value, imm=l0.value, s=s) + elif l1.is_imm(): + self.mc.ADD_ri(res.value, l0.value, imm=l1.value, s=s) + else: + self.mc.ADD_rr(res.value, l0.value, l1.value) + + def emit_op_increment_debug_counter(self, op, arglocs): + return # XXXX + base_loc, value_loc = arglocs + self.mc.LDR_ri(value_loc.value, base_loc.value, 0) + self.mc.ADD_ri(value_loc.value, value_loc.value, 1) + self.mc.STR_ri(value_loc.value, base_loc.value, 0) + + def emit_op_finish(self, op, arglocs): + self.mc.MOV_rr(r.x0.value, r.fp.value) + # exit function + self.gen_func_epilog() diff --git a/rpython/jit/backend/aarch64/regalloc.py b/rpython/jit/backend/aarch64/regalloc.py --- a/rpython/jit/backend/aarch64/regalloc.py +++ b/rpython/jit/backend/aarch64/regalloc.py @@ -6,9 +6,34 @@ ConstPtr, INT, REF, FLOAT) from rpython.jit.metainterp.history import TargetToken +from rpython.jit.metainterp.resoperation import rop from rpython.jit.backend.llsupport.regalloc import FrameManager, \ RegisterManager, TempVar, compute_vars_longevity, BaseRegalloc, \ get_scale +from rpython.rtyper.lltypesystem import lltype, rffi, rstr, llmemory +from rpython.jit.backend.aarch64 import registers as r + + +class TempInt(TempVar): + type = INT + + def __repr__(self): + return "" % (id(self),) + + +class TempPtr(TempVar): + type = REF + + def __repr__(self): + return "" % (id(self),) + + +class TempFloat(TempVar): + type = FLOAT + + def __repr__(self): + return "" % (id(self),) + class ARMFrameManager(FrameManager): @@ -168,3 +193,152 @@ for var in vars: if var is not None: # xxx kludgy self.possibly_free_var(var) + + def get_scratch_reg(self, type, forbidden_vars=[], selected_reg=None): + if type == FLOAT: + return self.vfprm.get_scratch_reg(type, forbidden_vars, + selected_reg) + else: + return self.rm.get_scratch_reg(type, forbidden_vars, selected_reg) + + def get_free_reg(self): + return self.rm.get_free_reg() + + def free_temp_vars(self): + self.rm.free_temp_vars() + self.vfprm.free_temp_vars() + + def make_sure_var_in_reg(self, var, forbidden_vars=[], + selected_reg=None, need_lower_byte=False): + if var.type == FLOAT: + return self.vfprm.make_sure_var_in_reg(var, forbidden_vars, + selected_reg, need_lower_byte) + else: + return self.rm.make_sure_var_in_reg(var, forbidden_vars, + selected_reg, need_lower_byte) + + def convert_to_imm(self, value): + if isinstance(value, ConstInt): + return self.rm.convert_to_imm(value) + else: + assert isinstance(value, ConstFloat) + return self.vfprm.convert_to_imm(value) + + def compute_hint_frame_locations(self, operations): + # optimization only: fill in the 'hint_frame_locations' dictionary + # of rm and xrm based on the JUMP at the end of the loop, by looking + # at where we would like the boxes to be after the jump. + op = operations[-1] + if op.getopnum() != rop.JUMP: + return + self.final_jump_op = op + descr = op.getdescr() + assert isinstance(descr, TargetToken) + if descr._ll_loop_code != 0: + # if the target LABEL was already compiled, i.e. if it belongs + # to some already-compiled piece of code + self._compute_hint_frame_locations_from_descr(descr) + #else: + # The loop ends in a JUMP going back to a LABEL in the same loop. + # We cannot fill 'hint_frame_locations' immediately, but we can + # wait until the corresponding prepare_op_label() to know where the + # we would like the boxes to be after the jump. + + def _compute_hint_frame_locations_from_descr(self, descr): + arglocs = self.assembler.target_arglocs(descr) + jump_op = self.final_jump_op + assert len(arglocs) == jump_op.numargs() + for i in range(jump_op.numargs()): + box = jump_op.getarg(i) + if not isinstance(box, Const): + loc = arglocs[i] + if loc is not None and loc.is_stack(): + self.frame_manager.hint_frame_pos[box] = ( + self.fm.get_loc_index(loc)) + + def position(self): + return self.rm.position + + def next_instruction(self): + self.rm.next_instruction() + self.vfprm.next_instruction() + + def prepare_op_increment_debug_counter(self, op): + boxes = op.getarglist() + a0, = boxes + base_loc = self.make_sure_var_in_reg(a0, boxes) + value_loc = self.get_scratch_reg(INT, boxes) + self.free_temp_vars() + return [base_loc, value_loc] + + def _prepare_op_int_add(self, op, fcond): + XXX + boxes = op.getarglist() + a0, a1 = boxes + imm_a0 = check_imm_box(a0) + imm_a1 = check_imm_box(a1) + if not imm_a0 and imm_a1: + l0 = self.make_sure_var_in_reg(a0, boxes) + l1 = self.convert_to_imm(a1) + elif imm_a0 and not imm_a1: + l0 = self.convert_to_imm(a0) + l1 = self.make_sure_var_in_reg(a1, boxes) + else: + l0 = self.make_sure_var_in_reg(a0, boxes) + l1 = self.make_sure_var_in_reg(a1, boxes) + return [l0, l1] + + def prepare_op_int_add(self, op): + arg0 = op.getarg(0) + arg1 = op.getarg(1) + # XXX support immediates + l0 = self.make_sure_var_in_reg(arg0, op.getarglist()) + l1 = self.make_sure_var_in_reg(arg1, op.getarglist()) + self.possibly_free_vars_for_op(op) + res = self.force_allocate_reg(op) + return [l0, l1, res] + + def prepare_op_finish(self, op): + # the frame is in fp, but we have to point where in the frame is + # the potential argument to FINISH + if op.numargs() == 1: + loc = self.make_sure_var_in_reg(op.getarg(0)) + locs = [loc] + else: + locs = [] + return locs + + prepare_op_nursery_ptr_increment = prepare_op_int_add + + def force_allocate_reg(self, var, forbidden_vars=[], selected_reg=None): + if var.type == FLOAT: + return self.vfprm.force_allocate_reg(var, forbidden_vars, + selected_reg) + else: + return self.rm.force_allocate_reg(var, forbidden_vars, + selected_reg) + + def _check_invariants(self): + self.rm._check_invariants() + self.vfprm._check_invariants() + + def get_final_frame_depth(self): + return self.frame_manager.get_frame_depth() + + +def notimplemented(self, op): + print "[ARM64/regalloc] %s not implemented" % op.getopname() + raise NotImplementedError(op) + + +operations = [notimplemented] * (rop._LAST + 1) + + +for key, value in rop.__dict__.items(): + key = key.lower() + if key.startswith('_'): + continue + methname = 'prepare_op_%s' % key + if hasattr(Regalloc, methname): + func = getattr(Regalloc, methname).im_func + operations[value] = func diff --git a/rpython/jit/backend/aarch64/registers.py b/rpython/jit/backend/aarch64/registers.py --- a/rpython/jit/backend/aarch64/registers.py +++ b/rpython/jit/backend/aarch64/registers.py @@ -15,6 +15,9 @@ lr = x30 fp = x29 + +# scratch registers that we use internally, but don't save them +# nor we use them for regalloc ip1 = x17 ip0 = x16 diff --git a/rpython/jit/backend/aarch64/test/test_instr_builder.py b/rpython/jit/backend/aarch64/test/test_instr_builder.py --- a/rpython/jit/backend/aarch64/test/test_instr_builder.py +++ b/rpython/jit/backend/aarch64/test/test_instr_builder.py @@ -45,3 +45,24 @@ cb = CodeBuilder() cb.MOV_rr(r1.value, r2.value) assert cb.hexdump() == assemble("MOV %r, %r" % (r1, r2)) + + @settings(max_examples=20) + @given(r1=st.sampled_from(r.registers), + immed=st.integers(min_value=0, max_value=(1<<16) - 1)) + def test_MOVN(self, r1, immed): + cb = CodeBuilder() + cb.MOVN_r_u16(r1.value, immed) + assert cb.hexdump() == assemble("MOV %r, %d" % (r1, ~immed)) + + @settings(max_examples=20) + @given(r1=st.sampled_from(r.registers), + immed=st.integers(min_value=0, max_value=(1<<16) - 1), + shift=st.sampled_from([0, 16, 32, 48])) + def test_MOV_r_u16(self, r1, immed, shift): + cb = CodeBuilder() + cb.MOV_r_u16(r1.value, immed, shift) + if shift == 0: + assert cb.hexdump() == assemble("MOVK %r, %d" % (r1, immed)) + else: + assert cb.hexdump() == assemble("MOVK %r, %d, lsl %d" % (r1, immed, shift)) + From pypy.commits at gmail.com Sat Feb 9 10:49:42 2019 From: pypy.commits at gmail.com (fijal) Date: Sat, 09 Feb 2019 07:49:42 -0800 (PST) Subject: [pypy-commit] extradoc extradoc: add sprint report Message-ID: <5c5ef696.1c69fb81.9ac05.df85@mx.google.com> Author: fijal Branch: extradoc Changeset: r5944:de18def1f720 Date: 2019-02-09 16:49 +0100 http://bitbucket.org/pypy/extradoc/changeset/de18def1f720/ Log: add sprint report diff --git a/blog/draft/2019-02-sprint.rst b/blog/draft/2019-02-sprint.rst new file mode 100644 --- /dev/null +++ b/blog/draft/2019-02-sprint.rst @@ -0,0 +1,66 @@ +Düsseldorf sprint report + +Hello everyone! + +We are happy to report a successful and well attended sprint that is wrapping up +in Düsseldorf, Germany. In the last week we had eighteen people sprinting at the +Heinrich-Heine-Universität Düsseldorf on various topics. Big chunk of the sprint +was dedicated to various discussions, since we did not manage to gather +core developers in one room in quite a while. Discussion topics included: + +* Funding and general sustainability of open source. + +* Catching up with CPython 3.7/3.8 - we are planning to release 3.6 some time + in the next few months and we will continue working on 3.7/3.8. + +* What to do with VMprof + +* How can we support Cython inside PyPy in a way that will be understood + by the JIT, hence fast. + +* The future of supporting the numeric stack on pypy - we have made significant + progress in the past few years and most of numeric stack works out of the box, + but deployment and performance remain problems. Improving on those problems + remain a very important focus for pypy as a project. + +* Using the presence of CPython developer (Łukasz Langa) and Graal python developer + (Tim Felgentreff) we discussed ways to collaborate in order to improve python + ecosystem across implementations. + +* Pierre-Yves David and Georges Racinet from octobus gave us an exciting demo + on `Heptapod`_, which adds mercurial support to gitlab. + +Some highlights of the coding tasks worked on: + +* Aarch64 (ARM64) JIT backend work has been started, we are able to run the first + test! Tobias Oberstein from Crossbar GmbH and Rodolph Perfetta from ARM joined the + sprint to help kickstart the project. + +* Long running math-improvements branch that was started by Stian Andreassen got merged + after bugfixes done by Alexander Schremmer. It should improve operations on large integers. + +* Arcane art of necromancy was used to revive long dormant regalloc branch started + and nearly finished by Carl Friedrich Bolz-Tereick. The branch got merged and gives + some modest speedups across the board. + +* Andrew Lawrence worked on MSI installer for PyPy on windows. + +* Łukasz worked on improving failing tests on PyPy 3.6 branch. He knows very obscure + details of CPython, hence we managed to progress very quickly on the 3.6 branch. + +* Matti Picus set up new benchmarking server for PyPy 3 branches. + +* Utf8 branch, which changes the internal representation of unicode might be finally + merged at some point very soon. That gives significant speedups in a lot of cases + handling strings. + +* Zlib was missing couple methods, which were added by Ronan Lamy and Julian Berman. + +* Manuel Jacob fixed RevDB failures. + +* Antonio Cuni and Matti Picus worked on 7.0 release which should happen in a few days. + +Best regards, +Maciej Fijałkowski, Carl Friedrich Bolz-Tereick and the whole PyPy team. + +.. _`heptapod`: https://heptapod.net From pypy.commits at gmail.com Sat Feb 9 11:18:10 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sat, 09 Feb 2019 08:18:10 -0800 (PST) Subject: [pypy-commit] extradoc extradoc: some typos Message-ID: <5c5efd42.1c69fb81.1c8a8.211d@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: extradoc Changeset: r5945:6cdc3695cb72 Date: 2019-02-09 17:18 +0100 http://bitbucket.org/pypy/extradoc/changeset/6cdc3695cb72/ Log: some typos diff --git a/blog/draft/2019-02-sprint.rst b/blog/draft/2019-02-sprint.rst --- a/blog/draft/2019-02-sprint.rst +++ b/blog/draft/2019-02-sprint.rst @@ -3,14 +3,15 @@ Hello everyone! We are happy to report a successful and well attended sprint that is wrapping up -in Düsseldorf, Germany. In the last week we had eighteen people sprinting at the -Heinrich-Heine-Universität Düsseldorf on various topics. Big chunk of the sprint -was dedicated to various discussions, since we did not manage to gather -core developers in one room in quite a while. Discussion topics included: +in Düsseldorf, Germany. In the last week we had eighteen people sprinting +at the Heinrich-Heine-Universität Düsseldorf on various topics. A big +chunk of the sprint was dedicated to various discussions, since we did not +manage to gather the core developers in one room in quite a while. +Discussion topics included: * Funding and general sustainability of open source. -* Catching up with CPython 3.7/3.8 - we are planning to release 3.6 some time +* Catching up with CPython 3.7/3.8 – we are planning to release 3.6 some time in the next few months and we will continue working on 3.7/3.8. * What to do with VMprof @@ -18,13 +19,13 @@ * How can we support Cython inside PyPy in a way that will be understood by the JIT, hence fast. -* The future of supporting the numeric stack on pypy - we have made significant - progress in the past few years and most of numeric stack works out of the box, +* The future of supporting the numeric stack on pypy – we have made significant + progress in the past few years and most of the numeric stack works out of the box, but deployment and performance remain problems. Improving on those problems - remain a very important focus for pypy as a project. + remains a very important focus for PyPy as a project. -* Using the presence of CPython developer (Łukasz Langa) and Graal python developer - (Tim Felgentreff) we discussed ways to collaborate in order to improve python +* Using the presence of a CPython developer (Łukasz Langa) and a Graal Python developer + (Tim Felgentreff) we discussed ways to collaborate in order to improve Python ecosystem across implementations. * Pierre-Yves David and Georges Racinet from octobus gave us an exciting demo @@ -36,23 +37,23 @@ test! Tobias Oberstein from Crossbar GmbH and Rodolph Perfetta from ARM joined the sprint to help kickstart the project. -* Long running math-improvements branch that was started by Stian Andreassen got merged +* The long running math-improvements branch that was started by Stian Andreassen got merged after bugfixes done by Alexander Schremmer. It should improve operations on large integers. -* Arcane art of necromancy was used to revive long dormant regalloc branch started +* The arcane art of necromancy was used to revive long dormant regalloc branch started and nearly finished by Carl Friedrich Bolz-Tereick. The branch got merged and gives some modest speedups across the board. * Andrew Lawrence worked on MSI installer for PyPy on windows. -* Łukasz worked on improving failing tests on PyPy 3.6 branch. He knows very obscure - details of CPython, hence we managed to progress very quickly on the 3.6 branch. +* Łukasz worked on improving failing tests on the PyPy 3.6 branch. He knows very obscure + details of CPython (e.g. how pickling works), hence we managed to progress very quickly. -* Matti Picus set up new benchmarking server for PyPy 3 branches. +* Matti Picus set up a new benchmarking server for PyPy 3 branches. -* Utf8 branch, which changes the internal representation of unicode might be finally - merged at some point very soon. That gives significant speedups in a lot of cases - handling strings. +* The Utf8 branch, which changes the internal representation of unicode might be finally + merged at some point very soon. We discussed and improved upon the last few + blockers. It gives significant speedups in a lot of cases handling strings. * Zlib was missing couple methods, which were added by Ronan Lamy and Julian Berman. @@ -60,6 +61,8 @@ * Antonio Cuni and Matti Picus worked on 7.0 release which should happen in a few days. +Now we are all quite exhausted, and are looking forward to catching up on sleep. + Best regards, Maciej Fijałkowski, Carl Friedrich Bolz-Tereick and the whole PyPy team. From pypy.commits at gmail.com Sat Feb 9 11:18:35 2019 From: pypy.commits at gmail.com (fijal) Date: Sat, 09 Feb 2019 08:18:35 -0800 (PST) Subject: [pypy-commit] pypy arm64: (bivab, rodolph, arigo) Message-ID: <5c5efd5b.1c69fb81.db3d6.f871@mx.google.com> Author: Maciej Fijalkowski Branch: arm64 Changeset: r95919:392250be4182 Date: 2019-02-09 16:18 +0000 http://bitbucket.org/pypy/pypy/changeset/392250be4182/ Log: (bivab, rodolph, arigo) Make the first test of test_runner pass on ARM64. Yay! diff --git a/rpython/jit/backend/aarch64/assembler.py b/rpython/jit/backend/aarch64/assembler.py --- a/rpython/jit/backend/aarch64/assembler.py +++ b/rpython/jit/backend/aarch64/assembler.py @@ -163,7 +163,31 @@ pass # XXX def reserve_gcref_table(self, allgcrefs): - pass + gcref_table_size = len(allgcrefs) * WORD + # align to a multiple of 16 and reserve space at the beginning + # of the machine code for the gc table. This lets us write + # machine code with relative addressing (LDR literal). + gcref_table_size = (gcref_table_size + 15) & ~15 + mc = self.mc + assert mc.get_relative_pos() == 0 + for i in range(gcref_table_size): + mc.writechar('\x00') + self.setup_gcrefs_list(allgcrefs) + + def patch_gcref_table(self, looptoken, rawstart): + # the gc table is at the start of the machine code + self.gc_table_addr = rawstart + tracer = self.cpu.gc_ll_descr.make_gcref_tracer(rawstart, + self._allgcrefs) + gcreftracers = self.get_asmmemmgr_gcreftracers(looptoken) + gcreftracers.append(tracer) # keepalive + self.teardown_gcrefs_list() + + def load_from_gc_table(self, regnum, index): + address_in_buffer = index * WORD # at the start of the buffer + p_location = self.mc.get_relative_pos(break_basic_block=False) + offset = address_in_buffer - p_location + self.mc.LDR_r_literal(regnum, offset) def materialize_loop(self, looptoken): self.datablockwrapper.done() # finish using cpu.asmmemmgr @@ -176,9 +200,6 @@ # self.codemap.get_final_bytecode(res, size)) return res - def patch_gcref_table(self, looptoken, rawstart): - pass - def process_pending_guards(self, rawstart): pass @@ -359,6 +380,32 @@ mc.RET_r(r.lr.value) + def store_reg(self, mc, source, base, ofs=0): + # uses r.ip1 as a temporary + if source.is_vfp_reg(): + return self._store_vfp_reg(mc, source, base, ofs) + else: + return self._store_core_reg(mc, source, base, ofs) + + def _store_vfp_reg(self, mc, source, base, ofs): + if check_imm_arg(ofs, VMEM_imm_size): + mc.VSTR(source.value, base.value, imm=ofs, cond=cond) + else: + mc.gen_load_int(helper.value, ofs, cond=cond) + mc.ADD_rr(helper.value, base.value, helper.value, cond=cond) + mc.VSTR(source.value, helper.value, cond=cond) + + def _store_core_reg(self, mc, source, base, ofs): + # uses r.ip1 as a temporary + # XXX fix: + assert ofs & 0x7 == 0 + assert 0 <= ofs < 32768 + mc.STR_ri(source.value, base.value, ofs) + #if check_imm_arg(ofs): + # mc.STR_ri(source.value, base.value, imm=ofs) + #else: + # mc.gen_load_int(r.ip1, ofs) + # mc.STR_rr(source.value, base.value, r.ip1) def not_implemented(msg): diff --git a/rpython/jit/backend/aarch64/codebuilder.py b/rpython/jit/backend/aarch64/codebuilder.py --- a/rpython/jit/backend/aarch64/codebuilder.py +++ b/rpython/jit/backend/aarch64/codebuilder.py @@ -18,6 +18,13 @@ def RET_r(self, arg): self.write32((0b1101011001011111 << 16) | (arg << 5)) + def STR_ri(self, rt, rn, offset): + base = 0b1111100100 + assert offset & 0x7 == 0 + assert 0 <= offset < 32768 + self.write32((base << 22) | ((offset >> 3) << 10) | + (rn << 5) | rt) + def STP_rr_preindex(self, reg1, reg2, rn, offset): base = 0b1010100110 assert -512 <= offset < 512 @@ -32,9 +39,6 @@ self.write32((base << 22) | ((0x7F & (offset >> 3)) << 15) | (reg2 << 10) | (rn << 5) | reg1) - def MOV_r_u16(self, rd, immed, shift): # u16 is an unsigned 16-bit - self.MOVK_r_u16(rd, immed, shift) - def MOV_rr(self, rd, rn): self.ORR_rr(rd, r.xzr.value, rn) @@ -49,6 +53,12 @@ assert shift in (0, 16, 32, 48) self.write32((base << 23) | (shift >> 4 << 21) | (immed << 5) | rd) + def MOVZ_r_u16(self, rd, immed, shift): + base = 0b110100101 + assert 0 <= immed < 1 << 16 + assert shift in (0, 16, 32, 48) + self.write32((base << 23) | (shift >> 4 << 21) | (immed << 5) | rd) + def MOVN_r_u16(self, rd, immed): base = 0b10010010100 assert 0 <= immed < 1 << 16 @@ -64,6 +74,7 @@ base = 0b1010100101 assert -512 <= offset < 512 assert offset & 0x7 == 0 + assert reg1 != reg2 self.write32((base << 22) | ((0x7F & (offset >> 3)) << 15) | (reg2 << 10) | (rn << 5) | reg1) @@ -71,6 +82,9 @@ base = 0b1010100011 assert -512 <= offset < 512 assert offset & 0x7 == 0 + assert reg1 != reg2 + assert rn != reg1 + assert rn != reg2 self.write32((base << 22) | ((0x7F & (offset >> 3)) << 15) | (reg2 << 10) | (rn << 5) | reg1) @@ -78,8 +92,13 @@ base = 0b1111100101 assert 0 <= immed <= 1<<15 assert immed & 0x7 == 0 - immed >>= 3 - self.write32((base << 22) | (immed << 10) | (rn << 5) | rt) + self.write32((base << 22) | (immed >> 3 << 10) | (rn << 5) | rt) + + def LDR_r_literal(self, rt, offset): + base = 0b01011000 + assert -(1 << 20) <= offset < (1<< 20) + assert offset & 0x3 == 0 + self.write32((base << 24) | ((0x7ffff & (offset >> 2)) << 5) | rt) def ADD_rr(self, rd, rn, rm): base = 0b10001011000 @@ -91,18 +110,11 @@ def gen_load_int(self, r, value): """r is the register number, value is the value to be loaded to the register""" - shift = 0 - if value < 0: - value = ~value - nxt = intmask(value & 0xFFFF) - self.MOVN_r_u16(r, nxt) - value >>= 16 - shift += 16 - while value: - nxt = intmask(value & 0xFFFF) - self.MOV_r_u16(r, nxt, shift) - value >>= 16 - shift += 16 + # XXX optimize! + self.MOVZ_r_u16(r, value & 0xFFFF, 0) + self.MOVK_r_u16(r, (value >> 16) & 0xFFFF, 16) + self.MOVK_r_u16(r, (value >> 32) & 0xFFFF, 32) + self.MOVK_r_u16(r, (value >> 48) & 0xFFFF, 48) class InstrBuilder(BlockBuilderMixin, AbstractAarch64Builder): diff --git a/rpython/jit/backend/aarch64/locations.py b/rpython/jit/backend/aarch64/locations.py --- a/rpython/jit/backend/aarch64/locations.py +++ b/rpython/jit/backend/aarch64/locations.py @@ -1,4 +1,5 @@ +from rpython.rlib.rarithmetic import r_int32 from rpython.jit.backend.aarch64.arch import WORD, JITFRAME_FIXED_SIZE from rpython.jit.metainterp.history import INT, FLOAT @@ -73,6 +74,7 @@ _immutable_ = True def __init__(self, value): + assert not isinstance(value, r_int32) self.value = value def getint(self): diff --git a/rpython/jit/backend/aarch64/opassembler.py b/rpython/jit/backend/aarch64/opassembler.py --- a/rpython/jit/backend/aarch64/opassembler.py +++ b/rpython/jit/backend/aarch64/opassembler.py @@ -1,4 +1,6 @@ +from rpython.jit.metainterp.history import (AbstractFailDescr, ConstInt, + INT, FLOAT, REF) from rpython.jit.backend.aarch64 import registers as r from rpython.jit.backend.llsupport.assembler import GuardToken, BaseAssembler @@ -30,6 +32,35 @@ self.mc.STR_ri(value_loc.value, base_loc.value, 0) def emit_op_finish(self, op, arglocs): + base_ofs = self.cpu.get_baseofs_of_frame_field() + if len(arglocs) > 0: + [return_val] = arglocs + self.store_reg(self.mc, return_val, r.fp, base_ofs) + ofs = self.cpu.get_ofs_of_frame_field('jf_descr') + + faildescrindex = self.get_gcref_from_faildescr(op.getdescr()) + self.load_from_gc_table(r.ip0.value, faildescrindex) + # XXX self.mov(fail_descr_loc, RawStackLoc(ofs)) + self.store_reg(self.mc, r.ip0, r.fp, ofs) + if op.numargs() > 0 and op.getarg(0).type == REF: + if self._finish_gcmap: + # we're returning with a guard_not_forced_2, and + # additionally we need to say that r0 contains + # a reference too: + self._finish_gcmap[0] |= r_uint(1) + gcmap = self._finish_gcmap + else: + gcmap = self.gcmap_for_finish + self.push_gcmap(self.mc, gcmap, store=True) + elif self._finish_gcmap: + # we're returning with a guard_not_forced_2 + gcmap = self._finish_gcmap + self.push_gcmap(self.mc, gcmap, store=True) + else: + # note that the 0 here is redundant, but I would rather + # keep that one and kill all the others + ofs = self.cpu.get_ofs_of_frame_field('jf_gcmap') + self.store_reg(self.mc, r.xzr, r.fp, ofs) self.mc.MOV_rr(r.x0.value, r.fp.value) # exit function self.gen_func_epilog() diff --git a/rpython/jit/backend/aarch64/regalloc.py b/rpython/jit/backend/aarch64/regalloc.py --- a/rpython/jit/backend/aarch64/regalloc.py +++ b/rpython/jit/backend/aarch64/regalloc.py @@ -120,7 +120,7 @@ def convert_to_imm(self, c): if isinstance(c, ConstInt): - val = rffi.cast(rffi.INT, c.value) + val = rffi.cast(lltype.Signed, c.value) return locations.ImmLocation(val) else: assert isinstance(c, ConstPtr) diff --git a/rpython/jit/backend/aarch64/test/test_instr_builder.py b/rpython/jit/backend/aarch64/test/test_instr_builder.py --- a/rpython/jit/backend/aarch64/test/test_instr_builder.py +++ b/rpython/jit/backend/aarch64/test/test_instr_builder.py @@ -1,4 +1,4 @@ -from hypothesis import given, settings, strategies as st +from hypothesis import given, settings, strategies as st, assume from rpython.jit.backend.aarch64 import registers as r from rpython.jit.backend.aarch64 import codebuilder from rpython.jit.backend.aarch64.test.gen import assemble @@ -19,7 +19,7 @@ @settings(max_examples=20) @given(r1=st.sampled_from(r.registers)) - def test_ret(self, r1): + def test_RET_r(self, r1): cb = CodeBuilder() cb.RET_r(r1.value) res = cb.hexdump() @@ -48,6 +48,24 @@ @settings(max_examples=20) @given(r1=st.sampled_from(r.registers), + immed=st.integers(min_value=0, max_value=(1<<16) - 1), + shift=st.integers(min_value=0, max_value=3)) + def test_MOVK(self, r1, immed, shift): + cb = CodeBuilder() + cb.MOVK_r_u16(r1.value, immed, shift * 16) + assert cb.hexdump() == assemble("MOVK %r, %d, lsl %d" % (r1, immed, shift * 16)) + + @settings(max_examples=20) + @given(r1=st.sampled_from(r.registers), + immed=st.integers(min_value=0, max_value=(1<<16) - 1), + shift=st.integers(min_value=0, max_value=3)) + def test_MOVZ(self, r1, immed, shift): + cb = CodeBuilder() + cb.MOVZ_r_u16(r1.value, immed, shift * 16) + assert cb.hexdump() == assemble("MOVZ %r, %d, lsl %d" % (r1, immed, shift * 16)) + + @settings(max_examples=20) + @given(r1=st.sampled_from(r.registers), immed=st.integers(min_value=0, max_value=(1<<16) - 1)) def test_MOVN(self, r1, immed): cb = CodeBuilder() @@ -55,14 +73,58 @@ assert cb.hexdump() == assemble("MOV %r, %d" % (r1, ~immed)) @settings(max_examples=20) - @given(r1=st.sampled_from(r.registers), - immed=st.integers(min_value=0, max_value=(1<<16) - 1), - shift=st.sampled_from([0, 16, 32, 48])) - def test_MOV_r_u16(self, r1, immed, shift): + @given(rt=st.sampled_from(r.registers), + rn=st.sampled_from(r.registers), + offset=st.integers(min_value=0, max_value=(1<<12)-1)) + def test_STR_ri(self, rt, rn, offset): cb = CodeBuilder() - cb.MOV_r_u16(r1.value, immed, shift) - if shift == 0: - assert cb.hexdump() == assemble("MOVK %r, %d" % (r1, immed)) - else: - assert cb.hexdump() == assemble("MOVK %r, %d, lsl %d" % (r1, immed, shift)) + cb.STR_ri(rt.value, rn.value, offset * 8) + assert cb.hexdump() == assemble("STR %r, [%r, %d]" % (rt, rn, offset * 8)) + @settings(max_examples=20) + @given(reg1=st.sampled_from(r.registers), + reg2=st.sampled_from(r.registers), + rn=st.sampled_from(r.registers), + offset=st.integers(min_value=-64, max_value=63)) + def test_LDP_rr(self, reg1, reg2, rn, offset): + assume(reg1.value != reg2.value) + cb = CodeBuilder() + cb.LDP_rri(reg1.value, reg2.value, rn.value, offset * 8) + assert cb.hexdump() == assemble("LDP %r, %r, [%r, %d]" % (reg1, reg2, rn, offset * 8)) + # + assume(rn.value != reg1.value) + assume(rn.value != reg2.value) + cb = CodeBuilder() + cb.LDP_rr_postindex(reg1.value, reg2.value, rn.value, offset * 8) + assert cb.hexdump() == assemble("LDP %r, %r, [%r], %d" % (reg1, reg2, rn, offset * 8)) + + @settings(max_examples=20) + @given(rt=st.sampled_from(r.registers), + rn=st.sampled_from(r.registers), + offset=st.integers(min_value=0, max_value=(1<<12)-1)) + def test_LDR_ri(self, rt, rn, offset): + cb = CodeBuilder() + cb.LDR_ri(rt.value, rn.value, offset * 8) + assert cb.hexdump() == assemble("LDR %r, [%r, %d]" % (rt, rn, offset * 8)) + + @settings(max_examples=20) + @given(rt=st.sampled_from(r.registers), + offset=st.integers(min_value=-(1<<18), max_value=(1<<18)-1)) + def test_LDR_r_literal(self, rt, offset): + cb = CodeBuilder() + cb.LDR_r_literal(rt.value, offset * 4) + assert cb.hexdump() == assemble("LDR %r, %d" % (rt, offset * 4)) + + @settings(max_examples=20) + @given(rd=st.sampled_from(r.registers), + rn=st.sampled_from(r.registers), + rm=st.sampled_from(r.registers)) + def test_ADD_rr(self, rd, rn, rm): + cb = CodeBuilder() + cb.ADD_rr(rd.value, rn.value, rm.value) + assert cb.hexdump() == assemble("ADD %r, %r, %r" % (rd, rn, rm)) + + def test_BRK(self): + cb = CodeBuilder() + cb.BRK() + assert cb.hexdump() == assemble("BRK 0") From pypy.commits at gmail.com Sat Feb 9 11:35:44 2019 From: pypy.commits at gmail.com (mattip) Date: Sat, 09 Feb 2019 08:35:44 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: refactor unicode_to_decimal to use only utf8 Message-ID: <5c5f0160.1c69fb81.59fb6.e8de@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95920:902af9e81bcc Date: 2019-02-09 14:10 +0100 http://bitbucket.org/pypy/pypy/changeset/902af9e81bcc/ Log: refactor unicode_to_decimal to use only utf8 diff --git a/TODO b/TODO --- a/TODO +++ b/TODO @@ -1,24 +1,22 @@ * find a better way to run "find" without creating the index storage, if one if one is not already readily available (understand cost now, improve after merge) * write the correct jit_elidable in _get_index_storage (Armin) -* improve performance of splitlines +* improve performance of splitlines (CF) * stop using runicode/unicode and move MAXUNICODE to rutf8 (Matti) -* think about cost of utf8 list strategy (Armin and CF) +* think about cost of utf8 list strategy (CF) * revisit why runicode import str_decode_utf_8_impl needed instead of runicode import str_decode_utf_8 * revisit all places where we do utf8.decode('utf-8'), they should work directly with utf8 (can be converted via runicode.str_decode_utf_8 as well) - rutf8.utf8_encode_mbcs - unicodehelper.fsencode - - unicodehelper.unicode_to_decimal_w - _winreg.interp_winreg * remove 'assert not isinstance(*, unicode) * add a flag that prevents support for unicode in rpython and enable it in PyPy (CF, Armin) * remove asserts from _WIN32 paths in rlib.rposix.re{name,place} * convert all realunicode_w to unicode_w after we flush out all old uses of unicode_w -* benchmark more (looks good so far) -* Review all uses of W_Unicode.text_w, right now it is exactly W_Unicode.utf8_w. +* view all uses of W_Unicode.text_w, right now it is exactly W_Unicode.utf8_w. It shoud only return valid utf8 (see 0be26dc39a59 which broke translation on win32 and failed tests on linux64). Then we can use it in places like _socket.interp_func.getaddrinfo instead of space.encode_unicode_object(w_port, diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -1901,26 +1901,29 @@ def unicode_to_decimal_w(space, w_unistr, allow_surrogates=False): if not isinstance(w_unistr, W_UnicodeObject): raise oefmt(space.w_TypeError, "expected unicode, got '%T'", w_unistr) - value = _rpy_unicode_to_decimal_w(space, w_unistr.utf8_w(space).decode('utf8')) - # XXX this is the only place in the code that this funcion is called. - return unicodehelper.encode_utf8(space, value, - allow_surrogates=allow_surrogates) - -def _rpy_unicode_to_decimal_w(space, unistr): - # XXX rewrite this to accept a utf8 string and use a StringBuilder - result = [u'\0'] * len(unistr) - for i in xrange(len(unistr)): - uchr = ord(unistr[i]) + utf8 = space.utf8_w(w_unistr) + lgt = space.len_w(w_unistr) + result = StringBuilder(lgt) + itr = rutf8.Utf8StringIterator(utf8) + for uchr in itr: if uchr > 127: if unicodedb.isspace(uchr): - result[i] = ' ' + result.append(' ') continue try: uchr = ord(u'0') + unicodedb.decimal(uchr) except KeyError: - pass - result[i] = unichr(uchr) - return u''.join(result) + w_encoding = space.newtext('decimal') + pos = itr.get_pos() + w_start = space.newint(pos) + w_end = space.newint(pos+1) + w_reason = space.newtext('invalid decimal Unicode string') + raise OperationError(space.w_UnicodeEncodeError, + space.newtuple([w_encoding, w_unistr, + w_start, w_end, + w_reason])) + result.append(chr(uchr)) + return result.build() @jit.elidable def g_encode_utf8(value): From pypy.commits at gmail.com Sat Feb 9 11:35:46 2019 From: pypy.commits at gmail.com (mattip) Date: Sat, 09 Feb 2019 08:35:46 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8: deal with s.encode() differently (where s can be either bytes or unicode) Message-ID: <5c5f0162.1c69fb81.68cfa.e7a7@mx.google.com> Author: Matti Picus Branch: unicode-utf8 Changeset: r95921:6cea85fa01f6 Date: 2019-02-09 16:49 +0100 http://bitbucket.org/pypy/pypy/changeset/6cea85fa01f6/ Log: deal with s.encode() differently (where s can be either bytes or unicode) diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py --- a/pypy/objspace/std/bytesobject.py +++ b/pypy/objspace/std/bytesobject.py @@ -465,10 +465,6 @@ raise oefmt(space.w_TypeError, "Cannot use string as modifiable buffer") - def descr_encode(self, space, w_encoding=None, w_errors=None): - w_uni = self.descr_decode(space, space.newtext('ascii'), space.newtext('strict')) - return space.call_method(w_uni, 'encode', w_encoding, w_errors) - def descr_getbuffer(self, space, w_flags): #from pypy.objspace.std.bufferobject import W_Buffer #return W_Buffer(StringBuffer(self._value)) @@ -873,7 +869,7 @@ center = interpindirect2app(W_AbstractBytesObject.descr_center), count = interpindirect2app(W_AbstractBytesObject.descr_count), decode = interpindirect2app(W_AbstractBytesObject.descr_decode), - encode = interpindirect2app(W_BytesObject.descr_encode), + encode = interpindirect2app(W_AbstractBytesObject.descr_encode), expandtabs = interpindirect2app(W_AbstractBytesObject.descr_expandtabs), find = interpindirect2app(W_AbstractBytesObject.descr_find), rfind = interpindirect2app(W_AbstractBytesObject.descr_rfind), diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -1075,7 +1075,13 @@ try: rutf8.check_ascii(s) except rutf8.CheckError as a: - eh = unicodehelper.encode_error_handler(space) + if space.isinstance_w(w_object, space.w_unicode): + eh = unicodehelper.encode_error_handler(space) + else: + # must be a bytes-like object. In order to encode it, + # first "decode" to unicode. Since we cannot, raise a + # UnicodeDecodeError, not a UnicodeEncodeError + eh = unicodehelper.decode_error_handler(space) eh(None, "ascii", "ordinal not in range(128)", s, a.pos, a.pos + 1) assert False, "always raises" From pypy.commits at gmail.com Sat Feb 9 11:35:47 2019 From: pypy.commits at gmail.com (mattip) Date: Sat, 09 Feb 2019 08:35:47 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8: update improved traces in tests Message-ID: <5c5f0163.1c69fb81.b6962.56c9@mx.google.com> Author: Matti Picus Branch: unicode-utf8 Changeset: r95922:624ea5b754de Date: 2019-02-09 17:32 +0100 http://bitbucket.org/pypy/pypy/changeset/624ea5b754de/ Log: update improved traces in tests diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py --- a/pypy/module/pypyjit/test_pypy_c/test_string.py +++ b/pypy/module/pypyjit/test_pypy_c/test_string.py @@ -23,22 +23,29 @@ guard_true(i14, descr=...) guard_not_invalidated(descr=...) i16 = int_eq(i6, %d) - i19 = call_i(ConstClass(ll_int_py_mod__Signed_Signed), i6, i10, descr=) - i21 = int_lt(i19, 0) + i83 = call_i(ConstClass(ll_int_py_mod__Signed_Signed), i6, i10, descr=) + i21 = int_lt(i83, 0) guard_false(i21, descr=...) - i22 = int_ge(i19, i10) + i22 = int_ge(i83, i10) guard_false(i22, descr=...) - i23 = strgetitem(p11, i19) - i24 = int_ge(i19, i12) + i89 = strgetitem(p55, i83) + i24 = int_ge(i83, i12) guard_false(i24, descr=...) - i25 = unicodegetitem(p13, i19) - p27 = newstr(1) - strsetitem(p27, 0, i23) - p30 = call_r(ConstClass(ll_str2unicode__rpy_stringPtr), p27, descr=...) + i87 = strgetitem(p13, i83) + i91 = int_le(i87, 127) + guard_true(i91, descr=...) + i93 = int_add(i83, 1) + i94 = int_gt(i93, i56) + guard_false(i94, descr=...) + p96 = newstr(1) + strsetitem(p96, 0, i89) + i98 = call_i(ConstClass(first_non_ascii_char), p96, descr=) guard_no_exception(descr=...) - i32 = call_i(ConstClass(_ll_2_str_eq_checknull_char__rpy_unicodePtr_UniChar), p30, i25, descr=...) - guard_true(i32, descr=...) - i34 = int_add(i6, 1) + i100 = int_lt(i98, 0) + guard_true(i100, descr=...) + i102 = call_i(ConstClass(_ll_4_str_eq_slice_char__rpy_stringPtr_Signed_Signed_Char), p55, i83, 1, i87, descr=) + guard_true(i102, descr=...) + i104 = int_add(i74, 1) --TICK-- jump(..., descr=...) """ % (-sys.maxint-1,)) @@ -209,9 +216,11 @@ guard_not_invalidated(descr=...) p80 = call_r(ConstClass(ll_str__IntegerR_SignedConst_Signed), i47, descr=) guard_no_exception(descr=...) - p53 = call_r(ConstClass(fast_str_decode_ascii), p80, descr=) + i51 = call_i(ConstClass(first_non_ascii_char), p80, descr=) guard_no_exception(descr=...) + i52 = int_lt(i51, 0) + guard_true(i52, descr=...) + i53 = strlen(p80) --TICK-- jump(..., descr=...) """) - # XXX remove the guard_nonnull above? From pypy.commits at gmail.com Sat Feb 9 13:57:02 2019 From: pypy.commits at gmail.com (mattip) Date: Sat, 09 Feb 2019 10:57:02 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8: remove most imports of runicode, except mbcs (win32). TBD: cpyext, micronumpy Message-ID: <5c5f227e.1c69fb81.45b91.18df@mx.google.com> Author: Matti Picus Branch: unicode-utf8 Changeset: r95924:322380023a4a Date: 2019-02-09 18:21 +0100 http://bitbucket.org/pypy/pypy/changeset/322380023a4a/ Log: remove most imports of runicode, except mbcs (win32). TBD: cpyext, micronumpy diff --git a/pypy/interpreter/astcompiler/optimize.py b/pypy/interpreter/astcompiler/optimize.py --- a/pypy/interpreter/astcompiler/optimize.py +++ b/pypy/interpreter/astcompiler/optimize.py @@ -5,7 +5,7 @@ from pypy.tool import stdlib_opcode as ops from pypy.interpreter.error import OperationError from rpython.rlib.unroll import unrolling_iterable -from rpython.rlib.runicode import MAXUNICODE +from rpython.rlib.rutf8 import MAXUNICODE from rpython.rlib.objectmodel import specialize diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -3,7 +3,7 @@ from pypy.interpreter.error import OperationError, oefmt from rpython.rlib.objectmodel import specialize from rpython.rlib.rstring import StringBuilder -from rpython.rlib import rutf8, runicode +from rpython.rlib import rutf8 from rpython.rlib.rarithmetic import r_uint, intmask from rpython.rtyper.lltypesystem import rffi from pypy.module.unicodedata import unicodedb @@ -40,23 +40,6 @@ space.newtext(msg)])) return raise_unicode_exception_encode - at specialize.memo() -def encode_unicode_error_handler(space): - # Fast version of the "strict" errors handler. - def raise_unicode_exception_encode(errors, encoding, msg, uni, - startingpos, endingpos): - assert isinstance(uni, unicode) - u_len = len(uni) - utf8 = runicode.unicode_encode_utf8sp(uni, u_len) - raise OperationError(space.w_UnicodeEncodeError, - space.newtuple([space.newtext(encoding), - space.newtext(utf8, u_len), - space.newint(startingpos), - space.newint(endingpos), - space.newtext(msg)])) - return u'', None, 0 - return raise_unicode_exception_encode - def default_error_encode( errors, encoding, msg, u, startingpos, endingpos): """A default handler, for tests""" @@ -935,23 +918,45 @@ return result.build() + at specialize.memo() +def _encode_unicode_error_handler(space): + # Fast version of the "strict" errors handler. + from rpython.rlib import runicode + def raise_unicode_exception_encode(errors, encoding, msg, uni, + startingpos, endingpos): + assert isinstance(uni, unicode) + u_len = len(uni) + utf8 = runicode.unicode_encode_utf8sp(uni, u_len) + raise OperationError(space.w_UnicodeEncodeError, + space.newtuple([space.newtext(encoding), + space.newtext(utf8, u_len), + space.newint(startingpos), + space.newint(endingpos), + space.newtext(msg)])) + return u'', None, 0 + return raise_unicode_exception_encode + + def encode_utf8(space, uni, allow_surrogates=False): # Note that Python3 tends to forbid *all* surrogates in utf-8. # If allow_surrogates=True, then revert to the Python 2 behavior # which never raises UnicodeEncodeError. Surrogate pairs are then # allowed, either paired or lone. A paired surrogate is considered # like the non-BMP character it stands for. See also *_utf8sp(). + from rpython.rlib import runicode assert isinstance(uni, unicode) return runicode.unicode_encode_utf_8( uni, len(uni), "strict", - errorhandler=encode_unicode_error_handler(space), + errorhandler=_encode_unicode_error_handler(space), allow_surrogates=allow_surrogates) def encode_utf8sp(space, uni, allow_surrogates=True): + xxx # Surrogate-preserving utf-8 encoding. Any surrogate character # turns into its 3-bytes encoding, whether it is paired or not. # This should always be reversible, and the reverse is # decode_utf8sp(). + from rpython.rlib import runicode return runicode.unicode_encode_utf8sp(uni, len(uni)) def decode_utf8sp(space, string): diff --git a/pypy/module/_codecs/__init__.py b/pypy/module/_codecs/__init__.py --- a/pypy/module/_codecs/__init__.py +++ b/pypy/module/_codecs/__init__.py @@ -1,5 +1,4 @@ from pypy.interpreter.mixedmodule import MixedModule -from rpython.rlib import runicode from rpython.rlib.objectmodel import not_rpython from pypy.module._codecs import interp_codecs @@ -90,6 +89,7 @@ @not_rpython def __init__(self, space, *args): # mbcs codec is Windows specific, and based on rffi. + from rpython.rlib import runicode if (hasattr(runicode, 'str_decode_mbcs')): self.interpleveldefs['mbcs_encode'] = 'interp_codecs.mbcs_encode' self.interpleveldefs['mbcs_decode'] = 'interp_codecs.mbcs_decode' diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -1,8 +1,7 @@ from rpython.rlib import jit, rutf8 from rpython.rlib.objectmodel import we_are_translated, not_rpython from rpython.rlib.rstring import StringBuilder, UnicodeBuilder -from rpython.rlib import runicode -from rpython.rlib.runicode import code_to_unichr, MAXUNICODE +from rpython.rlib.rutf8 import MAXUNICODE from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault @@ -378,7 +377,7 @@ raise oefmt(space.w_TypeError, "handler must be callable") # ____________________________________________________________ -# delegation to runicode/unicodehelper +# delegation to unicodehelper def _find_implementation(impl_name): func = getattr(unicodehelper, impl_name) @@ -447,6 +446,7 @@ ]: make_decoder_wrapper(decoder) +from rpython.rlib import runicode if hasattr(runicode, 'str_decode_mbcs'): make_encoder_wrapper('mbcs_encode') make_decoder_wrapper('mbcs_decode') 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,7 +1,7 @@ import sys from rpython.rlib.rstring import StringBuilder from rpython.rlib.objectmodel import specialize, always_inline, r_dict -from rpython.rlib import rfloat, runicode, rutf8 +from rpython.rlib import rfloat, rutf8 from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rlib.rarithmetic import r_uint from pypy.interpreter.error import oefmt diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -1,5 +1,5 @@ from rpython.rtyper.lltypesystem import rffi, lltype -from rpython.rlib import rstring, runicode +from rpython.rlib import rstring from rpython.tool.sourcetools import func_renamer from pypy.interpreter.error import OperationError, oefmt @@ -192,7 +192,8 @@ @cpython_api([], Py_UNICODE, error=CANNOT_FAIL) def PyUnicode_GetMax(space): """Get the maximum ordinal for a Unicode character.""" - return runicode.UNICHR(runicode.MAXUNICODE) + from rpython.rlib import runicode, rutf8 + return runicode.UNICHR(rutf8.MAXUNICODE) @cpython_api([rffi.VOIDP], rffi.CCHARP, error=CANNOT_FAIL) def PyUnicode_AS_DATA(space, ref): diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py --- a/pypy/module/sys/vm.py +++ b/pypy/module/sys/vm.py @@ -3,7 +3,7 @@ """ from rpython.rlib import jit -from rpython.rlib.runicode import MAXUNICODE +from rpython.rlib.rutf8 import MAXUNICODE from pypy.interpreter import gateway from pypy.interpreter.error import oefmt diff --git a/pypy/objspace/std/newformat.py b/pypy/objspace/std/newformat.py --- a/pypy/objspace/std/newformat.py +++ b/pypy/objspace/std/newformat.py @@ -5,7 +5,7 @@ import string from pypy.interpreter.error import OperationError, oefmt -from rpython.rlib import rstring, runicode, rlocale, rfloat, jit, rutf8 +from rpython.rlib import rstring, rlocale, rfloat, jit, rutf8 from rpython.rlib.objectmodel import specialize from rpython.rlib.rfloat import formatd from rpython.rlib.rarithmetic import r_uint, intmask diff --git a/rpython/rlib/runicode.py b/rpython/rlib/runicode.py --- a/rpython/rlib/runicode.py +++ b/rpython/rlib/runicode.py @@ -9,7 +9,7 @@ # We always use MAXUNICODE = 0x10ffff when unicode objects use utf8 -if 1 or rffi.sizeof(lltype.UniChar) == 4: +if rffi.sizeof(lltype.UniChar) == 4: MAXUNICODE = 0x10ffff allow_surrogate_by_default = False else: diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -26,6 +26,9 @@ from rpython.rlib.unicodedata import unicodedb from rpython.rtyper.lltypesystem import lltype, rffi +# We always use MAXUNICODE = 0x10ffff when unicode objects use utf8 +MAXUNICODE = 0x10ffff +allow_surrogate_by_default = False # we need a way to accept both r_uint and int(nonneg=True) #@signature(types.int_nonneg(), types.bool(), returns=types.str()) From pypy.commits at gmail.com Sat Feb 9 13:57:00 2019 From: pypy.commits at gmail.com (mattip) Date: Sat, 09 Feb 2019 10:57:00 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8: merge default into branch Message-ID: <5c5f227c.1c69fb81.ed68b.c5f9@mx.google.com> Author: Matti Picus Branch: unicode-utf8 Changeset: r95923:b96e9dd44e46 Date: 2019-02-09 17:36 +0100 http://bitbucket.org/pypy/pypy/changeset/b96e9dd44e46/ Log: merge default into branch diff too long, truncating to 2000 out of 9094 lines diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -4,8 +4,10 @@ *~ .*.swp .idea +.mypy_cache .project .pydevproject +.vscode __pycache__ .cache/ diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -58,3 +58,12 @@ 3f6eaa010fce78cc7973bdc1dfdb95970f08fed2 release-pypy3.5-v5.10.1 ab0b9caf307db6592905a80b8faffd69b39005b8 release-pypy2.7-v6.0.0 fdd60ed87e941677e8ea11acf9f1819466521bf2 release-pypy3.5-v6.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +c8805ee6d7846ca2722b106eeaa2f128c699aba3 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +928a4f70d3de7d17449456946154c5da6e600162 release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +fb40f7a5524c77b80e6c468e087d621610137261 release-pypy3.6-v7.0.0 diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -40,16 +40,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -59,8 +59,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -70,10 +70,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -114,12 +114,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -134,8 +134,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -144,10 +145,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -163,6 +164,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -176,6 +178,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -187,7 +190,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -198,7 +200,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -210,6 +211,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -217,12 +219,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -242,7 +246,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -270,12 +273,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -290,10 +296,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -301,28 +309,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -332,6 +338,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -340,6 +347,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -349,8 +357,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -364,7 +373,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -384,12 +392,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz diff --git a/extra_tests/cffi_tests/cffi0/test_function.py b/extra_tests/cffi_tests/cffi0/test_function.py --- a/extra_tests/cffi_tests/cffi0/test_function.py +++ b/extra_tests/cffi_tests/cffi0/test_function.py @@ -46,14 +46,15 @@ assert x != math.sin(1.23) # rounding effects assert abs(x - math.sin(1.23)) < 1E-6 - def test_lround_no_return_value(self): + def test_getenv_no_return_value(self): # check that 'void'-returning functions work too ffi = FFI(backend=self.Backend()) ffi.cdef(""" - void lround(double x); + void getenv(char *); """) - m = ffi.dlopen(lib_m) - x = m.lround(1.23) + needs_dlopen_none() + m = ffi.dlopen(None) + x = m.getenv(b"FOO") assert x is None def test_dlopen_filename(self): diff --git a/extra_tests/cffi_tests/cffi1/test_pkgconfig.py b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py new file mode 100644 --- /dev/null +++ b/extra_tests/cffi_tests/cffi1/test_pkgconfig.py @@ -0,0 +1,95 @@ +# Generated by pypy/tool/import_cffi.py +import sys +import subprocess +import py +import cffi.pkgconfig as pkgconfig +from cffi import PkgConfigError + + +def mock_call(libname, flag): + assert libname=="foobarbaz" + flags = { + "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", + "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", + } + return flags[flag] + + +def test_merge_flags(): + d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} + d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} + + pkgconfig.merge_flags(d1, d2) + assert d1 == { + "ham": [1, 2, 3], + "spam" : ["a", "b", "c", "spam", "spam", "spam"], + "bar" : ["b", "a", "z"], + "foo" : []} + + +def test_pkgconfig(): + assert pkgconfig.flags_from_pkgconfig([]) == {} + + saved = pkgconfig.call + try: + pkgconfig.call = mock_call + flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) + finally: + pkgconfig.call = saved + assert flags == { + 'include_dirs': ['/usr/include/python3.6m'], + 'library_dirs': ['/usr/lib64'], + 'libraries': ['python3.6'], + 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], + 'extra_compile_args': ['-O42'], + 'extra_link_args': ['-shared'] + } + +class mock_subprocess: + PIPE = Ellipsis + class Popen: + def __init__(self, cmd, stdout, stderr): + if mock_subprocess.RESULT is None: + raise OSError("oops can't run") + assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] + def communicate(self): + bout, berr, rc = mock_subprocess.RESULT + self.returncode = rc + return bout, berr + +def test_call(): + saved = pkgconfig.subprocess + try: + pkgconfig.subprocess = mock_subprocess + + mock_subprocess.RESULT = None + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "cannot run pkg-config: oops can't run" + + mock_subprocess.RESULT = b"", "Foo error!\n", 1 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "Foo error!" + + mock_subprocess.RESULT = b"abc\\def\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value).startswith("pkg-config --cflags libfoo returned an " + "unsupported backslash-escaped output:") + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + if sys.version_info >= (3,): + mock_subprocess.RESULT = b"\xff\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, + "libfoo", "--cflags", encoding="utf-8") + assert str(e.value) == ( + "pkg-config --cflags libfoo returned bytes that cannot be " + "decoded with encoding 'utf-8':\nb'\\xff\\n'") + + finally: + pkgconfig.subprocess = saved diff --git a/lib_pypy/_csv.py b/lib_pypy/_csv.py deleted file mode 100644 --- a/lib_pypy/_csv.py +++ /dev/null @@ -1,573 +0,0 @@ -"""CSV parsing and writing. - -This module provides classes that assist in the reading and writing -of Comma Separated Value (CSV) files, and implements the interface -described by PEP 305. Although many CSV files are simple to parse, -the format is not formally defined by a stable specification and -is subtle enough that parsing lines of a CSV file with something -like line.split(\",\") is bound to fail. The module supports three -basic APIs: reading, writing, and registration of dialects. - - -DIALECT REGISTRATION: - -Readers and writers support a dialect argument, which is a convenient -handle on a group of settings. When the dialect argument is a string, -it identifies one of the dialects previously registered with the module. -If it is a class or instance, the attributes of the argument are used as -the settings for the reader or writer: - - class excel: - delimiter = ',' - quotechar = '\"' - escapechar = None - doublequote = True - skipinitialspace = False - lineterminator = '\\r\\n' - quoting = QUOTE_MINIMAL - -SETTINGS: - - * quotechar - specifies a one-character string to use as the - quoting character. It defaults to '\"'. - * delimiter - specifies a one-character string to use as the - field separator. It defaults to ','. - * skipinitialspace - specifies how to interpret whitespace which - immediately follows a delimiter. It defaults to False, which - means that whitespace immediately following a delimiter is part - of the following field. - * lineterminator - specifies the character sequence which should - terminate rows. - * quoting - controls when quotes should be generated by the writer. - It can take on any of the following module constants: - - csv.QUOTE_MINIMAL means only when required, for example, when a - field contains either the quotechar or the delimiter - csv.QUOTE_ALL means that quotes are always placed around fields. - csv.QUOTE_NONNUMERIC means that quotes are always placed around - fields which do not parse as integers or floating point - numbers. - csv.QUOTE_NONE means that quotes are never placed around fields. - * escapechar - specifies a one-character string used to escape - the delimiter when quoting is set to QUOTE_NONE. - * doublequote - controls the handling of quotes inside fields. When - True, two consecutive quotes are interpreted as one during read, - and when writing, each quote character embedded in the data is - written as two quotes. -""" - -__version__ = "1.0" - -QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE = range(4) -_dialects = {} -_field_limit = 128 * 1024 # max parsed field size - -class Error(Exception): - pass - -class Dialect(object): - """CSV dialect - - The Dialect type records CSV parsing and generation options.""" - - __slots__ = ["_delimiter", "_doublequote", "_escapechar", - "_lineterminator", "_quotechar", "_quoting", - "_skipinitialspace", "_strict"] - - def __new__(cls, dialect, **kwargs): - - for name in kwargs: - if '_' + name not in Dialect.__slots__: - raise TypeError("unexpected keyword argument '%s'" % - (name,)) - - if dialect is not None: - if isinstance(dialect, basestring): - dialect = get_dialect(dialect) - - # Can we reuse this instance? - if (isinstance(dialect, Dialect) - and all(value is None for value in kwargs.itervalues())): - return dialect - - self = object.__new__(cls) - - - def set_char(x): - if x is None: - return None - if isinstance(x, str) and len(x) <= 1: - return x - raise TypeError("%r must be a 1-character string" % (name,)) - def set_str(x): - if isinstance(x, str): - return x - raise TypeError("%r must be a string" % (name,)) - def set_quoting(x): - if x in range(4): - return x - raise TypeError("bad 'quoting' value") - - attributes = {"delimiter": (',', set_char), - "doublequote": (True, bool), - "escapechar": (None, set_char), - "lineterminator": ("\r\n", set_str), - "quotechar": ('"', set_char), - "quoting": (QUOTE_MINIMAL, set_quoting), - "skipinitialspace": (False, bool), - "strict": (False, bool), - } - - # Copy attributes - notset = object() - for name in Dialect.__slots__: - name = name[1:] - value = notset - if name in kwargs: - value = kwargs[name] - elif dialect is not None: - value = getattr(dialect, name, notset) - - # mapping by name: (default, converter) - if value is notset: - value = attributes[name][0] - if name == 'quoting' and not self.quotechar: - value = QUOTE_NONE - else: - converter = attributes[name][1] - if converter: - value = converter(value) - - setattr(self, '_' + name, value) - - if not self.delimiter: - raise TypeError("delimiter must be set") - - if self.quoting != QUOTE_NONE and not self.quotechar: - raise TypeError("quotechar must be set if quoting enabled") - - if not self.lineterminator: - raise TypeError("lineterminator must be set") - - return self - - delimiter = property(lambda self: self._delimiter) - doublequote = property(lambda self: self._doublequote) - escapechar = property(lambda self: self._escapechar) - lineterminator = property(lambda self: self._lineterminator) - quotechar = property(lambda self: self._quotechar) - quoting = property(lambda self: self._quoting) - skipinitialspace = property(lambda self: self._skipinitialspace) - strict = property(lambda self: self._strict) - - -def _call_dialect(dialect_inst, kwargs): - return Dialect(dialect_inst, **kwargs) - -def register_dialect(name, dialect=None, **kwargs): - """Create a mapping from a string name to a dialect class. - dialect = csv.register_dialect(name, dialect)""" - if not isinstance(name, basestring): - raise TypeError("dialect name must be a string or unicode") - - dialect = _call_dialect(dialect, kwargs) - _dialects[name] = dialect - -def unregister_dialect(name): - """Delete the name/dialect mapping associated with a string name.\n - csv.unregister_dialect(name)""" - try: - del _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def get_dialect(name): - """Return the dialect instance associated with name. - dialect = csv.get_dialect(name)""" - try: - return _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def list_dialects(): - """Return a list of all know dialect names - names = csv.list_dialects()""" - return list(_dialects) - -class Reader(object): - """CSV reader - - Reader objects are responsible for reading and parsing tabular data - in CSV format.""" - - - (START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD, - IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD, - EAT_CRNL) = range(8) - - def __init__(self, iterator, dialect=None, **kwargs): - self.dialect = _call_dialect(dialect, kwargs) - self.input_iter = iter(iterator) - self.line_num = 0 - - self._parse_reset() - - def _parse_reset(self): - self.field = '' - self.fields = [] - self.state = self.START_RECORD - self.numeric_field = False - - def __iter__(self): - return self - - def next(self): - self._parse_reset() - while True: - try: - line = next(self.input_iter) - except StopIteration: - # End of input OR exception - if len(self.field) > 0: - raise Error("newline inside string") - raise - - self.line_num += 1 - - if '\0' in line: - raise Error("line contains NULL byte") - pos = 0 - while pos < len(line): - pos = self._parse_process_char(line, pos) - self._parse_eol() - - if self.state == self.START_RECORD: - break - - fields = self.fields - self.fields = [] - return fields - - def _parse_process_char(self, line, pos): - c = line[pos] - if self.state == self.IN_FIELD: - # in unquoted field - pos2 = pos - while True: - if c in '\n\r': - # end of line - return [fields] - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.EAT_CRNL - elif c == self.dialect.escapechar: - # possible escaped character - pos2 -= 1 - self.state = self.ESCAPED_CHAR - elif c == self.dialect.delimiter: - # save field - wait for new field - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.START_FIELD - else: - # normal character - save in field - pos2 += 1 - if pos2 < len(line): - c = line[pos2] - continue - break - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - 1 - - elif self.state == self.START_RECORD: - if c in '\n\r': - self.state = self.EAT_CRNL - else: - self.state = self.START_FIELD - # restart process - self._parse_process_char(line, pos) - - elif self.state == self.START_FIELD: - if c in '\n\r': - # save empty field - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # start quoted field - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.escapechar: - # possible escaped character - self.state = self.ESCAPED_CHAR - elif c == ' ' and self.dialect.skipinitialspace: - # ignore space at start of field - pass - elif c == self.dialect.delimiter: - # save empty field - self._parse_save_field() - else: - # begin new unquoted field - if self.dialect.quoting == QUOTE_NONNUMERIC: - self.numeric_field = True - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.IN_QUOTED_FIELD: - if c == self.dialect.escapechar: - # possible escape character - self.state = self.ESCAPE_IN_QUOTED_FIELD - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - if self.dialect.doublequote: - # doublequote; " represented by "" - self.state = self.QUOTE_IN_QUOTED_FIELD - else: - #end of quote part of field - self.state = self.IN_FIELD - else: - # normal character - save in field - self._parse_add_char(c) - - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # doublequote - seen a quote in a quoted field - if (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # save "" as " - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.delimiter: - # save field - wait for new field - self._parse_save_field() - self.state = self.START_FIELD - elif c in '\r\n': - # end of line - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif not self.dialect.strict: - self._parse_add_char(c) - self.state = self.IN_FIELD - else: - raise Error("'%c' expected after '%c'" % - (self.dialect.delimiter, self.dialect.quotechar)) - - elif self.state == self.EAT_CRNL: - if c not in '\r\n': - raise Error("new-line character seen in unquoted field - " - "do you need to open the file " - "in universal-newline mode?") - - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - return pos + 1 - - def _parse_eol(self): - if self.state == self.EAT_CRNL: - self.state = self.START_RECORD - elif self.state == self.START_RECORD: - # empty line - return [] - pass - elif self.state == self.IN_FIELD: - # in unquoted field - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.START_FIELD: - # save empty field - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char('\n') - self.state = self.IN_FIELD - elif self.state == self.IN_QUOTED_FIELD: - pass - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char('\n') - self.state = self.IN_QUOTED_FIELD - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - def _parse_save_field(self): - field, self.field = self.field, '' - if self.numeric_field: - self.numeric_field = False - field = float(field) - self.fields.append(field) - - def _parse_add_char(self, c): - if len(self.field) + len(c) > _field_limit: - raise Error("field larger than field limit (%d)" % (_field_limit)) - self.field += c - - -class Writer(object): - """CSV writer - - Writer objects are responsible for generating tabular data - in CSV format from sequence input.""" - - def __init__(self, file, dialect=None, **kwargs): - if not (hasattr(file, 'write') and callable(file.write)): - raise TypeError("argument 1 must have a 'write' method") - self.writeline = file.write - self.dialect = _call_dialect(dialect, kwargs) - - def _join_reset(self): - self.rec = [] - self.num_fields = 0 - - def _join_append(self, field, quoted, quote_empty): - dialect = self.dialect - # If this is not the first field we need a field separator - if self.num_fields > 0: - self.rec.append(dialect.delimiter) - - if dialect.quoting == QUOTE_NONE: - need_escape = tuple(dialect.lineterminator) + ( - dialect.escapechar, # escapechar always first - dialect.delimiter, dialect.quotechar) - - else: - for c in tuple(dialect.lineterminator) + ( - dialect.delimiter, dialect.escapechar): - if c and c in field: - quoted = True - - need_escape = () - if dialect.quotechar in field: - if dialect.doublequote: - field = field.replace(dialect.quotechar, - dialect.quotechar * 2) - quoted = True - else: - need_escape = (dialect.quotechar,) - - - for c in need_escape: - if c and c in field: - if not dialect.escapechar: - raise Error("need to escape, but no escapechar set") - field = field.replace(c, dialect.escapechar + c) - - # If field is empty check if it needs to be quoted - if field == '' and quote_empty: - if dialect.quoting == QUOTE_NONE: - raise Error("single empty field record must be quoted") - quoted = 1 - - if quoted: - field = dialect.quotechar + field + dialect.quotechar - - self.rec.append(field) - self.num_fields += 1 - - - - def writerow(self, row): - dialect = self.dialect - try: - rowlen = len(row) - except TypeError: - raise Error("sequence expected") - - # join all fields in internal buffer - self._join_reset() - - for field in row: - quoted = False - if dialect.quoting == QUOTE_NONNUMERIC: - try: - float(field) - except: - quoted = True - # This changed since 2.5: - # quoted = not isinstance(field, (int, long, float)) - elif dialect.quoting == QUOTE_ALL: - quoted = True - - if field is None: - value = "" - elif isinstance(field, float): - value = repr(field) - else: - value = str(field) - self._join_append(value, quoted, rowlen == 1) - - # add line terminator - self.rec.append(dialect.lineterminator) - - self.writeline(''.join(self.rec)) - - def writerows(self, rows): - for row in rows: - self.writerow(row) - -def reader(*args, **kwargs): - """ - csv_reader = reader(iterable [, dialect='excel'] - [optional keyword args]) - for row in csv_reader: - process(row) - - The "iterable" argument can be any object that returns a line - of input for each iteration, such as a file object or a list. The - optional \"dialect\" parameter is discussed below. The function - also accepts optional keyword arguments which override settings - provided by the dialect. - - The returned object is an iterator. Each iteration returns a row - of the CSV file (which can span multiple input lines)""" - - return Reader(*args, **kwargs) - -def writer(*args, **kwargs): - """ - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - for row in sequence: - csv_writer.writerow(row) - - [or] - - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - csv_writer.writerows(rows) - - The \"fileobj\" argument can be any object that supports the file API.""" - return Writer(*args, **kwargs) - - -undefined = object() -def field_size_limit(limit=undefined): - """Sets an upper limit on parsed fields. - csv.field_size_limit([limit]) - - Returns old limit. If limit is not given, no new limit is set and - the old limit is returned""" - - global _field_limit - old_limit = _field_limit - - if limit is not undefined: - if not isinstance(limit, (int, long)): - raise TypeError("int expected, got %s" % - (limit.__class__.__name__,)) - _field_limit = limit - - return old_limit 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 @@ -3,6 +3,7 @@ from .api import FFI from .error import CDefError, FFIError, VerificationError, VerificationMissing +from .error import PkgConfigError __version__ = "1.12.0" __version_info__ = (1, 12, 0) 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 @@ -592,7 +592,7 @@ if sys.platform == "win32": # we need 'libpypy-c.lib'. Current distributions of # pypy (>= 4.1) contain it as 'libs/python27.lib'. - pythonlib = "python27" + pythonlib = "python{0[0]}{0[1]}".format(sys.version_info) if hasattr(sys, 'prefix'): ensure('library_dirs', os.path.join(sys.prefix, 'libs')) else: @@ -643,6 +643,16 @@ self._assigned_source = (str(module_name), source, source_extension, kwds) + def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, + source_extension='.c', **kwds): + from . import pkgconfig + if not isinstance(pkgconfig_libs, list): + raise TypeError("the pkgconfig_libs argument must be a list " + "of package names") + kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) + pkgconfig.merge_flags(kwds, kwds2) + self.set_source(module_name, source, source_extension, **kwds) + def distutils_extension(self, tmpdir='build', verbose=True): from distutils.dir_util import mkpath from .recompiler import recompile diff --git a/lib_pypy/cffi/error.py b/lib_pypy/cffi/error.py --- a/lib_pypy/cffi/error.py +++ b/lib_pypy/cffi/error.py @@ -1,8 +1,9 @@ class FFIError(Exception): - pass + __module__ = 'cffi' class CDefError(Exception): + __module__ = 'cffi' def __str__(self): try: current_decl = self.args[1] @@ -16,8 +17,15 @@ class VerificationError(Exception): """ An error raised when verification fails """ + __module__ = 'cffi' class VerificationMissing(Exception): """ An error raised when incomplete structures are passed into cdef, but no verification has been done """ + __module__ = 'cffi' + +class PkgConfigError(Exception): + """ An error raised for missing modules in pkg-config + """ + __module__ = 'cffi' diff --git a/lib_pypy/cffi/pkgconfig.py b/lib_pypy/cffi/pkgconfig.py new file mode 100644 --- /dev/null +++ b/lib_pypy/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except EnvironmentError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret diff --git a/lib_pypy/pwd.py b/lib_pypy/pwd.py deleted file mode 100644 --- a/lib_pypy/pwd.py +++ /dev/null @@ -1,114 +0,0 @@ -# indirectly based on ctypes implementation: Victor Stinner, 2008-05-08 -""" -This module provides access to the Unix password database. -It is available on all Unix versions. - -Password database entries are reported as 7-tuples containing the following -items from the password database (see `'), in order: -pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell. -The uid and gid items are integers, all others are strings. An -exception is raised if the entry asked for cannot be found. -""" - -from _pwdgrp_cffi import ffi, lib -import _structseq -import thread -_lock = thread.allocate_lock() - -try: from __pypy__ import builtinify -except ImportError: builtinify = lambda f: f - - -class struct_passwd: - """ - pwd.struct_passwd: Results from getpw*() routines. - - This object may be accessed either as a tuple of - (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell) - or via the object attributes as named in the above tuple. - """ - __metaclass__ = _structseq.structseqtype - name = "pwd.struct_passwd" - - pw_name = _structseq.structseqfield(0) - pw_passwd = _structseq.structseqfield(1) - pw_uid = _structseq.structseqfield(2) - pw_gid = _structseq.structseqfield(3) - pw_gecos = _structseq.structseqfield(4) - pw_dir = _structseq.structseqfield(5) - pw_shell = _structseq.structseqfield(6) - - -def _mkpwent(pw): - return struct_passwd([ - ffi.string(pw.pw_name), - ffi.string(pw.pw_passwd), - pw.pw_uid, - pw.pw_gid, - ffi.string(pw.pw_gecos), - ffi.string(pw.pw_dir), - ffi.string(pw.pw_shell)]) - - at builtinify -def getpwuid(uid): - """ - getpwuid(uid) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given numeric user ID. - See pwd.__doc__ for more on password database entries. - """ - with _lock: - pw = lib.getpwuid(uid) - if not pw: - raise KeyError("getpwuid(): uid not found: %s" % uid) - return _mkpwent(pw) - - at builtinify -def getpwnam(name): - """ - getpwnam(name) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given user name. - See pwd.__doc__ for more on password database entries. - """ - if not isinstance(name, basestring): - raise TypeError("expected string") - name = str(name) - with _lock: - pw = lib.getpwnam(name) - if not pw: - raise KeyError("getpwname(): name not found: %s" % name) - return _mkpwent(pw) - - at builtinify -def getpwall(): - """ - getpwall() -> list_of_entries - Return a list of all available password database entries, in arbitrary order. - See pwd.__doc__ for more on password database entries. - """ - users = [] - with _lock: - lib.setpwent() - while True: - pw = lib.getpwent() - if not pw: - break - users.append(_mkpwent(pw)) - lib.endpwent() - return users - -__all__ = ('struct_passwd', 'getpwuid', 'getpwnam', 'getpwall') - -if __name__ == "__main__": -# Uncomment next line to test CPython implementation -# from pwd import getpwuid, getpwnam, getpwall - from os import getuid - uid = getuid() - pw = getpwuid(uid) - print("uid %s: %s" % (pw.pw_uid, pw)) - name = pw.pw_name - print("name %r: %s" % (name, getpwnam(name))) - print("All:") - for pw in getpwall(): - print(pw) diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst --- a/pypy/doc/build.rst +++ b/pypy/doc/build.rst @@ -220,11 +220,12 @@ Making a debug build of PyPy ---------------------------- -If the Makefile is rerun with the lldebug or lldebug0 target, appropriate -compilation flags are added to add debug info and reduce compiler optimizations -to ``-O0`` respectively. If you stop in a debugger, you will see the -very wordy machine-generated C code from the rpython translation step, which -takes a little bit of reading to relate back to the rpython code. +Rerun the ``Makefile`` with the ``make lldebug`` or ``make lldebug0`` target, +which will build in a way that running under a debugger makes sense. +Appropriate compilation flags are added to add debug info, and for ``lldebug0`` +compiler optimizations are fully disabled. If you stop in a debugger, you will +see the very wordy machine-generated C code from the rpython translation step, +which takes a little bit of reading to relate back to the rpython code. Build cffi import libraries for the stdlib ------------------------------------------ diff --git a/pypy/doc/contributor.rst b/pypy/doc/contributor.rst --- a/pypy/doc/contributor.rst +++ b/pypy/doc/contributor.rst @@ -7,16 +7,16 @@ Armin Rigo Maciej Fijalkowski Carl Friedrich Bolz-Tereick + Antonio Cuni Amaury Forgeot d'Arc - Antonio Cuni Matti Picus Samuele Pedroni Ronan Lamy Alex Gaynor Philip Jenvey + Richard Plangger Brian Kearns - Richard Plangger - Michael Hudson + Michael Hudson-Doyle Manuel Jacob David Schneider Holger Krekel @@ -26,8 +26,8 @@ Anders Chrigstrom Wim Lavrijsen Eric van Riet Paap + Remi Meier Richard Emslie - Remi Meier Alexander Schremmer Dan Villiom Podlaski Christiansen Lukas Diekmann @@ -37,10 +37,10 @@ Niklaus Haldimann Camillo Bruni Laura Creighton - Romain Guillebert Toon Verwaest Leonardo Santagada Seo Sanghyeon + Romain Guillebert Ronny Pfannschmidt Justin Peel Raffael Tfirst @@ -81,12 +81,12 @@ Squeaky Edd Barrett Timo Paulssen + Laurence Tratt Marius Gedminas Nicolas Truessel Alexandre Fayolle Simon Burton Martin Matusiak - Laurence Tratt Wenzhu Man Konstantin Lopuhin John Witulski @@ -101,8 +101,9 @@ Jean-Philippe St. Pierre Guido van Rossum Pavel Vinogradov + Stefan Beyer + William Leslie Paweł Piotr Przeradowski - William Leslie marky1991 Ilya Osadchiy Tobias Oberstein @@ -111,10 +112,10 @@ Taavi Burns Adrian Kuhn tav + Stian Andreassen Georg Brandl Joannah Nanjekye Bert Freudenberg - Stian Andreassen Wanja Saatkamp Mike Blume Gerald Klix @@ -130,6 +131,7 @@ Vasily Kuznetsov Preston Timmons David Ripton + Pieter Zieschang Dusty Phillips Lukas Renggli Guenter Jantzen @@ -143,6 +145,7 @@ Andrew Durdin Ben Young Michael Schneider + Yusuke Tsutsumi Nicholas Riley Jason Chu Igor Trindade Oliveira @@ -154,7 +157,6 @@ Mariano Anaya anatoly techtonik Karl Bartel - Stefan Beyer Gabriel Lavoie Jared Grubb Alecsandru Patrascu @@ -165,7 +167,6 @@ Victor Stinner Andrews Medina Aaron Iles - p_zieschang at yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -177,6 +178,7 @@ Mikael Schönenberg Stanislaw Halik Mihnea Saracin + Matt Jackson Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -184,12 +186,14 @@ Anders Qvist Corbin Simpson Chirag Jadwani + Pauli Virtanen Jonathan David Riehl Beatrice During Alex Perry Robert Zaremba Alan McIntyre Alexander Sedov + David C Ellis Vaibhav Sood Reuben Cummings Attila Gobi @@ -209,7 +213,6 @@ Arjun Naik Aaron Gallagher Alexis Daboville - Pieter Zieschang Karl Ramm Lukas Vacek Omer Katz @@ -237,12 +240,15 @@ Catalin Gabriel Manciu Jacob Oscarson Ryan Gonzalez + Antoine Dupre Kristjan Valur Jonsson Lucio Torre Richard Lancaster Dan Buch Lene Wagner Tomo Cocoa + Miro Hrončok + Anthony Sottile David Lievens Neil Blakey-Milner Henrik Vendelbo @@ -257,10 +263,12 @@ Bobby Impollonia Roberto De Ioris Jeong YunWon + andrewjlawrence Christopher Armstrong Aaron Tubbs Vasantha Ganesh K Jason Michalski + Radu Ciorba Markus Holtermann Andrew Thompson Yusei Tahara @@ -268,28 +276,26 @@ Fabio Niephaus Akira Li Gustavo Niemeyer - Rafał Gałczyński + Nate Bragg Lucas Stadler roberto at goyle + Carl Bordum Hansen Matt Bogosian Yury V. Zaytsev florinpapa Anders Sigfridsson - Matt Jackson Nikolay Zinov rafalgalczynski at gmail.com Joshua Gilbert Anna Katrina Dominguez Kim Jin Su Amber Brown - Miro Hrončok - Anthony Sottile - Nate Bragg + Andrew Stepanov + Rafał Gałczyński Ben Darnell Juan Francisco Cantero Hurtado Godefroid Chappelle Julian Berman - Michael Hudson-Doyle Stephan Busemann Dan Colish timo @@ -299,6 +305,7 @@ halgari Jim Baker Chris Lambacher + John Aldis coolbutuseless at gmail.com Mike Bayer Rodrigo Araújo @@ -307,6 +314,7 @@ OlivierBlanvillain Jonas Pfannschmidt Zearin + Johan Forsberg Andrey Churin Dan Crosta reubano at gmail.com @@ -316,8 +324,9 @@ Steve Papanik Eli Stevens Boglarka Vezer - gabrielg + gabrielg at ec2-54-146-239-158.compute-1.amazonaws.com PavloKapyshin + Hervé Beraud Tomer Chachamu Christopher Groskopf Asmo Soinio @@ -331,7 +340,6 @@ Michael Chermside Anna Ravencroft remarkablerocket - Pauli Virtanen Petre Vijiac Berker Peksag Christian Muirhead @@ -351,12 +359,13 @@ Zooko Wilcox-O Hearn James Lan jiaaro + Evgenii Gorinov Markus Unterwaditzer Kristoffer Kleine Graham Markall Dan Loewenherz werat - Andrew Stepanov + Filip Salomonsson Niclas Olofsson Chris Pressey Tobias Diaz diff --git a/pypy/doc/how-to-release.rst b/pypy/doc/how-to-release.rst --- a/pypy/doc/how-to-release.rst +++ b/pypy/doc/how-to-release.rst @@ -16,9 +16,6 @@ How to Create a PyPy Release ++++++++++++++++++++++++++++ -Overview --------- - As a meta rule setting up issues in the tracker for items here may help not forgetting things. A set of todo files may also work. @@ -28,17 +25,54 @@ Release Steps -------------- +++++++++++++++ -* If needed, make a release branch -* Bump the - pypy version number in module/sys/version.py and in - module/cpyext/include/patchlevel.h and in doc/conf.py. The branch - will capture the revision number of this change for the release. +Make the release branch +------------------------ - Some of the next updates may be done before or after branching; make - sure things are ported back to the trunk and to the branch as - necessary. +This is needed only in case you are doing a new major version; if not, you can +probably reuse the existing release branch. + +We want to be able to freely merge default into the branch and vice-versa; +thus we need to do a complicate dance to avoid to patch the version number +when we do a merge:: + + $ hg up -r default + $ # edit the version to e.g. 7.0.0-final + $ hg ci + $ hg branch release-pypy2.7-7.x && hg ci + $ hg up -r default + $ # edit the version to 7.1.0-alpha0 + $ hg ci + $ hg up -r release-pypy2.7-7.x + $ hg merge default + $ # edit the version to AGAIN 7.0.0-final + $ hg ci + +Then, we need to do the same for the 3.x branch:: + + $ hg up -r py3.5 + $ hg merge default # this brings the version fo 7.1.0-alpha0 + $ hg branch release-pypy3.5-7.x + $ # edit the version to 7.0.0-final + $ hg ci + $ hg up -r py3.5 + $ hg merge release-pypy3.5-7.x + $ # edit the version to 7.1.0-alpha0 + $ hg ci + +To change the version, you need to edit three files: + + - ``module/sys/version.py`` + + - ``module/cpyext/include/patchlevel.h`` + + - ``doc/conf.py`` + + +Other steps +----------- + * Make sure the RPython builds on the buildbot pass with no failures 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-v7.0.0.rst release-v6.0.0.rst release-v5.10.1.rst release-v5.10.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-7.0.0.rst whatsnew-pypy2-6.0.0.rst whatsnew-pypy2-5.10.0.rst whatsnew-pypy2-5.10.0.rst @@ -41,6 +42,7 @@ .. toctree:: whatsnew-pypy3-head.rst + whatsnew-pypy3-7.0.0.rst whatsnew-pypy3-5.9.0.rst whatsnew-pypy3-5.8.0.rst whatsnew-pypy3-5.7.0.rst diff --git a/pypy/doc/interpreter.rst b/pypy/doc/interpreter.rst --- a/pypy/doc/interpreter.rst +++ b/pypy/doc/interpreter.rst @@ -156,7 +156,7 @@ environment found in `Frames`. Frames and Functions have references to a code object. Here is a list of Code attributes: -* ``co_flags`` flags if this code object has nested scopes/generators +* ``co_flags`` flags if this code object has nested scopes/generators/etc. * ``co_stacksize`` the maximum depth the stack can reach while executing the code * ``co_code`` the actual bytecode string diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-v7.0.0.rst @@ -0,0 +1,151 @@ +====================================================== +PyPy v7.0.0: triple release of 2.7, 3.5 and 3.6-alpha +====================================================== + +The PyPy team is proud to release the version 7.0.0 of PyPy, which includes +three different interpreters: + + - PyPy2.7, which is an interpreter supporting the syntax and the features of + Python 2.7 + + - PyPy3.5, which supports Python 3.5 + + - PyPy3.6-alpha: this is the first official release of PyPy to support 3.6 + features, although it is still considered alpha quality. + +All the interpreters are based on much the same codebase, thus the triple +release. + +Until we can work with downstream providers to distribute builds with PyPy, we +have made packages for some common packages `available as wheels`_. + +The GC `hooks`_ , which can be used to gain more insights into its +performance, has been improved and it is now possible to manually manage the +GC by using a combination of ``gc.disable`` and ``gc.collect_step``. See the +`GC blog post`_. + + +We updated the `cffi`_ module included in PyPy to version 1.12, and the +`cppyy`_ backend to 1.4. Please use these to wrap your C and C++ code, +respectively, for a JIT friendly experience. + +As always, this release is 100% compatible with the previous one and fixed +several issues and bugs raised by the growing community of PyPy users. +We strongly recommend updating. + +The PyPy3.6 release and the Windows PyPy3.5 release are still not production +quality so your mileage may vary. There are open issues with incomplete +compatibility and c-extension support. + +The utf8 branch that changes internal representation of unicode to utf8 did not +make it into the release, so there is still more goodness coming. +You can download the v7.0 releases here: + + http://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project. If PyPy is not quite good enough for your needs, we are available for +direct consulting work. + +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. + +.. _`PyPy`: index.html +.. _`RPython`: https://rpython.readthedocs.org +.. _`help`: project-ideas.html +.. _`cffi`: http://cffi.readthedocs.io +.. _`cppyy`: https://cppyy.readthedocs.io +.. _`available as wheels`: https://github.com/antocuni/pypy-wheels +.. _`GC blog post`: https://morepypy.blogspot.com/2019/01/pypy-for-low-latency-systems.html + + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7, 3.5 and 3.6. 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 release supports: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS X 64 bits, Windows 32 bits, OpenBSD, FreeBSD) + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +Unfortunately at the moment of writing our ARM buildbots are out of service, +so for now we are **not** releasing any binary for the ARM architecture. + +.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://rpython.readthedocs.io/en/latest/examples.html + + +Changelog +========= + +If not specified, the changes are shared across versions + +* Support ``__set_name__``, ``__init_subclass__`` (Py3.6) +* Support ``cppyy`` in Py3.5 and Py3.6 +* Use implementation-specific site directories in ``sysconfig`` (Py3.5, Py3.6) +* Adding detection of gcc to ``sysconfig`` (Py3.5, Py3.6) +* Fix multiprocessing regression on newer glibcs +* Make sure 'blocking-ness' of socket is set along with default timeout +* Include ``crypt.h`` for ``crypt()`` on Linux +* Improve and re-organize the contributing_ documentation +* Make the ``__module__`` attribute writable, fixing an incompatibility with + NumPy 1.16 +* Implement ``Py_ReprEnter``, ``Py_ReprLeave(), ``PyMarshal_ReadObjectFromString``, + ``PyMarshal_WriteObjectToString``, ``PyObject_DelItemString``, + ``PyMapping_DelItem``, ``PyMapping_DelItemString``, ``PyEval_GetFrame``, + ``PyOS_InputHook``, ``PyErr_FormatFromCause`` (Py3.6), +* Implement new wordcode instruction encoding (Py3.6) +* Log additional gc-minor and gc-collect-step info in the PYPYLOG +* The ``reverse-debugger`` (revdb) branch has been merged to the default + branch, so it should always be up-to-date. You still need a special pypy + build, but you can compile it from the same source as the one we distribute + for the v7.0.0 release. For more information, see + https://bitbucket.org/pypy/revdb +* Support underscores in numerical literals like ``'4_2'`` (Py3.6) +* Pre-emptively raise MemoryError if the size of dequeue in ``_collections.deque`` + is too large (Py3.5) +* Fix multithreading issues in calls to ``os.setenv`` +* Add missing defines and typedefs for numpy and pandas on MSVC +* Add CPython macros like ``Py_NAN`` to header files +* Rename the ``MethodType`` to ``instancemethod``, like CPython +* Better support for `async with` in generators (Py3.5, Py3.6) +* Improve the performance of ``pow(a, b, c)`` if ``c`` is a large integer +* Now ``vmprof`` works on FreeBSD +* Support GNU Hurd, fixes for FreeBSD +* Add deprecation warning if type of result of ``__float__`` is float inherited + class (Py3.6) +* Fix async generator bug when yielding a ``StopIteration`` (Py3.6) +* Speed up ``max(list-of-int)`` from non-jitted code +* Fix Windows ``os.listdir()`` for some cases (see CPython #32539) +* Add ``select.PIPE_BUF`` +* Use ``subprocess`` to avoid shell injection in ``shutil`` module - backport + of https://bugs.python.org/issue34540 +* Rename ``_Py_ZeroStruct`` to ``_Py_FalseStruct`` (Py3.5, Py3.6) +* Remove some cpyext names for Py3.5, Py3.6 +* Enable use of unicode file names in ``dlopen`` +* Backport CPython fix for ``thread.RLock`` +* Make GC hooks measure time in seconds (as opposed to an opaque unit) +* Refactor and reorganize tests in ``test_lib_pypy`` +* Check error values in ``socket.setblocking`` (Py3.6) +* Add support for FsPath to os.unlink() (Py3.6) +* Fix freezing builtin modules at translation +* Tweak ``W_UnicodeDictionaryStrategy`` which speeds up dictionaries with only + unicode keys + +We also refactored many parts of the JIT bridge optimizations, as well as cpyext +internals, and together with new contributors fixed issues, added new +documentation, and cleaned up the codebase. + +.. _contributing: http://doc.pypy.org/en/latest/contributing.html 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,78 +1,30 @@ ========================== -What's new in PyPy2.7 6.0+ +What's new in PyPy2.7 7.0+ ========================== -.. this is a revision shortly after release-pypy-6.0.0 -.. startrev: e50e11af23f1 +.. this is a revision shortly after release-pypy-7.0.0 +.. startrev: 481c69f7d81f -.. branch: cppyy-packaging +.. branch: zlib-copying-third-time-a-charm -Main items: vastly better template resolution and improved performance. In -detail: upgrade to backend 1.4, improved handling of templated methods and -functions (in particular automatic deduction of types), improved pythonization -interface, range of compatibility fixes for Python3, free functions now take -fast libffi path when possible, moves for strings (incl. from Python str), -easier/faster handling of std::vector by numpy, improved and faster object -identity preservation +Make sure zlib decompressobjs have their streams deallocated immediately +on flush. -.. branch: socket_default_timeout_blockingness +.. branch: zlib-copying-redux -Make sure 'blocking-ness' of socket is set along with default timeout +Fix calling copy on already-flushed compressobjs. -.. branch: crypt_h +.. branch: zlib-copying -Include crypt.h for crypt() on Linux +The zlib module's compressobj and decompressobj now expose copy methods +as they do on CPython. -.. branch: gc-more-logging -Log additional gc-minor and gc-collect-step info in the PYPYLOG +.. branch: math-improvements -.. branch: reverse-debugger +Improve performance of long operations where one of the operands fits into +an int. -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb +.. branch: regalloc-playgrounds -.. branch: pyparser-improvements-3 - -Small refactorings in the Python parser. - -.. branch: fix-readme-typo - -.. branch: avoid_shell_injection_in_shutil - -.. branch: unicode-utf8-re -.. branch: utf8-io - -Utf8 handling for unicode - -Backport CPython fix for possible shell injection issue in `distutils.spawn`, -https://bugs.python.org/issue34540 - -.. branch: cffi_dlopen_unicode - -Enable use of unicode file names in `dlopen` - -.. branch: rlock-in-rpython - -Backport CPython fix for `thread.RLock` - - -.. branch: expose-gc-time - -Make GC hooks measure time in seconds (as opposed to an opaque unit). - -.. branch: cleanup-test_lib_pypy - -Update most test_lib_pypy/ tests and move them to extra_tests/. - -.. branch: gc-disable - -Make it possible to manually manage the GC by using a combination of -gc.disable() and gc.collect_step(). Make sure to write a proper release -announcement in which we explain that existing programs could leak memory if -they run for too much time between a gc.disable()/gc.enable() - -.. branch: unicode-utf8 - -Use utf8 internally to represent unicode - +Improve register allocation in the JIT. diff --git a/pypy/doc/whatsnew-pypy2-5.10.0.rst b/pypy/doc/whatsnew-pypy2-5.10.0.rst --- a/pypy/doc/whatsnew-pypy2-5.10.0.rst +++ b/pypy/doc/whatsnew-pypy2-5.10.0.rst @@ -1,42 +1,42 @@ -========================== -What's new in PyPy2.7 5.10 -========================== - -.. this is a revision shortly after release-pypy2.7-v5.9.0 -.. startrev:d56dadcef996 - - -.. branch: cppyy-packaging - -Cleanup and improve cppyy packaging - -.. branch: docs-osx-brew-openssl - -.. branch: keep-debug-symbols - -Add a smartstrip tool, which can optionally keep the debug symbols in a -separate file, instead of just stripping them away. Use it in packaging - -.. branch: bsd-patches - -Fix failures on FreeBSD, contributed by David Naylor as patches on the issue -tracker (issues 2694, 2695, 2696, 2697) - -.. branch: run-extra-tests - -Run extra_tests/ in buildbot - -.. branch: vmprof-0.4.10 - -Upgrade the _vmprof backend to vmprof 0.4.10 - -.. branch: fix-vmprof-stacklet-switch -.. branch: fix-vmprof-stacklet-switch-2 - -Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) - -.. branch: win32-vcvars - -.. branch: rdict-fast-hash - -Make it possible to declare that the hash function of an r_dict is fast in RPython. From pypy.commits at gmail.com Sun Feb 10 07:11:00 2019 From: pypy.commits at gmail.com (stefanor) Date: Sun, 10 Feb 2019 04:11:00 -0800 (PST) Subject: [pypy-commit] pypy default: Include on Gnu/Hurd Message-ID: <5c6014d4.1c69fb81.6dd43.0710@mx.google.com> Author: Stefano Rivera Branch: Changeset: r95925:95a74e2dcf65 Date: 2019-02-10 14:10 +0200 http://bitbucket.org/pypy/pypy/changeset/95a74e2dcf65/ Log: Include on Gnu/Hurd Issue: #2948 diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -254,7 +254,7 @@ 'sched.h', 'grp.h', 'dirent.h', 'sys/stat.h', 'fcntl.h', 'signal.h', 'sys/utsname.h', _ptyh] - if sys.platform.startswith('linux'): + if sys.platform.startswith('linux') or sys.platform.startswith('gnu'): includes.append('sys/sysmacros.h') if sys.platform.startswith('freebsd') or sys.platform.startswith('openbsd'): includes.append('sys/ttycom.h') From pypy.commits at gmail.com Sun Feb 10 09:35:29 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 10 Feb 2019 06:35:29 -0800 (PST) Subject: [pypy-commit] extradoc extradoc: tweaks Message-ID: <5c6036b1.1c69fb81.45b91.8b95@mx.google.com> Author: Matti Picus Branch: extradoc Changeset: r5946:87555f3a7bb9 Date: 2019-02-10 16:35 +0200 http://bitbucket.org/pypy/extradoc/changeset/87555f3a7bb9/ Log: tweaks diff --git a/blog/draft/2019-02-sprint.rst b/blog/draft/2019-02-sprint.rst --- a/blog/draft/2019-02-sprint.rst +++ b/blog/draft/2019-02-sprint.rst @@ -2,14 +2,14 @@ Hello everyone! -We are happy to report a successful and well attended sprint that is wrapping up -in Düsseldorf, Germany. In the last week we had eighteen people sprinting +We are happy to report a successful and well attended PyPy sprint +in Düsseldorf, Germany. From Feb 4 to Feb 13 we had eighteen people sprinting at the Heinrich-Heine-Universität Düsseldorf on various topics. A big -chunk of the sprint was dedicated to various discussions, since we did not -manage to gather the core developers in one room in quite a while. +chunk of the sprint was dedicated to various discussions, since we have not +managed to gather the core developers in one room for quite a while. Discussion topics included: -* Funding and general sustainability of open source. +* Funding and general sustainability of open source, and PyPy in particular. * Catching up with CPython 3.7/3.8 – we are planning to release 3.6 some time in the next few months and we will continue working on 3.7/3.8. @@ -34,7 +34,7 @@ Some highlights of the coding tasks worked on: * Aarch64 (ARM64) JIT backend work has been started, we are able to run the first - test! Tobias Oberstein from Crossbar GmbH and Rodolph Perfetta from ARM joined the + test! Tobias Oberstein from `Crossbar GmbH`_ and Rodolph Perfetta from ARM joined the sprint to help kickstart the project. * The long running math-improvements branch that was started by Stian Andreassen got merged @@ -44,7 +44,7 @@ and nearly finished by Carl Friedrich Bolz-Tereick. The branch got merged and gives some modest speedups across the board. -* Andrew Lawrence worked on MSI installer for PyPy on windows. +* Andrew Lawrence worked on a MSI installer for PyPy on windows. * Łukasz worked on improving failing tests on the PyPy 3.6 branch. He knows very obscure details of CPython (e.g. how pickling works), hence we managed to progress very quickly. @@ -53,9 +53,9 @@ * The Utf8 branch, which changes the internal representation of unicode might be finally merged at some point very soon. We discussed and improved upon the last few - blockers. It gives significant speedups in a lot of cases handling strings. + blockers. It gives significant speedups in many cases of string handling. -* Zlib was missing couple methods, which were added by Ronan Lamy and Julian Berman. +* Zlib was missing a couple of methods, which were added by Ronan Lamy and Julian Berman. * Manuel Jacob fixed RevDB failures. @@ -67,3 +67,4 @@ Maciej Fijałkowski, Carl Friedrich Bolz-Tereick and the whole PyPy team. .. _`heptapod`: https://heptapod.net +.. _`Crossbar GmbH`: https://crossbar.io/ From pypy.commits at gmail.com Sun Feb 10 10:15:33 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 10 Feb 2019 07:15:33 -0800 (PST) Subject: [pypy-commit] pypy default: skip test on old zlib version Message-ID: <5c604015.1c69fb81.f85dd.cd13@mx.google.com> Author: Matti Picus Branch: Changeset: r95926:0105b3a8389f Date: 2019-02-10 17:13 +0200 http://bitbucket.org/pypy/pypy/changeset/0105b3a8389f/ Log: skip test on old zlib version diff --git a/rpython/rlib/test/test_rzlib.py b/rpython/rlib/test/test_rzlib.py --- a/rpython/rlib/test/test_rzlib.py +++ b/rpython/rlib/test/test_rzlib.py @@ -274,7 +274,7 @@ rzlib.deflateEnd(copied) assert bytes1 + bytes_copy == compressed - + at py.test.mark.skipif(rzlib.ZLIB_VERSION == '1.2.8', reason='does not error check') def test_unsuccessful_compress_copy(): """ Errors during unsuccesful deflateCopy operations raise RZlibErrors. From pypy.commits at gmail.com Sun Feb 10 10:15:35 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 10 Feb 2019 07:15:35 -0800 (PST) Subject: [pypy-commit] pypy default: typo Message-ID: <5c604017.1c69fb81.d860b.418e@mx.google.com> Author: Matti Picus Branch: Changeset: r95927:5ad9ac2d9d1e Date: 2019-02-10 17:14 +0200 http://bitbucket.org/pypy/pypy/changeset/5ad9ac2d9d1e/ Log: typo 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 @@ -25,6 +25,6 @@ Improve performance of long operations where one of the operands fits into an int. -.. branch: regalloc-playgrounds +.. branch: regalloc-playground Improve register allocation in the JIT. From pypy.commits at gmail.com Sun Feb 10 16:02:05 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Sun, 10 Feb 2019 13:02:05 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: Work in progress - implemented do_WSARecv. It has a bug. Message-ID: <5c60914d.1c69fb81.5b9cd.43e1@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r95928:93581a9e8006 Date: 2019-02-10 20:59 +0000 http://bitbucket.org/pypy/pypy/changeset/93581a9e8006/ Log: Work in progress - implemented do_WSARecv. It has a bug. diff --git a/lib_pypy/_overlapped.py b/lib_pypy/_overlapped.py --- a/lib_pypy/_overlapped.py +++ b/lib_pypy/_overlapped.py @@ -13,13 +13,25 @@ from _pypy_winbase_cffi import ffi as _ffi _kernel32 = _ffi.dlopen('kernel32') +_winsock2 = _ffi.dlopen('Ws2_32') + GetVersion = _kernel32.GetVersion NULL = _ffi.NULL - from _winapi import INVALID_HANDLE_VALUE, _MAX_PATH , _Z import _winapi +DisconnectEx = _ffi.NULL + +def _int2intptr(int2cast): + return _ffi.cast("ULONG_PTR", int2cast) + +def _int2dword(int2cast): + return _ffi.cast("DWORD", int2cast) + +def _int2handle(val): + return _ffi.cast("HANDLE", val) + from enum import Enum class OverlappedType(Enum): TYPE_NONE = 0 @@ -34,7 +46,6 @@ TYPE_WAIT_NAMED_PIPE_AND_CONNECT = 9 TYPE_TRANSMIT_FILE = 10 - class Overlapped(object): def __init__(self, handle): self.overlapped = _ffi.new('OVERLAPPED[1]') @@ -86,8 +97,9 @@ else: self.pending = 0 raise _winapi._WinError() - if self.completed and self.read_buffer: - if transferred != len(self.read_buffer): + if self.completed and self.allocated_buffer: + import pdb; pdb.set_trace() + if transferred[0] != len(self.allocated_buffer[0]): raise _winapi._WinError() return transferred[0], err @@ -105,24 +117,44 @@ return result def WSARecv(self ,handle, size, flags): + handle = _int2handle(handle) + flags = _int2dword(flags) if self.type != OverlappedType.TYPE_NONE: raise _winapi._WinError() self.type = OverlappedType.TYPE_READ self.handle = handle - self.allocated_buffer = _ffi.buffer(max(1, size)) - return do_WSARecv(self, handle, self.allocated_buffer[:], size, flags) + self.allocated_buffer = _ffi.new("BYTE[]", max(1,size)) + return self.do_WSARecv(handle, self.allocated_buffer, size, flags) + def do_WSARecv(self, handle, allocatedbuffer, size, flags): + nread = _ffi.new("LPDWORD") + wsabuff = _ffi.new("WSABUF[1]") + buffercount = _ffi.new("DWORD[1]", [1]) + wsabuff[0].len = size + wsabuff[0].buf = allocatedbuffer + result = _winsock2.WSARecv(handle, wsabuff, _int2dword(1), nread, _ffi.cast("LPDWORD",flags), self.overlapped, _ffi.NULL) + if result: + self.error = _winapi.ERROR_SUCCESS + else: + self.error = _kernel32.GetLastError() + + if self.error == _winapi.ERROR_BROKEN_PIPE: + mark_as_completed(self.overlapped) + return SetFromWindowsErr(err) + elif self.error in [_winapi.ERROR_SUCCESS, _winapi.ERROR_MORE_DATA, _winapi.ERROR_IO_PENDING] : + return None + else: + self.type = OverlappedType.TYPE_NOT_STARTED + return SetFromWindowsErr(err) -def _int2intptr(int2cast): - return _ffi.cast("ULONG_PTR", int2cast) + def getresult(self, wait=False): + return self.GetOverlappedResult(wait) -def _int2dword(int2cast): - return _ffi.cast("DWORD", int2cast) - -def _int2handle(val): - return _ffi.cast("HANDLE", val) - +def mark_as_completed(overlapped): + overlapped.overlapped.Internal = _ffi.NULL + if overlapped.overlapped.hEvent != _ffi.NULL: + SetEvent(overlapped.overlapped.hEvent) def CreateEvent(eventattributes, manualreset, initialstate, name): event = _kernel32.CreateEventW(NULL, manualreset, initialstate, _Z(name)) @@ -203,5 +235,7 @@ return newwaitobject - - \ No newline at end of file +# In CPython this function converts a windows error into a python object +# Not sure what we should do here. +def SetFromWindowsErr(error): + return error \ No newline at end of file diff --git a/lib_pypy/_pypy_winbase_build.py b/lib_pypy/_pypy_winbase_build.py --- a/lib_pypy/_pypy_winbase_build.py +++ b/lib_pypy/_pypy_winbase_build.py @@ -90,7 +90,6 @@ HANDLE hEvent; } OVERLAPPED, *LPOVERLAPPED; - DWORD WINAPI GetVersion(void); BOOL WINAPI CreatePipe(PHANDLE, PHANDLE, void *, DWORD); HANDLE WINAPI CreateNamedPipeA(LPCSTR, DWORD, DWORD, DWORD, DWORD, DWORD, @@ -136,7 +135,7 @@ LPOVERLAPPED Overlapped; } PostCallbackData, *LPPostCallbackData; -typedef VOID (WINAPI *WAITORTIMERCALLBACK) ( PVOID, BOOL); +typedef VOID (WINAPI *WAITORTIMERCALLBACK) (PVOID, BOOL); BOOL WINAPI RegisterWaitForSingleObject(PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, ULONG, ULONG); BOOL WINAPI PostQueuedCompletionStatus(HANDLE, DWORD, ULONG_PTR, LPOVERLAPPED); @@ -148,7 +147,23 @@ """) -# -------------------- +# -------------------- Win Sock 2 ---------------------- + +ffi.cdef(""" +typedef struct _WSABUF { + ULONG len; + CHAR *buf; +} WSABUF, *LPWSABUF; + +typedef HANDLE SOCKET; +SOCKET __stdcall socket(int, int, int); +typedef BOOL (__stdcall * LPFN_DISCONNECTEX) (SOCKET, LPOVERLAPPED, DWORD, DWORD); +typedef VOID (*LPOVERLAPPED_COMPLETION_ROUTINE) (DWORD, DWORD, LPVOID); + +int __stdcall WSARecv(SOCKET, LPWSABUF, DWORD, LPDWORD, LPDWORD, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE); + + +""") if __name__ == "__main__": ffi.compile() diff --git a/lib_pypy/_pypy_winbase_cffi.py b/lib_pypy/_pypy_winbase_cffi.py --- a/lib_pypy/_pypy_winbase_cffi.py +++ b/lib_pypy/_pypy_winbase_cffi.py @@ -3,8 +3,8 @@ ffi = _cffi_backend.FFI('_pypy_winbase_cffi', _version = 0x2601, - _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xCC\x03\x00\x00\x13\x11\x00\x00\xD1\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xCB\x03\x00\x00\xC7\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xC2\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xC6\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x31\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x80\x03\x00\x00\x5E\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x5E\x11\x00\x00\x5E\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x3B\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x5E\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x7B\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x7B\x0D\x00\x00\x00\x0F\x00\x00\x7B\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xCA\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xCC\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x83\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x80\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x86\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x86\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x5E\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x8C\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x83\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xD1\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xD1\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xC9\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x02\x01\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xD0\x03\x00\x00\x04\x01\x00\x00\x00\x01', - _globals = (b'\x00\x00\x2C\x23CancelIo',0,b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x82\x23CreateEventA',0,b'\x00\x00\x88\x23CreateEventW',0,b'\x00\x00\x8E\x23CreateFileA',0,b'\x00\x00\xB6\x23CreateFileW',0,b'\x00\x00\xA4\x23CreateIoCompletionPort',0,b'\x00\x00\x97\x23CreateNamedPipeA',0,b'\x00\x00\xAC\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x5D\x23CreateProcessW',0,b'\x00\x00\x54\x23DuplicateHandle',0,b'\x00\x00\xAA\x23GetCurrentProcess',0,b'\x00\x00\x3D\x23GetExitCodeProcess',0,b'\x00\x00\x78\x23GetLastError',0,b'\x00\x00\x73\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x41\x23GetQueuedCompletionStatus',0,b'\x00\x00\xA1\x23GetStdHandle',0,b'\x00\x00\x78\x23GetVersion',0,b'\x00\x00\x4E\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x6C\x23SetErrorMode',0,b'\x00\x00\xBF\x23SetEvent',0,b'\x00\x00\x48\x23SetNamedPipeHandleState',0,b'\x00\x00\x39\x23TerminateProcess',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x6F\x23WaitForSingleObject',0,b'\x00\x00\x69\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x7D\x23_getwch',0,b'\x00\x00\x7D\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x7F\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x7A\x23_ungetwch',0), - _struct_unions = ((b'\x00\x00\x00\xCE\x00\x00\x00\x03$1',b'\x00\x00\xCD\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xCD\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xC7\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xCB\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x7B\x11wShowWindow',b'\x00\x00\x7B\x11cbReserved2',b'\x00\x00\xCF\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xC6\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xCE\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xC9\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x31\x11Overlapped'),(b'\x00\x00\x00\xCA\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle')), - _typenames = (b'\x00\x00\x00\x31LPOVERLAPPED',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xC8LPPostCallbackData',b'\x00\x00\x00\x83LPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\xC6OVERLAPPED',b'\x00\x00\x00\xC7PROCESS_INFORMATION',b'\x00\x00\x00\x83PSECURITY_ATTRIBUTES',b'\x00\x00\x00\xC9PostCallbackData',b'\x00\x00\x00\xCASECURITY_ATTRIBUTES',b'\x00\x00\x00\xCBSTARTUPINFO',b'\x00\x00\x00\x27WAITORTIMERCALLBACK',b'\x00\x00\x00\x7Bwint_t'), + _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xE6\x03\x00\x00\x13\x11\x00\x00\xEC\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xE4\x03\x00\x00\xE0\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xDB\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xDF\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xE5\x03\x00\x00\x0A\x01\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x31\x11\x00\x00\xD3\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x31\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x8F\x03\x00\x00\x6D\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x6D\x11\x00\x00\x6D\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x4A\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x6D\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x8A\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x8A\x0D\x00\x00\x00\x0F\x00\x00\x8A\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xE3\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xE6\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x92\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x8F\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x95\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x95\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x6D\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x9B\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xEC\x0D\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x00\x0F\x00\x00\xEC\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xEC\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xE2\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x07\x09\x00\x00\x02\x01\x00\x00\x39\x03\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xEB\x03\x00\x00\x04\x01\x00\x00\x00\x01', + _globals = (b'\x00\x00\x2C\x23CancelIo',0,b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x91\x23CreateEventA',0,b'\x00\x00\x97\x23CreateEventW',0,b'\x00\x00\x9D\x23CreateFileA',0,b'\x00\x00\xCA\x23CreateFileW',0,b'\x00\x00\xB8\x23CreateIoCompletionPort',0,b'\x00\x00\xA6\x23CreateNamedPipeA',0,b'\x00\x00\xC0\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x6C\x23CreateProcessW',0,b'\x00\x00\x63\x23DuplicateHandle',0,b'\x00\x00\xBE\x23GetCurrentProcess',0,b'\x00\x00\x4C\x23GetExitCodeProcess',0,b'\x00\x00\x87\x23GetLastError',0,b'\x00\x00\x82\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x50\x23GetQueuedCompletionStatus',0,b'\x00\x00\xB5\x23GetStdHandle',0,b'\x00\x00\x87\x23GetVersion',0,b'\x00\x00\x5D\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x7B\x23SetErrorMode',0,b'\x00\x00\xD8\x23SetEvent',0,b'\x00\x00\x57\x23SetNamedPipeHandleState',0,b'\x00\x00\x48\x23TerminateProcess',0,b'\x00\x00\x3F\x23WSARecv',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x7E\x23WaitForSingleObject',0,b'\x00\x00\x78\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x8C\x23_getwch',0,b'\x00\x00\x8C\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x8E\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x89\x23_ungetwch',0,b'\x00\x00\xB0\x23socket',0), + _struct_unions = ((b'\x00\x00\x00\xE9\x00\x00\x00\x03$1',b'\x00\x00\xE8\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xE8\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xE0\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xE4\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x8A\x11wShowWindow',b'\x00\x00\x8A\x11cbReserved2',b'\x00\x00\xEA\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xDF\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xE9\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xE2\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x31\x11Overlapped'),(b'\x00\x00\x00\xE3\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle'),(b'\x00\x00\x00\xE5\x00\x00\x00\x02_WSABUF',b'\x00\x00\x18\x11len',b'\x00\x00\x13\x11buf')), + _typenames = (b'\x00\x00\x00\xE7LPFN_DISCONNECTEX',b'\x00\x00\x00\x31LPOVERLAPPED',b'\x00\x00\x00\x46LPOVERLAPPED_COMPLETION_ROUTINE',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xE1LPPostCallbackData',b'\x00\x00\x00\x92LPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\x41LPWSABUF',b'\x00\x00\x00\xDFOVERLAPPED',b'\x00\x00\x00\xE0PROCESS_INFORMATION',b'\x00\x00\x00\x92PSECURITY_ATTRIBUTES',b'\x00\x00\x00\xE2PostCallbackData',b'\x00\x00\x00\xE3SECURITY_ATTRIBUTES',b'\x00\x00\x00\x15SOCKET',b'\x00\x00\x00\xE4STARTUPINFO',b'\x00\x00\x00\x27WAITORTIMERCALLBACK',b'\x00\x00\x00\xE5WSABUF',b'\x00\x00\x00\x8Awint_t'), ) From pypy.commits at gmail.com Mon Feb 11 04:11:39 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 01:11:39 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8: remove done items from TODO Message-ID: <5c613c4b.1c69fb81.68c5b.94bb@mx.google.com> Author: Matti Picus Branch: unicode-utf8 Changeset: r95929:069cf9f2f5b3 Date: 2019-02-09 20:16 +0100 http://bitbucket.org/pypy/pypy/changeset/069cf9f2f5b3/ Log: remove done items from TODO diff --git a/TODO b/TODO --- a/TODO +++ b/TODO @@ -1,6 +1,4 @@ * find a better way to run "find" without creating the index storage, if one is not already readily available (understand cost now, improve after merge) -* write the correct jit_elidable in _get_index_storage (Armin) * improve performance of splitlines -* stop using runicode/unicode and move MAXUNICODE to rutf8 (Matti) * think about cost of utf8 list strategy (Armin and CF) From pypy.commits at gmail.com Mon Feb 11 04:11:44 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 01:11:44 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: merge py3.5 into branch Message-ID: <5c613c50.1c69fb81.3b46d.2f11@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95930:7b314aebd25c Date: 2019-02-09 20:17 +0100 http://bitbucket.org/pypy/pypy/changeset/7b314aebd25c/ Log: merge py3.5 into branch diff too long, truncating to 2000 out of 3827 lines diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -4,8 +4,10 @@ *~ .*.swp .idea +.mypy_cache .project .pydevproject +.vscode __pycache__ .venv .cache diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -58,3 +58,6 @@ 3f6eaa010fce78cc7973bdc1dfdb95970f08fed2 release-pypy3.5-v5.10.1 ab0b9caf307db6592905a80b8faffd69b39005b8 release-pypy2.7-v6.0.0 fdd60ed87e941677e8ea11acf9f1819466521bf2 release-pypy3.5-v6.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 diff --git a/lib_pypy/_csv.py b/lib_pypy/_csv.py deleted file mode 100644 --- a/lib_pypy/_csv.py +++ /dev/null @@ -1,573 +0,0 @@ -"""CSV parsing and writing. - -This module provides classes that assist in the reading and writing -of Comma Separated Value (CSV) files, and implements the interface -described by PEP 305. Although many CSV files are simple to parse, -the format is not formally defined by a stable specification and -is subtle enough that parsing lines of a CSV file with something -like line.split(\",\") is bound to fail. The module supports three -basic APIs: reading, writing, and registration of dialects. - - -DIALECT REGISTRATION: - -Readers and writers support a dialect argument, which is a convenient -handle on a group of settings. When the dialect argument is a string, -it identifies one of the dialects previously registered with the module. -If it is a class or instance, the attributes of the argument are used as -the settings for the reader or writer: - - class excel: - delimiter = ',' - quotechar = '\"' - escapechar = None - doublequote = True - skipinitialspace = False - lineterminator = '\\r\\n' - quoting = QUOTE_MINIMAL - -SETTINGS: - - * quotechar - specifies a one-character string to use as the - quoting character. It defaults to '\"'. - * delimiter - specifies a one-character string to use as the - field separator. It defaults to ','. - * skipinitialspace - specifies how to interpret whitespace which - immediately follows a delimiter. It defaults to False, which - means that whitespace immediately following a delimiter is part - of the following field. - * lineterminator - specifies the character sequence which should - terminate rows. - * quoting - controls when quotes should be generated by the writer. - It can take on any of the following module constants: - - csv.QUOTE_MINIMAL means only when required, for example, when a - field contains either the quotechar or the delimiter - csv.QUOTE_ALL means that quotes are always placed around fields. - csv.QUOTE_NONNUMERIC means that quotes are always placed around - fields which do not parse as integers or floating point - numbers. - csv.QUOTE_NONE means that quotes are never placed around fields. - * escapechar - specifies a one-character string used to escape - the delimiter when quoting is set to QUOTE_NONE. - * doublequote - controls the handling of quotes inside fields. When - True, two consecutive quotes are interpreted as one during read, - and when writing, each quote character embedded in the data is - written as two quotes. -""" - -__version__ = "1.0" - -QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE = range(4) -_dialects = {} -_field_limit = 128 * 1024 # max parsed field size - -class Error(Exception): - pass - -class Dialect(object): - """CSV dialect - - The Dialect type records CSV parsing and generation options.""" - - __slots__ = ["_delimiter", "_doublequote", "_escapechar", - "_lineterminator", "_quotechar", "_quoting", - "_skipinitialspace", "_strict"] - - def __new__(cls, dialect, **kwargs): - - for name in kwargs: - if '_' + name not in Dialect.__slots__: - raise TypeError("unexpected keyword argument '%s'" % - (name,)) - - if dialect is not None: - if isinstance(dialect, str): - dialect = get_dialect(dialect) - - # Can we reuse this instance? - if (isinstance(dialect, Dialect) - and all(value is None for value in kwargs.values())): - return dialect - - self = object.__new__(cls) - - - def set_char(x): - if x is None: - return None - if isinstance(x, str) and len(x) <= 1: - return x - raise TypeError("%r must be a 1-character string" % (name,)) - def set_str(x): - if isinstance(x, str): - return x - raise TypeError("%r must be a string" % (name,)) - def set_quoting(x): - if x in range(4): - return x - raise TypeError("bad 'quoting' value") - - attributes = {"delimiter": (',', set_char), - "doublequote": (True, bool), - "escapechar": (None, set_char), - "lineterminator": ("\r\n", set_str), - "quotechar": ('"', set_char), - "quoting": (QUOTE_MINIMAL, set_quoting), - "skipinitialspace": (False, bool), - "strict": (False, bool), - } - - # Copy attributes - notset = object() - for name in Dialect.__slots__: - name = name[1:] - value = notset - if name in kwargs: - value = kwargs[name] - elif dialect is not None: - value = getattr(dialect, name, notset) - - # mapping by name: (default, converter) - if value is notset: - value = attributes[name][0] - if name == 'quoting' and not self.quotechar: - value = QUOTE_NONE - else: - converter = attributes[name][1] - if converter: - value = converter(value) - - setattr(self, '_' + name, value) - - if not self.delimiter: - raise TypeError("delimiter must be set") - - if self.quoting != QUOTE_NONE and not self.quotechar: - raise TypeError("quotechar must be set if quoting enabled") - - if not self.lineterminator: - raise TypeError("lineterminator must be set") - - return self - - delimiter = property(lambda self: self._delimiter) - doublequote = property(lambda self: self._doublequote) - escapechar = property(lambda self: self._escapechar) - lineterminator = property(lambda self: self._lineterminator) - quotechar = property(lambda self: self._quotechar) - quoting = property(lambda self: self._quoting) - skipinitialspace = property(lambda self: self._skipinitialspace) - strict = property(lambda self: self._strict) - - -def _call_dialect(dialect_inst, kwargs): - return Dialect(dialect_inst, **kwargs) - -def register_dialect(name, dialect=None, **kwargs): - """Create a mapping from a string name to a dialect class. - dialect = csv.register_dialect(name, dialect)""" - if not isinstance(name, str): - raise TypeError("dialect name must be a string or unicode") - - dialect = _call_dialect(dialect, kwargs) - _dialects[name] = dialect - -def unregister_dialect(name): - """Delete the name/dialect mapping associated with a string name.\n - csv.unregister_dialect(name)""" - try: - del _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def get_dialect(name): - """Return the dialect instance associated with name. - dialect = csv.get_dialect(name)""" - try: - return _dialects[name] - except KeyError: - raise Error("unknown dialect") - -def list_dialects(): - """Return a list of all know dialect names - names = csv.list_dialects()""" - return list(_dialects) - -class Reader(object): - """CSV reader - - Reader objects are responsible for reading and parsing tabular data - in CSV format.""" - - - (START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD, - IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD, - EAT_CRNL) = range(8) - - def __init__(self, iterator, dialect=None, **kwargs): - self.dialect = _call_dialect(dialect, kwargs) - self.input_iter = iter(iterator) - self.line_num = 0 - - self._parse_reset() - - def _parse_reset(self): - self.field = '' - self.fields = [] - self.state = self.START_RECORD - self.numeric_field = False - - def __iter__(self): - return self - - def __next__(self): - self._parse_reset() - while True: - try: - line = next(self.input_iter) - except StopIteration: - # End of input OR exception - if len(self.field) > 0: - raise Error("newline inside string") - raise - - self.line_num += 1 - - if '\0' in line: - raise Error("line contains NULL byte") - pos = 0 - while pos < len(line): - pos = self._parse_process_char(line, pos) - self._parse_eol() - - if self.state == self.START_RECORD: - break - - fields = self.fields - self.fields = [] - return fields - - def _parse_process_char(self, line, pos): - c = line[pos] - if self.state == self.IN_FIELD: - # in unquoted field - pos2 = pos - while True: - if c in '\n\r': - # end of line - return [fields] - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.EAT_CRNL - elif c == self.dialect.escapechar: - # possible escaped character - pos2 -= 1 - self.state = self.ESCAPED_CHAR - elif c == self.dialect.delimiter: - # save field - wait for new field - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - self._parse_save_field() - self.state = self.START_FIELD - else: - # normal character - save in field - pos2 += 1 - if pos2 < len(line): - c = line[pos2] - continue - break - if pos2 > pos: - self._parse_add_char(line[pos:pos2]) - pos = pos2 - 1 - - elif self.state == self.START_RECORD: - if c in '\n\r': - self.state = self.EAT_CRNL - else: - self.state = self.START_FIELD - # restart process - self._parse_process_char(line, pos) - - elif self.state == self.START_FIELD: - if c in '\n\r': - # save empty field - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # start quoted field - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.escapechar: - # possible escaped character - self.state = self.ESCAPED_CHAR - elif c == ' ' and self.dialect.skipinitialspace: - # ignore space at start of field - pass - elif c == self.dialect.delimiter: - # save empty field - self._parse_save_field() - else: - # begin new unquoted field - if self.dialect.quoting == QUOTE_NONNUMERIC: - self.numeric_field = True - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char(c) - self.state = self.IN_FIELD - - elif self.state == self.IN_QUOTED_FIELD: - if c == self.dialect.escapechar: - # possible escape character - self.state = self.ESCAPE_IN_QUOTED_FIELD - elif (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - if self.dialect.doublequote: - # doublequote; " represented by "" - self.state = self.QUOTE_IN_QUOTED_FIELD - else: - #end of quote part of field - self.state = self.IN_FIELD - else: - # normal character - save in field - self._parse_add_char(c) - - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # doublequote - seen a quote in a quoted field - if (c == self.dialect.quotechar - and self.dialect.quoting != QUOTE_NONE): - # save "" as " - self._parse_add_char(c) - self.state = self.IN_QUOTED_FIELD - elif c == self.dialect.delimiter: - # save field - wait for new field - self._parse_save_field() - self.state = self.START_FIELD - elif c in '\r\n': - # end of line - return [fields] - self._parse_save_field() - self.state = self.EAT_CRNL - elif not self.dialect.strict: - self._parse_add_char(c) - self.state = self.IN_FIELD - else: - raise Error("'%c' expected after '%c'" % - (self.dialect.delimiter, self.dialect.quotechar)) - - elif self.state == self.EAT_CRNL: - if c not in '\r\n': - raise Error("new-line character seen in unquoted field - " - "do you need to open the file " - "in universal-newline mode?") - - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - return pos + 1 - - def _parse_eol(self): - if self.state == self.EAT_CRNL: - self.state = self.START_RECORD - elif self.state == self.START_RECORD: - # empty line - return [] - pass - elif self.state == self.IN_FIELD: - # in unquoted field - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.START_FIELD: - # save empty field - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - elif self.state == self.ESCAPED_CHAR: - self._parse_add_char('\n') - self.state = self.IN_FIELD - elif self.state == self.IN_QUOTED_FIELD: - pass - elif self.state == self.ESCAPE_IN_QUOTED_FIELD: - self._parse_add_char('\n') - self.state = self.IN_QUOTED_FIELD - elif self.state == self.QUOTE_IN_QUOTED_FIELD: - # end of line - return [fields] - self._parse_save_field() - self.state = self.START_RECORD - else: - raise RuntimeError("unknown state: %r" % (self.state,)) - - def _parse_save_field(self): - field, self.field = self.field, '' - if self.numeric_field: - self.numeric_field = False - field = float(field) - self.fields.append(field) - - def _parse_add_char(self, c): - if len(self.field) + len(c) > _field_limit: - raise Error("field larger than field limit (%d)" % (_field_limit)) - self.field += c - - -class Writer(object): - """CSV writer - - Writer objects are responsible for generating tabular data - in CSV format from sequence input.""" - - def __init__(self, file, dialect=None, **kwargs): - if not (hasattr(file, 'write') and callable(file.write)): - raise TypeError("argument 1 must have a 'write' method") - self.writeline = file.write - self.dialect = _call_dialect(dialect, kwargs) - - def _join_reset(self): - self.rec = [] - self.num_fields = 0 - - def _join_append(self, field, quoted, quote_empty): - dialect = self.dialect - # If this is not the first field we need a field separator - if self.num_fields > 0: - self.rec.append(dialect.delimiter) - - if dialect.quoting == QUOTE_NONE: - need_escape = tuple(dialect.lineterminator) + ( - dialect.escapechar, # escapechar always first - dialect.delimiter, dialect.quotechar) - - else: - for c in tuple(dialect.lineterminator) + ( - dialect.delimiter, dialect.escapechar): - if c and c in field: - quoted = True - - need_escape = () - if dialect.quotechar in field: - if dialect.doublequote: - field = field.replace(dialect.quotechar, - dialect.quotechar * 2) - quoted = True - else: - need_escape = (dialect.quotechar,) - - - for c in need_escape: - if c and c in field: - if not dialect.escapechar: - raise Error("need to escape, but no escapechar set") - field = field.replace(c, dialect.escapechar + c) - - # If field is empty check if it needs to be quoted - if field == '' and quote_empty: - if dialect.quoting == QUOTE_NONE: - raise Error("single empty field record must be quoted") - quoted = 1 - - if quoted: - field = dialect.quotechar + field + dialect.quotechar - - self.rec.append(field) - self.num_fields += 1 - - - - def writerow(self, row): - dialect = self.dialect - try: - rowlen = len(row) - except TypeError: - raise Error("sequence expected") - - # join all fields in internal buffer - self._join_reset() - - for field in row: - quoted = False - if dialect.quoting == QUOTE_NONNUMERIC: - try: - float(field) - except: - quoted = True - # This changed since 2.5: - # quoted = not isinstance(field, (int, long, float)) - elif dialect.quoting == QUOTE_ALL: - quoted = True - - if field is None: - value = "" - elif isinstance(field, float): - value = repr(field) - else: - value = str(field) - self._join_append(value, quoted, rowlen == 1) - - # add line terminator - self.rec.append(dialect.lineterminator) - - self.writeline(''.join(self.rec)) - - def writerows(self, rows): - for row in rows: - self.writerow(row) - -def reader(*args, **kwargs): - """ - csv_reader = reader(iterable [, dialect='excel'] - [optional keyword args]) - for row in csv_reader: - process(row) - - The "iterable" argument can be any object that returns a line - of input for each iteration, such as a file object or a list. The - optional \"dialect\" parameter is discussed below. The function - also accepts optional keyword arguments which override settings - provided by the dialect. - - The returned object is an iterator. Each iteration returns a row - of the CSV file (which can span multiple input lines)""" - - return Reader(*args, **kwargs) - -def writer(*args, **kwargs): - """ - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - for row in sequence: - csv_writer.writerow(row) - - [or] - - csv_writer = csv.writer(fileobj [, dialect='excel'] - [optional keyword args]) - csv_writer.writerows(rows) - - The \"fileobj\" argument can be any object that supports the file API.""" - return Writer(*args, **kwargs) - - -undefined = object() -def field_size_limit(limit=undefined): - """Sets an upper limit on parsed fields. - csv.field_size_limit([limit]) - - Returns old limit. If limit is not given, no new limit is set and - the old limit is returned""" - - global _field_limit - old_limit = _field_limit - - if limit is not undefined: - if not isinstance(limit, int): - raise TypeError("int expected, got %s" % - (limit.__class__.__name__,)) - _field_limit = limit - - return old_limit diff --git a/lib_pypy/cffi/pkgconfig.py b/lib_pypy/cffi/pkgconfig.py new file mode 100644 --- /dev/null +++ b/lib_pypy/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except EnvironmentError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret diff --git a/lib_pypy/pwd.py b/lib_pypy/pwd.py deleted file mode 100644 --- a/lib_pypy/pwd.py +++ /dev/null @@ -1,113 +0,0 @@ -# indirectly based on ctypes implementation: Victor Stinner, 2008-05-08 -""" -This module provides access to the Unix password database. -It is available on all Unix versions. - -Password database entries are reported as 7-tuples containing the following -items from the password database (see `'), in order: -pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell. -The uid and gid items are integers, all others are strings. An -exception is raised if the entry asked for cannot be found. -""" - -from _pwdgrp_cffi import ffi, lib -import _structseq -import _thread -_lock = _thread.allocate_lock() - -try: from __pypy__ import builtinify -except ImportError: builtinify = lambda f: f - - -class struct_passwd(metaclass=_structseq.structseqtype): - """ - pwd.struct_passwd: Results from getpw*() routines. - - This object may be accessed either as a tuple of - (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell) - or via the object attributes as named in the above tuple. - """ - name = "pwd.struct_passwd" - - pw_name = _structseq.structseqfield(0) - pw_passwd = _structseq.structseqfield(1) - pw_uid = _structseq.structseqfield(2) - pw_gid = _structseq.structseqfield(3) - pw_gecos = _structseq.structseqfield(4) - pw_dir = _structseq.structseqfield(5) - pw_shell = _structseq.structseqfield(6) - - -def _mkpwent(pw): - return struct_passwd([ - ffi.string(pw.pw_name), - ffi.string(pw.pw_passwd), - pw.pw_uid, - pw.pw_gid, - ffi.string(pw.pw_gecos), - ffi.string(pw.pw_dir), - ffi.string(pw.pw_shell)]) - - at builtinify -def getpwuid(uid): - """ - getpwuid(uid) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given numeric user ID. - See pwd.__doc__ for more on password database entries. - """ - with _lock: - pw = lib.getpwuid(uid) - if not pw: - raise KeyError("getpwuid(): uid not found: %s" % uid) - return _mkpwent(pw) - - at builtinify -def getpwnam(name): - """ - getpwnam(name) -> (pw_name,pw_passwd,pw_uid, - pw_gid,pw_gecos,pw_dir,pw_shell) - Return the password database entry for the given user name. - See pwd.__doc__ for more on password database entries. - """ - if not isinstance(name, basestring): - raise TypeError("expected string") - name = str(name) - with _lock: - pw = lib.getpwnam(name) - if not pw: - raise KeyError("getpwname(): name not found: %s" % name) - return _mkpwent(pw) - - at builtinify -def getpwall(): - """ - getpwall() -> list_of_entries - Return a list of all available password database entries, in arbitrary order. - See pwd.__doc__ for more on password database entries. - """ - users = [] - with _lock: - lib.setpwent() - while True: - pw = lib.getpwent() - if not pw: - break - users.append(_mkpwent(pw)) - lib.endpwent() - return users - -__all__ = ('struct_passwd', 'getpwuid', 'getpwnam', 'getpwall') - -if __name__ == "__main__": -# Uncomment next line to test CPython implementation -# from pwd import getpwuid, getpwnam, getpwall - from os import getuid - uid = getuid() - pw = getpwuid(uid) - print("uid %s: %s" % (pw.pw_uid, pw)) - name = pw.pw_name - print("name %r: %s" % (name, getpwnam(name))) - print("All:") - for pw in getpwall(): - print(pw) 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 @@ -5,16 +5,17 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f -.. branch: unicode-utf8-re +.. branch: zlib-copying-redux -.. branch: utf8-io +Fix calling copy on already-flushed compressobjs. -Utf8 handling for unicode +.. branch: zlib-copying -.. branch: pyparser-improvements-3 -Small refactorings in the Python parser. +The zlib module's compressobj and decompressobj now expose copy methods +as they do on CPython. -.. branch: unicode-utf8 +.. math-improvements -Use utf8 internally to represent unicode +Improve performance of long operations where one of the operands fits into +an int. \ No newline at end of file diff --git a/pypy/module/_sre/interp_sre.py b/pypy/module/_sre/interp_sre.py --- a/pypy/module/_sre/interp_sre.py +++ b/pypy/module/_sre/interp_sre.py @@ -705,7 +705,7 @@ assert idx >= 0 return fmarks[idx], fmarks[idx+1] else: - raise oefmt(space.w_IndexError, "group index out of range") + raise oefmt(space.w_IndexError, "no such group") def _last_index(self): mark = self.ctx.match_marks diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -44,10 +44,10 @@ return OperationError(w_error, space.newtext(msg)) - at unwrap_spec(string='bufferstr', level=int) -def compress(space, string, level=rzlib.Z_DEFAULT_COMPRESSION): + at unwrap_spec(data='bufferstr', level=int) +def compress(space, data, level=rzlib.Z_DEFAULT_COMPRESSION): """ - compress(string[, level]) -- Returned compressed string. + compress(data[, level]) -- Returned compressed string. Optional arg level is the compression level, in 1-9. """ @@ -57,7 +57,7 @@ except ValueError: raise zlib_error(space, "Bad compression level") try: - result = rzlib.compress(stream, string, rzlib.Z_FINISH) + result = rzlib.compress(stream, data, rzlib.Z_FINISH) finally: rzlib.deflateEnd(stream) except rzlib.RZlibError as e: @@ -113,20 +113,9 @@ Wrapper around zlib's z_stream structure which provides convenient compression functionality. """ - def __init__(self, space, level=rzlib.Z_DEFAULT_COMPRESSION, - method=rzlib.Z_DEFLATED, # \ - wbits=rzlib.MAX_WBITS, # \ undocumented - memLevel=rzlib.DEF_MEM_LEVEL, # / parameters - strategy=rzlib.Z_DEFAULT_STRATEGY, # / - zdict=None): + def __init__(self, space, stream): ZLibObject.__init__(self, space) - try: - self.stream = rzlib.deflateInit(level, method, wbits, - memLevel, strategy, zdict=zdict) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") + self.stream = stream self.register_finalizer(space) def _finalize_(self): @@ -158,6 +147,25 @@ raise zlib_error(space, e.msg) return space.newbytes(result) + def copy(self, space): + """ + copy() -- Return a copy of the compression object. + """ + try: + self.lock() + try: + if not self.stream: + raise oefmt( + space.w_ValueError, + "Compressor was already flushed", + ) + copied = rzlib.deflateCopy(self.stream) + finally: + self.unlock() + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + return Compress(space=space, stream=copied) + @unwrap_spec(mode="c_int") def flush(self, space, mode=rzlib.Z_FINISH): """ @@ -203,16 +211,23 @@ zdict = None else: zdict = space.charbuf_w(w_zdict) - stream = space.allocate_instance(Compress, w_subtype) - stream = space.interp_w(Compress, stream) - Compress.__init__(stream, space, level, - method, wbits, memLevel, strategy, zdict) - return stream + w_stream = space.allocate_instance(Compress, w_subtype) + w_stream = space.interp_w(Compress, w_stream) + try: + stream = rzlib.deflateInit(level, method, wbits, memLevel, strategy, + zdict=zdict) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Compress.__init__(w_stream, space, stream) + return w_stream Compress.typedef = TypeDef( 'Compress', __new__ = interp2app(Compress___new__), + copy = interp2app(Compress.copy), compress = interp2app(Compress.compress), flush = interp2app(Compress.flush), __doc__ = """compressobj([level]) -- Return a compressor object. @@ -226,7 +241,7 @@ Wrapper around zlib's z_stream structure which provides convenient decompression functionality. """ - def __init__(self, space, wbits=rzlib.MAX_WBITS, zdict=None): + def __init__(self, space, stream, zdict, unused_data, unconsumed_tail): """ Initialize a new decompression object. @@ -236,16 +251,12 @@ inflateInit2. """ ZLibObject.__init__(self, space) - self.unused_data = '' - self.unconsumed_tail = '' + + self.stream = stream + self.zdict = zdict + self.unused_data = unused_data + self.unconsumed_tail = unconsumed_tail self.eof = False - try: - self.stream = rzlib.inflateInit(wbits, zdict=zdict) - except rzlib.RZlibError as e: - raise zlib_error(space, e.msg) - except ValueError: - raise oefmt(space.w_ValueError, "Invalid initialization option") - self.zdict = zdict self.register_finalizer(space) def _finalize_(self): @@ -295,6 +306,27 @@ self._save_unconsumed_input(data, finished, unused_len) return space.newbytes(string) + def copy(self, space): + """ + copy() -- Return a copy of the decompression object. + """ + try: + self.lock() + try: + copied = rzlib.inflateCopy(self.stream) + finally: + self.unlock() + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + + return Decompress( + space=space, + stream=copied, + unused_data=self.unused_data, + unconsumed_tail=self.unconsumed_tail, + zdict=self.zdict, + ) + def flush(self, space, w_length=None): """ flush( [length] ) -- This is kept for backward compatibility, @@ -331,10 +363,16 @@ zdict = None else: zdict = space.charbuf_w(w_zdict) - stream = space.allocate_instance(Decompress, w_subtype) - stream = space.interp_w(Decompress, stream) - Decompress.__init__(stream, space, wbits, zdict) - return stream + w_stream = space.allocate_instance(Decompress, w_subtype) + w_stream = space.interp_w(Decompress, w_stream) + try: + stream = rzlib.inflateInit(wbits, zdict=zdict) + except rzlib.RZlibError as e: + raise zlib_error(space, e.msg) + except ValueError: + raise oefmt(space.w_ValueError, "Invalid initialization option") + Decompress.__init__(w_stream, space, stream, zdict, '', '') + return w_stream def default_buffer_size(space): return space.newint(rzlib.OUTPUT_BUFFER_SIZE) @@ -342,6 +380,7 @@ Decompress.typedef = TypeDef( 'Decompress', __new__ = interp2app(Decompress___new__), + copy = interp2app(Decompress.copy), decompress = interp2app(Decompress.decompress), flush = interp2app(Decompress.flush), unused_data = interp_attrproperty('unused_data', Decompress, wrapfn="newbytes"), diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -10,6 +10,13 @@ except ImportError: py.test.skip("no zlib module on this host Python") +from pypy.interpreter.gateway import interp2app +try: + from pypy.module.zlib import interp_zlib + from rpython.rlib import rzlib +except ImportError: + import py; py.test.skip("no zlib C library on this machine") + class AppTestZlib(object): spaceconfig = dict(usemodules=['zlib']) @@ -21,6 +28,8 @@ compression and decompression tests have a little real data to assert against. """ + cls.w_runappdirect = cls.space.wrap(cls.runappdirect) + cls.w_zlib = cls.space.getbuiltinmodule('zlib') expanded = b'some bytes which will be compressed' cls.w_expanded = cls.space.newbytes(expanded) @@ -29,6 +38,22 @@ py.path.local(pypy.__file__).dirpath().dirpath() .join('LICENSE').read()) + def intentionally_break_a_z_stream(space, w_zobj): + """ + Intentionally break the z_stream associated with a + compressobj or decompressobj in a way that causes their copy + methods to raise RZlibErrors. + """ + from rpython.rtyper.lltypesystem import rffi, lltype + w_zobj.stream.c_zalloc = rffi.cast( + lltype.typeOf(w_zobj.stream.c_zalloc), + rzlib.Z_NULL, + ) + + cls.w_intentionally_break_a_z_stream = cls.space.wrap( + interp2app(intentionally_break_a_z_stream), + ) + def test_def_buf_size(self): assert self.zlib.DEF_BUF_SIZE >= 0 @@ -330,3 +355,61 @@ dco = zlib.decompressobj(wbits=-zlib.MAX_WBITS, zdict=zdict) uncomp = dco.decompress(comp) + dco.flush() assert zdict == uncomp + + def test_decompress_copy(self): + decompressor = self.zlib.decompressobj() + d1 = decompressor.decompress(self.compressed[:10]) + assert d1 + + copied = decompressor.copy() + + from_copy = copied.decompress(self.compressed[10:]) + from_decompressor = decompressor.decompress(self.compressed[10:]) + + assert (d1 + from_copy) == (d1 + from_decompressor) + + def test_cannot_copy_decompressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") + decompressor = self.zlib.decompressobj() + self.intentionally_break_a_z_stream(zobj=decompressor) + raises(self.zlib.error, decompressor.copy) + + def test_decompress_copy_carries_along_state(self): + """ + Decompressor.unused_data and unconsumed_tail are carried along when a + copy is done. + """ + decompressor = self.zlib.decompressobj() + decompressor.decompress(self.compressed, max_length=5) + unconsumed_tail = decompressor.unconsumed_tail + assert unconsumed_tail + assert decompressor.copy().unconsumed_tail == unconsumed_tail + + decompressor.decompress(decompressor.unconsumed_tail) + decompressor.decompress(b"xxx") + unused_data = decompressor.unused_data + assert unused_data + assert decompressor.copy().unused_data == unused_data + + def test_compress_copy(self): + compressor = self.zlib.compressobj() + d1 = compressor.compress(self.expanded[:10]) + assert d1 + + copied = compressor.copy() + + from_copy = copied.compress(self.expanded[10:]) + from_compressor = compressor.compress(self.expanded[10:]) + + assert (d1 + from_copy) == (d1 + from_compressor) + + def test_cannot_copy_compressor_with_stream_in_inconsistent_state(self): + if self.runappdirect: skip("can't run with -A") + compressor = self.zlib.compressobj() + self.intentionally_break_a_z_stream(zobj=compressor) + raises(self.zlib.error, compressor.copy) + + def test_cannot_copy_compressor_with_flushed_stream(self): + compressor = self.zlib.compressobj() + compressor.flush() + raises(ValueError, compressor.copy) diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py --- a/pypy/objspace/std/intobject.py +++ b/pypy/objspace/std/intobject.py @@ -437,7 +437,7 @@ return ix -def _pow_ovf2long(space, iv, iw, w_modulus): +def _pow_ovf2long(space, iv, w_iv, iw, w_iw, w_modulus): if space.is_none(w_modulus) and _recover_with_smalllong(space): from pypy.objspace.std.smalllongobject import _pow as _pow_small try: @@ -446,9 +446,12 @@ return _pow_small(space, r_longlong(iv), iw, r_longlong(0)) except (OverflowError, ValueError): pass - from pypy.objspace.std.longobject import W_LongObject - w_iv = W_LongObject.fromint(space, iv) - w_iw = W_LongObject.fromint(space, iw) + from pypy.objspace.std.longobject import W_LongObject, W_AbstractLongObject + if w_iv is None or not isinstance(w_iv, W_AbstractLongObject): + w_iv = W_LongObject.fromint(space, iv) + if w_iw is None or not isinstance(w_iw, W_AbstractLongObject): + w_iw = W_LongObject.fromint(space, iw) + return w_iv.descr_pow(space, w_iw, w_modulus) @@ -456,7 +459,7 @@ op = getattr(operator, opname, None) assert op or ovf2small - def ovf2long(space, x, y): + def ovf2long(space, x, w_x, y, w_y): """Handle overflowing to smalllong or long""" if _recover_with_smalllong(space): if ovf2small: @@ -468,9 +471,12 @@ b = r_longlong(y) return W_SmallLongObject(op(a, b)) - from pypy.objspace.std.longobject import W_LongObject - w_x = W_LongObject.fromint(space, x) - w_y = W_LongObject.fromint(space, y) + from pypy.objspace.std.longobject import W_LongObject, W_AbstractLongObject + if w_x is None or not isinstance(w_x, W_AbstractLongObject): + w_x = W_LongObject.fromint(space, x) + if w_y is None or not isinstance(w_y, W_AbstractLongObject): + w_y = W_LongObject.fromint(space, y) + return getattr(w_x, 'descr_' + opname)(space, w_y) return ovf2long @@ -630,12 +636,18 @@ # can't return NotImplemented (space.pow doesn't do full # ternary, i.e. w_modulus.__zpow__(self, w_exponent)), so # handle it ourselves - return _pow_ovf2long(space, x, y, w_modulus) + return _pow_ovf2long(space, x, self, y, w_exponent, w_modulus) try: result = _pow(space, x, y, z) - except (OverflowError, ValueError): - return _pow_ovf2long(space, x, y, w_modulus) + except OverflowError: + return _pow_ovf2long(space, x, self, y, w_exponent, w_modulus) + except ValueError: + # float result, so let avoid a roundtrip in rbigint. + self = self.descr_float(space) + w_exponent = w_exponent.descr_float(space) + return space.pow(self, w_exponent, space.w_None) + return space.newint(result) @unwrap_spec(w_modulus=WrappedDefault(None)) @@ -685,7 +697,7 @@ try: z = ovfcheck(op(x, y)) except OverflowError: - return ovf2long(space, x, y) + return ovf2long(space, x, self, y, w_other) else: z = op(x, y) return wrapint(space, z) @@ -709,7 +721,7 @@ try: z = ovfcheck(op(y, x)) except OverflowError: - return ovf2long(space, y, x) + return ovf2long(space, y, w_other, x, self) # XXX write a test else: z = op(y, x) return wrapint(space, z) @@ -743,7 +755,7 @@ try: return func(space, x, y) except OverflowError: - return ovf2long(space, x, y) + return ovf2long(space, x, self, y, w_other) else: return func(space, x, y) elif isinstance(w_other, W_AbstractIntObject): @@ -760,7 +772,7 @@ try: return func(space, y, x) except OverflowError: - return ovf2long(space, y, x) + return ovf2long(space, y, w_other, x, self) else: return func(space, y, x) elif isinstance(w_other, W_AbstractIntObject): diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py --- a/pypy/objspace/std/longobject.py +++ b/pypy/objspace/std/longobject.py @@ -234,22 +234,16 @@ descr_gt = _make_descr_cmp('gt') descr_ge = _make_descr_cmp('ge') - def _make_generic_descr_binop_noncommutative(opname): - methname = opname + '_' if opname in ('and', 'or') else opname - descr_rname = 'descr_r' + opname - op = getattr(rbigint, methname) + def descr_sub(self, space, w_other): + if isinstance(w_other, W_IntObject): + return W_LongObject(self.num.int_sub(w_other.int_w(space))) + elif not isinstance(w_other, W_AbstractLongObject): + return space.w_NotImplemented + return W_LongObject(self.num.sub(w_other.asbigint())) - @func_renamer('descr_' + opname) - @delegate_other - def descr_binop(self, space, w_other): - return W_LongObject(op(self.num, w_other.asbigint())) - - @func_renamer(descr_rname) - @delegate_other - def descr_rbinop(self, space, w_other): - return W_LongObject(op(w_other.asbigint(), self.num)) - - return descr_binop, descr_rbinop + @delegate_other + def descr_rsub(self, space, w_other): + return W_LongObject(w_other.asbigint().sub(self.num)) def _make_generic_descr_binop(opname): if opname not in COMMUTATIVE_OPS: @@ -281,28 +275,23 @@ return descr_binop, descr_rbinop descr_add, descr_radd = _make_generic_descr_binop('add') - descr_sub, descr_rsub = _make_generic_descr_binop_noncommutative('sub') + descr_mul, descr_rmul = _make_generic_descr_binop('mul') descr_and, descr_rand = _make_generic_descr_binop('and') descr_or, descr_ror = _make_generic_descr_binop('or') descr_xor, descr_rxor = _make_generic_descr_binop('xor') - def _make_descr_binop(func, int_func=None): + def _make_descr_binop(func, int_func): opname = func.__name__[1:] - if int_func: - @func_renamer('descr_' + opname) - def descr_binop(self, space, w_other): - if isinstance(w_other, W_IntObject): - return int_func(self, space, w_other.int_w(space)) - elif not isinstance(w_other, W_AbstractLongObject): - return space.w_NotImplemented - return func(self, space, w_other) - else: - @delegate_other - @func_renamer('descr_' + opname) - def descr_binop(self, space, w_other): - return func(self, space, w_other) + @func_renamer('descr_' + opname) + def descr_binop(self, space, w_other): + if isinstance(w_other, W_IntObject): + return int_func(self, space, w_other.int_w(space)) + elif not isinstance(w_other, W_AbstractLongObject): + return space.w_NotImplemented + return func(self, space, w_other) + @delegate_other @func_renamer('descr_r' + opname) def descr_rbinop(self, space, w_other): @@ -322,10 +311,10 @@ raise oefmt(space.w_OverflowError, "shift count too large") return W_LongObject(self.num.lshift(shift)) - def _int_lshift(self, space, w_other): - if w_other < 0: + def _int_lshift(self, space, other): + if other < 0: raise oefmt(space.w_ValueError, "negative shift count") - return W_LongObject(self.num.lshift(w_other)) + return W_LongObject(self.num.lshift(other)) descr_lshift, descr_rlshift = _make_descr_binop(_lshift, _int_lshift) @@ -338,11 +327,11 @@ raise oefmt(space.w_OverflowError, "shift count too large") return newlong(space, self.num.rshift(shift)) - def _int_rshift(self, space, w_other): - if w_other < 0: + def _int_rshift(self, space, other): + if other < 0: raise oefmt(space.w_ValueError, "negative shift count") - return newlong(space, self.num.rshift(w_other)) + return newlong(space, self.num.rshift(other)) descr_rshift, descr_rrshift = _make_descr_binop(_rshift, _int_rshift) def _floordiv(self, space, w_other): @@ -353,14 +342,14 @@ "long division or modulo by zero") return newlong(space, z) - def _floordiv(self, space, w_other): + def _int_floordiv(self, space, other): try: - z = self.num.floordiv(w_other.asbigint()) + z = self.num.int_floordiv(other) except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "integer division or modulo by zero") return newlong(space, z) - descr_floordiv, descr_rfloordiv = _make_descr_binop(_floordiv) + descr_floordiv, descr_rfloordiv = _make_descr_binop(_floordiv, _int_floordiv) def _mod(self, space, w_other): try: @@ -370,9 +359,9 @@ "integer division or modulo by zero") return newlong(space, z) - def _int_mod(self, space, w_other): + def _int_mod(self, space, other): try: - z = self.num.int_mod(w_other) + z = self.num.int_mod(other) except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "long division or modulo by zero") @@ -386,7 +375,16 @@ raise oefmt(space.w_ZeroDivisionError, "integer division or modulo by zero") return space.newtuple([newlong(space, div), newlong(space, mod)]) - descr_divmod, descr_rdivmod = _make_descr_binop(_divmod) + + def _int_divmod(self, space, other): + try: + div, mod = self.num.int_divmod(other) + except ZeroDivisionError: + raise oefmt(space.w_ZeroDivisionError, + "long division or modulo by zero") + return space.newtuple([newlong(space, div), newlong(space, mod)]) + + descr_divmod, descr_rdivmod = _make_descr_binop(_divmod, _int_divmod) def _hash_long(space, v): diff --git a/pypy/objspace/std/test/test_intobject.py b/pypy/objspace/std/test/test_intobject.py --- a/pypy/objspace/std/test/test_intobject.py +++ b/pypy/objspace/std/test/test_intobject.py @@ -738,6 +738,11 @@ raises(ValueError, int, '00000000000000000000000000000000000007', 0) raises(ValueError, int, '00000000000000000077777777777777777777', 0) + def test_some_rops(self): + b = 2 ** 31 + x = -b + assert x.__rsub__(2) == (2 + b) + class AppTestIntShortcut(AppTestInt): spaceconfig = {"objspace.std.intshortcut": True} diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py --- a/pypy/tool/release/package.py +++ b/pypy/tool/release/package.py @@ -121,6 +121,8 @@ pypydir.ensure('include', dir=True) if sys.platform == 'win32': + os.environ['PATH'] = str(basedir.join('externals').join('bin')) + ';' + \ + os.environ.get('PATH', '') src,tgt = binaries[0] pypyw = src.new(purebasename=src.purebasename + 'w') if pypyw.exists(): 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 pmaj=2 # python main version pmin=7 # python minor version -maj=6 +maj=7 min=0 rev=0 branchname=release-pypy$pmaj.$pmin-$maj.x # ==OR== release-$maj.x # ==OR== release-$maj.$min.x @@ -13,7 +13,7 @@ hg log -r $tagname || exit 1 hgrev=`hg id -r $tagname -i` -rel=pypy$pmaj-v$maj.$min.$rev +rel=pypy$pmaj.$pmin-v$maj.$min.$rev # The script should be run in an empty in the pypy tree, i.e. pypy/tmp if [ "`ls . | wc -l`" != "0" ] then @@ -23,7 +23,7 @@ # Download latest builds from the buildmaster, rename the top # level directory, and repackage ready to be uploaded to bitbucket -for plat in linux linux64 linux-armhf-raspbian linux-armel osx64 s390x +for plat in linux linux64 osx64 s390x # linux-armhf-raspbian linux-armel do echo downloading package for $plat if wget -q --show-progress http://buildbot.pypy.org/nightly/$branchname/pypy-c-jit-latest-$plat.tar.bz2 diff --git a/rpython/conftest.py b/rpython/conftest.py --- a/rpython/conftest.py +++ b/rpython/conftest.py @@ -6,15 +6,16 @@ option = None try: - from hypothesis import settings, __version__ + from hypothesis import settings except ImportError: pass else: - if __version__[:2] < '3.6': - s = settings(deadline=None) - settings.register_profile('default', s) - else: + try: settings.register_profile('default', deadline=None) + except Exception: + import warnings + warnings.warn("Version of hypothesis too old, " + "cannot set the deadline to None") settings.load_profile('default') def braindead_deindent(self): diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py --- a/rpython/rlib/_rsocket_rffi.py +++ b/rpython/rlib/_rsocket_rffi.py @@ -1191,14 +1191,14 @@ inet_ntoa = external('inet_ntoa', [in_addr], rffi.CCHARP) -if _POSIX: - inet_pton = external('inet_pton', [rffi.INT, rffi.CCHARP, - rffi.VOIDP], rffi.INT, - save_err=SAVE_ERR) - inet_ntop = external('inet_ntop', [rffi.INT, rffi.VOIDP, CCHARP, - socklen_t], CCHARP, - save_err=SAVE_ERR) +inet_pton = external('inet_pton', [rffi.INT, rffi.CCHARP, + rffi.VOIDP], rffi.INT, + save_err=SAVE_ERR) + +inet_ntop = external('inet_ntop', [rffi.INT, rffi.VOIDP, CCHARP, + socklen_t], CCHARP, + save_err=SAVE_ERR) inet_addr = external('inet_addr', [rffi.CCHARP], rffi.UINT) socklen_t_ptr = lltype.Ptr(rffi.CFixedArray(socklen_t, 1)) diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py --- a/rpython/rlib/rarithmetic.py +++ b/rpython/rlib/rarithmetic.py @@ -612,6 +612,7 @@ r_ulonglong = build_int('r_ulonglong', False, 64) r_longlonglong = build_int('r_longlonglong', True, 128) +r_ulonglonglong = build_int('r_ulonglonglong', False, 128) longlongmax = r_longlong(LONGLONG_TEST - 1) if r_longlong is not r_int: diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py --- a/rpython/rlib/rbigint.py +++ b/rpython/rlib/rbigint.py @@ -27,6 +27,7 @@ else: UDIGIT_MASK = longlongmask LONG_TYPE = rffi.__INT128_T + ULONG_TYPE = rffi.__UINT128_T if LONG_BIT > SHIFT: STORE_TYPE = lltype.Signed UNSIGNED_TYPE = lltype.Unsigned @@ -40,6 +41,7 @@ STORE_TYPE = lltype.Signed UNSIGNED_TYPE = lltype.Unsigned LONG_TYPE = rffi.LONGLONG + ULONG_TYPE = rffi.ULONGLONG MASK = int((1 << SHIFT) - 1) FLOAT_MULTIPLIER = float(1 << SHIFT) @@ -97,6 +99,9 @@ def _widen_digit(x): return rffi.cast(LONG_TYPE, x) +def _unsigned_widen_digit(x): + return rffi.cast(ULONG_TYPE, x) + @specialize.argtype(0) def _store_digit(x): return rffi.cast(STORE_TYPE, x) @@ -108,6 +113,7 @@ NULLDIGIT = _store_digit(0) ONEDIGIT = _store_digit(1) +NULLDIGITS = [NULLDIGIT] def _check_digits(l): for x in l: @@ -133,22 +139,26 @@ def specialize_call(self, hop): hop.exception_cannot_occur() +def intsign(i): + return -1 if i < 0 else 1 class rbigint(object): """This is a reimplementation of longs using a list of digits.""" _immutable_ = True - _immutable_fields_ = ["_digits"] - - def __init__(self, digits=[NULLDIGIT], sign=0, size=0): + _immutable_fields_ = ["_digits[*]", "size", "sign"] + + def __init__(self, digits=NULLDIGITS, sign=0, size=0): if not we_are_translated(): _check_digits(digits) make_sure_not_resized(digits) self._digits = digits + assert size >= 0 self.size = size or len(digits) + self.sign = sign - # __eq__ and __ne__ method exist for testingl only, they are not RPython! + # __eq__ and __ne__ method exist for testing only, they are not RPython! @not_rpython def __eq__(self, other): if not isinstance(other, rbigint): @@ -159,6 +169,7 @@ def __ne__(self, other): return not (self == other) + @specialize.argtype(1) def digit(self, x): """Return the x'th digit, as an int.""" return self._digits[x] @@ -170,6 +181,12 @@ return _widen_digit(self._digits[x]) widedigit._always_inline_ = True + def uwidedigit(self, x): + """Return the x'th digit, as a long long int if needed + to have enough room to contain two digits.""" + return _unsigned_widen_digit(self._digits[x]) + uwidedigit._always_inline_ = True + def udigit(self, x): """Return the x'th digit, as an unsigned int.""" return _load_unsigned_digit(self._digits[x]) @@ -183,7 +200,9 @@ setdigit._always_inline_ = True def numdigits(self): - return self.size + w = self.size + assert w > 0 + return w numdigits._always_inline_ = True @staticmethod @@ -196,13 +215,15 @@ if intval < 0: sign = -1 ival = -r_uint(intval) + carry = ival >> SHIFT elif intval > 0: sign = 1 ival = r_uint(intval) + carry = 0 else: return NULLRBIGINT - carry = ival >> SHIFT + if carry: return rbigint([_store_digit(ival & MASK), _store_digit(carry)], sign, 2) @@ -509,23 +530,22 @@ return True @jit.elidable - def int_eq(self, other): + def int_eq(self, iother): """ eq with int """ - - if not int_in_valid_range(other): - # Fallback to Long. - return self.eq(rbigint.fromint(other)) + if not int_in_valid_range(iother): + # Fallback to Long. + return self.eq(rbigint.fromint(iother)) if self.numdigits() > 1: return False - return (self.sign * self.digit(0)) == other + return (self.sign * self.digit(0)) == iother def ne(self, other): return not self.eq(other) - def int_ne(self, other): - return not self.int_eq(other) + def int_ne(self, iother): + return not self.int_eq(iother) @jit.elidable def lt(self, other): @@ -563,59 +583,38 @@ return False @jit.elidable - def int_lt(self, other): + def int_lt(self, iother): """ lt where other is an int """ - if not int_in_valid_range(other): + if not int_in_valid_range(iother): # Fallback to Long. - return self.lt(rbigint.fromint(other)) - - osign = 1 - if other == 0: - osign = 0 - elif other < 0: - osign = -1 - - if self.sign > osign: - return False - elif self.sign < osign: - return True - - digits = self.numdigits() - - if digits > 1: - if osign == 1: - return False - else: - return True - - d1 = self.sign * self.digit(0) - if d1 < other: - return True - return False + return self.lt(rbigint.fromint(iother)) + + return _x_int_lt(self, iother, False) def le(self, other): return not other.lt(self) - def int_le(self, other): - # Alternative that might be faster, reimplant this. as a check with other + 1. But we got to check for overflow - # or reduce valid range. - - if self.int_eq(other): - return True - return self.int_lt(other) + def int_le(self, iother): + """ le where iother is an int """ + + if not int_in_valid_range(iother): + # Fallback to Long. + return self.le(rbigint.fromint(iother)) + + return _x_int_lt(self, iother, True) def gt(self, other): return other.lt(self) - def int_gt(self, other): - return not self.int_le(other) + def int_gt(self, iother): + return not self.int_le(iother) def ge(self, other): return not self.lt(other) - def int_ge(self, other): - return not self.int_lt(other) + def int_ge(self, iother): + return not self.int_lt(iother) @jit.elidable def hash(self): @@ -635,20 +634,20 @@ return result @jit.elidable - def int_add(self, other): - if not int_in_valid_range(other): + def int_add(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.add(rbigint.fromint(other)) + return self.add(rbigint.fromint(iother)) elif self.sign == 0: - return rbigint.fromint(other) - elif other == 0: + return rbigint.fromint(iother) + elif iother == 0: return self - sign = -1 if other < 0 else 1 + sign = intsign(iother) if self.sign == sign: - result = _x_int_add(self, other) + result = _x_int_add(self, iother) else: - result = _x_int_sub(self, other) + result = _x_int_sub(self, iother) result.sign *= -1 result.sign *= sign return result @@ -658,7 +657,7 @@ if other.sign == 0: return self elif self.sign == 0: - return rbigint(other._digits[:other.size], -other.sign, other.size) + return rbigint(other._digits[:other.numdigits()], -other.sign, other.numdigits()) elif self.sign == other.sign: result = _x_sub(self, other) else: @@ -667,93 +666,94 @@ return result @jit.elidable - def int_sub(self, other): - if not int_in_valid_range(other): + def int_sub(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.sub(rbigint.fromint(other)) - elif other == 0: + return self.sub(rbigint.fromint(iother)) + elif iother == 0: return self elif self.sign == 0: - return rbigint.fromint(-other) - elif self.sign == (-1 if other < 0 else 1): - result = _x_int_sub(self, other) + return rbigint.fromint(-iother) + elif self.sign == intsign(iother): + result = _x_int_sub(self, iother) else: - result = _x_int_add(self, other) + result = _x_int_add(self, iother) result.sign *= self.sign return result @jit.elidable - def mul(self, b): - asize = self.numdigits() - bsize = b.numdigits() - - a = self - - if asize > bsize: - a, b, asize, bsize = b, a, bsize, asize - - if a.sign == 0 or b.sign == 0: + def mul(self, other): + selfsize = self.numdigits() + othersize = other.numdigits() + + if selfsize > othersize: + self, other, selfsize, othersize = other, self, othersize, selfsize + + if self.sign == 0 or other.sign == 0: return NULLRBIGINT - if asize == 1: - if a._digits[0] == ONEDIGIT: - return rbigint(b._digits[:b.size], a.sign * b.sign, b.size) - elif bsize == 1: - res = b.widedigit(0) * a.widedigit(0) + if selfsize == 1: + if self._digits[0] == ONEDIGIT: + return rbigint(other._digits[:othersize], self.sign * other.sign, othersize) + elif othersize == 1: + res = other.uwidedigit(0) * self.udigit(0) carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], a.sign * b.sign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * other.sign, 2) else: - return rbigint([_store_digit(res & MASK)], a.sign * b.sign, 1) - - result = _x_mul(a, b, a.digit(0)) + return rbigint([_store_digit(res & MASK)], self.sign * other.sign, 1) + + result = _x_mul(self, other, self.digit(0)) elif USE_KARATSUBA: - if a is b: + if self is other: i = KARATSUBA_SQUARE_CUTOFF else: i = KARATSUBA_CUTOFF - if asize <= i: - result = _x_mul(a, b) - """elif 2 * asize <= bsize: - result = _k_lopsided_mul(a, b)""" + if selfsize <= i: + result = _x_mul(self, other) + """elif 2 * selfsize <= othersize: + result = _k_lopsided_mul(self, other)""" else: - result = _k_mul(a, b) + result = _k_mul(self, other) else: - result = _x_mul(a, b) - - result.sign = a.sign * b.sign + result = _x_mul(self, other) + + result.sign = self.sign * other.sign return result @jit.elidable - def int_mul(self, b): - if not int_in_valid_range(b): + def int_mul(self, iother): + if not int_in_valid_range(iother): # Fallback to long. - return self.mul(rbigint.fromint(b)) - - if self.sign == 0 or b == 0: + return self.mul(rbigint.fromint(iother)) + + if self.sign == 0 or iother == 0: return NULLRBIGINT asize = self.numdigits() - digit = abs(b) - bsign = -1 if b < 0 else 1 + digit = abs(iother) + + othersign = intsign(iother) if digit == 1: - return rbigint(self._digits[:self.size], self.sign * bsign, asize) + if othersign == 1: + return self + return rbigint(self._digits[:asize], self.sign * othersign, asize) elif asize == 1: - res = self.widedigit(0) * digit + udigit = r_uint(digit) + res = self.uwidedigit(0) * udigit carry = res >> SHIFT if carry: - return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * bsign, 2) + return rbigint([_store_digit(res & MASK), _store_digit(carry)], self.sign * othersign, 2) else: - return rbigint([_store_digit(res & MASK)], self.sign * bsign, 1) - + return rbigint([_store_digit(res & MASK)], self.sign * othersign, 1) elif digit & (digit - 1) == 0: result = self.lqshift(ptwotable[digit]) else: result = _muladd1(self, digit) - result.sign = self.sign * bsign + result.sign = self.sign * othersign return result @jit.elidable @@ -763,12 +763,10 @@ @jit.elidable def floordiv(self, other): - if self.sign == 1 and other.numdigits() == 1 and other.sign == 1: - digit = other.digit(0) - if digit == 1: - return rbigint(self._digits[:self.size], 1, self.size) - elif digit and digit & (digit - 1) == 0: - return self.rshift(ptwotable[digit]) + if other.numdigits() == 1: + otherint = other.digit(0) * other.sign + assert int_in_valid_range(otherint) + return self.int_floordiv(otherint) div, mod = _divrem(self, other) if mod.sign * other.sign == -1: @@ -782,6 +780,37 @@ return self.floordiv(other) @jit.elidable + def int_floordiv(self, iother): + if not int_in_valid_range(iother): From pypy.commits at gmail.com Mon Feb 11 04:11:48 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 01:11:48 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: merge unicode-utf8 into branch Message-ID: <5c613c54.1c69fb81.198c6.c029@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95931:e08614e7b2b3 Date: 2019-02-09 20:38 +0100 http://bitbucket.org/pypy/pypy/changeset/e08614e7b2b3/ Log: merge unicode-utf8 into branch diff too long, truncating to 2000 out of 4233 lines diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -61,3 +61,9 @@ 9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +c8805ee6d7846ca2722b106eeaa2f128c699aba3 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +928a4f70d3de7d17449456946154c5da6e600162 release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +fb40f7a5524c77b80e6c468e087d621610137261 release-pypy3.6-v7.0.0 diff --git a/TODO b/TODO --- a/TODO +++ b/TODO @@ -1,8 +1,6 @@ * find a better way to run "find" without creating the index storage, if one if one is not already readily available (understand cost now, improve after merge) -* write the correct jit_elidable in _get_index_storage (Armin) * improve performance of splitlines (CF) -* stop using runicode/unicode and move MAXUNICODE to rutf8 (Matti) * think about cost of utf8 list strategy (CF) * revisit why runicode import str_decode_utf_8_impl needed instead of runicode import str_decode_utf_8 diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst --- a/pypy/doc/release-v7.0.0.rst +++ b/pypy/doc/release-v7.0.0.rst @@ -39,7 +39,7 @@ The utf8 branch that changes internal representation of unicode to utf8 did not make it into the release, so there is still more goodness coming. -You can download the v6.0 releases here: +You can download the v7.0 releases here: http://pypy.org/download.html 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 @@ -5,6 +5,11 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f +.. branch: zlib-copying-third-time-a-charm + +Make sure zlib decompressobjs have their streams deallocated immediately +on flush. + .. branch: zlib-copying-redux Fix calling copy on already-flushed compressobjs. @@ -15,7 +20,11 @@ as they do on CPython. -.. math-improvements +.. branch: math-improvements Improve performance of long operations where one of the operands fits into -an int. \ No newline at end of file +an int. + +.. branch: regalloc-playgrounds + +Improve register allocation in the JIT. diff --git a/pypy/doc/whatsnew-pypy2-5.10.0.rst b/pypy/doc/whatsnew-pypy2-5.10.0.rst --- a/pypy/doc/whatsnew-pypy2-5.10.0.rst +++ b/pypy/doc/whatsnew-pypy2-5.10.0.rst @@ -1,42 +1,42 @@ -========================== -What's new in PyPy2.7 5.10 -========================== - -.. this is a revision shortly after release-pypy2.7-v5.9.0 -.. startrev:d56dadcef996 - - -.. branch: cppyy-packaging - -Cleanup and improve cppyy packaging - -.. branch: docs-osx-brew-openssl - -.. branch: keep-debug-symbols - -Add a smartstrip tool, which can optionally keep the debug symbols in a -separate file, instead of just stripping them away. Use it in packaging - -.. branch: bsd-patches - -Fix failures on FreeBSD, contributed by David Naylor as patches on the issue -tracker (issues 2694, 2695, 2696, 2697) - -.. branch: run-extra-tests - -Run extra_tests/ in buildbot - -.. branch: vmprof-0.4.10 - -Upgrade the _vmprof backend to vmprof 0.4.10 - -.. branch: fix-vmprof-stacklet-switch -.. branch: fix-vmprof-stacklet-switch-2 - -Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) - -.. branch: win32-vcvars - -.. branch: rdict-fast-hash - -Make it possible to declare that the hash function of an r_dict is fast in RPython. +========================== +What's new in PyPy2.7 5.10 +========================== + +.. this is a revision shortly after release-pypy2.7-v5.9.0 +.. startrev:d56dadcef996 + + +.. branch: cppyy-packaging + +Cleanup and improve cppyy packaging + +.. branch: docs-osx-brew-openssl + +.. branch: keep-debug-symbols + +Add a smartstrip tool, which can optionally keep the debug symbols in a +separate file, instead of just stripping them away. Use it in packaging + +.. branch: bsd-patches + +Fix failures on FreeBSD, contributed by David Naylor as patches on the issue +tracker (issues 2694, 2695, 2696, 2697) + +.. branch: run-extra-tests + +Run extra_tests/ in buildbot + +.. branch: vmprof-0.4.10 + +Upgrade the _vmprof backend to vmprof 0.4.10 + +.. branch: fix-vmprof-stacklet-switch +.. branch: fix-vmprof-stacklet-switch-2 + +Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) + +.. branch: win32-vcvars + +.. branch: rdict-fast-hash + +Make it possible to declare that the hash function of an r_dict is fast in RPython. diff --git a/pypy/doc/whatsnew-pypy2-6.0.0.rst b/pypy/doc/whatsnew-pypy2-6.0.0.rst --- a/pypy/doc/whatsnew-pypy2-6.0.0.rst +++ b/pypy/doc/whatsnew-pypy2-6.0.0.rst @@ -1,132 +1,128 @@ -=========================== -What's new in PyPy2.7 5.10+ -=========================== - -.. this is a revision shortly after release-pypy2.7-v5.10.0 -.. startrev: 6b024edd9d12 - -.. branch: cpyext-avoid-roundtrip - -Big refactoring of some cpyext code, which avoids a lot of nonsense when -calling C from Python and vice-versa: the result is a big speedup in -function/method calls, up to 6 times faster. - -.. branch: cpyext-datetime2 - -Support ``tzinfo`` field on C-API datetime objects, fixes latest pandas HEAD - - -.. branch: mapdict-size-limit - -Fix a corner case of mapdict: When an instance is used like a dict (using -``setattr`` and ``getattr``, or ``.__dict__``) and a lot of attributes are -added, then the performance using mapdict is linear in the number of -attributes. This is now fixed (by switching to a regular dict after 80 -attributes). - - -.. branch: cpyext-faster-arg-passing - -When using cpyext, improve the speed of passing certain objects from PyPy to C -code, most notably None, True, False, types, all instances of C-defined types. -Before, a dict lookup was needed every time such an object crossed over, now it -is just a field read. - - -.. branch: 2634_datetime_timedelta_performance - -Improve datetime + timedelta performance. - -.. branch: memory-accounting - -Improve way to describe memory - -.. branch: msvc14 - -Allow compilaiton with Visual Studio 2017 compiler suite on windows - -.. branch: winapi - -Update _winapi and internal _winbase_cffi (via _winbase_build) for python 3 - -.. branch: refactor-slots - -Refactor cpyext slots. - - -.. branch: call-loopinvariant-into-bridges - -Speed up branchy code that does a lot of function inlining by saving one call -to read the TLS in most bridges. - -.. branch: rpython-sprint - -Refactor in rpython signatures - -.. branch: cpyext-tls-operror2 - -Store error state thread-locally in executioncontext, fixes issue #2764 - -.. branch: cpyext-fast-typecheck - -Optimize `Py*_Check` for `Bool`, `Float`, `Set`. Also refactor and simplify -`W_PyCWrapperObject` which is used to call slots from the C-API, greatly -improving microbenchmarks in https://github.com/antocuni/cpyext-benchmarks - - -.. branch: fix-sre-problems - -Fix two (unrelated) JIT bugs manifesting in the re module: - -- green fields are broken and were thus disabled, plus their usage removed from - the _sre implementation - -- in rare "trace is too long" situations, the JIT could break behaviour - arbitrarily. - -.. branch: jit-hooks-can-be-disabled - -Be more efficient about JIT hooks. Make it possible for the frontend to declare -that jit hooks are currently not enabled at all. in that case, the list of ops -does not have to be created in the case of the on_abort hook (which is -expensive). - - -.. branch: pyparser-improvements - -Improve speed of Python parser, improve ParseError messages slightly. - -.. branch: ioctl-arg-size - -Work around possible bugs in upstream ioctl users, like CPython allocate at -least 1024 bytes for the arg in calls to ``ioctl(fd, request, arg)``. Fixes -issue #2776 - -.. branch: cpyext-subclass-setattr - -Fix for python-level classes that inherit from C-API types, previously the -`w_obj` was not necessarily preserved throughout the lifetime of the `pyobj` -which led to cases where instance attributes were lost. Fixes issue #2793 - - -.. branch: pyparser-improvements-2 - -Improve line offsets that are reported by SyntaxError. Improve error messages -for a few situations, including mismatched parenthesis. - -.. branch: issue2752 - -Fix a rare GC bug that was introduced more than one year ago, but was -not diagnosed before issue #2752. - -.. branch: gc-hooks - -Introduce GC hooks, as documented in doc/gc_info.rst - -.. branch: gc-hook-better-timestamp - -Improve GC hooks - -.. branch: cppyy-packaging - -Update backend to 0.6.0 and support exceptions through wrappers +=========================== +What's new in PyPy2.7 5.10+ +=========================== + +.. this is a revision shortly after release-pypy2.7-v5.10.0 +.. startrev: 6b024edd9d12 + +.. branch: cpyext-avoid-roundtrip + +Big refactoring of some cpyext code, which avoids a lot of nonsense when +calling C from Python and vice-versa: the result is a big speedup in +function/method calls, up to 6 times faster. + +.. branch: cpyext-datetime2 + +Support ``tzinfo`` field on C-API datetime objects, fixes latest pandas HEAD + + +.. branch: mapdict-size-limit + +Fix a corner case of mapdict: When an instance is used like a dict (using +``setattr`` and ``getattr``, or ``.__dict__``) and a lot of attributes are +added, then the performance using mapdict is linear in the number of +attributes. This is now fixed (by switching to a regular dict after 80 +attributes). + + +.. branch: cpyext-faster-arg-passing + +When using cpyext, improve the speed of passing certain objects from PyPy to C +code, most notably None, True, False, types, all instances of C-defined types. +Before, a dict lookup was needed every time such an object crossed over, now it +is just a field read. + + +.. branch: 2634_datetime_timedelta_performance + +Improve datetime + timedelta performance. + +.. branch: memory-accounting + +Improve way to describe memory + +.. branch: msvc14 + +Allow compilaiton with Visual Studio 2017 compiler suite on windows + +.. branch: refactor-slots + +Refactor cpyext slots. + + +.. branch: call-loopinvariant-into-bridges + +Speed up branchy code that does a lot of function inlining by saving one call +to read the TLS in most bridges. + +.. branch: rpython-sprint + +Refactor in rpython signatures + +.. branch: cpyext-tls-operror2 + +Store error state thread-locally in executioncontext, fixes issue #2764 + +.. branch: cpyext-fast-typecheck + +Optimize `Py*_Check` for `Bool`, `Float`, `Set`. Also refactor and simplify +`W_PyCWrapperObject` which is used to call slots from the C-API, greatly +improving microbenchmarks in https://github.com/antocuni/cpyext-benchmarks + + +.. branch: fix-sre-problems + +Fix two (unrelated) JIT bugs manifesting in the re module: + +- green fields are broken and were thus disabled, plus their usage removed from + the _sre implementation + +- in rare "trace is too long" situations, the JIT could break behaviour + arbitrarily. + +.. branch: jit-hooks-can-be-disabled + +Be more efficient about JIT hooks. Make it possible for the frontend to declare +that jit hooks are currently not enabled at all. in that case, the list of ops +does not have to be created in the case of the on_abort hook (which is +expensive). + + +.. branch: pyparser-improvements + +Improve speed of Python parser, improve ParseError messages slightly. + +.. branch: ioctl-arg-size + +Work around possible bugs in upstream ioctl users, like CPython allocate at +least 1024 bytes for the arg in calls to ``ioctl(fd, request, arg)``. Fixes +issue #2776 + +.. branch: cpyext-subclass-setattr + +Fix for python-level classes that inherit from C-API types, previously the +`w_obj` was not necessarily preserved throughout the lifetime of the `pyobj` +which led to cases where instance attributes were lost. Fixes issue #2793 + + +.. branch: pyparser-improvements-2 + +Improve line offsets that are reported by SyntaxError. Improve error messages +for a few situations, including mismatched parenthesis. + +.. branch: issue2752 + +Fix a rare GC bug that was introduced more than one year ago, but was +not diagnosed before issue #2752. + +.. branch: gc-hooks + +Introduce GC hooks, as documented in doc/gc_info.rst + +.. branch: gc-hook-better-timestamp + +Improve GC hooks + +.. branch: cppyy-packaging + +Update backend to 0.6.0 and support exceptions through wrappers diff --git a/pypy/doc/whatsnew-pypy2-7.0.0.rst b/pypy/doc/whatsnew-pypy2-7.0.0.rst --- a/pypy/doc/whatsnew-pypy2-7.0.0.rst +++ b/pypy/doc/whatsnew-pypy2-7.0.0.rst @@ -1,81 +1,69 @@ -========================== -What's new in PyPy2.7 6.0+ -========================== - -.. this is a revision shortly after release-pypy-6.0.0 -.. startrev: e50e11af23f1 - -.. branch: cppyy-packaging - -Main items: vastly better template resolution and improved performance. In -detail: upgrade to backend 1.4, improved handling of templated methods and -functions (in particular automatic deduction of types), improved pythonization -interface, range of compatibility fixes for Python3, free functions now take -fast libffi path when possible, moves for strings (incl. from Python str), -easier/faster handling of std::vector by numpy, improved and faster object -identity preservation - -.. branch: socket_default_timeout_blockingness - -Make sure 'blocking-ness' of socket is set along with default timeout - -.. branch: crypt_h - -Include crypt.h for crypt() on Linux - -.. branch: gc-more-logging - -Log additional gc-minor and gc-collect-step info in the PYPYLOG - -.. branch: reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb - -.. branch: pyparser-improvements-3 - -Small refactorings in the Python parser. - -.. branch: fix-readme-typo - -.. branch: avoid_shell_injection_in_shutil - -.. branch: unicode-utf8-re - -.. branch: utf8-io - -Utf8 handling for unicode - -.. branch: pyparser-improvements-3 -Small refactorings in the Python parser. - -Backport CPython fix for possible shell injection issue in `distutils.spawn`, -https://bugs.python.org/issue34540 - -.. branch: cffi_dlopen_unicode - -Enable use of unicode file names in `dlopen` - -.. branch: rlock-in-rpython - -Backport CPython fix for `thread.RLock` - - -.. branch: expose-gc-time - -Make GC hooks measure time in seconds (as opposed to an opaque unit). - -.. branch: cleanup-test_lib_pypy - -Update most test_lib_pypy/ tests and move them to extra_tests/. - -.. branch: gc-disable - -Make it possible to manually manage the GC by using a combination of -gc.disable() and gc.collect_step(). Make sure to write a proper release -announcement in which we explain that existing programs could leak memory if -they run for too much time between a gc.disable()/gc.enable() - -.. branch: unicode-utf8 - -Use utf8 internally to represent unicode +========================== +What's new in PyPy2.7 6.0+ +========================== + +.. this is a revision shortly after release-pypy-6.0.0 +.. startrev: e50e11af23f1 + +.. branch: cppyy-packaging + +Main items: vastly better template resolution and improved performance. In +detail: upgrade to backend 1.4, improved handling of templated methods and +functions (in particular automatic deduction of types), improved pythonization +interface, range of compatibility fixes for Python3, free functions now take +fast libffi path when possible, moves for strings (incl. from Python str), +easier/faster handling of std::vector by numpy, improved and faster object +identity preservation + +.. branch: socket_default_timeout_blockingness + +Make sure 'blocking-ness' of socket is set along with default timeout + +.. branch: crypt_h + +Include crypt.h for crypt() on Linux + +.. branch: gc-more-logging + +Log additional gc-minor and gc-collect-step info in the PYPYLOG + +.. branch: reverse-debugger + +The reverse-debugger branch has been merged. For more information, see +https://bitbucket.org/pypy/revdb + + +.. branch: pyparser-improvements-3 + +Small refactorings in the Python parser. + +.. branch: fix-readme-typo + +.. branch: avoid_shell_injection_in_shutil + +Backport CPython fix for possible shell injection issue in `distutils.spawn`, +https://bugs.python.org/issue34540 + +.. branch: cffi_dlopen_unicode + +Enable use of unicode file names in `dlopen` + +.. branch: rlock-in-rpython + +Backport CPython fix for `thread.RLock` + + +.. branch: expose-gc-time + +Make GC hooks measure time in seconds (as opposed to an opaque unit). + +.. branch: cleanup-test_lib_pypy + +Update most test_lib_pypy/ tests and move them to extra_tests/. + +.. branch: gc-disable + +Make it possible to manually manage the GC by using a combination of +gc.disable() and gc.collect_step(). Make sure to write a proper release +announcement in which we explain that existing programs could leak memory if +they run for too much time between a gc.disable()/gc.enable() diff --git a/pypy/doc/whatsnew-pypy3-5.10.0.rst b/pypy/doc/whatsnew-pypy3-5.10.0.rst --- a/pypy/doc/whatsnew-pypy3-5.10.0.rst +++ b/pypy/doc/whatsnew-pypy3-5.10.0.rst @@ -1,21 +1,7 @@ -========================= -What's new in PyPy3 5.9+ -========================= - -.. this is the revision after release-pypy3.5-5.9 -.. startrev: be41e3ac0a29 - -.. branch: sched_yield -Add sched_yield posix attribute - -.. branch: py3.5-appexec -Raise if space.is_true(space.appexec()) used in app level tests, fix tests -that did this - -.. branch: py3.5-mac-embedding -Download and patch dependencies when building cffi-based stdlib modules - -.. branch: os_lockf - -.. branch: py3.5-xattr -Add posix.*attr() functions +======================== +What's new in PyPy3 7.0+ +======================== + +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c + diff --git a/pypy/doc/whatsnew-pypy3-6.0.0.rst b/pypy/doc/whatsnew-pypy3-6.0.0.rst --- a/pypy/doc/whatsnew-pypy3-6.0.0.rst +++ b/pypy/doc/whatsnew-pypy3-6.0.0.rst @@ -1,28 +1,7 @@ -========================= -What's new in PyPy3 5.10+ -========================= +======================== +What's new in PyPy3 7.0+ +======================== -.. this is the revision after release-pypy3.5-v5.10 -.. startrev: 34c63fba0bba +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c -.. branch: hroncok/fix-typeerror-str-does-not-support-the-b-1514414905375 - -Fix for bytestrings in console repl - -.. branch: py3-winreg - -Update winreg module to use unicode, wide-strings - -.. branch: cpyext-py3-instancemethod-attributes - -Add missing ``__doc__``, ``__module__``, ``__name__`` attributes to -``instancemethod`` - -.. branch: winapi - -Update support for _winapi cffi module for python3 - -.. branch: py3.5-refactor-slots - -Refactor cpyext slots. - diff --git a/pypy/interpreter/astcompiler/optimize.py b/pypy/interpreter/astcompiler/optimize.py --- a/pypy/interpreter/astcompiler/optimize.py +++ b/pypy/interpreter/astcompiler/optimize.py @@ -5,7 +5,7 @@ from pypy.tool import stdlib_opcode as ops from pypy.interpreter.error import OperationError from rpython.rlib.unroll import unrolling_iterable -from rpython.rlib.runicode import MAXUNICODE +from rpython.rlib.rutf8 import MAXUNICODE from rpython.rlib.objectmodel import specialize diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -3,7 +3,7 @@ from pypy.interpreter.error import OperationError, oefmt from rpython.rlib.objectmodel import specialize from rpython.rlib.rstring import StringBuilder -from rpython.rlib import rutf8, runicode +from rpython.rlib import rutf8 from rpython.rlib.rarithmetic import r_uint, intmask from rpython.rtyper.lltypesystem import rffi from pypy.module.unicodedata import unicodedb @@ -40,23 +40,6 @@ space.newtext(msg)])) return raise_unicode_exception_encode - at specialize.memo() -def encode_unicode_error_handler(space): - # Fast version of the "strict" errors handler. - def raise_unicode_exception_encode(errors, encoding, msg, uni, - startingpos, endingpos): - assert isinstance(uni, unicode) - u_len = len(uni) - utf8 = runicode.unicode_encode_utf8sp(uni, u_len) - raise OperationError(space.w_UnicodeEncodeError, - space.newtuple([space.newtext(encoding), - space.newtext(utf8, u_len), - space.newint(startingpos), - space.newint(endingpos), - space.newtext(msg)])) - return u'', None, 0 - return raise_unicode_exception_encode - def default_error_encode( errors, encoding, msg, u, startingpos, endingpos): """A default handler, for tests""" @@ -1022,23 +1005,45 @@ return result.build() + at specialize.memo() +def _encode_unicode_error_handler(space): + # Fast version of the "strict" errors handler. + from rpython.rlib import runicode + def raise_unicode_exception_encode(errors, encoding, msg, uni, + startingpos, endingpos): + assert isinstance(uni, unicode) + u_len = len(uni) + utf8 = runicode.unicode_encode_utf8sp(uni, u_len) + raise OperationError(space.w_UnicodeEncodeError, + space.newtuple([space.newtext(encoding), + space.newtext(utf8, u_len), + space.newint(startingpos), + space.newint(endingpos), + space.newtext(msg)])) + return u'', None, 0 + return raise_unicode_exception_encode + + def encode_utf8(space, uni, allow_surrogates=False): # Note that Python3 tends to forbid *all* surrogates in utf-8. # If allow_surrogates=True, then revert to the Python 2 behavior # which never raises UnicodeEncodeError. Surrogate pairs are then # allowed, either paired or lone. A paired surrogate is considered # like the non-BMP character it stands for. See also *_utf8sp(). + from rpython.rlib import runicode assert isinstance(uni, unicode) return runicode.unicode_encode_utf_8( uni, len(uni), "strict", - errorhandler=encode_unicode_error_handler(space), + errorhandler=_encode_unicode_error_handler(space), allow_surrogates=allow_surrogates) def encode_utf8sp(space, uni, allow_surrogates=True): + xxx # Surrogate-preserving utf-8 encoding. Any surrogate character # turns into its 3-bytes encoding, whether it is paired or not. # This should always be reversible, and the reverse is # decode_utf8sp(). + from rpython.rlib import runicode return runicode.unicode_encode_utf8sp(uni, len(uni)) def decode_utf8sp(space, string): diff --git a/pypy/module/_codecs/__init__.py b/pypy/module/_codecs/__init__.py --- a/pypy/module/_codecs/__init__.py +++ b/pypy/module/_codecs/__init__.py @@ -1,5 +1,4 @@ from pypy.interpreter.mixedmodule import MixedModule -from rpython.rlib import runicode from rpython.rlib.objectmodel import not_rpython from pypy.module._codecs import interp_codecs @@ -89,6 +88,7 @@ @not_rpython def __init__(self, space, *args): # mbcs codec is Windows specific, and based on rffi. + from rpython.rlib import runicode if (hasattr(runicode, 'str_decode_mbcs')): self.interpleveldefs['mbcs_encode'] = 'interp_codecs.mbcs_encode' self.interpleveldefs['mbcs_decode'] = 'interp_codecs.mbcs_decode' diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -2,7 +2,7 @@ from rpython.rlib import jit, rutf8 from rpython.rlib.objectmodel import we_are_translated, not_rpython from rpython.rlib.rstring import StringBuilder, UnicodeBuilder -from rpython.rlib import runicode +from rpython.rlib.rutf8 import MAXUNICODE from rpython.rlib.runicode import raw_unicode_escape_helper from pypy.interpreter.error import OperationError, oefmt @@ -649,7 +649,6 @@ return _call_codec(space, w_decoder, w_obj, "decoding", encoding, errors) # ____________________________________________________________ -# delegation to runicode/unicodehelper def _find_implementation(impl_name): func = getattr(unicodehelper, impl_name) @@ -721,7 +720,8 @@ ]: make_decoder_wrapper(decoder) -if hasattr(unicodehelper, 'str_decode_mbcs'): +from rpython.rlib import runicode +if hasattr(runicode, 'str_decode_mbcs'): make_encoder_wrapper('mbcs_encode') make_decoder_wrapper('mbcs_decode') diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -1,4 +1,5 @@ from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rlib import rstring from rpython.rlib.rarithmetic import widen from rpython.rlib import rstring, runicode, rutf8 from rpython.tool.sourcetools import func_renamer @@ -266,7 +267,8 @@ @cpython_api([], Py_UNICODE, error=CANNOT_FAIL) def PyUnicode_GetMax(space): """Get the maximum ordinal for a Unicode character.""" - return runicode.UNICHR(runicode.MAXUNICODE) + from rpython.rlib import runicode, rutf8 + return runicode.UNICHR(rutf8.MAXUNICODE) @cts.decl("int _PyUnicode_Ready(PyObject *unicode)", error=-1) def _PyUnicode_Ready(space, w_obj): diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py --- a/pypy/module/sys/vm.py +++ b/pypy/module/sys/vm.py @@ -3,7 +3,7 @@ """ from rpython.rlib import jit -from rpython.rlib.runicode import MAXUNICODE +from rpython.rlib.rutf8 import MAXUNICODE from pypy.interpreter import gateway from pypy.interpreter.error import oefmt diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py --- a/pypy/module/zlib/interp_zlib.py +++ b/pypy/module/zlib/interp_zlib.py @@ -351,6 +351,9 @@ else: string, finished, unused_len = result self._save_unconsumed_input(data, finished, unused_len) + if finished: + rzlib.inflateEnd(self.stream) + self.stream = rzlib.null_stream return space.newbytes(string) diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py --- a/pypy/objspace/std/bytesobject.py +++ b/pypy/objspace/std/bytesobject.py @@ -438,10 +438,6 @@ space.check_buf_flags(flags, True) return SimpleView(StringBuffer(self._value)) - def descr_encode(self, space, w_encoding=None, w_errors=None): - w_uni = self.descr_decode(space, space.newtext('ascii'), space.newtext('strict')) - return space.call_method(w_uni, 'encode', w_encoding, w_errors) - def descr_getbuffer(self, space, w_flags): #from pypy.objspace.std.bufferobject import W_Buffer #return W_Buffer(StringBuffer(self._value)) diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -988,13 +988,12 @@ descr_rmul = descr_mul def _get_index_storage(self): - # XXX write the correct jit.elidable - if self._index_storage == rutf8.null_storage(): - storage = rutf8.create_utf8_index_storage(self._utf8, self._length) - else: - storage = self._index_storage - if not jit.isconstant(self): - self._index_storage = storage + return jit.conditional_call_elidable(self._index_storage, + W_UnicodeObject._compute_index_storage, self) + + def _compute_index_storage(self): + storage = rutf8.create_utf8_index_storage(self._utf8, self._length) + self._index_storage = storage return storage def _getitem_result(self, space, index): diff --git a/rpython/annotator/binaryop.py b/rpython/annotator/binaryop.py --- a/rpython/annotator/binaryop.py +++ b/rpython/annotator/binaryop.py @@ -727,6 +727,10 @@ return [get_setitem, op.simple_call(get_setitem.result, v_idx, v_value)] + at op.contains.register_transform(SomeInstance) +def contains_SomeInstance(annotator, v_ins, v_idx): + get_contains = op.getattr(v_ins, const('__contains__')) + return [get_contains, op.simple_call(get_contains.result, v_idx)] class __extend__(pairtype(SomeIterator, SomeIterator)): diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py --- a/rpython/annotator/test/test_annrpython.py +++ b/rpython/annotator/test/test_annrpython.py @@ -4065,6 +4065,20 @@ assert len(a.translator.graphs) == 2 # fn, __setitem__ assert isinstance(s, annmodel.SomeInteger) + def test_instance_contains(self): + class A(object): + def __contains__(self, i): + return i & 1 == 0 + + def fn(i): + a = A() + return 0 in a and 1 not in a + + a = self.RPythonAnnotator() + s = a.build_types(fn, [int]) + assert len(a.translator.graphs) == 2 # fn, __contains__ + assert isinstance(s, annmodel.SomeBool) + def test_instance_getslice(self): class A(object): def __getslice__(self, stop, start): diff --git a/rpython/jit/backend/llsupport/regalloc.py b/rpython/jit/backend/llsupport/regalloc.py --- a/rpython/jit/backend/llsupport/regalloc.py +++ b/rpython/jit/backend/llsupport/regalloc.py @@ -1,4 +1,4 @@ -import os +import sys from rpython.jit.metainterp.history import Const, REF, JitCellToken from rpython.rlib.objectmodel import we_are_translated, specialize from rpython.jit.metainterp.resoperation import rop, AbstractValue @@ -67,6 +67,7 @@ @specialize.arg(1) def foreach(self, function, arg): + # XXX unused? node = self.master_node while node is not None: function(arg, node.val) @@ -297,12 +298,12 @@ def is_still_alive(self, v): # Check if 'v' is alive at the current position. # Return False if the last usage is strictly before. - return self.longevity[v][1] >= self.position + return self.longevity[v].last_usage >= self.position def stays_alive(self, v): # Check if 'v' stays alive after the current position. # Return False if the last usage is before or at position. - return self.longevity[v][1] > self.position + return self.longevity[v].last_usage > self.position def next_instruction(self, incr=1): self.position += incr @@ -319,7 +320,7 @@ self._check_type(v) if isinstance(v, Const): return - if v not in self.longevity or self.longevity[v][1] <= self.position: + if v not in self.longevity or self.longevity[v].last_usage <= self.position: if v in self.reg_bindings: self.free_regs.append(self.reg_bindings[v]) del self.reg_bindings[v] @@ -355,7 +356,7 @@ for v in self.reg_bindings: if v not in self.longevity: llop.debug_print(lltype.Void, "variable %s not in longevity\n" % v.repr({})) - assert self.longevity[v][1] > self.position + assert self.longevity[v].last_usage > self.position def try_allocate_reg(self, v, selected_reg=None, need_lower_byte=False): """ Try to allocate a register, if we have one free. @@ -366,6 +367,9 @@ returns allocated register or None, if not possible. """ self._check_type(v) + if isinstance(v, TempVar): + self.longevity[v] = Lifetime(self.position, self.position) + # YYY all subtly similar code assert not isinstance(v, Const) if selected_reg is not None: res = self.reg_bindings.get(v, None) @@ -385,42 +389,55 @@ loc = self.reg_bindings.get(v, None) if loc is not None and loc not in self.no_lower_byte_regs: return loc - for i in range(len(self.free_regs) - 1, -1, -1): - reg = self.free_regs[i] - if reg not in self.no_lower_byte_regs: - if loc is not None: - self.free_regs[i] = loc - else: - del self.free_regs[i] - self.reg_bindings[v] = reg - return reg - return None + free_regs = [reg for reg in self.free_regs + if reg not in self.no_lower_byte_regs] + newloc = self.longevity.try_pick_free_reg( + self.position, v, free_regs) + if newloc is None: + return None + self.free_regs.remove(newloc) + if loc is not None: + self.free_regs.append(loc) + self.reg_bindings[v] = newloc + return newloc try: return self.reg_bindings[v] except KeyError: - if self.free_regs: - loc = self.free_regs.pop() - self.reg_bindings[v] = loc - return loc + loc = self.longevity.try_pick_free_reg( + self.position, v, self.free_regs) + if loc is None: + return None + self.reg_bindings[v] = loc + self.free_regs.remove(loc) + return loc - def _spill_var(self, v, forbidden_vars, selected_reg, + def _spill_var(self, forbidden_vars, selected_reg, need_lower_byte=False): - v_to_spill = self._pick_variable_to_spill(v, forbidden_vars, + v_to_spill = self._pick_variable_to_spill(forbidden_vars, selected_reg, need_lower_byte=need_lower_byte) loc = self.reg_bindings[v_to_spill] + self._sync_var_to_stack(v_to_spill) del self.reg_bindings[v_to_spill] - if self.frame_manager.get(v_to_spill) is None: - newloc = self.frame_manager.loc(v_to_spill) - self.assembler.regalloc_mov(loc, newloc) return loc - def _pick_variable_to_spill(self, v, forbidden_vars, selected_reg=None, - need_lower_byte=False): - """ Slightly less silly algorithm. - """ - cur_max_age = -1 + def _pick_variable_to_spill(self, forbidden_vars, selected_reg=None, + need_lower_byte=False, regs=None): + + # try to spill a variable that has no further real usages, ie that only + # appears in failargs or in a jump + # if that doesn't exist, spill the variable that has a real_usage that + # is the furthest away from the current position + + # YYY check for fixed variable usages + if regs is None: + regs = self.reg_bindings.keys() + + cur_max_use_distance = -1 + position = self.position candidate = None - for next in self.reg_bindings: + cur_max_age_failargs = -1 + candidate_from_failargs = None + for next in regs: reg = self.reg_bindings[next] if next in forbidden_vars: continue @@ -431,13 +448,25 @@ continue if need_lower_byte and reg in self.no_lower_byte_regs: continue - max_age = self.longevity[next][1] - if cur_max_age < max_age: - cur_max_age = max_age - candidate = next - if candidate is None: - raise NoVariableToSpill - return candidate + lifetime = self.longevity[next] + if lifetime.is_last_real_use_before(position): + # this variable has no "real" use as an argument to an op left + # it is only used in failargs, and maybe in a jump. spilling is + # fine + max_age = lifetime.last_usage + if cur_max_age_failargs < max_age: + cur_max_age_failargs = max_age + candidate_from_failargs = next + else: + use_distance = lifetime.next_real_usage(position) - position + if cur_max_use_distance < use_distance: + cur_max_use_distance = use_distance + candidate = next + if candidate_from_failargs is not None: + return candidate_from_failargs + if candidate is not None: + return candidate + raise NoVariableToSpill def force_allocate_reg(self, v, forbidden_vars=[], selected_reg=None, need_lower_byte=False): @@ -450,12 +479,12 @@ """ self._check_type(v) if isinstance(v, TempVar): - self.longevity[v] = (self.position, self.position) + self.longevity[v] = Lifetime(self.position, self.position) loc = self.try_allocate_reg(v, selected_reg, need_lower_byte=need_lower_byte) if loc: return loc - loc = self._spill_var(v, forbidden_vars, selected_reg, + loc = self._spill_var(forbidden_vars, selected_reg, need_lower_byte=need_lower_byte) prev_loc = self.reg_bindings.get(v, None) if prev_loc is not None: @@ -468,7 +497,7 @@ self.bindings_to_frame_reg[v] = None def force_spill_var(self, var): - self._sync_var(var) + self._sync_var_to_stack(var) try: loc = self.reg_bindings[var] del self.reg_bindings[var] @@ -502,7 +531,7 @@ if selected_reg in self.free_regs: self.assembler.regalloc_mov(immloc, selected_reg) return selected_reg - loc = self._spill_var(v, forbidden_vars, selected_reg) + loc = self._spill_var(forbidden_vars, selected_reg) self.free_regs.append(loc) self.assembler.regalloc_mov(immloc, loc) return loc @@ -523,6 +552,7 @@ loc = self.force_allocate_reg(v, forbidden_vars, selected_reg, need_lower_byte=need_lower_byte) if prev_loc is not loc: + self.assembler.num_reloads += 1 self.assembler.regalloc_mov(prev_loc, loc) return loc @@ -530,15 +560,7 @@ reg = self.reg_bindings[from_v] del self.reg_bindings[from_v] self.reg_bindings[to_v] = reg - - def _move_variable_away(self, v, prev_loc): - if self.free_regs: - loc = self.free_regs.pop() - self.reg_bindings[v] = loc - self.assembler.regalloc_mov(prev_loc, loc) - else: - loc = self.frame_manager.loc(v) - self.assembler.regalloc_mov(prev_loc, loc) + return reg def force_result_in_reg(self, result_v, v, forbidden_vars=[]): """ Make sure that result is in the same register as v. @@ -548,41 +570,39 @@ self._check_type(result_v) self._check_type(v) if isinstance(v, Const): - if self.free_regs: - loc = self.free_regs.pop() - else: - loc = self._spill_var(v, forbidden_vars, None) - self.assembler.regalloc_mov(self.convert_to_imm(v), loc) - self.reg_bindings[result_v] = loc - return loc - if v not in self.reg_bindings: - # v not in a register. allocate one for result_v and move v there - prev_loc = self.frame_manager.loc(v) - loc = self.force_allocate_reg(result_v, forbidden_vars) - self.assembler.regalloc_mov(prev_loc, loc) - return loc - if self.longevity[v][1] > self.position: - # we need to find a new place for variable v and - # store result in the same place - loc = self.reg_bindings[v] - del self.reg_bindings[v] - if self.frame_manager.get(v) is None: - self._move_variable_away(v, loc) - self.reg_bindings[result_v] = loc - else: - self._reallocate_from_to(v, result_v) - loc = self.reg_bindings[result_v] - return loc + result_loc = self.force_allocate_reg(result_v, forbidden_vars) + self.assembler.regalloc_mov(self.convert_to_imm(v), result_loc) + return result_loc + v_keeps_living = self.longevity[v].last_usage > self.position + # there are two cases where we should allocate a new register for + # result: + # 1) v is itself not in a register + # 2) v keeps on being live. if there is a free register, we need a move + # anyway, so we can use force_allocate_reg on result_v to make sure any + # fixed registers are used + if (v not in self.reg_bindings or (v_keeps_living and self.free_regs)): + v_loc = self.loc(v) + result_loc = self.force_allocate_reg(result_v, forbidden_vars) + self.assembler.regalloc_mov(v_loc, result_loc) + return result_loc + if v_keeps_living: + # since there are no free registers, v needs to go to the stack. + # sync it there. + self._sync_var_to_stack(v) + return self._reallocate_from_to(v, result_v) - def _sync_var(self, v): + def _sync_var_to_stack(self, v): + self.assembler.num_spills += 1 if not self.frame_manager.get(v): reg = self.reg_bindings[v] to = self.frame_manager.loc(v) self.assembler.regalloc_mov(reg, to) + else: + self.assembler.num_spills_to_existing += 1 # otherwise it's clean def _bc_spill(self, v, new_free_regs): - self._sync_var(v) + self._sync_var_to_stack(v) new_free_regs.append(self.reg_bindings.pop(v)) def before_call(self, force_store=[], save_all_regs=0): @@ -650,7 +670,7 @@ move_or_spill = [] for v, reg in self.reg_bindings.items(): - max_age = self.longevity[v][1] + max_age = self.longevity[v].last_usage if v not in force_store and max_age <= self.position: # variable dies del self.reg_bindings[v] @@ -671,45 +691,32 @@ else: # this is a register like eax/rax, which needs either # spilling or moving. - move_or_spill.append((v, max_age)) + move_or_spill.append(v) if len(move_or_spill) > 0: - while len(self.free_regs) > 0: - new_reg = self.free_regs.pop() - if new_reg in self.save_around_call_regs: - new_free_regs.append(new_reg) # not this register... - continue - # This 'new_reg' is suitable for moving a candidate to. - # Pick the one with the smallest max_age. (This - # is one step of a naive sorting algo, slow in theory, - # but the list should always be very small so it - # doesn't matter.) - best_i = 0 - smallest_max_age = move_or_spill[0][1] - for i in range(1, len(move_or_spill)): - max_age = move_or_spill[i][1] - if max_age < smallest_max_age: - best_i = i - smallest_max_age = max_age - v, max_age = move_or_spill.pop(best_i) - # move from 'reg' to 'new_reg' + free_regs = [reg for reg in self.free_regs + if reg not in self.save_around_call_regs] + # chose which to spill using the usual spill heuristics + while len(move_or_spill) > len(free_regs): + v = self._pick_variable_to_spill([], regs=move_or_spill) + self._bc_spill(v, new_free_regs) + move_or_spill.remove(v) + assert len(move_or_spill) <= len(free_regs) + for v in move_or_spill: + # search next good reg + new_reg = None + while True: + new_reg = self.free_regs.pop() + if new_reg in self.save_around_call_regs: + new_free_regs.append(new_reg) # not this register... + continue + break + assert new_reg is not None # must succeed reg = self.reg_bindings[v] - if not we_are_translated(): - if move_or_spill: - assert max_age <= min([_a for _, _a in move_or_spill]) - assert reg in save_sublist - assert reg in self.save_around_call_regs - assert new_reg not in self.save_around_call_regs + self.assembler.num_moves_calls += 1 self.assembler.regalloc_mov(reg, new_reg) self.reg_bindings[v] = new_reg # change the binding new_free_regs.append(reg) - # - if len(move_or_spill) == 0: - break - else: - # no more free registers to move to, spill the rest - for v, max_age in move_or_spill: - self._bc_spill(v, new_free_regs) # re-add registers in 'new_free_regs', but in reverse order, # so that the last ones (added just above, from @@ -772,7 +779,7 @@ # of COND_CALL don't accept a cc as input if next_op.getarg(0) is not op: return False - if self.longevity[op][1] > i + 1: + if self.longevity[op].last_usage > i + 1: return False if opnum != rop.COND_CALL: if op in operations[i + 1].getfailargs(): @@ -786,61 +793,295 @@ descr = op.getdescr() assert isinstance(descr, JitCellToken) if op.numargs() == 2: - self.rm._sync_var(op.getarg(1)) + self.rm._sync_var_to_stack(op.getarg(1)) return [self.loc(op.getarg(0)), self.fm.loc(op.getarg(1))] else: assert op.numargs() == 1 return [self.loc(op.getarg(0))] +# ____________________________________________________________ + + + +UNDEF_POS = -42 + +class Lifetime(object): + def __init__(self, definition_pos=UNDEF_POS, last_usage=UNDEF_POS): + # all positions are indexes into the operations list + + # the position where the variable is defined + self.definition_pos = definition_pos + # the position where the variable is last used. this includes failargs + # and jumps + self.last_usage = last_usage + + # *real* usages, ie as an argument to an operation (as opposed to jump + # arguments or in failargs) + self.real_usages = None + + # fixed registers are positions where the variable *needs* to be in a + # specific register + self.fixed_positions = None + + # another Lifetime that lives after the current one that would like to + # share a register with this variable + self.share_with = None + + # the other lifetime will have this variable set to self.definition_pos + self._definition_pos_shared = UNDEF_POS + + def last_usage_including_sharing(self): + while self.share_with is not None: + self = self.share_with + return self.last_usage + + def is_last_real_use_before(self, position): + if self.real_usages is None: + return True + return self.real_usages[-1] <= position + + def next_real_usage(self, position): + assert position >= self.definition_pos + # binary search + l = self.real_usages + low = 0 + high = len(l) + if position >= l[-1]: + return -1 + while low < high: + mid = low + (high - low) // 2 # no overflow ;-) + if position < l[mid]: + high = mid + else: + low = mid + 1 + return l[low] + + def definition_pos_shared(self): + if self._definition_pos_shared != UNDEF_POS: + return self._definition_pos_shared + else: + return self.definition_pos + + def fixed_register(self, position, reg): + """ registers a fixed register use for the variable at position in + register reg. returns the position from where on the register should be + held free. """ + assert self.definition_pos <= position <= self.last_usage + if self.fixed_positions is None: + self.fixed_positions = [] + res = self.definition_pos_shared() + else: + assert position > self.fixed_positions[-1][0] + res = self.fixed_positions[-1][0] + self.fixed_positions.append((position, reg)) + return res + + def find_fixed_register(self, opindex): + # XXX could use binary search + if self.fixed_positions is not None: + for (index, reg) in self.fixed_positions: + if opindex <= index: + return reg + if self.share_with is not None: + return self.share_with.find_fixed_register(opindex) + + def _check_invariants(self): + assert self.definition_pos <= self.last_usage + if self.real_usages is not None: + assert sorted(self.real_usages) == self.real_usages + assert self.last_usage >= max(self.real_usages) + assert self.definition_pos < min(self.real_usages) + + def __repr__(self): + if self.fixed_positions: + s = " " + ", ".join("@%s in %s" % (index, reg) for (index, reg) in self.fixed_positions) + else: + s = "" + return "%s:%s(%s)%s" % (self.definition_pos, self.real_usages, self.last_usage, s) + + +class FixedRegisterPositions(object): + def __init__(self, register): + self.register = register + + self.index_lifetimes = [] + + def fixed_register(self, opindex, definition_pos): + if self.index_lifetimes: + assert opindex > self.index_lifetimes[-1][0] + self.index_lifetimes.append((opindex, definition_pos)) + + def free_until_pos(self, opindex): + # XXX could use binary search + for (index, definition_pos) in self.index_lifetimes: + if opindex <= index: + if definition_pos >= opindex: + return definition_pos + else: + # the variable doesn't exist or didn't make it into the + # register despite being defined already. so we don't care + # too much, and can say that the variable is free until + # index + return index + return sys.maxint + + def __repr__(self): + return "%s: fixed at %s" % (self.register, self.index_lifetimes) + + +class LifetimeManager(object): + def __init__(self, longevity): + self.longevity = longevity + + # dictionary maps register to FixedRegisterPositions + self.fixed_register_use = {} + + def fixed_register(self, opindex, register, var=None): + """ Tell the LifetimeManager that variable var *must* be in register at + operation opindex. var can be None, if no variable at all can be in + that register at the point.""" + if var is None: + definition_pos = opindex + else: + varlifetime = self.longevity[var] + definition_pos = varlifetime.fixed_register(opindex, register) + if register not in self.fixed_register_use: + self.fixed_register_use[register] = FixedRegisterPositions(register) + self.fixed_register_use[register].fixed_register(opindex, definition_pos) + + def try_use_same_register(self, v0, v1): + """ Try to arrange things to put v0 and v1 into the same register. + v0 must be defined before v1""" + # only works in limited situations now + longevityvar0 = self[v0] + longevityvar1 = self[v1] + assert longevityvar0.definition_pos < longevityvar1.definition_pos + if longevityvar0.last_usage != longevityvar1.definition_pos: + return # not supported for now + longevityvar0.share_with = longevityvar1 + longevityvar1._definition_pos_shared = longevityvar0.definition_pos_shared() + + def longest_free_reg(self, position, free_regs): + """ for every register in free_regs, compute how far into the future + that register can remain free, according to the constraints of the + fixed registers. Find the register that is free the longest. Return a + tuple (reg, free_until_pos). """ + free_until_pos = {} + max_free_pos = position + best_reg = None + # reverse for compatibility with old code + for i in range(len(free_regs) - 1, -1, -1): + reg = free_regs[i] + fixed_reg_pos = self.fixed_register_use.get(reg, None) + if fixed_reg_pos is None: + return reg, sys.maxint + else: + free_until_pos = fixed_reg_pos.free_until_pos(position) + if free_until_pos > max_free_pos: + best_reg = reg + max_free_pos = free_until_pos + return best_reg, max_free_pos + + def free_reg_whole_lifetime(self, position, v, free_regs): + """ try to find a register from free_regs for v at position that's + free for the whole lifetime of v. pick the one that is blocked first + *after* the lifetime of v. """ + longevityvar = self[v] + min_fixed_use_after = sys.maxint + best_reg = None + unfixed_reg = None + for reg in free_regs: + fixed_reg_pos = self.fixed_register_use.get(reg, None) + if fixed_reg_pos is None: + unfixed_reg = reg + continue + use_after = fixed_reg_pos.free_until_pos(position) + if use_after <= longevityvar.last_usage_including_sharing(): + # can't fit + continue + assert use_after >= longevityvar.last_usage_including_sharing() + if use_after < min_fixed_use_after: + best_reg = reg + min_fixed_use_after = use_after + if best_reg is not None: + return best_reg + + # no fitting fixed registers. pick a non-fixed one + return unfixed_reg + + def try_pick_free_reg(self, position, v, free_regs): + if not free_regs: + return None + longevityvar = self[v] + # check whether there is a fixed register and whether it's free + reg = longevityvar.find_fixed_register(position) + if reg is not None and reg in free_regs: + return reg + + # try to find a register that's free for the whole lifetime of v + # pick the one that is blocked first *after* the lifetime of v + loc = self.free_reg_whole_lifetime(position, v, free_regs) + if loc is not None: + return loc + + # can't fit v completely, so pick the register that's free the longest + loc, free_until = self.longest_free_reg(position, free_regs) + if loc is not None: + return loc + # YYY could check whether it's best to spill v here, but hard + # to do in the current system + return None + + def __contains__(self, var): + return var in self.longevity + + def __getitem__(self, var): + return self.longevity[var] + + def __setitem__(self, var, val): + self.longevity[var] = val + def compute_vars_longevity(inputargs, operations): - # compute a dictionary that maps variables to index in - # operations that is a "last-time-seen" - - # returns a pair longevity/useful. Non-useful variables are ones that - # never appear in the assembler or it does not matter if they appear on - # stack or in registers. Main example is loop arguments that go - # only to guard operations or to jump or to finish - last_used = {} - last_real_usage = {} + # compute a dictionary that maps variables to Lifetime information + # if a variable is not in the dictionary, it's operation is dead because + # it's side-effect-free and the result is unused + longevity = {} for i in range(len(operations)-1, -1, -1): op = operations[i] - if op.type != 'v': - if op not in last_used and rop.has_no_side_effect(op.opnum): + opnum = op.getopnum() + if op not in longevity: + if op.type != 'v' and rop.has_no_side_effect(opnum): + # result not used, operation has no side-effect, it can be + # removed continue - opnum = op.getopnum() + longevity[op] = Lifetime(definition_pos=i, last_usage=i) + else: + longevity[op].definition_pos = i for j in range(op.numargs()): arg = op.getarg(j) if isinstance(arg, Const): continue - if arg not in last_used: - last_used[arg] = i + if arg not in longevity: + lifetime = longevity[arg] = Lifetime(last_usage=i) + else: + lifetime = longevity[arg] if opnum != rop.JUMP and opnum != rop.LABEL: - if arg not in last_real_usage: - last_real_usage[arg] = i + if lifetime.real_usages is None: + lifetime.real_usages = [] + lifetime.real_usages.append(i) if rop.is_guard(op.opnum): for arg in op.getfailargs(): if arg is None: # hole continue assert not isinstance(arg, Const) - if arg not in last_used: - last_used[arg] = i + if arg not in longevity: + longevity[arg] = Lifetime(last_usage=i) # - longevity = {} - for i, arg in enumerate(operations): - if arg.type != 'v' and arg in last_used: - assert not isinstance(arg, Const) - assert i < last_used[arg] - longevity[arg] = (i, last_used[arg]) - del last_used[arg] for arg in inputargs: assert not isinstance(arg, Const) - if arg not in last_used: - longevity[arg] = (-1, -1) - else: - longevity[arg] = (0, last_used[arg]) - del last_used[arg] - assert len(last_used) == 0 + if arg not in longevity: + longevity[arg] = Lifetime(-1, -1) if not we_are_translated(): produced = {} @@ -851,9 +1092,15 @@ if not isinstance(arg, Const): assert arg in produced produced[op] = None - - return longevity, last_real_usage + for lifetime in longevity.itervalues(): + if lifetime.real_usages is not None: + lifetime.real_usages.reverse() + if not we_are_translated(): + lifetime._check_invariants() + return LifetimeManager(longevity) + +# YYY unused? def is_comparison_or_ovf_op(opnum): return rop.is_comparison(opnum) or rop.is_ovf(opnum) diff --git a/rpython/jit/backend/llsupport/test/test_regalloc.py b/rpython/jit/backend/llsupport/test/test_regalloc.py --- a/rpython/jit/backend/llsupport/test/test_regalloc.py +++ b/rpython/jit/backend/llsupport/test/test_regalloc.py @@ -1,9 +1,19 @@ import py +import sys from rpython.jit.metainterp.history import ConstInt, INT, FLOAT -from rpython.jit.backend.llsupport.regalloc import FrameManager, LinkedList -from rpython.jit.backend.llsupport.regalloc import RegisterManager as BaseRegMan +from rpython.jit.metainterp.history import BasicFailDescr, TargetToken +from rpython.jit.metainterp.resoperation import rop from rpython.jit.metainterp.resoperation import InputArgInt, InputArgRef,\ InputArgFloat +from rpython.jit.backend.detect_cpu import getcpuclass +from rpython.jit.backend.llsupport.regalloc import FrameManager, LinkedList +from rpython.jit.backend.llsupport.regalloc import RegisterManager as BaseRegMan,\ + Lifetime as RealLifetime, UNDEF_POS, BaseRegalloc, compute_vars_longevity,\ + LifetimeManager +from rpython.jit.tool.oparser import parse +from rpython.jit.codewriter.effectinfo import EffectInfo +from rpython.rtyper.lltypesystem import lltype +from rpython.rtyper.annlowlevel import llhelper def newboxes(*values): return [InputArgInt(v) for v in values] @@ -11,33 +21,61 @@ def newrefboxes(count): return [InputArgRef() for _ in range(count)] +def Lifetime(definition_pos=UNDEF_POS, last_usage=UNDEF_POS, + real_usages=UNDEF_POS): + if real_usages == UNDEF_POS: + real_usages = last_usage + lifetime = RealLifetime(definition_pos, last_usage) + if isinstance(real_usages, int): + real_usages = [real_usages] + lifetime.real_usages = real_usages + return lifetime + + def boxes_and_longevity(num): res = [] longevity = {} for i in range(num): box = InputArgInt(0) res.append(box) - longevity[box] = (0, 1) + longevity[box] = Lifetime(0, 1) return res, longevity class FakeReg(object): def __init__(self, i): self.n = i + def _getregkey(self): + return self.n + def is_memory_reference(self): + return False def __repr__(self): return 'r%d' % self.n r0, r1, r2, r3 = [FakeReg(i) for i in range(4)] +r4, r5, r6, r7, r8, r9 = [FakeReg(i) for i in range(4, 10)] + regs = [r0, r1, r2, r3] class RegisterManager(BaseRegMan): all_regs = regs + + def __init__(self, longevity, frame_manager=None, assembler=None): + if isinstance(longevity, dict): + longevity = LifetimeManager(longevity) + BaseRegMan.__init__(self, longevity, frame_manager, assembler) + def convert_to_imm(self, v): return v class FakeFramePos(object): def __init__(self, pos, box_type): self.pos = pos + self.value = pos self.box_type = box_type + def _getregkey(self): + return ~self.value + def is_memory_reference(self): + return True def __repr__(self): return 'FramePos<%d,%s>' % (self.pos, self.box_type) def __eq__(self, other): @@ -66,17 +104,311 @@ assert isinstance(loc, FakeFramePos) return loc.pos +class FakeCPU(object): + def get_baseofs_of_frame_field(self): + return 0 + class MockAsm(object): def __init__(self): self.moves = [] - + self.emitted = [] + self.cpu = FakeCPU() + + # XXX register allocation statistics to be removed later + self.num_moves_calls = 0 + self.num_moves_jump = 0 + self.num_spills = 0 + self.num_spills_to_existing = 0 + self.num_reloads = 0 + + self.preamble_num_moves_calls = 0 + self.preamble_num_moves_jump = 0 + self.preamble_num_spills = 0 + self.preamble_num_spills_to_existing = 0 + self.preamble_num_reloads = 0 + def regalloc_mov(self, from_loc, to_loc): self.moves.append((from_loc, to_loc)) + self.emitted.append(("move", to_loc, from_loc)) + + +def test_lifetime_next_real_usage(): + lt = RealLifetime(0, 1000) + lt.real_usages = [0, 1, 5, 10, 24, 35, 55, 56, 57, 90, 92, 100] + for i in range(100): + next = lt.next_real_usage(i) + assert next in lt.real_usages + assert next > i + assert lt.real_usages[lt.real_usages.index(next) - 1] <= i + assert lt.next_real_usage(100) == -1 + assert lt.next_real_usage(101) == -1 + +def test_fixed_position(): + b0, b1, b2 = newboxes(0, 0, 0) + l0 = Lifetime(0, 5) + l1 = Lifetime(2, 9) + l2 = Lifetime(0, 9) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(1, r0, b0) + longevity.fixed_register(4, r2, b0) + longevity.fixed_register(5, r1, b1) + longevity.fixed_register(8, r1, b1) + + assert l0.fixed_positions == [(1, r0), (4, r2)] + assert l1.fixed_positions == [(5, r1), (8, r1)] + assert l2.fixed_positions is None + + fpr0 = longevity.fixed_register_use[r0] + fpr1 = longevity.fixed_register_use[r1] + fpr2 = longevity.fixed_register_use[r2] + assert r3 not in longevity.fixed_register_use + assert fpr0.index_lifetimes == [(1, 0)] + assert fpr1.index_lifetimes == [(5, 2), (8, 5)] + assert fpr2.index_lifetimes == [(4, 1)] + +def test_fixed_position_none(): + b0, b1, b2 = newboxes(0, 0, 0) + l0 = Lifetime(0, 5) + l1 = Lifetime(2, 9) + l2 = Lifetime(0, 9) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(1, r0) + longevity.fixed_register(4, r2) + longevity.fixed_register(5, r1) + longevity.fixed_register(8, r1) + + fpr0 = longevity.fixed_register_use[r0] + fpr1 = longevity.fixed_register_use[r1] + fpr2 = longevity.fixed_register_use[r2] + assert r3 not in longevity.fixed_register_use + assert fpr0.index_lifetimes == [(1, 1)] + assert fpr1.index_lifetimes == [(5, 5), (8, 8)] + assert fpr2.index_lifetimes == [(4, 4)] + + +def test_free_until_pos_none(): + longevity = LifetimeManager({}) + longevity.fixed_register(5, r1, None) + longevity.fixed_register(8, r1, None) + longevity.fixed_register(35, r1, None) + + fpr1 = longevity.fixed_register_use[r1] + + assert fpr1.free_until_pos(0) == 5 + assert fpr1.free_until_pos(1) == 5 + assert fpr1.free_until_pos(2) == 5 + assert fpr1.free_until_pos(3) == 5 + assert fpr1.free_until_pos(4) == 5 + assert fpr1.free_until_pos(5) == 5 + assert fpr1.free_until_pos(10) == 35 + assert fpr1.free_until_pos(20) == 35 + assert fpr1.free_until_pos(30) == 35 + assert fpr1.free_until_pos(36) == sys.maxint + +def test_free_until_pos(): + b0, b1, b2 = newboxes(0, 0, 0) + l0 = Lifetime(0, 5) + l1 = Lifetime(2, 9) + l2 = Lifetime(30, 40) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(5, r1, b1) + longevity.fixed_register(8, r1, b1) + longevity.fixed_register(35, r1, b2) + + fpr1 = longevity.fixed_register_use[r1] + + # simple cases: we are before the beginning of the lifetime of the variable + # in the fixed register, then it's free until the definition of the + # variable + assert fpr1.free_until_pos(0) == 2 + assert fpr1.free_until_pos(1) == 2 + assert fpr1.free_until_pos(2) == 2 + assert fpr1.free_until_pos(10) == 30 + assert fpr1.free_until_pos(20) == 30 + assert fpr1.free_until_pos(30) == 30 + + # after the fixed use, we are fine anyway + assert fpr1.free_until_pos(36) == sys.maxint + assert fpr1.free_until_pos(50) == sys.maxint + + # asking for a position *after* the definition of the variable in the fixed + # register means the variable didn't make it into the fixed register, but + # at the latest by the use point it will have to go there + assert fpr1.free_until_pos(3) == 5 + assert fpr1.free_until_pos(4) == 5 + assert fpr1.free_until_pos(5) == 5 + assert fpr1.free_until_pos(6) == 8 + assert fpr1.free_until_pos(7) == 8 + assert fpr1.free_until_pos(8) == 8 + assert fpr1.free_until_pos(31) == 35 + assert fpr1.free_until_pos(32) == 35 + assert fpr1.free_until_pos(33) == 35 + assert fpr1.free_until_pos(34) == 35 + assert fpr1.free_until_pos(35) == 35 + +def test_free_until_pos_different_regs(): + b0, b1, b2 = newboxes(0, 0, 0) + l0 = Lifetime(0, 5) + l1 = Lifetime(2, 9) + l2 = Lifetime(30, 40) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(1, r0, b0) + longevity.fixed_register(4, r2, b0) + fpr2 = longevity.fixed_register_use[r2] + # the definition of b0 is before the other fixed register use of r0, so the + # earliest b0 can be in r2 is that use point at index 1 + assert fpr2.free_until_pos(0) == 1 + + +def test_longest_free_reg(): + b0, b1, b2 = newboxes(0, 0, 0) + l0 = Lifetime(0, 5) + l1 = Lifetime(2, 9) + l2 = Lifetime(30, 40) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(1, r0, b0) + longevity.fixed_register(4, r2, b0) + longevity.fixed_register(5, r1, b1) + longevity.fixed_register(8, r1, b1) + longevity.fixed_register(35, r1, b2) + + assert longevity.longest_free_reg(0, [r0, r1, r2]) == (r1, 2) + +def test_try_pick_free_reg(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(0, 4) + l1 = Lifetime(2, 20) + l2 = Lifetime(6, 20) + l3 = Lifetime(8, 20) + l4 = Lifetime(0, 10) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2, b3: l3, b4: l4}) + longevity.fixed_register(3, r1, b1) + longevity.fixed_register(7, r2, b2) + longevity.fixed_register(9, r3, b3) + + # a best fit + loc = longevity.try_pick_free_reg(0, b0, [r1, r2, r3, r4, r5]) + assert loc is r2 + + # does not fit into any of the fixed regs, use a non-fixed one + loc = longevity.try_pick_free_reg(0, b4, [r5, r2, r3, r4, r1]) + assert loc in [r4, r5] + + # all available are fixed but var doesn't fit completely into any of these. + # pick the biggest interval + loc = longevity.try_pick_free_reg(0, b4, [r1, r2, r3]) + assert loc is r3 + +def test_try_pick_free_reg_bug(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(10, 30) + l1 = Lifetime(0, 15) + longevity = LifetimeManager({b0: l0, b1: l1}) + longevity.fixed_register(20, r0, b0) + + # does not fit into r0, use r1 + loc = longevity.try_pick_free_reg(0, b1, [r0, r1]) + assert loc == r1 + +def test_try_pick_free_reg_bug2(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(1, 2) + l1 = Lifetime(2, 4) + longevity = LifetimeManager({b0: l0, b1: l1}) + longevity.fixed_register(4, r1, b1) + + # does not fit into r0, use r1 + loc = longevity.try_pick_free_reg(0, b0, [r0, r1]) + assert loc == r0 + +def test_simple_coalescing(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(0, 4) + l1 = Lifetime(4, 20) + l2 = Lifetime(4, 20) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2}) + longevity.fixed_register(10, r1, b1) + longevity.fixed_register(10, r2, b2) + longevity.try_use_same_register(b0, b2) + + loc = longevity.try_pick_free_reg(0, b0, [r0, r1, r2, r3, r4]) + assert loc is r2 + +def test_coalescing_blocks_regs_correctly(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(10, 30) + l1 = Lifetime(30, 40) + l2 = Lifetime(30, 40) + l3 = Lifetime(0, 15) + l4 = Lifetime(0, 5) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2, b3: l3, b4: l4}) + longevity.try_use_same_register(b0, b1) + longevity.fixed_register(35, r1, b1) + longevity.fixed_register(35, r2, b2) + + loc = longevity.try_pick_free_reg(0, b3, [r1, r2]) + # r2 is picked, otherwise b0 can't end up in r1 + assert loc is r2 + + loc = longevity.try_pick_free_reg(0, b4, [r1, r2]) + # r1 is picked, because b4 fits before b0 + assert loc is r1 + +def test_coalescing_non_fixed_regs(): + b0, b1, b2, b3, b4 = newboxes(0, 0, 0, 0, 0) + l0 = Lifetime(0, 10) + l1 = Lifetime(10, 20) + l2 = Lifetime(25, 40) + l3 = Lifetime(15, 40) + longevity = LifetimeManager({b0: l0, b1: l1, b2: l2, b3: l3}) + longevity.try_use_same_register(b0, b1) + longevity.fixed_register(35, r2, b2) + longevity.fixed_register(35, r3, b3) + + loc = longevity.try_pick_free_reg(0, b0, [r1, r2, r3]) + # r2 is picked, otherwise b1 can't end up in the same reg as b0 + assert loc is r2 + + +def test_chained_coalescing(): + # 5 + b4 + # | + # 10 + b0 | + # | | + # | 15 + + # | + # + + # 20 From pypy.commits at gmail.com Mon Feb 11 04:11:50 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 01:11:50 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: fix merge Message-ID: <5c613c56.1c69fb81.9696f.948d@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95932:c5c905981a21 Date: 2019-02-10 15:54 +0200 http://bitbucket.org/pypy/pypy/changeset/c5c905981a21/ Log: fix merge diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -56,6 +56,7 @@ def fsdecode(space, w_string): from pypy.module._codecs import interp_codecs + from rpython.rlib import runicode state = space.fromcache(interp_codecs.CodecState) errorhandler=state.decode_error_handler if _WIN32: @@ -314,6 +315,7 @@ def str_decode_mbcs(s, errors, final, errorhandler, force_ignore=True): slen = len(s) + from rpython.rlib import runicode res, size = runicode.str_decode_mbcs(s, slen, errors, final=final, errorhandler=errorhandler, force_ignore=force_ignore) res_utf8 = runicode.unicode_encode_utf_8(res, len(res), 'strict') @@ -1624,7 +1626,7 @@ if size == 0: return '', 0 - if runicode.MAXUNICODE < 65536: + if rutf8.MAXUNICODE < 65536: unicode_bytes = 2 else: unicode_bytes = 4 @@ -1651,7 +1653,7 @@ for j in range(start, stop, step): t += r_uint(ord(s[pos + j])) << (h*8) h += 1 - if t > runicode.MAXUNICODE: + if t > rutf8.MAXUNICODE: r, pos, rettype = errorhandler(errors, "unicode_internal", "unichr(%d) not in range" % (t,), s, pos, pos + unicode_bytes) @@ -1668,7 +1670,7 @@ if size == 0: return '' - if runicode.MAXUNICODE < 65536: + if rutf8.MAXUNICODE < 65536: unicode_bytes = 2 else: unicode_bytes = 4 From pypy.commits at gmail.com Mon Feb 11 04:11:52 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 01:11:52 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: remove most runicode from pypy, refactor FormatErrorW, add utf8 to SocketError Message-ID: <5c613c58.1c69fb81.a5a0e.b5b9@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95933:914068c8b956 Date: 2019-02-11 00:03 +0200 http://bitbucket.org/pypy/pypy/changeset/914068c8b956/ Log: remove most runicode from pypy, refactor FormatErrorW, add utf8 to SocketError diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -9,7 +9,7 @@ from rpython.rlib.objectmodel import we_are_translated, specialize from rpython.rlib.objectmodel import dont_inline, not_rpython from rpython.rlib import rstack, rstackovf -from rpython.rlib import rwin32, runicode +from rpython.rlib import rwin32, rutf8 from pypy.interpreter import debug @@ -20,8 +20,8 @@ def strerror(errno): """Translate an error code to a unicode message string.""" from pypy.module._codecs.locale import str_decode_locale_surrogateescape - uni = str_decode_locale_surrogateescape(os.strerror(errno)) - return runicode.unicode_encode_utf_8(uni, len(uni), 'strict'), len(uni) + utf8, lgt = str_decode_locale_surrogateescape(os.strerror(errno)) + return utf8, lgt class OperationError(Exception): """Interpreter-level exception that signals an exception that should be @@ -524,7 +524,6 @@ result = str(value.encode('utf-8')) lgt += len(value) else: - from rpython.rlib import rutf8 result = str(value) try: lgt += rutf8.check_utf8(result, True) @@ -632,13 +631,14 @@ if rwin32.WIN32 and isinstance(e, WindowsError): winerror = e.winerror try: - msg = rwin32.FormatErrorW(winerror) + msg, lgt = rwin32.FormatErrorW(winerror) except ValueError: - msg = u'Windows Error %d' % winerror + msg = 'Windows Error %d' % winerror + lgt = len(msg) w_errno = space.w_None w_winerror = space.newint(winerror) - msg_utf8 = runicode.unicode_encode_utf_8(msg, len(msg), 'strict') - w_msg = space.newtext(msg_utf8, len(msg)) + msg_utf8 = rutf8.str_encode_utf_8(msg, lgt, 'strict') + w_msg = space.newtext(msg_utf8, lgt) else: errno = e.errno if errno == EINTR: diff --git a/pypy/interpreter/test/test_unicodehelper.py b/pypy/interpreter/test/test_unicodehelper.py --- a/pypy/interpreter/test/test_unicodehelper.py +++ b/pypy/interpreter/test/test_unicodehelper.py @@ -9,7 +9,7 @@ import struct import sys from pypy.interpreter.unicodehelper import ( - encode_utf8, str_decode_utf8, utf8_encode_utf_32_be, str_decode_utf_32_be) + str_decode_utf8, utf8_encode_utf_32_be, str_decode_utf_32_be) from pypy.interpreter.unicodehelper import encode_utf8sp, decode_utf8sp from pypy.interpreter.unicodehelper import utf8_encode_ascii, str_decode_ascii from pypy.interpreter import unicodehelper as uh @@ -30,35 +30,6 @@ def decode_utf8(u): return str_decode_utf8(u, "strict", True, fake_eh) -def test_encode_utf8(): - space = FakeSpace() - assert encode_utf8(space, u"abc") == "abc" - assert encode_utf8(space, u"\u1234") == "\xe1\x88\xb4" - py.test.raises(Hit, encode_utf8, space, u"\ud800") - py.test.raises(Hit, encode_utf8, space, u"\udc00") - if option.runappdirect or sys.maxunicode > 0xFFFF: - # for the following test, go to lengths to avoid CPython's - # optimizer and .pyc file storage, which collapse the two - # surrogates into one - c = u"\udc00" - py.test.raises(Hit, encode_utf8, space, u"\ud800" + c) - -def test_encode_utf8_allow_surrogates(): - sp = FakeSpace() - assert encode_utf8(sp, u"\ud800", allow_surrogates=True) == "\xed\xa0\x80" - assert encode_utf8(sp, u"\udc00", allow_surrogates=True) == "\xed\xb0\x80" - c = u"\udc00" - got = encode_utf8(sp, u"\ud800" + c, allow_surrogates=True) - assert got == "\xf0\x90\x80\x80" - -def test_encode_utf8sp(): - sp = FakeSpace() - assert encode_utf8sp(sp, u"\ud800") == "\xed\xa0\x80" - assert encode_utf8sp(sp, u"\udc00") == "\xed\xb0\x80" - c = u"\udc00" - got = encode_utf8sp(sp, u"\ud800" + c) - assert got == "\xed\xa0\x80\xed\xb0\x80" - def test_decode_utf8(): assert decode_utf8("abc") == ("abc", 3, 3) assert decode_utf8("\xe1\x88\xb4") == ("\xe1\x88\xb4", 1, 3) diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -62,13 +62,18 @@ if _WIN32: bytes = space.bytes_w(w_string) slen = len(bytes) - uni, size = runicode.str_decode_mbcs(bytes, slen, 'strict', final=True, + uni, lgt = runicode.str_decode_mbcs(bytes, slen, 'strict', final=True, errorhandler=errorhandler, force_ignore=False) + + utf8 = uni.encode('utf-8') + + utf8 = uni.encode('utf-8') + + utf8 = uni.encode('utf-8') elif 0 and _MACOSX: bytes = space.bytes_w(w_string) - utf8 = str_decode_utf8(bytes, 'surrogateescape', True, errorhandler, - allow_surrogates=False)[0] - uni = utf8.decode('utf-8') + utf8, lgt, pos = str_decode_utf8(bytes, 'surrogateescape', True, + errorhandler, allow_surrogates=False) elif space.sys.filesystemencoding is None or state.codec_need_encodings: # bootstrap check: if the filesystemencoding isn't initialized # or the filesystem codec is implemented in Python we cannot @@ -77,15 +82,13 @@ from pypy.module._codecs.locale import ( str_decode_locale_surrogateescape) bytes = space.bytes_w(w_string) - uni = str_decode_locale_surrogateescape(bytes) + utf8, lgt = str_decode_locale_surrogateescape(bytes) else: from pypy.module.sys.interp_encoding import getfilesystemencoding return space.call_method(w_string, 'decode', getfilesystemencoding(space), space.newtext('surrogateescape')) - assert isinstance(uni, unicode) - return space.newtext(runicode.unicode_encode_utf_8(uni, - len(uni), 'strict', allow_surrogates=True), len(uni)) + return space.newtext(utf8, lgt) def fsencode(space, w_uni): from pypy.module._codecs import interp_codecs @@ -318,7 +321,7 @@ from rpython.rlib import runicode res, size = runicode.str_decode_mbcs(s, slen, errors, final=final, errorhandler=errorhandler, force_ignore=force_ignore) - res_utf8 = runicode.unicode_encode_utf_8(res, len(res), 'strict') + res_utf8 = unicode_encode_utf_8(res, len(res), 'strict') return res_utf8, len(res), len(res) def str_decode_utf8(s, errors, final, errorhandler, allow_surrogates=False): @@ -1010,6 +1013,7 @@ @specialize.memo() def _encode_unicode_error_handler(space): # Fast version of the "strict" errors handler. + # used only in (unused) encode_utf8 from rpython.rlib import runicode def raise_unicode_exception_encode(errors, encoding, msg, uni, startingpos, endingpos): @@ -1032,6 +1036,7 @@ # which never raises UnicodeEncodeError. Surrogate pairs are then # allowed, either paired or lone. A paired surrogate is considered # like the non-BMP character it stands for. See also *_utf8sp(). + xxx from rpython.rlib import runicode assert isinstance(uni, unicode) return runicode.unicode_encode_utf_8( @@ -1051,7 +1056,7 @@ def decode_utf8sp(space, string): # Surrogate-preserving utf-8 decoding. Assuming there is no # encoding error, it should always be reversible, and the reverse is - # encode_utf8sp(). + # unused encode_utf8sp(). return str_decode_utf8(string, "string", True, decode_never_raise, allow_surrogates=True) diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -39,7 +39,7 @@ so it needs to be converted by the codec Returns (str_or_none, newpos) as error - handlers used outside runicode return utf8 + handlers return utf8 so we add whether they used unicode or bytes """ w_errorhandler = lookup_error(space, errors) if decode: @@ -455,8 +455,7 @@ ch = 0 if ch == 0: raise OperationError(space.type(w_exc), w_exc) - ch_utf8 = runicode.unicode_encode_utf_8(unichr(ch), 1, 'strict', - allow_surrogates=True) + ch_utf8 = rutf8.unichr_as_utf8(ch, allow_surrogates=True) return space.newtuple([space.newtext(ch_utf8, 1), space.newint(start + bytelength)]) else: diff --git a/pypy/module/_codecs/locale.py b/pypy/module/_codecs/locale.py --- a/pypy/module/_codecs/locale.py +++ b/pypy/module/_codecs/locale.py @@ -6,10 +6,12 @@ import py import sys from rpython.rlib.objectmodel import we_are_translated -from rpython.rlib.rstring import UnicodeBuilder, assert_str0 -from rpython.rlib.runicode import (code_to_unichr, +from rpython.rlib.rstring import StringBuilder, assert_str0 +from rpython.rlib.runicode import ( default_unicode_error_decode, default_unicode_error_encode) +from rpython.rlib.rutf8 import unichr_as_utf8 from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib.rarithmetic import r_uint from rpython.translator import cdir from rpython.translator.tool.cbuild import ExternalCompilationInfo @@ -38,15 +40,13 @@ lltype.Void) -def unicode_encode_locale_surrogateescape(u, errorhandler=None): +def unicode_encode_locale_surrogateescape(u): """Encode unicode via the locale codecs (POSIX wcstombs) with the surrogateescape handler. - The optional errorhandler is only called in the case of fatal - errors. + The errorhandler is never called """ - if errorhandler is None: - errorhandler = default_unicode_error_encode + errorhandler = default_unicode_error_encode with lltype.scoped_alloc(rffi.SIZE_TP.TO, 1) as errorposp: with scoped_unicode2rawwcharp(u) as ubuf: @@ -64,15 +64,14 @@ pypy_wchar2char_free(sbuf) -def str_decode_locale_surrogateescape(s, errorhandler=None): +def str_decode_locale_surrogateescape(s): """Decode strs via the locale codecs (POSIX mrbtowc) with the surrogateescape handler. - The optional errorhandler is only called in the case of fatal + The errorhandler is never called errors. """ - if errorhandler is None: - errorhandler = default_unicode_error_decode + errorhandler = default_unicode_error_decode with lltype.scoped_alloc(rffi.SIZE_TP.TO, 1) as sizep: with rffi.scoped_str2charp(s) as sbuf: @@ -82,7 +81,7 @@ errmsg = _errmsg("pypy_char2wchar") errorhandler('strict', 'filesystemencoding', errmsg, s, 0, 1) size = rffi.cast(lltype.Signed, sizep[0]) - return rawwcharp2unicoden(ubuf, size) + return rawwcharp2utf8en(ubuf, size), size finally: pypy_char2wchar_free(ubuf) @@ -138,14 +137,17 @@ _unicode2rawwcharp_loop._annenforceargs_ = [unicode, None] -def rawwcharp2unicoden(wcp, maxlen): - b = UnicodeBuilder(maxlen) +def rawwcharp2utf8en(wcp, maxlen): + b = StringBuilder(maxlen) i = 0 - while i < maxlen and rffi.cast(lltype.Signed, wcp[i]) != 0: - b.append(code_to_unichr(wcp[i])) + while i < maxlen: + v = r_uint(wcp[i]) + if v == 0: + break + b.append(unichr_as_utf8(v, True)) i += 1 return assert_str0(b.build()) -rawwcharp2unicoden._annenforceargs_ = [None, int] +rawwcharp2utf8en._annenforceargs_ = [None, int] def _should_merge_surrogates(): diff --git a/pypy/module/_codecs/test/test_locale.py b/pypy/module/_codecs/test/test_locale.py --- a/pypy/module/_codecs/test/test_locale.py +++ b/pypy/module/_codecs/test/test_locale.py @@ -57,8 +57,8 @@ locale_decoder = str_decode_locale_surrogateescape utf8_decoder = self.getdecoder('utf-8') for val in 'foo', ' \xe6\x97\xa5\xe6\x9c\xac', '\xf0\x93\x88\x8c': - assert (locale_decoder(val).encode('utf8') == - utf8_decoder(val, 'strict', True, None)[0]) + assert (locale_decoder(val) == + utf8_decoder(val, 'strict', True, None)[:2]) def test_decode_locale_errorhandler(self): self.setlocale("en_US.UTF-8") @@ -67,5 +67,5 @@ decode_error_handler = self.getstate().decode_error_handler val = 'foo\xe3bar' expected = utf8_decoder(val, 'surrogateescape', True, - decode_error_handler)[0] - assert locale_decoder(val).encode('utf8') == expected + decode_error_handler) + assert locale_decoder(val) == expected[:2] diff --git a/pypy/module/_socket/interp_socket.py b/pypy/module/_socket/interp_socket.py --- a/pypy/module/_socket/interp_socket.py +++ b/pypy/module/_socket/interp_socket.py @@ -855,7 +855,7 @@ @specialize.arg(2) def converted_error(space, e, eintr_retry=False): - message = e.get_msg_unicode() + message, lgt = e.get_msg_utf8() w_exception_class = get_error(space, e.applevelerrcls) if isinstance(e, SocketErrorWithErrno): if e.errno == errno.EINTR: @@ -863,9 +863,10 @@ if eintr_retry: return # only return None if eintr_retry==True w_exception = space.call_function(w_exception_class, space.newint(e.errno), - space.newtext(message)) + space.newtext(message, lgt)) else: - w_exception = space.call_function(w_exception_class, space.newtext(message)) + w_exception = space.call_function(w_exception_class, + space.newtext(message, lgt)) raise OperationError(w_exception_class, w_exception) def explicit_socket_error(space, msg): diff --git a/pypy/module/array/reconstructor.py b/pypy/module/array/reconstructor.py --- a/pypy/module/array/reconstructor.py +++ b/pypy/module/array/reconstructor.py @@ -5,7 +5,7 @@ from pypy.interpreter.gateway import unwrap_spec from pypy.interpreter.error import oefmt from pypy.interpreter.argument import Arguments -from rpython.rlib import runicode, rbigint +from rpython.rlib import rutf8, rbigint from rpython.rlib.rstruct import ieee from rpython.rtyper.lltypesystem import rffi @@ -155,7 +155,7 @@ elif typecode == 'B': return UNSIGNED_INT8 elif typecode == 'u': - if runicode.MAXUNICODE == 0xffff: + if rutf8.MAXUNICODE == 0xffff: return UTF16_LE + IS_BIG_ENDIAN else: return UTF32_LE + IS_BIG_ENDIAN diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -1,7 +1,7 @@ from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib import rstring from rpython.rlib.rarithmetic import widen -from rpython.rlib import rstring, runicode, rutf8 +from rpython.rlib import rstring, rutf8 from rpython.tool.sourcetools import func_renamer from pypy.interpreter.error import OperationError, oefmt @@ -83,12 +83,11 @@ Creates the unicode in the interpreter. The PyUnicodeObject buffer must not be modified after this call. """ - s = rffi.wcharpsize2unicode(get_wbuffer(py_obj), get_wsize(py_obj)) - s_utf8 = runicode.unicode_encode_utf_8(s, len(s), 'strict', - allow_surrogates=True) + lgt = get_wsize(py_obj) + s_utf8 = rffi.wcharpsize2utf8(get_wbuffer(py_obj), lgt) w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type)) w_obj = space.allocate_instance(unicodeobject.W_UnicodeObject, w_type) - w_obj.__init__(s_utf8, len(s)) + w_obj.__init__(s_utf8, lgt) track_reference(space, py_obj, w_obj) return w_obj diff --git a/pypy/module/select/interp_select.py b/pypy/module/select/interp_select.py --- a/pypy/module/select/interp_select.py +++ b/pypy/module/select/interp_select.py @@ -80,10 +80,10 @@ if timeout < 0: timeout = 0 continue - message = e.get_msg_unicode() + message, lgt = e.get_msg_utf8() raise OperationError(space.w_OSError, space.newtuple([space.newint(e.errno), - space.newtext(message)])) + space.newtext(message, lgt)])) finally: self.running = False break diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -7,8 +7,6 @@ from rpython.rlib.rstring import ( StringBuilder, split, rsplit, UnicodeBuilder, replace_count, startswith, endswith) -from rpython.rlib.runicode import ( - unicode_encode_utf8_forbid_surrogates, SurrogateError) from rpython.rlib import rutf8, jit from pypy.interpreter import unicodehelper @@ -1924,8 +1922,9 @@ result.append(chr(uchr)) return result.build() +from rpython.rlib.runicode import unicode_encode_utf8_forbid_surrogates @jit.elidable -def g_encode_utf8(value): +def XXX_g_encode_utf8(value): """This is a global function because of jit.conditional_call_value""" return unicode_encode_utf8_forbid_surrogates(value, len(value)) diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py --- a/rpython/rlib/_rsocket_rffi.py +++ b/rpython/rlib/_rsocket_rffi.py @@ -1369,8 +1369,15 @@ return rwin32.FormatError(errno) def socket_strerror_unicode(errno): + return rwin32.FormatErrorW(errno)[0] + + def gai_strerror_unicode(errno): + return rwin32.FormatErrorW(errno)[0] + + def socket_strerror_utf8(errno): return rwin32.FormatErrorW(errno) - def gai_strerror_unicode(errno): + + def gai_strerror_utf8(errno): return rwin32.FormatErrorW(errno) # WinSock does not use a bitmask in select, and uses @@ -1386,7 +1393,16 @@ def socket_strerror_unicode(errno): return socket_strerror_str(errno).decode('latin-1') + def gai_strerror_unicode(errno): return gai_strerror_str(errno).decode('latin-1') + def socket_strerror_utf8(errno): + msg = socket_strerror_str(errno) + return msg, len(msg) + + def gai_strerror_utf8(errno): + msg = gai_strerror_str(errno) + return msg, len(msg) + MAX_FD_SIZE = FD_SETSIZE diff --git a/rpython/rlib/rdynload.py b/rpython/rlib/rdynload.py --- a/rpython/rlib/rdynload.py +++ b/rpython/rlib/rdynload.py @@ -228,18 +228,16 @@ res = rwin32.LoadLibrary(name) if not res: err = rwin32.GetLastError_saved() - ustr = rwin32.FormatErrorW(err) - # DLOpenError unicode msg breaks translation of cpyext create_extension_module - raise DLOpenError(ustr.encode('utf-8')) + ustr, lgt = rwin32.FormatErrorW(err) + raise DLOpenError(ustr) return res def dlopenex(name): res = rwin32.LoadLibraryExA(name) if not res: err = rwin32.GetLastError_saved() - ustr = rwin32.FormatErrorW(err) - # DLOpenError unicode msg breaks translation of cpyext create_extension_module - raise DLOpenError(ustr.encode('utf-8')) + ustr, lgt = rwin32.FormatErrorW(err) + raise DLOpenError(ustr) return res def dlopenU(name, mode=-1): @@ -247,9 +245,8 @@ res = rwin32.LoadLibraryW(name) if not res: err = rwin32.GetLastError_saved() - ustr = rwin32.FormatErrorW(err) - # DLOpenError unicode msg breaks translation of cpyext create_extension_module - raise DLOpenError(ustr.encode('utf-8')) + ustr, lgt = rwin32.FormatErrorW(err) + raise DLOpenError(ustr) return res def dlclose(handle): diff --git a/rpython/rlib/rpoll.py b/rpython/rlib/rpoll.py --- a/rpython/rlib/rpoll.py +++ b/rpython/rlib/rpoll.py @@ -30,6 +30,8 @@ return _c.socket_strerror_str(self.errno) def get_msg_unicode(self): return _c.socket_strerror_unicode(self.errno) + def get_msg_utf8(self): + return _c.socket_strerror_utf8(self.errno) class SelectError(Exception): def __init__(self, errno): @@ -38,6 +40,8 @@ return _c.socket_strerror_str(self.errno) def get_msg_unicode(self): return _c.socket_strerror_unicode(self.errno) + def get_msg_utf8(self): + return _c.socket_strerror_utf8(self.errno) # ____________________________________________________________ # poll() for POSIX systems diff --git a/rpython/rlib/rsocket.py b/rpython/rlib/rsocket.py --- a/rpython/rlib/rsocket.py +++ b/rpython/rlib/rsocket.py @@ -1301,6 +1301,9 @@ return '' def get_msg_unicode(self): return self.get_msg().decode('latin-1') + def get_msg_utf8(self): + msg = self.get_msg() + return msg, len(msg) def __str__(self): return self.get_msg() @@ -1319,6 +1322,8 @@ return _c.socket_strerror_str(self.errno) def get_msg_unicode(self): return _c.socket_strerror_unicode(self.errno) + def get_msg_utf8(self): + return _c.socket_strerror_utf8(self.errno) def last_error(): return CSocketError(_c.geterrno()) @@ -1329,6 +1334,8 @@ return _c.gai_strerror_str(self.errno) def get_msg_unicode(self): return _c.gai_strerror_unicode(self.errno) + def get_msg_utf8(self): + return _c.gai_strerror_utf8(self.errno) class HSocketError(SocketError): applevelerrcls = 'herror' diff --git a/rpython/rlib/rwin32.py b/rpython/rlib/rwin32.py --- a/rpython/rlib/rwin32.py +++ b/rpython/rlib/rwin32.py @@ -269,6 +269,9 @@ def FormatError(code): return llimpl_FormatError(code) def FormatErrorW(code): + """ + returns utf8, n_codepoints + """ return llimpl_FormatErrorW(code) def llimpl_FormatError(code): @@ -326,7 +329,7 @@ if buflen <= 0: result = u'Windows Error %d' % (code,) else: - result = rffi.wcharpsize2unicode(s_buf, buflen) + result = rffi.wcharpsize2utf8(s_buf, buflen), buflen finally: LocalFree(rffi.cast(rffi.VOIDP, buf[0])) lltype.free(buf, flavor='raw') diff --git a/rpython/rlib/test/test_rwin32.py b/rpython/rlib/test/test_rwin32.py --- a/rpython/rlib/test/test_rwin32.py +++ b/rpython/rlib/test/test_rwin32.py @@ -90,9 +90,9 @@ assert '%2' in msg def test_formaterror_unicode(): - msg = rwin32.FormatErrorW(34) - assert type(msg) is unicode - assert u'%2' in msg + msg, lgt = rwin32.FormatErrorW(34) + assert type(msg) is str + assert '%2' in msg def test_loadlibraryA(): # test0 can be loaded alone, but test1 requires the modified search path From pypy.commits at gmail.com Mon Feb 11 04:11:53 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 01:11:53 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: win32 fixes, still uses runicode for str_decode_mbcs Message-ID: <5c613c59.1c69fb81.2d891.1ebf@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95934:18628545b899 Date: 2019-02-11 11:10 +0200 http://bitbucket.org/pypy/pypy/changeset/18628545b899/ Log: win32 fixes, still uses runicode for str_decode_mbcs diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -637,8 +637,7 @@ lgt = len(msg) w_errno = space.w_None w_winerror = space.newint(winerror) - msg_utf8 = rutf8.str_encode_utf_8(msg, lgt, 'strict') - w_msg = space.newtext(msg_utf8, lgt) + w_msg = space.newtext(msg, lgt) else: errno = e.errno if errno == EINTR: diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -321,8 +321,8 @@ from rpython.rlib import runicode res, size = runicode.str_decode_mbcs(s, slen, errors, final=final, errorhandler=errorhandler, force_ignore=force_ignore) - res_utf8 = unicode_encode_utf_8(res, len(res), 'strict') - return res_utf8, len(res), len(res) + res_utf8 = runicode.unicode_encode_utf_8(res, size, 'strict') + return res_utf8, len(res), size def str_decode_utf8(s, errors, final, errorhandler, allow_surrogates=False): """ Same as checking for the valid utf8, but we know the utf8 is not diff --git a/rpython/rlib/rwin32.py b/rpython/rlib/rwin32.py --- a/rpython/rlib/rwin32.py +++ b/rpython/rlib/rwin32.py @@ -306,7 +306,7 @@ return result def llimpl_FormatErrorW(code): - "Return a unicode message corresponding to the given Windows error code." + "Return a utf8-encoded msg and its length" buf = lltype.malloc(rffi.CWCHARPP.TO, 1, flavor='raw') buf[0] = lltype.nullptr(rffi.CWCHARP.TO) try: @@ -327,7 +327,8 @@ buflen -= 1 if buflen <= 0: - result = u'Windows Error %d' % (code,) + msg = 'Windows Error %d' % (code,) + result = msg, len(msg) else: result = rffi.wcharpsize2utf8(s_buf, buflen), buflen finally: From pypy.commits at gmail.com Mon Feb 11 04:26:48 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 11 Feb 2019 01:26:48 -0800 (PST) Subject: [pypy-commit] pypy default: make the test not fail Message-ID: <5c613fd8.1c69fb81.3cf1.c544@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r95935:413357a1f973 Date: 2019-02-11 10:25 +0100 http://bitbucket.org/pypy/pypy/changeset/413357a1f973/ Log: make the test not fail we could do better here, but should be part of looking at some more examples of the results of the register allocator more generally. diff --git a/rpython/jit/backend/x86/test/test_regalloc.py b/rpython/jit/backend/x86/test/test_regalloc.py --- a/rpython/jit/backend/x86/test/test_regalloc.py +++ b/rpython/jit/backend/x86/test/test_regalloc.py @@ -419,4 +419,6 @@ # 1 because lifetime of i172 does not end at the int_xor # 1 ptr to save before call # 3 for argument shuffling - assert len(self.filter_log_moves()) == 11 + + # XXX there is an additional mov, find out why! + assert len(self.filter_log_moves()) == 12 From pypy.commits at gmail.com Mon Feb 11 05:51:06 2019 From: pypy.commits at gmail.com (antocuni) Date: Mon, 11 Feb 2019 02:51:06 -0800 (PST) Subject: [pypy-commit] pypy default: fix broken links Message-ID: <5c61539a.1c69fb81.1bfee.0536@mx.google.com> Author: Antonio Cuni Branch: Changeset: r95936:7d3116e601d5 Date: 2019-02-11 11:50 +0100 http://bitbucket.org/pypy/pypy/changeset/7d3116e601d5/ Log: fix broken links diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst --- a/pypy/doc/release-v7.0.0.rst +++ b/pypy/doc/release-v7.0.0.rst @@ -19,11 +19,12 @@ Until we can work with downstream providers to distribute builds with PyPy, we have made packages for some common packages `available as wheels`_. -The GC `hooks`_ , which can be used to gain more insights into its +The `GC hooks`_ , which can be used to gain more insights into its performance, has been improved and it is now possible to manually manage the GC by using a combination of ``gc.disable`` and ``gc.collect_step``. See the `GC blog post`_. +.. _`GC hooks`: http://doc.pypy.org/en/latest/gc_info.html#semi-manual-gc-management We updated the `cffi`_ module included in PyPy to version 1.12, and the `cppyy`_ backend to 1.4. Please use these to wrap your C and C++ code, @@ -49,7 +50,7 @@ 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 +and `RPython`_ documentation improvements, tweaking popular modules to run on pypy, or general `help`_ with making RPython's JIT even better. .. _`PyPy`: index.html From pypy.commits at gmail.com Mon Feb 11 06:38:33 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 03:38:33 -0800 (PST) Subject: [pypy-commit] pypy.org extradoc: rebuild pages Message-ID: <5c615eb9.1c69fb81.4a27c.be60@mx.google.com> Author: Matti Picus Branch: extradoc Changeset: r934:4915afa633dc Date: 2019-02-11 13:38 +0200 http://bitbucket.org/pypy/pypy.org/changeset/4915afa633dc/ Log: rebuild pages diff --git a/download.html b/download.html --- a/download.html +++ b/download.html @@ -66,10 +66,11 @@ as stable as the release, but they contain numerous bugfixes and performance improvements.

We provide binaries for x86, ARM, PPC and s390x running on different operating systems such as -Linux, Mac OS X and Windows:

+Linux, Mac OS X and Windows (what's new in PyPy 7.0?):

    -
  • the Python2.7 compatible release — PyPy2.7 v6.0 — (what's new in PyPy2.7?)
  • -
  • the Python3.5 compatible release — PyPy3.5 v6.0 — (what's new in PyPy3.5?).
  • +
  • the Python2.7 compatible release — PyPy2.7 v7.0
  • +
  • the Python3.5 compatible release — PyPy3.5 v7.0
  • +
  • the Python3.6 compatible release, alpha quality — PyPy3.6 v7.0
  • the Python2.7 Software Transactional Memory special release — PyPy-STM 2.5.1 (Linux x86-64 only)
    @@ -108,36 +109,32 @@
  • or translate your own PyPy.
-
-

Python2.7 compatible PyPy 6.0.0

+
+

Python2.7 compatible PyPy 7.0

-
-

Python 3.5.3 compatible PyPy3.5 v6.0.0

+
+

Python 3.5.3 compatible PyPy3.5 v7.0.0

@@ -152,6 +149,17 @@ libraries: …. Unless you want to hack a lot, try out the portable Linux binaries.

+
+

Python 3.6 compatible PyPy3.6 v7.0.0-alpha

+ +

PyPy-STM 2.5.1

This is a special version of PyPy! See the Software Transactional @@ -381,20 +389,38 @@

Checksums

-

Here are the checksums for each of the downloads

-

pypy2.7-5.10.0 sha256:

+

Here are the checksums for each of the downloads of PyPy 7.0.0 and the older 6.0.0.

+

pypy2.7-7.0.0 sha256:

-ee1980467ac8cc9fa9d609f7da93c5282503e59a548781248fe1914a7199d540  pypy2-v5.10.0-linux32.tar.bz2
-da85af9240220179493ad66c857934dc7ea91aef8f168cd293a2d99af8346ee2  pypy2-v5.10.0-linux64.tar.bz2
-6fdd55dd8f674efd06f76edb60a09a03b9b04a5fbc56741f416a94a0b9d2ff91  pypy2-v5.10.0-linux-armel.tar.bz2
-5ec3617bb9a07a0a0b2f3c8fbe69912345da4696cdb0a2aca7889b6f1e74435c  pypy2-v5.10.0-linux-armhf-raspbian.tar.bz2
-7e4120f0a83529a6851cbae0ec107dc7085ba8a4aeff4e7bd9da9aadb1ef37a4  pypy2-v5.10.0-osx64.tar.bz2
-dab4dccfa71820c4f803f5a82e13f76517bfde5fafe1e5fba6ff58ef2ba318ab  pypy2-v5.10.0-s390x.tar.bz2
-1209f2db718e6afda17528baa5138177a14a0938588a7d3e1b7c722c483079a8  pypy2-v5.10.0-src.tar.bz2
-89304eb886f84b5c65f3f4079445ef018cdb9a6e59ef4ed2095d37248a3fefcc  pypy2-v5.10.0-src.zip
-350914f9b70404781674f2f188f84d440d9d25da46ed9733b3f98269a510e033  pypy2-v5.10.0-win32.zip
-9afa1a36a5fc55ebc3e80576f05f44294f2b0de279862286fe00f5ee139965b1  pypy2-v5.10.0-ppc64.tar.bz2
-2c32ccfa80e3e2ec56b4cc848526046d7b0de1f2f1a92b0cedeb414ec76745ab  pypy2-v5.10.0-ppc64le.tar.bz2
+446fc208dd77a0048368da830564e6e4180bcd786e524b5369c61785af5c903a  pypy2.7-v7.0.0-linux32.tar.bz2
+971b1909f9fe960c4c643a6940d3f8a60d9a7a2937119535ab0cfaf83498ecd7  pypy2.7-v7.0.0-linux64.tar.bz2
+e7ecb029d9c7a59388838fc4820a50a2f5bee6536010031060e3dfa882730dc8  pypy2.7-v7.0.0-osx64.tar.bz2
+2ce390d93fa57ba912066a8b6439588bd9cf6aa9cef44d892b8e3e6dba64615e  pypy2.7-v7.0.0-s390x.tar.bz2
+04477a41194240cd71e485c3f41dec35a787d1b3bc030f9aa59e5e81bcf4118b  pypy2.7-v7.0.0-win32.zip
+165ffdf49a04c3ebdc966f76e67dd1767ad699657215dd83ca6996ab8ed87f52  pypy2.7-v7.0.0-ppc64.tar.bz2
+cfb0e2e9b1434e94ea559548c7486c8e7b4319a397309e8ed3783d9beadf1c6c  pypy2.7-v7.0.0-ppc64le.tar.bz2
+f51d8bbfc4e73a8a01820b7871a45d13c59f1399822cdf8a19388c69eb20c18c  pypy2.7-v7.0.0-src.tar.bz2
+77c8c02cf412a5f8182ffe8845877cffa506e5a5ce3a7cd835483fdc1202afd4  pypy2.7-v7.0.0-src.zip
+
+

pypy 3.5-v7.0.0 sha256:

+
+b8db8fbca9621de8ea8cd7184b322f2dddb2f385e8e5a63dfb75bb3fea4b2e3f  pypy3.5-v7.0.0-linux32.tar.bz2
+729e3c54325969c98bd3658c6342b9f5987b96bad1d6def04250a08401b54c4b  pypy3.5-v7.0.0-linux64.tar.bz2
+7c6d71653d9b1a7946d1eeebbf24b454fe934fba8b0c39f648bdc545fb2895ce  pypy3.5-v7.0.0-osx64.tar.bz2
+d588b045cc0d3a75c31fce54c1d181b1206ad9a5dd272fe79160a6268401605f  pypy3.5-v7.0.0-s390x.tar.bz2
+23e30b00ab61f24578059e4643fbf0221982faffd874898b5737fc5b334ca0ab  pypy3.5-v7.0.0-ppc64.tar.bz2
+2912884da05abc2cdf71dd337c3f280095351312c1a1732a52b6878174a0fd02  pypy3.5-v7.0.0-ppc64le.tar.bz2
+TODO win32
+b2ddb0f45cb4e0384fb498ef7fcca2ac96c730b9000affcf8d730169397f017f  pypy3.5-v7.0.0-src.tar.bz2
+3aa3a921c163667761165dbd2070e56d6715979fe9cc1f135d58ea0692a05a1e  pypy3.5-v7.0.0-src.zip
+
+

pypy 3.6-v7.0.0-alpha sha256:

+
+8576bde0760c239040706cf4952995eb0e77938b175885392a465a0d1616173d  pypy3.6-v7.0.0-linux64.tar.bz2
+4a95ffd61fd2d626a9c099db6e44889c2a7eecee9cb1cbc29e06603c218ba8e2  pypy3.6-v7.0.0-osx64.tar.bz2
+645d81472d16922fd592e9261da449cb19847ff7d5eaa89bcf05d9214b6b2698  pypy3.6-v7.0.0-win32.zip
+7ccbf81db5c647fa0c27636c7d18d059d2570fff7eaffc03857c67bee84b8a26  pypy3.6-v7.0.0-src.tar.bz2
+867dce40a63caccde161d90a0792e69f2a510a1f3147b694731052be52fafe5c  pypy3.6-v7.0.0-src.zip
 

pypy2.7-6.0.0 sha256:

@@ -408,22 +434,6 @@
 3553b19447cdb627919cc37d76979e15dc755b085e979f5ffa9b25933ec343b3  pypy2-v6.0.0-src.zip
 6e2210dae1ae721ed4eb9cba19f15453514b64111511c84f24843c4fdefdaf7f  pypy2-v6.0.0-win32.zip
 
-

pypy 3.5-v5.10.0 sha256:

-
-f5ced20934fff78e55c72aa82a4703954349a5a8099b94e77d74b96a94326a2c  pypy3-v5.10.0-osx64-2.tar.bz2
-
-

pypy 3.5-v5.10.1 sha256:

-
-a6ceca9ee5dc511de7902164464b88311fec9366c5673d0c00528eda862bbe54  pypy3-v5.10.1-linux32.tar.bz2
-75a276e1ee1863967bbacb70c5bff636de200768c0ec90e72f7ec17aace0aefe  pypy3-v5.10.1-linux64.tar.bz2
-5065e9ad958d06b9612ba974f43997d20168d4245c054dd43270e4b458782282  pypy3-v5.10.1-linux-armel.tar.bz2
-203dd595fbad7055340b23326f20c85b0d6c11c4877e3559a437611fc2ac40c2  pypy3-v5.10.1-linux-armhf-raspbian.tar.bz2
-52f006611513c995fdebba6e72d394186d4085460408cbbe086e5467bf3fb9b6  pypy3-v5.10.1-osx64.tar.bz2
-f5548e06e2fc0c24ec8b6e3c5b09f90081818f7caa3e436dc312592611724713  pypy3-v5.10.1-src.tar.bz2
-182378d7aab395ee6cf539fb011ec0e384624282834aaaed4a663972a5aa8797  pypy3-v5.10.1-src.zip
-4edf4f021689a529e5a631c5cca72a1a9dc19a6ea2091e64289cdd5b60eaf929  pypy3-v5.10.1-win32.zip
-9ce98481cddede40a3357f7462f2c894bb96f178e2e8715d04feda1476ec1563  pypy3-v5.10.1-s390x.tar.bz2
-

pypy 3.5-v6.0.0 sha256:

 b04eeee5160e6cb5f8962de80f077ea1dc7be34e77d74bf075519c23603f5ff9  pypy3-v6.0.0-linux32.tar.bz2
diff --git a/index.html b/index.html
--- a/index.html
+++ b/index.html
@@ -63,7 +63,7 @@
 

Welcome to PyPy

PyPy is a fast, compliant alternative implementation of the Python -language (2.7.13 and 3.5.3). It has several advantages and distinct features:

+language (2.7.13 and 3.5.3, 3.6). It has several advantages and distinct features:

  • Speed: thanks to its Just-in-Time compiler, Python programs From pypy.commits at gmail.com Mon Feb 11 06:56:02 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 11 Feb 2019 03:56:02 -0800 (PST) Subject: [pypy-commit] pypy sys-getsizeof: close abandoned branch Message-ID: <5c6162d2.1c69fb81.c6a10.d053@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: sys-getsizeof Changeset: r95937:ce962d90778a Date: 2019-02-11 12:24 +0100 http://bitbucket.org/pypy/pypy/changeset/ce962d90778a/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 06:56:04 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 11 Feb 2019 03:56:04 -0800 (PST) Subject: [pypy-commit] pypy fix-longevity: close branch, it's superseded by the merged regalloc-playground Message-ID: <5c6162d4.1c69fb81.c6a10.d055@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: fix-longevity Changeset: r95938:147bb1ff2239 Date: 2019-02-11 12:34 +0100 http://bitbucket.org/pypy/pypy/changeset/147bb1ff2239/ Log: close branch, it's superseded by the merged regalloc-playground From pypy.commits at gmail.com Mon Feb 11 06:56:05 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 11 Feb 2019 03:56:05 -0800 (PST) Subject: [pypy-commit] pypy regalloc: close branch, it's superseded by the merged regalloc-playground Message-ID: <5c6162d5.1c69fb81.5d866.546b@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: regalloc Changeset: r95939:a343830b763a Date: 2019-02-11 12:39 +0100 http://bitbucket.org/pypy/pypy/changeset/a343830b763a/ Log: close branch, it's superseded by the merged regalloc-playground From pypy.commits at gmail.com Mon Feb 11 06:57:37 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 11 Feb 2019 03:57:37 -0800 (PST) Subject: [pypy-commit] pypy bitset-intsets: in-progress: start implementing integer sets as bitsets Message-ID: <5c616331.1c69fb81.aa8cf.2f70@mx.google.com> Author: Carl Friedrich Bolz Branch: bitset-intsets Changeset: r95940:bfd90a10b38c Date: 2016-02-27 15:39 +0100 http://bitbucket.org/pypy/pypy/changeset/bfd90a10b38c/ Log: in-progress: start implementing integer sets as bitsets diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py --- a/pypy/objspace/std/setobject.py +++ b/pypy/objspace/std/setobject.py @@ -13,6 +13,14 @@ from rpython.rlib.rarithmetic import intmask, r_uint from rpython.rlib import rerased, jit +def popcount(i): + # XXX + i = r_uint(i) + res = 0 + while i: + res += i & 1 + i >>= 1 + return res UNROLL_CUTOFF = 5 @@ -687,7 +695,7 @@ # __________________ methods called on W_SetObject _________________ def clear(self, w_set): - raise NotImplementedError + w_set.switch_to_empty_strategy() def copy_real(self, w_set): raise NotImplementedError @@ -878,9 +886,6 @@ def length(self, w_set): return len(self.unerase(w_set.sstorage)) - def clear(self, w_set): - w_set.switch_to_empty_strategy() - def copy_real(self, w_set): # may be used internally on frozen sets, although frozenset().copy() # returns self in frozenset_copy__Frozenset. @@ -1281,7 +1286,7 @@ return UnicodeIteratorImplementation(self.space, self, w_set) -class IntegerSetStrategy(AbstractUnwrappedSetStrategy, SetStrategy): +class IntegerSetStrategy(SetStrategy): erase, unerase = rerased.new_erasing_pair("integer") erase = staticmethod(erase) unerase = staticmethod(unerase) @@ -1289,6 +1294,9 @@ intersect_jmp = jit.JitDriver(greens = [], reds = 'auto', name='set(int).intersect') + BITMASK = 3 + KEYMASK = ~BITMASK + def get_empty_storage(self): return self.erase({}) @@ -1296,7 +1304,16 @@ return {} def listview_int(self, w_set): - return self.unerase(w_set.sstorage).keys() + result = [] + items = self.unerase(w_set.sstorage).iteritems() + for key, value in items: + i = 0 + while value: + if value & 1: + result.append(key | i) + value >>= 1 + i += 1 + return result def is_correct_type(self, w_key): return type(w_key) is W_IntObject @@ -1321,6 +1338,231 @@ def iter(self, w_set): return IntegerIteratorImplementation(self.space, self, w_set) + def _set_key(self, setdata, i): + setdata[i & self.KEYMASK] = setdata.get(i & self.KEYMASK, 0) | (1 << (i & self.BITMASK)) + + def _set_key_wrapped(self, setdata, w_key): + self._set_key(setdata, self.unwrap(w_key)) + + def _remove_key(self, setdata, i): + key = i & self.KEYMASK + item = setdata.get(key, 0) + bit = (1 << (i & self.BITMASK)) + is_there = item & bit + if is_there: + if item & (~bit) == 0: + del setdata[key] + else: + setdata[key] = item & (~bit) + return bool(is_there) + + def _has_key(self, setdata, i): + return bool(setdata.get(i & self.KEYMASK, 0) & (1 << (i & self.BITMASK))) + + def _has_key_wrapped(self, setdata, w_key): + return self._has_key(setdata, self.unwrap(w_key)) + + @jit.look_inside_iff(lambda self, list_w: + jit.loop_unrolling_heuristic(list_w, len(list_w), UNROLL_CUTOFF)) + def get_storage_from_list(self, list_w): + setdata = {} + for w_item in list_w: + self._set_key_wrapped(setdata, w_item) + return self.erase(setdata) + + @jit.look_inside_iff(lambda self, items: + jit.loop_unrolling_heuristic(items, len(items), UNROLL_CUTOFF)) + def get_storage_from_unwrapped_list(self, items): + setdata = self.get_empty_dict() + for item in items: + self._set_key(setdata, item) + return self.erase(setdata) + + def get_storage_copy(self, w_set): + d = self.unerase(w_set.sstorage) + copy = self.erase(d.copy()) + return copy + + def copy_real(self, w_set): + # may be used internally on frozen sets, although frozenset().copy() + # returns self in frozenset_copy__Frozenset. + strategy = w_set.strategy + d = self.unerase(w_set.sstorage) + storage = self.erase(d.copy()) + clone = w_set.from_storage_and_strategy(storage, strategy) + return clone + + def add(self, w_set, w_key): + if self.is_correct_type(w_key): + d = self.unerase(w_set.sstorage) + self._set_key_wrapped(d, w_key) + else: + w_set.switch_to_object_strategy(self.space) + w_set.add(w_key) + + def remove(self, w_set, w_item): + d = self.unerase(w_set.sstorage) + if not self.is_correct_type(w_item): + #XXX check type of w_item and immediately return False in some cases + w_set.switch_to_object_strategy(self.space) + return w_set.remove(w_item) + + key = self.unwrap(w_item) + return self._remove_key(d, key) + + def has_key(self, w_set, w_key): + if not self.is_correct_type(w_key): + #XXX check type of w_item and immediately return False in some cases + w_set.switch_to_object_strategy(self.space) + return w_set.has_key(w_key) + d = self.unerase(w_set.sstorage) + return self._has_key_wrapped(d, w_key) + + def length(self, w_set): + l = 0 + # XXX too slow! + for value in self.unerase(w_set.sstorage).itervalues(): + assert value + l += popcount(value) + return l + + def getdict_w(self, w_set): + result = newset(self.space) + items = self.unerase(w_set.sstorage).iteritems() + for key, value in items: + i = 0 + while value: + if value & 1: + result[self.wrap(key | i)] = None + value >>= 1 + i += 1 + return result + + def update(self, w_set, w_other): + if self is w_other.strategy: + d_set = self.unerase(w_set.sstorage) + d_other = self.unerase(w_other.sstorage) + d_set.update(d_other) + return + if w_other.length() == 0: + return + w_set.switch_to_object_strategy(self.space) + w_set.update(w_other) + + def XXX_symmetric_difference_base(self, w_set, w_other): + if self is w_other.strategy: + strategy = w_set.strategy + storage = self._symmetric_difference_unwrapped(w_set, w_other) + else: + w_set.switch_to_object_strategy(self.space) + return w_set.symmetric_difference(w_other) + return storage, strategy + + def XXXsymmetric_difference(self, w_set, w_other): + if w_other.length() == 0: + return w_set.copy_real() + storage, strategy = self._symmetric_difference_base(w_set, w_other) + return w_set.from_storage_and_strategy(storage, strategy) + + def symmetric_difference_update(self, w_set, w_other): + if w_other.length() == 0: + return + if self is w_other.strategy: + w_set.sstorage = self._symmetric_difference_unwrapped(w_set, w_other) + else: + w_set.switch_to_object_strategy(self.space) + w_set.symmetric_difference_update(w_other) + + def _symmetric_difference_unwrapped(self, w_set, w_other): + d_new = self.get_empty_dict() + d_this = self.unerase(w_set.sstorage) + d_other = self.unerase(w_other.sstorage) + # XXX + for key, item in d_other.iteritems(): + new = item ^ d_this.get(key, 0) + if new: + d_new[key] = new + for key, item in d_this.iteritems(): + new = item ^ d_other.get(key, 0) + if new: + d_new[key] = new + + storage = self.erase(d_new) + return storage + + def _symmetric_difference_wrapped(self, w_set, w_other): + newsetdata = newset(self.space) + for obj in self.unerase(w_set.sstorage): + w_item = self.wrap(obj) + if not w_other.has_key(w_item): + newsetdata[w_item] = None + + w_iterator = w_other.iter() + while True: + w_item = w_iterator.next_entry() + if w_item is None: + break + if not w_set.has_key(w_item): + newsetdata[w_item] = None + + strategy = self.space.fromcache(ObjectSetStrategy) + return strategy.erase(newsetdata) + + def _intersect_base(self, w_set, w_other): + if self is w_other.strategy: + strategy = self + if w_set.length() > w_other.length(): + # swap operands + storage = self._intersect_unwrapped(w_other, w_set) + else: + storage = self._intersect_unwrapped(w_set, w_other) + elif not self.may_contain_equal_elements(w_other.strategy): + strategy = self.space.fromcache(EmptySetStrategy) + storage = strategy.get_empty_storage() + else: + strategy = self.space.fromcache(ObjectSetStrategy) + if w_set.length() > w_other.length(): + # swap operands + storage = w_other.strategy._intersect_wrapped(w_other, w_set) + else: + storage = self._intersect_wrapped(w_set, w_other) + return storage, strategy + + def XXX_intersect_wrapped(self, w_set, w_other): + result = newset(self.space) + for key in self.unerase(w_set.sstorage): + self.intersect_jmp.jit_merge_point() + w_key = self.wrap(key) + if w_other.has_key(w_key): + result[w_key] = None + + strategy = self.space.fromcache(ObjectSetStrategy) + return strategy.erase(result) + + def _intersect_unwrapped(self, w_set, w_other): + result = self.get_empty_dict() + d_this = self.unerase(w_set.sstorage) + d_other = self.unerase(w_other.sstorage) + for key, value in d_this.iteritems(): + value = value & d_other.get(key, 0) + if value: + result[key] = value + return self.erase(result) + + def intersect(self, w_set, w_other): + storage, strategy = self._intersect_base(w_set, w_other) + return w_set.from_storage_and_strategy(storage, strategy) + + def intersect_update(self, w_set, w_other): + if w_set.length() > w_other.length(): + w_intersection = w_other.intersect(w_set) + strategy = w_intersection.strategy + storage = w_intersection.sstorage + else: + storage, strategy = self._intersect_base(w_set, w_other) + w_set.strategy = strategy + w_set.sstorage = storage + class ObjectSetStrategy(AbstractUnwrappedSetStrategy, SetStrategy): erase, unerase = rerased.new_erasing_pair("object") @@ -1489,14 +1731,36 @@ def __init__(self, space, strategy, w_set): IteratorImplementation.__init__(self, space, strategy, w_set) d = strategy.unerase(w_set.sstorage) - self.iterator = d.iterkeys() + self.iterator = d.iteritems() + self.key_from_dict = 0 + self.value_from_dict = 0 + self.intvalue = 0 def next_entry(self): - # note that this 'for' loop only runs once, at most - for key in self.iterator: - return self.space.wrap(key) - else: - return None + value_from_dict = self.value_from_dict + key_from_dict = self.key_from_dict + intvalue = self.intvalue + if not value_from_dict: + # note that this 'for' loop only runs once, at most + for item in self.iterator: + key_from_dict, value_from_dict = item + self.key_from_dict = key_from_dict + assert value_from_dict + intvalue = 0 + break + else: + return None + while True: + result = intvalue + should_return = value_from_dict & 1 + value_from_dict >>= 1 + intvalue += 1 + if should_return: + self.value_from_dict = value_from_dict + self.intvalue = intvalue + return self.space.wrap(key_from_dict | result) + + class IdentityIteratorImplementation(IteratorImplementation): def __init__(self, space, strategy, w_set): diff --git a/pypy/objspace/std/test/test_setstrategies.py b/pypy/objspace/std/test/test_setstrategies.py --- a/pypy/objspace/std/test/test_setstrategies.py +++ b/pypy/objspace/std/test/test_setstrategies.py @@ -1,3 +1,4 @@ +import sys from pypy.objspace.std.setobject import W_SetObject from pypy.objspace.std.setobject import ( BytesIteratorImplementation, BytesSetStrategy, EmptySetStrategy, @@ -5,6 +6,12 @@ UnicodeIteratorImplementation, UnicodeSetStrategy) from pypy.objspace.std.listobject import W_ListObject + +from hypothesis import strategies, given + +ints = strategies.integers(-sys.maxint-1, sys.maxint) +intlists = strategies.lists(ints) + class TestW_SetStrategies: def wrapped(self, l): @@ -144,3 +151,85 @@ # s = W_SetObject(space, self.wrapped([u"a", u"b"])) assert sorted(space.listview_unicode(s)) == [u"a", u"b"] + + +class TestSetHypothesis: + def wrapped(self, l): + return W_ListObject(self.space, [self.space.wrap(x) for x in l]) + + def wrap(self, x): + return self.space.wrap(x) + + def intset(self, content): + return W_SetObject(self.space, self.wrapped(content)) + + @given(intlists, ints) + def test_intset_added_element_in_set(self, content, i): + s = self.intset(content) + w_i = self.wrap(i) + s.add(w_i) + assert s.has_key(w_i) + + @given(intlists, ints) + def test_remove(self, content, i): + s = self.intset(content + [i]) + w_i = self.wrap(i) + s.remove(w_i) + assert not s.has_key(w_i) + + @given(intlists, ints) + def test_length(self, content, i): + s = self.intset(content) + assert len(set(content)) == s.length() + + @given(intlists, intlists) + def test_update(self, c1, c2): + s1 = self.intset(c1) + s2 = self.intset(c2) + s1.update(s2) + for i in c1: + assert s1.has_key(self.wrap(i)) + for i in c2: + assert s1.has_key(self.wrap(i)) + # XXX check that no additional keys + + @given(intlists, intlists) + def test_symmetric_update(self, c1, c2): + s1 = self.intset(c1) + s2 = self.intset(c2) + s1.symmetric_difference_update(s2) + s1.length() + for i in c1: + if i not in c2: + assert s1.has_key(self.wrap(i)) + else: + assert not s1.has_key(self.wrap(i)) + for i in c2: + if i not in c1: + assert s1.has_key(self.wrap(i)) + else: + assert not s1.has_key(self.wrap(i)) + # XXX check that no additional keys + + @given(intlists, intlists) + def XXXtest_update_vs_not(self, c1, c2): + return #XXX write me! + + @given(intlists, intlists) + def test_intersect(self, c1, c2): + s1 = self.intset(c1) + s2 = self.intset(c2) + s = s1.intersect(s2) + for i in c1: + if i in c2: + assert s.has_key(self.wrap(i)) + else: + assert not s.has_key(self.wrap(i)) + for i in c2: + if i in c1: + assert s.has_key(self.wrap(i)) + else: + assert not s.has_key(self.wrap(i)) + # XXX check that no additional keys + + From pypy.commits at gmail.com Mon Feb 11 06:57:39 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 11 Feb 2019 03:57:39 -0800 (PST) Subject: [pypy-commit] pypy bitset-intsets: implement difference and issubset Message-ID: <5c616333.1c69fb81.6dd43.b16d@mx.google.com> Author: Carl Friedrich Bolz Branch: bitset-intsets Changeset: r95941:9481b4febfd0 Date: 2016-02-27 16:33 +0100 http://bitbucket.org/pypy/pypy/changeset/9481b4febfd0/ Log: implement difference and issubset diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py --- a/pypy/objspace/std/setobject.py +++ b/pypy/objspace/std/setobject.py @@ -1563,6 +1563,106 @@ w_set.strategy = strategy w_set.sstorage = storage + def difference(self, w_set, w_other): + storage = self._difference_base(w_set, w_other) + w_newset = w_set.from_storage_and_strategy(storage, w_set.strategy) + return w_newset + + def _difference_base(self, w_set, w_other): + if self is w_other.strategy: + storage = self._difference_unwrapped(w_set, w_other) + elif not w_set.strategy.may_contain_equal_elements(w_other.strategy): + d = self.unerase(w_set.sstorage) + storage = self.erase(d.copy()) + else: + storage = self._difference_wrapped(w_set, w_other) + return storage + + def _difference_unwrapped(self, w_set, w_other): + self_dict = self.unerase(w_set.sstorage) + other_dict = self.unerase(w_other.sstorage) + result_dict = self.get_empty_dict() + for key, value in self_dict.iteritems(): + new = value & (~other_dict.get(key, 0)) + if new: + result_dict[key] = new + return self.erase(result_dict) + + def _difference_update_unwrapped(self, w_set, w_other): + my_dict = self.unerase(w_set.sstorage) + if w_set.sstorage is w_other.sstorage: + my_dict.clear() + return + other_dict = self.unerase(w_other.sstorage) + for key, value in other_dict.iteritems(): + try: + new = my_dict[key] & (~value) + except KeyError: + pass + else: + if new: + my_dict[key] = new + else: + del my_dict[key] + + def _difference_update_wrapped(self, w_set, w_other): + w_iterator = w_other.iter() + while True: + w_item = w_iterator.next_entry() + if w_item is None: + break + w_set.remove(w_item) + + def difference_update(self, w_set, w_other): + if self.length(w_set) < w_other.strategy.length(w_other): + # small_set -= big_set: compute the difference as a new set + storage = self._difference_base(w_set, w_other) + w_set.sstorage = storage + else: + # big_set -= small_set: be more subtle + if self is w_other.strategy: + self._difference_update_unwrapped(w_set, w_other) + elif w_set.strategy.may_contain_equal_elements(w_other.strategy): + self._difference_update_wrapped(w_set, w_other) + + def equals(self, w_set, w_other): + if w_set.length() != w_other.length(): + return False + if w_set.length() == 0: + return True + # it's possible to have 0-length strategy that's not empty + if w_set.strategy is w_other.strategy: + return self._issubset_unwrapped(w_set, w_other) + if not self.may_contain_equal_elements(w_other.strategy): + return False + items = self.unerase(w_set.sstorage).keys() + for key in items: + if not w_other.has_key(self.wrap(key)): + return False + return True + + def _issubset_unwrapped(self, w_set, w_other): + d_set = self.unerase(w_set.sstorage) + d_other = self.unerase(w_other.sstorage) + for key, keyhash in iterkeys_with_hash(d_set): + if not contains_with_hash(d_other, key, keyhash): + return False + return True + + def _issubset_wrapped(self, w_set, w_other): + XXX + + def issubset(self, w_set, w_other): + if w_set.length() == 0: + return True + + if w_set.strategy is w_other.strategy: + return self._issubset_unwrapped(w_set, w_other) + elif not w_set.strategy.may_contain_equal_elements(w_other.strategy): + return False + else: + return self._issubset_wrapped(w_set, w_other) + class ObjectSetStrategy(AbstractUnwrappedSetStrategy, SetStrategy): erase, unerase = rerased.new_erasing_pair("object") diff --git a/pypy/objspace/std/test/test_setobject.py b/pypy/objspace/std/test/test_setobject.py --- a/pypy/objspace/std/test/test_setobject.py +++ b/pypy/objspace/std/test/test_setobject.py @@ -93,7 +93,7 @@ w_set = W_SetObject(self.space) _initialize_set(self.space, w_set, w_list) assert w_set.strategy is intstr - assert intstr.unerase(w_set.sstorage) == {1:None, 2:None, 3:None} + #assert intstr.unerase(w_set.sstorage) == {1:None, 2:None, 3:None} w_list = W_ListObject(self.space, [w("1"), w("2"), w("3")]) w_set = W_SetObject(self.space) diff --git a/pypy/objspace/std/test/test_setstrategies.py b/pypy/objspace/std/test/test_setstrategies.py --- a/pypy/objspace/std/test/test_setstrategies.py +++ b/pypy/objspace/std/test/test_setstrategies.py @@ -212,6 +212,21 @@ # XXX check that no additional keys @given(intlists, intlists) + def test_difference_update(self, c1, c2): + s1 = self.intset(c1) + s2 = self.intset(c2) + s1.difference_update(s2) + s1.length() + for i in c1: + if i not in c2: + assert s1.has_key(self.wrap(i)) + else: + assert not s1.has_key(self.wrap(i)) + for i in c2: + assert not s1.has_key(self.wrap(i)) + # XXX check that no additional keys + + @given(intlists, intlists) def XXXtest_update_vs_not(self, c1, c2): return #XXX write me! @@ -233,3 +248,11 @@ # XXX check that no additional keys + @given(intlists, intlists) + def test_issubset(self, c1, c2): + s1 = self.intset(c1) + s2 = self.intset(c1) + for i in c2: + s2.remove(self.wrap(i)) + assert s2.issubset(s1) + assert not s1.issubset(s2) or s1.equals(s2) From pypy.commits at gmail.com Mon Feb 11 06:57:40 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 11 Feb 2019 03:57:40 -0800 (PST) Subject: [pypy-commit] pypy bitset-intsets: fix update Message-ID: <5c616334.1c69fb81.4c056.405b@mx.google.com> Author: Carl Friedrich Bolz Branch: bitset-intsets Changeset: r95942:6ce89c9c6bae Date: 2016-02-27 17:31 +0100 http://bitbucket.org/pypy/pypy/changeset/6ce89c9c6bae/ Log: fix update diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py --- a/pypy/objspace/std/setobject.py +++ b/pypy/objspace/std/setobject.py @@ -1442,7 +1442,8 @@ if self is w_other.strategy: d_set = self.unerase(w_set.sstorage) d_other = self.unerase(w_other.sstorage) - d_set.update(d_other) + for key, item in d_other.iteritems(): + d_set[key] = d_set.get(key, 0) | item return if w_other.length() == 0: return diff --git a/pypy/objspace/std/test/test_setstrategies.py b/pypy/objspace/std/test/test_setstrategies.py --- a/pypy/objspace/std/test/test_setstrategies.py +++ b/pypy/objspace/std/test/test_setstrategies.py @@ -247,6 +247,17 @@ assert not s.has_key(self.wrap(i)) # XXX check that no additional keys + @given(intlists, intlists) + def test_union(self, c1, c2): + s1 = self.intset(c1) + s2 = self.intset(c2) + s = s1.copy_real() + s.update(s2) + for i in c1: + assert s.has_key(self.wrap(i)) + for i in c2: + assert s.has_key(self.wrap(i)) + # XXX check that no additional keys @given(intlists, intlists) def test_issubset(self, c1, c2): From pypy.commits at gmail.com Mon Feb 11 11:23:10 2019 From: pypy.commits at gmail.com (antocuni) Date: Mon, 11 Feb 2019 08:23:10 -0800 (PST) Subject: [pypy-commit] pypy.org extradoc: fix the download links and regenerate HTML Message-ID: <5c61a16e.1c69fb81.bef48.e1e9@mx.google.com> Author: Antonio Cuni Branch: extradoc Changeset: r935:43d47b661208 Date: 2019-02-11 17:22 +0100 http://bitbucket.org/pypy/pypy.org/changeset/43d47b661208/ Log: fix the download links and regenerate HTML diff --git a/download.html b/download.html --- a/download.html +++ b/download.html @@ -112,16 +112,16 @@

    Python2.7 compatible PyPy 7.0

    @@ -129,12 +129,12 @@

    Python 3.5.3 compatible PyPy3.5 v7.0.0

    @@ -152,10 +152,10 @@

    Python 3.6 compatible PyPy3.6 v7.0.0-alpha

    diff --git a/source/download.txt b/source/download.txt --- a/source/download.txt +++ b/source/download.txt @@ -98,20 +98,22 @@ * `All our downloads,`__ including previous versions. We also have a mirror_, but please use only if you have troubles accessing the links above -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-linux32.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-linux64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-osx64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-win32.zip -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-ppc64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-ppc64le.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-s390x.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-src.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2-v7.0.0-src.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-linux32.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-linux64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-osx64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-win32.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-ppc64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-ppc64le.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-s390x.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-src.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-src.zip .. _`vcredist_x86.exe`: http://www.microsoft.com/en-us/download/details.aspx?id=5582 .. __: https://bitbucket.org/pypy/pypy/downloads .. _mirror: http://buildbot.pypy.org/mirror/ .. _FreshPorts: http://www.freshports.org/lang/pypy + + Python 3.5.3 compatible PyPy3.5 v7.0.0 --------------------------------------- @@ -126,13 +128,13 @@ * `All our downloads,`__ including previous versions. We also have a mirror_, but please use only if you have troubles accessing the links above -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-linux32.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-linux64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-osx64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-win32.zip -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-s390x.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-src.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-src.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.5-v7.0.0-linux32.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.5-v7.0.0-linux64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.5-v7.0.0-osx64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.5-v7.0.0-win32.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.5-v7.0.0-s390x.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.5-v7.0.0-src.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.5-v7.0.0-src.zip .. __: https://bitbucket.org/pypy/pypy/downloads If your CPU is really, really old, it may be a x86-32 without SSE2. @@ -159,11 +161,11 @@ * `All our downloads,`__ including previous versions. We also have a mirror_, but please use only if you have troubles accessing the links above -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-linux64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-osx64.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-win32.zip -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-src.tar.bz2 -.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3-v7.0.0-src.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.0.0-linux64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.0.0-osx64.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.0.0-win32.zip +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.0.0-src.tar.bz2 +.. __: https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.0.0-src.zip .. __: https://bitbucket.org/pypy/pypy/downloads From pypy.commits at gmail.com Mon Feb 11 11:30:07 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 08:30:07 -0800 (PST) Subject: [pypy-commit] pypy.org extradoc: document the site update process Message-ID: <5c61a30f.1c69fb81.7bef4.3f6e@mx.google.com> Author: Matti Picus Branch: extradoc Changeset: r936:2cfb8fb9d732 Date: 2019-02-11 18:29 +0200 http://bitbucket.org/pypy/pypy.org/changeset/2cfb8fb9d732/ Log: document the site update process diff --git a/README b/README --- a/README +++ b/README @@ -2,3 +2,9 @@ These html files are not written by hand! See source/README. + +The site is pull into pypy.org by a cron job on a psf-hosted instance, +see https://github.com/python/psf-chef/blob/master/cookbooks/pypy-home/recipes/default.rb +The people with access to the instance are the ones in +https://github.com/python/psf-chef/tree/master/data_bags/users +with pypy-home access From pypy.commits at gmail.com Mon Feb 11 12:21:12 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:12 -0800 (PST) Subject: [pypy-commit] pypy default: skip test that crashes on old zlib version Message-ID: <5c61af08.1c69fb81.8eee9.26a0@mx.google.com> Author: Matti Picus Branch: Changeset: r95943:c080e584813c Date: 2019-02-11 14:02 +0200 http://bitbucket.org/pypy/pypy/changeset/c080e584813c/ Log: skip test that crashes on old zlib version diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -3,17 +3,18 @@ """ import sys +import py try: import zlib except ImportError: - import py; py.test.skip("no zlib module on this host Python") + py.test.skip("no zlib module on this host Python") from pypy.interpreter.gateway import interp2app try: from pypy.module.zlib import interp_zlib from rpython.rlib import rzlib except ImportError: - import py; py.test.skip("no zlib C library on this machine") + py.test.skip("no zlib C library on this machine") def test_unsigned_to_signed_32bit(): assert interp_zlib.unsigned_to_signed_32bit(123) == 123 @@ -344,6 +345,7 @@ assert (d1 + from_copy) == (d1 + from_compressor) + @py.test.mark.skipif(rzlib.ZLIB_VERSION == '1.2.8', reason='does not error check') def test_cannot_copy_compressor_with_stream_in_inconsistent_state(self): if self.runappdirect: skip("can't run with -A") compressor = self.zlib.compressobj() From pypy.commits at gmail.com Mon Feb 11 12:21:14 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:14 -0800 (PST) Subject: [pypy-commit] pypy rmod-radd-slots: close abandoned branch Message-ID: <5c61af0a.1c69fb81.59fb6.0138@mx.google.com> Author: Matti Picus Branch: rmod-radd-slots Changeset: r95944:ce5a8c7604a6 Date: 2019-02-11 18:43 +0200 http://bitbucket.org/pypy/pypy/changeset/ce5a8c7604a6/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:16 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:16 -0800 (PST) Subject: [pypy-commit] pypy ndarray-promote: close abandoned branch Message-ID: <5c61af0c.1c69fb81.512f9.6db9@mx.google.com> Author: Matti Picus Branch: ndarray-promote Changeset: r95945:cf24682f0f6d Date: 2019-02-11 18:55 +0200 http://bitbucket.org/pypy/pypy/changeset/cf24682f0f6d/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:18 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:18 -0800 (PST) Subject: [pypy-commit] pypy pypy3-release-2.6.x: close abandoned branch Message-ID: <5c61af0e.1c69fb81.ce7a9.740b@mx.google.com> Author: Matti Picus Branch: pypy3-release-2.6.x Changeset: r95946:7cf7426ddc77 Date: 2019-02-11 18:56 +0200 http://bitbucket.org/pypy/pypy/changeset/7cf7426ddc77/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:20 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:20 -0800 (PST) Subject: [pypy-commit] pypy pypy3-release-2.3.x: close abandoned branch Message-ID: <5c61af10.1c69fb81.42300.6eaa@mx.google.com> Author: Matti Picus Branch: pypy3-release-2.3.x Changeset: r95947:25560ec3b2f5 Date: 2019-02-11 18:56 +0200 http://bitbucket.org/pypy/pypy/changeset/25560ec3b2f5/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:22 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:22 -0800 (PST) Subject: [pypy-commit] pypy pypy3-release-2.4.x: close abandoned branch Message-ID: <5c61af12.1c69fb81.a2de.9b28@mx.google.com> Author: Matti Picus Branch: pypy3-release-2.4.x Changeset: r95948:072df3de15c5 Date: 2019-02-11 18:57 +0200 http://bitbucket.org/pypy/pypy/changeset/072df3de15c5/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:24 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:24 -0800 (PST) Subject: [pypy-commit] pypy release-pypy3.3-v5: close abandoned branch Message-ID: <5c61af14.1c69fb81.a57c1.013d@mx.google.com> Author: Matti Picus Branch: release-pypy3.3-v5 Changeset: r95949:6dfb3af9716d Date: 2019-02-11 18:57 +0200 http://bitbucket.org/pypy/pypy/changeset/6dfb3af9716d/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:25 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:25 -0800 (PST) Subject: [pypy-commit] pypy release-5.x: close abandoned branch Message-ID: <5c61af15.1c69fb81.d860b.c393@mx.google.com> Author: Matti Picus Branch: release-5.x Changeset: r95950:1ce3b640e7d7 Date: 2019-02-11 18:58 +0200 http://bitbucket.org/pypy/pypy/changeset/1ce3b640e7d7/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:27 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:27 -0800 (PST) Subject: [pypy-commit] pypy numpy_broadcast_nd: close abandoned branch Message-ID: <5c61af17.1c69fb81.7041b.b366@mx.google.com> Author: Matti Picus Branch: numpy_broadcast_nd Changeset: r95951:a32aff107924 Date: 2019-02-11 18:59 +0200 http://bitbucket.org/pypy/pypy/changeset/a32aff107924/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:29 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:29 -0800 (PST) Subject: [pypy-commit] pypy cpyext-inheritance: close abandoned branch Message-ID: <5c61af19.1c69fb81.65c40.dcaf@mx.google.com> Author: Matti Picus Branch: cpyext-inheritance Changeset: r95952:fba889ae9aaa Date: 2019-02-11 19:03 +0200 http://bitbucket.org/pypy/pypy/changeset/fba889ae9aaa/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:31 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:31 -0800 (PST) Subject: [pypy-commit] pypy cpyext-debug-type_dealloc: close abandoned branch Message-ID: <5c61af1b.1c69fb81.3d9cc.bd15@mx.google.com> Author: Matti Picus Branch: cpyext-debug-type_dealloc Changeset: r95953:91b5766bb8c6 Date: 2019-02-11 19:04 +0200 http://bitbucket.org/pypy/pypy/changeset/91b5766bb8c6/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:32 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:32 -0800 (PST) Subject: [pypy-commit] pypy override-tp_as-methods: close abandoned branch Message-ID: <5c61af1c.1c69fb81.b6962.58a6@mx.google.com> Author: Matti Picus Branch: override-tp_as-methods Changeset: r95954:6e767f90b99a Date: 2019-02-11 19:05 +0200 http://bitbucket.org/pypy/pypy/changeset/6e767f90b99a/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:34 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:34 -0800 (PST) Subject: [pypy-commit] pypy matplotlib: close abandoned branch Message-ID: <5c61af1e.1c69fb81.6ec08.6f51@mx.google.com> Author: Matti Picus Branch: matplotlib Changeset: r95955:6fcafa0bb5ea Date: 2019-02-11 19:06 +0200 http://bitbucket.org/pypy/pypy/changeset/6fcafa0bb5ea/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:36 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:36 -0800 (PST) Subject: [pypy-commit] pypy win32-vmprof: close abandoned branch Message-ID: <5c61af20.1c69fb81.a1abb.0ff7@mx.google.com> Author: Matti Picus Branch: win32-vmprof Changeset: r95956:e4e1582c4390 Date: 2019-02-11 19:07 +0200 http://bitbucket.org/pypy/pypy/changeset/e4e1582c4390/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 12:21:37 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 09:21:37 -0800 (PST) Subject: [pypy-commit] pypy non-linux-vmprof-stacklet-switch-2: close abandoned branch Message-ID: <5c61af21.1c69fb81.dcd3.a052@mx.google.com> Author: Matti Picus Branch: non-linux-vmprof-stacklet-switch-2 Changeset: r95957:fa16a566a0a7 Date: 2019-02-11 19:08 +0200 http://bitbucket.org/pypy/pypy/changeset/fa16a566a0a7/ Log: close abandoned branch From pypy.commits at gmail.com Mon Feb 11 13:36:04 2019 From: pypy.commits at gmail.com (stevie_92) Date: Mon, 11 Feb 2019 10:36:04 -0800 (PST) Subject: [pypy-commit] pypy cpyext-gc-trialdeletion: Close branch cpyext-gc-trialdeletion. Message-ID: <5c61c094.1c69fb81.cae4f.094f@mx.google.com> Author: Stefan Beyer Branch: cpyext-gc-trialdeletion Changeset: r95958:8ec9653041d2 Date: 2019-02-11 18:35 +0000 http://bitbucket.org/pypy/pypy/changeset/8ec9653041d2/ Log: Close branch cpyext-gc-trialdeletion. From pypy.commits at gmail.com Mon Feb 11 14:07:09 2019 From: pypy.commits at gmail.com (rlamy) Date: Mon, 11 Feb 2019 11:07:09 -0800 (PST) Subject: [pypy-commit] pypy Opcode-class: Close obsolete branch Message-ID: <5c61c7dd.1c69fb81.7fcf.55db@mx.google.com> Author: Ronan Lamy Branch: Opcode-class Changeset: r95959:0cdae6b1a07e Date: 2019-02-11 19:05 +0000 http://bitbucket.org/pypy/pypy/changeset/0cdae6b1a07e/ Log: Close obsolete branch From pypy.commits at gmail.com Mon Feb 11 19:18:57 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 16:18:57 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: fix logic, remove dead code Message-ID: <5c6210f1.1c69fb81.6716d.5b74@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95960:5a61af129d87 Date: 2019-02-12 01:33 +0200 http://bitbucket.org/pypy/pypy/changeset/5a61af129d87/ Log: fix logic, remove dead code diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -1901,8 +1901,7 @@ utf8 = space.utf8_w(w_unistr) lgt = space.len_w(w_unistr) result = StringBuilder(lgt) - itr = rutf8.Utf8StringIterator(utf8) - for uchr in itr: + for uchr in rutf8.Utf8StringIterator(utf8): if uchr > 127: if unicodedb.isspace(uchr): result.append(' ') @@ -1910,23 +1909,9 @@ try: uchr = ord(u'0') + unicodedb.decimal(uchr) except KeyError: - w_encoding = space.newtext('decimal') - pos = itr.get_pos() - w_start = space.newint(pos) - w_end = space.newint(pos+1) - w_reason = space.newtext('invalid decimal Unicode string') - raise OperationError(space.w_UnicodeEncodeError, - space.newtuple([w_encoding, w_unistr, - w_start, w_end, - w_reason])) - result.append(chr(uchr)) + pass + result.append(rutf8.unichr_as_utf8(r_uint(uchr), True)) return result.build() -from rpython.rlib.runicode import unicode_encode_utf8_forbid_surrogates - at jit.elidable -def XXX_g_encode_utf8(value): - """This is a global function because of jit.conditional_call_value""" - return unicode_encode_utf8_forbid_surrogates(value, len(value)) - _repr_function = rutf8.make_utf8_escape_function( pass_printable=True, quotes=True, prefix='') From pypy.commits at gmail.com Tue Feb 12 02:22:18 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 23:22:18 -0800 (PST) Subject: [pypy-commit] pypy taskengine-sorted-optionals: close abandoned branch Message-ID: <5c62742a.1c69fb81.43f16.6b14@mx.google.com> Author: Matti Picus Branch: taskengine-sorted-optionals Changeset: r95961:879181847bd8 Date: 2019-02-12 09:18 +0200 http://bitbucket.org/pypy/pypy/changeset/879181847bd8/ Log: close abandoned branch From pypy.commits at gmail.com Tue Feb 12 02:22:20 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 23:22:20 -0800 (PST) Subject: [pypy-commit] pypy inline-taskengine: close abandoned branch Message-ID: <5c62742c.1c69fb81.9c804.c3b6@mx.google.com> Author: Matti Picus Branch: inline-taskengine Changeset: r95962:468c9599a1f6 Date: 2019-02-12 09:18 +0200 http://bitbucket.org/pypy/pypy/changeset/468c9599a1f6/ Log: close abandoned branch From pypy.commits at gmail.com Tue Feb 12 02:22:22 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 23:22:22 -0800 (PST) Subject: [pypy-commit] pypy numpypy-ctypes: close abandoned branch Message-ID: <5c62742e.1c69fb81.6716d.0759@mx.google.com> Author: Matti Picus Branch: numpypy-ctypes Changeset: r95963:7dc47b5a8a95 Date: 2019-02-12 09:18 +0200 http://bitbucket.org/pypy/pypy/changeset/7dc47b5a8a95/ Log: close abandoned branch From pypy.commits at gmail.com Tue Feb 12 02:22:23 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 23:22:23 -0800 (PST) Subject: [pypy-commit] pypy numpy-record-type-pure-python: close abandoned branch Message-ID: <5c62742f.1c69fb81.670f7.76d0@mx.google.com> Author: Matti Picus Branch: numpy-record-type-pure-python Changeset: r95964:32693f76ec8f Date: 2019-02-12 09:19 +0200 http://bitbucket.org/pypy/pypy/changeset/32693f76ec8f/ Log: close abandoned branch From pypy.commits at gmail.com Tue Feb 12 02:22:25 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 23:22:25 -0800 (PST) Subject: [pypy-commit] pypy struct-double: close abandoned branch Message-ID: <5c627431.1c69fb81.9696f.0afb@mx.google.com> Author: Matti Picus Branch: struct-double Changeset: r95965:877a16704f95 Date: 2019-02-12 09:19 +0200 http://bitbucket.org/pypy/pypy/changeset/877a16704f95/ Log: close abandoned branch From pypy.commits at gmail.com Tue Feb 12 02:22:27 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 23:22:27 -0800 (PST) Subject: [pypy-commit] pypy dynamic-specialized-tuple: close abandoned branch Message-ID: <5c627433.1c69fb81.13abd.c877@mx.google.com> Author: Matti Picus Branch: dynamic-specialized-tuple Changeset: r95966:5b69b3275f06 Date: 2019-02-12 09:19 +0200 http://bitbucket.org/pypy/pypy/changeset/5b69b3275f06/ Log: close abandoned branch From pypy.commits at gmail.com Tue Feb 12 02:22:29 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 11 Feb 2019 23:22:29 -0800 (PST) Subject: [pypy-commit] pypy jit-sys-exc-info: close abandoned branch Message-ID: <5c627435.1c69fb81.8f4d8.9ace@mx.google.com> Author: Matti Picus Branch: jit-sys-exc-info Changeset: r95967:b1f2ea41a0c5 Date: 2019-02-12 09:20 +0200 http://bitbucket.org/pypy/pypy/changeset/b1f2ea41a0c5/ Log: close abandoned branch From pypy.commits at gmail.com Tue Feb 12 06:40:28 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 12 Feb 2019 03:40:28 -0800 (PST) Subject: [pypy-commit] pypy default: fix test for linux 32 Message-ID: <5c62b0ac.1c69fb81.c1bf8.b829@mx.google.com> Author: Matti Picus Branch: Changeset: r95968:20486c92ed2a Date: 2019-02-12 12:54 +0200 http://bitbucket.org/pypy/pypy/changeset/20486c92ed2a/ Log: fix test for linux 32 diff --git a/rpython/memory/gc/test/test_direct.py b/rpython/memory/gc/test/test_direct.py --- a/rpython/memory/gc/test/test_direct.py +++ b/rpython/memory/gc/test/test_direct.py @@ -781,7 +781,7 @@ def large_malloc(): # malloc an object which is large enough to trigger a major collection threshold = self.gc.next_major_collection_threshold - self.malloc(VAR, int(threshold/8)) + self.malloc(VAR, int(threshold/4)) summary = debuglog.summary() debuglog.reset() return summary From pypy.commits at gmail.com Tue Feb 12 08:11:56 2019 From: pypy.commits at gmail.com (rlamy) Date: Tue, 12 Feb 2019 05:11:56 -0800 (PST) Subject: [pypy-commit] pypy rpath-enforceargs: Close obsolete branch Message-ID: <5c62c61c.1c69fb81.7a49e.0fa4@mx.google.com> Author: Ronan Lamy Branch: rpath-enforceargs Changeset: r95970:2bb6bffd210f Date: 2019-02-12 13:11 +0000 http://bitbucket.org/pypy/pypy/changeset/2bb6bffd210f/ Log: Close obsolete branch From pypy.commits at gmail.com Tue Feb 12 13:57:07 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 12 Feb 2019 10:57:07 -0800 (PST) Subject: [pypy-commit] pypy default: try to fix arm Message-ID: <5c631703.1c69fb81.43f16.5297@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r95975:26471ac5ce5f Date: 2019-02-12 19:56 +0100 http://bitbucket.org/pypy/pypy/changeset/26471ac5ce5f/ Log: try to fix arm diff --git a/rpython/jit/backend/arm/regalloc.py b/rpython/jit/backend/arm/regalloc.py --- a/rpython/jit/backend/arm/regalloc.py +++ b/rpython/jit/backend/arm/regalloc.py @@ -305,9 +305,8 @@ operations = cpu.gc_ll_descr.rewrite_assembler(cpu, operations, allgcrefs) # compute longevity of variables - longevity, last_real_usage = compute_vars_longevity(inputargs, operations) + longevity = compute_vars_longevity(inputargs, operations) self.longevity = longevity - self.last_real_usage = last_real_usage fm = self.frame_manager asm = self.assembler self.vfprm = VFPRegisterManager(longevity, fm, asm) @@ -1062,7 +1061,7 @@ position = self.rm.position for arg in inputargs: assert not isinstance(arg, Const) - if self.last_real_usage.get(arg, -1) <= position: + if self.longevity[arg].is_last_real_use_before(position): self.force_spill_var(arg) # From pypy.commits at gmail.com Tue Feb 12 14:49:46 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 12 Feb 2019 11:49:46 -0800 (PST) Subject: [pypy-commit] pypy promote-unicode: close to-be-merged branch Message-ID: <5c63235a.1c69fb81.8166a.0e12@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: promote-unicode Changeset: r95980:9617f2038bf2 Date: 2019-02-12 20:10 +0100 http://bitbucket.org/pypy/pypy/changeset/9617f2038bf2/ Log: close to-be-merged branch From pypy.commits at gmail.com Tue Feb 12 14:49:48 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 12 Feb 2019 11:49:48 -0800 (PST) Subject: [pypy-commit] pypy default: merge promote-unicode Message-ID: <5c63235c.1c69fb81.e5759.2ccc@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r95981:9d4fe930924e Date: 2019-02-12 20:11 +0100 http://bitbucket.org/pypy/pypy/changeset/9d4fe930924e/ Log: merge promote-unicode mostly for completeness sake: support for rlib.jit.promote_unicode, which behaves like promote_string, but for rpython unicode objects. 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 @@ -28,3 +28,8 @@ .. branch: regalloc-playground Improve register allocation in the JIT. + + +.. branch: promote-unicode + +Implement rlib.jit.promote_unicode to complement promote_string diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py --- a/rpython/jit/codewriter/jtransform.py +++ b/rpython/jit/codewriter/jtransform.py @@ -596,6 +596,23 @@ op1 = SpaceOperation('str_guard_value', [op.args[0], c, descr], op.result) return [SpaceOperation('-live-', [], None), op1, None] + if (hints.get('promote_unicode') and + op.args[0].concretetype is not lltype.Void): + U = lltype.Ptr(rstr.UNICODE) + assert op.args[0].concretetype == U + self._register_extra_helper(EffectInfo.OS_UNIEQ_NONNULL, + "str.eq_nonnull", + [U, U], + lltype.Signed, + EffectInfo.EF_ELIDABLE_CANNOT_RAISE) + descr, p = self.callcontrol.callinfocollection.callinfo_for_oopspec( + EffectInfo.OS_UNIEQ_NONNULL) + # XXX this is fairly ugly way of creating a constant, + # however, callinfocollection has no better interface + c = Constant(p.adr.ptr, lltype.typeOf(p.adr.ptr)) + op1 = SpaceOperation('str_guard_value', [op.args[0], c, descr], + op.result) + return [SpaceOperation('-live-', [], None), op1, None] if hints.get('force_virtualizable'): return SpaceOperation('hint_force_virtualizable', [op.args[0]], None) if hints.get('force_no_const'): # for tests only diff --git a/rpython/jit/codewriter/test/test_jtransform.py b/rpython/jit/codewriter/test/test_jtransform.py --- a/rpython/jit/codewriter/test/test_jtransform.py +++ b/rpython/jit/codewriter/test/test_jtransform.py @@ -94,7 +94,7 @@ return True return False def callinfo_for_oopspec(self, oopspecindex): - assert oopspecindex == effectinfo.EffectInfo.OS_STREQ_NONNULL + # assert oopspecindex == effectinfo.EffectInfo.OS_STREQ_NONNULL class c: class adr: ptr = 1 @@ -1129,6 +1129,21 @@ assert op1.result == v2 assert op0.opname == '-live-' +def test_unicode_promote(): + PUNICODE = lltype.Ptr(rstr.UNICODE) + v1 = varoftype(PUNICODE) + v2 = varoftype(PUNICODE) + op = SpaceOperation('hint', + [v1, Constant({'promote_unicode': True}, lltype.Void)], + v2) + tr = Transformer(FakeCPU(), FakeBuiltinCallControl()) + op0, op1, _ = tr.rewrite_operation(op) + assert op1.opname == 'str_guard_value' + assert op1.args[0] == v1 + assert op1.args[2] == 'calldescr' + assert op1.result == v2 + assert op0.opname == '-live-' + def test_double_promote_str(): PSTR = lltype.Ptr(rstr.STR) v1 = varoftype(PSTR) diff --git a/rpython/jit/metainterp/test/test_string.py b/rpython/jit/metainterp/test/test_string.py --- a/rpython/jit/metainterp/test/test_string.py +++ b/rpython/jit/metainterp/test/test_string.py @@ -3,7 +3,7 @@ from rpython.jit.metainterp.test.support import LLJitMixin from rpython.rlib.debug import debug_print from rpython.rlib.jit import (JitDriver, dont_look_inside, we_are_jitted, - promote_string) + promote_string, promote_unicode) from rpython.rlib.rstring import StringBuilder, UnicodeBuilder @@ -518,6 +518,19 @@ self.meta_interp(f, [0]) self.check_resops(call_r=2, call_i=5) + def test_promote_unicode(self): + driver = JitDriver(greens = [], reds = ['n']) + + def f(n): + while n < 21: + driver.jit_merge_point(n=n) + promote_unicode(unicode(str(n % 3))) + n += 1 + return 0 + + self.meta_interp(f, [0]) + self.check_resops(call_r=4, call_i=5) + def test_join_chars(self): jitdriver = JitDriver(reds=['a', 'b', 'c', 'i'], greens=[]) _str = self._str diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py --- a/rpython/rlib/jit.py +++ b/rpython/rlib/jit.py @@ -84,6 +84,7 @@ * promote - promote the argument from a variable into a constant * promote_string - same, but promote string by *value* + * promote_unicode - same, but promote unicode string by *value* * access_directly - directly access a virtualizable, as a structure and don't treat it as a virtualizable * fresh_virtualizable - means that virtualizable was just allocated. @@ -126,6 +127,9 @@ def promote_string(x): return hint(x, promote_string=True) +def promote_unicode(x): + return hint(x, promote_unicode=True) + def dont_look_inside(func): """ Make sure the JIT does not trace inside decorated function (it becomes a call instead) From pypy.commits at gmail.com Tue Feb 12 17:29:19 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 12 Feb 2019 14:29:19 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: raise correct error Message-ID: <5c6348bf.1c69fb81.a0105.0308@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95987:d89df30bad0b Date: 2019-02-12 23:10 +0200 http://bitbucket.org/pypy/pypy/changeset/d89df30bad0b/ Log: raise correct error diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -1901,16 +1901,30 @@ utf8 = space.utf8_w(w_unistr) lgt = space.len_w(w_unistr) result = StringBuilder(lgt) + pos = 0 for uchr in rutf8.Utf8StringIterator(utf8): if uchr > 127: if unicodedb.isspace(uchr): result.append(' ') + pos += 1 continue try: uchr = ord(u'0') + unicodedb.decimal(uchr) except KeyError: pass - result.append(rutf8.unichr_as_utf8(r_uint(uchr), True)) + try: + c = rutf8.unichr_as_utf8(r_uint(uchr)) + except ValueError: + w_encoding = space.newtext('utf-8') + w_start = space.newint(pos) + w_end = space.newint(pos+1) + w_reason = space.newtext('surrogates not allowed') + raise OperationError(space.w_UnicodeEncodeError, + space.newtuple([w_encoding, w_unistr, + w_start, w_end, + w_reason])) + result.append(c) + pos += 1 return result.build() _repr_function = rutf8.make_utf8_escape_function( From pypy.commits at gmail.com Tue Feb 12 17:29:21 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 12 Feb 2019 14:29:21 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: allow surrogates in wcharpsize2utf8 Message-ID: <5c6348c1.1c69fb81.48433.eae0@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95988:6165ec8e5e76 Date: 2019-02-12 23:19 +0200 http://bitbucket.org/pypy/pypy/changeset/6165ec8e5e76/ Log: allow surrogates in wcharpsize2utf8 diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py --- a/rpython/rtyper/lltypesystem/rffi.py +++ b/rpython/rtyper/lltypesystem/rffi.py @@ -1029,7 +1029,7 @@ s = StringBuilder(size) for i in range(size): - rutf8.unichr_as_utf8_append(s, ord(w[i])) + rutf8.unichr_as_utf8_append(s, ord(w[i]), True) return s.build() def wcharp2utf8(w): From pypy.commits at gmail.com Tue Feb 12 18:52:36 2019 From: pypy.commits at gmail.com (mjacob) Date: Tue, 12 Feb 2019 15:52:36 -0800 (PST) Subject: [pypy-commit] pypy py3.5-ssl-revdb: Remove unnecessary variable. Message-ID: <5c635c44.1c69fb81.31251.2178@mx.google.com> Author: Manuel Jacob Branch: py3.5-ssl-revdb Changeset: r95989:9dbb4911ba4d Date: 2019-02-12 19:54 +0100 http://bitbucket.org/pypy/pypy/changeset/9dbb4911ba4d/ Log: Remove unnecessary variable. diff --git a/lib_pypy/_cffi_ssl/_stdssl/__init__.py b/lib_pypy/_cffi_ssl/_stdssl/__init__.py --- a/lib_pypy/_cffi_ssl/_stdssl/__init__.py +++ b/lib_pypy/_cffi_ssl/_stdssl/__init__.py @@ -443,10 +443,9 @@ sock = self.get_socket_or_connection_gone() if buffer_into is None: - dest = ffi.new("char[]", length) + mem = ffi.new("char[]", length) if length == 0: return b"" - mem = dest else: mem = ffi.from_buffer(buffer_into) if length <= 0 or length > len(buffer_into): @@ -500,7 +499,7 @@ raise pyssl_error(self, count) if not buffer_into: - return _bytes_with_len(dest, count) + return _bytes_with_len(mem, count) return count From pypy.commits at gmail.com Tue Feb 12 18:52:38 2019 From: pypy.commits at gmail.com (mjacob) Date: Tue, 12 Feb 2019 15:52:38 -0800 (PST) Subject: [pypy-commit] pypy py3.5-ssl-revdb: Defer creation of C buffer. Message-ID: <5c635c46.1c69fb81.4897b.5ac6@mx.google.com> Author: Manuel Jacob Branch: py3.5-ssl-revdb Changeset: r95990:5c289da45ef2 Date: 2019-02-12 19:58 +0100 http://bitbucket.org/pypy/pypy/changeset/5c289da45ef2/ Log: Defer creation of C buffer. diff --git a/lib_pypy/_cffi_ssl/_stdssl/__init__.py b/lib_pypy/_cffi_ssl/_stdssl/__init__.py --- a/lib_pypy/_cffi_ssl/_stdssl/__init__.py +++ b/lib_pypy/_cffi_ssl/_stdssl/__init__.py @@ -443,17 +443,17 @@ sock = self.get_socket_or_connection_gone() if buffer_into is None: - mem = ffi.new("char[]", length) if length == 0: return b"" + mem = ffi.new("char[]", length) else: - mem = ffi.from_buffer(buffer_into) if length <= 0 or length > len(buffer_into): length = len(buffer_into) if length > sys.maxsize: raise OverflowError("maximum length can't fit in a C 'int'") if len(buffer_into) == 0: return 0 + mem = ffi.from_buffer(buffer_into) if sock: timeout = _socket_timeout(sock) From pypy.commits at gmail.com Tue Feb 12 18:52:40 2019 From: pypy.commits at gmail.com (mjacob) Date: Tue, 12 Feb 2019 15:52:40 -0800 (PST) Subject: [pypy-commit] =?utf-8?q?pypy_py3=2E5-ssl-revdb=3A_Fix_=5FSSLSock?= =?utf-8?q?et=2Eread=28=29_for_buffers_that_can=E2=80=99t_get_their_raw_ad?= =?utf-8?q?dresses_taken_=28e=2Eg=2E_when_running_on_top_of_RevDB=29=2E?= Message-ID: <5c635c48.1c69fb81.f30ce.2675@mx.google.com> Author: Manuel Jacob Branch: py3.5-ssl-revdb Changeset: r95991:664e95442ff7 Date: 2019-02-12 20:12 +0100 http://bitbucket.org/pypy/pypy/changeset/664e95442ff7/ Log: Fix _SSLSocket.read() for buffers that can’t get their raw addresses taken (e.g. when running on top of RevDB). diff --git a/lib_pypy/_cffi_ssl/_stdssl/__init__.py b/lib_pypy/_cffi_ssl/_stdssl/__init__.py --- a/lib_pypy/_cffi_ssl/_stdssl/__init__.py +++ b/lib_pypy/_cffi_ssl/_stdssl/__init__.py @@ -453,7 +453,12 @@ raise OverflowError("maximum length can't fit in a C 'int'") if len(buffer_into) == 0: return 0 - mem = ffi.from_buffer(buffer_into) + try: + mem = ffi.from_buffer(buffer_into) + zero_copy = True + except TypeError: + mem = ffi.new("char[]", length) + zero_copy = False if sock: timeout = _socket_timeout(sock) @@ -500,8 +505,10 @@ if not buffer_into: return _bytes_with_len(mem, count) - - return count + else: + if not zero_copy: + buffer_into[0:count] = ffi.buffer(mem)[0:count] + return count if HAS_ALPN: def selected_alpn_protocol(self): From pypy.commits at gmail.com Tue Feb 12 18:52:41 2019 From: pypy.commits at gmail.com (mjacob) Date: Tue, 12 Feb 2019 15:52:41 -0800 (PST) Subject: [pypy-commit] pypy py3.5-ssl-revdb: Share code. Message-ID: <5c635c49.1c69fb81.b9aaa.d9d1@mx.google.com> Author: Manuel Jacob Branch: py3.5-ssl-revdb Changeset: r95992:08a735234778 Date: 2019-02-12 20:27 +0100 http://bitbucket.org/pypy/pypy/changeset/08a735234778/ Log: Share code. diff --git a/lib_pypy/_cffi_ssl/_stdssl/utility.py b/lib_pypy/_cffi_ssl/_stdssl/utility.py --- a/lib_pypy/_cffi_ssl/_stdssl/utility.py +++ b/lib_pypy/_cffi_ssl/_stdssl/utility.py @@ -15,7 +15,7 @@ def _str_to_ffi_buffer(view): if isinstance(view, str): - return ffi.from_buffer(view.encode()) + view = view.encode() elif isinstance(view, memoryview): # NOTE pypy limitation StringBuffer does not allow # to get a raw address to the string! From pypy.commits at gmail.com Wed Feb 13 03:20:46 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 00:20:46 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8: backport changes to rpython from unicode-utf8-py3 Message-ID: <5c63d35e.1c69fb81.a0105.bdd2@mx.google.com> Author: Matti Picus Branch: unicode-utf8 Changeset: r95993:275eabb360c2 Date: 2019-02-13 00:51 +0200 http://bitbucket.org/pypy/pypy/changeset/275eabb360c2/ Log: backport changes to rpython from unicode-utf8-py3 diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py --- a/rpython/rlib/_rsocket_rffi.py +++ b/rpython/rlib/_rsocket_rffi.py @@ -1369,8 +1369,15 @@ return rwin32.FormatError(errno) def socket_strerror_unicode(errno): + return rwin32.FormatErrorW(errno)[0] + + def gai_strerror_unicode(errno): + return rwin32.FormatErrorW(errno)[0] + + def socket_strerror_utf8(errno): return rwin32.FormatErrorW(errno) - def gai_strerror_unicode(errno): + + def gai_strerror_utf8(errno): return rwin32.FormatErrorW(errno) # WinSock does not use a bitmask in select, and uses @@ -1386,7 +1393,16 @@ def socket_strerror_unicode(errno): return socket_strerror_str(errno).decode('latin-1') + def gai_strerror_unicode(errno): return gai_strerror_str(errno).decode('latin-1') + def socket_strerror_utf8(errno): + msg = socket_strerror_str(errno) + return msg, len(msg) + + def gai_strerror_utf8(errno): + msg = gai_strerror_str(errno) + return msg, len(msg) + MAX_FD_SIZE = FD_SETSIZE diff --git a/rpython/rlib/rdynload.py b/rpython/rlib/rdynload.py --- a/rpython/rlib/rdynload.py +++ b/rpython/rlib/rdynload.py @@ -228,18 +228,16 @@ res = rwin32.LoadLibrary(name) if not res: err = rwin32.GetLastError_saved() - ustr = rwin32.FormatErrorW(err) - # DLOpenError unicode msg breaks translation of cpyext create_extension_module - raise DLOpenError(ustr.encode('utf-8')) + ustr, lgt = rwin32.FormatErrorW(err) + raise DLOpenError(ustr) return res def dlopenex(name): res = rwin32.LoadLibraryExA(name) if not res: err = rwin32.GetLastError_saved() - ustr = rwin32.FormatErrorW(err) - # DLOpenError unicode msg breaks translation of cpyext create_extension_module - raise DLOpenError(ustr.encode('utf-8')) + ustr, lgt = rwin32.FormatErrorW(err) + raise DLOpenError(ustr) return res def dlopenU(name, mode=-1): @@ -247,9 +245,8 @@ res = rwin32.LoadLibraryW(name) if not res: err = rwin32.GetLastError_saved() - ustr = rwin32.FormatErrorW(err) - # DLOpenError unicode msg breaks translation of cpyext create_extension_module - raise DLOpenError(ustr.encode('utf-8')) + ustr, lgt = rwin32.FormatErrorW(err) + raise DLOpenError(ustr) return res def dlclose(handle): diff --git a/rpython/rlib/rpoll.py b/rpython/rlib/rpoll.py --- a/rpython/rlib/rpoll.py +++ b/rpython/rlib/rpoll.py @@ -30,6 +30,8 @@ return _c.socket_strerror_str(self.errno) def get_msg_unicode(self): return _c.socket_strerror_unicode(self.errno) + def get_msg_utf8(self): + return _c.socket_strerror_utf8(self.errno) class SelectError(Exception): def __init__(self, errno): @@ -38,6 +40,8 @@ return _c.socket_strerror_str(self.errno) def get_msg_unicode(self): return _c.socket_strerror_unicode(self.errno) + def get_msg_utf8(self): + return _c.socket_strerror_utf8(self.errno) # ____________________________________________________________ # poll() for POSIX systems diff --git a/rpython/rlib/rsocket.py b/rpython/rlib/rsocket.py --- a/rpython/rlib/rsocket.py +++ b/rpython/rlib/rsocket.py @@ -1301,6 +1301,9 @@ return '' def get_msg_unicode(self): return self.get_msg().decode('latin-1') + def get_msg_utf8(self): + msg = self.get_msg() + return msg, len(msg) def __str__(self): return self.get_msg() @@ -1319,6 +1322,8 @@ return _c.socket_strerror_str(self.errno) def get_msg_unicode(self): return _c.socket_strerror_unicode(self.errno) + def get_msg_utf8(self): + return _c.socket_strerror_utf8(self.errno) def last_error(): return CSocketError(_c.geterrno()) @@ -1329,6 +1334,8 @@ return _c.gai_strerror_str(self.errno) def get_msg_unicode(self): return _c.gai_strerror_unicode(self.errno) + def get_msg_utf8(self): + return _c.gai_strerror_utf8(self.errno) class HSocketError(SocketError): applevelerrcls = 'herror' diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -158,18 +158,19 @@ def codepoint_at_pos(code, pos): """ Give a codepoint in code at pos - assumes valid utf8, no checking! """ + lgt = len(code) ordch1 = ord(code[pos]) - if ordch1 <= 0x7F: + if ordch1 <= 0x7F or pos +1 >= lgt: return ordch1 ordch2 = ord(code[pos+1]) - if ordch1 <= 0xDF: + if ordch1 <= 0xDF or pos +2 >= lgt: # 110yyyyy 10zzzzzz -> 00000000 00000yyy yyzzzzzz return (ordch1 << 6) + ordch2 - ( (0xC0 << 6) + 0x80 ) ordch3 = ord(code[pos+2]) - if ordch1 <= 0xEF: + if ordch1 <= 0xEF or pos + 3 >= lgt: # 1110xxxx 10yyyyyy 10zzzzzz -> 00000000 xxxxyyyy yyzzzzzz return (ordch1 << 12) + (ordch2 << 6) + ordch3 - ( (0xE0 << 12) + (0x80 << 6) + 0x80 ) @@ -767,6 +768,9 @@ if ordch1 <= 0x7F: self._pos = pos + 1 return ordch1 + if pos + 1 >= len(code): + self._pos = pos + 1 + return ordch1 ordch2 = ord(code[pos+1]) if ordch1 <= 0xDF: @@ -818,3 +822,63 @@ res.append_slice(s, start, end) i = end return res.build() + +# ____________________________________________________________ +# MBCS codecs for Windows + +if sys.platform == 'win32': + from rpython.rtyper.lltypesystem import lltype, rffi + from rpython.rlib.runicode import CP_ACP, BOOLP, WideCharToMultiByte + from rpython.rlib import rwin32 + + def utf8_encode_mbcs(s, errors, errorhandler, + force_replace=True): + # TODO: do the encoding without decoding utf8 -> unicode + uni = s.decode('utf8') + lgt = len(uni) + if not force_replace and errors not in ('strict', 'replace'): + msg = "mbcs encoding does not support errors='%s'" % errors + errorhandler('strict', 'mbcs', msg, s, 0, 0) + + if lgt == 0: + return '' + + if force_replace or errors == 'replace': + flags = 0 + used_default_p = lltype.nullptr(BOOLP.TO) + else: + # strict + flags = rwin32.WC_NO_BEST_FIT_CHARS + used_default_p = lltype.malloc(BOOLP.TO, 1, flavor='raw') + used_default_p[0] = rffi.cast(rwin32.BOOL, False) + + try: + with rffi.scoped_nonmoving_unicodebuffer(uni) as dataptr: + # first get the size of the result + mbcssize = WideCharToMultiByte(CP_ACP, flags, + dataptr, lgt, None, 0, + None, used_default_p) + if mbcssize == 0: + raise rwin32.lastSavedWindowsError() + # If we used a default char, then we failed! + if (used_default_p and + rffi.cast(lltype.Bool, used_default_p[0])): + errorhandler('strict', 'mbcs', "invalid character", + s, 0, 0) + + with rffi.scoped_alloc_buffer(mbcssize) as buf: + # do the conversion + if WideCharToMultiByte(CP_ACP, flags, + dataptr, lgt, buf.raw, mbcssize, + None, used_default_p) == 0: + raise rwin32.lastSavedWindowsError() + if (used_default_p and + rffi.cast(lltype.Bool, used_default_p[0])): + errorhandler('strict', 'mbcs', "invalid character", + s, 0, 0) + result = buf.str(mbcssize) + assert result is not None + return result + finally: + if used_default_p: + lltype.free(used_default_p, flavor='raw') diff --git a/rpython/rlib/rwin32.py b/rpython/rlib/rwin32.py --- a/rpython/rlib/rwin32.py +++ b/rpython/rlib/rwin32.py @@ -269,6 +269,9 @@ def FormatError(code): return llimpl_FormatError(code) def FormatErrorW(code): + """ + returns utf8, n_codepoints + """ return llimpl_FormatErrorW(code) def llimpl_FormatError(code): @@ -303,7 +306,7 @@ return result def llimpl_FormatErrorW(code): - "Return a unicode message corresponding to the given Windows error code." + "Return a utf8-encoded msg and its length" buf = lltype.malloc(rffi.CWCHARPP.TO, 1, flavor='raw') buf[0] = lltype.nullptr(rffi.CWCHARP.TO) try: @@ -324,9 +327,10 @@ buflen -= 1 if buflen <= 0: - result = u'Windows Error %d' % (code,) + msg = 'Windows Error %d' % (code,) + result = msg, len(msg) else: - result = rffi.wcharpsize2unicode(s_buf, buflen) + result = rffi.wcharpsize2utf8(s_buf, buflen), buflen finally: LocalFree(rffi.cast(rffi.VOIDP, buf[0])) lltype.free(buf, flavor='raw') diff --git a/rpython/rlib/test/test_rwin32.py b/rpython/rlib/test/test_rwin32.py --- a/rpython/rlib/test/test_rwin32.py +++ b/rpython/rlib/test/test_rwin32.py @@ -90,9 +90,9 @@ assert '%2' in msg def test_formaterror_unicode(): - msg = rwin32.FormatErrorW(34) - assert type(msg) is unicode - assert u'%2' in msg + msg, lgt = rwin32.FormatErrorW(34) + assert type(msg) is str + assert '%2' in msg def test_loadlibraryA(): # test0 can be loaded alone, but test1 requires the modified search path diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py --- a/rpython/rtyper/lltypesystem/rffi.py +++ b/rpython/rtyper/lltypesystem/rffi.py @@ -1029,7 +1029,7 @@ s = StringBuilder(size) for i in range(size): - rutf8.unichr_as_utf8_append(s, ord(w[i])) + rutf8.unichr_as_utf8_append(s, ord(w[i]), True) return s.build() def wcharp2utf8(w): diff --git a/rpython/rtyper/lltypesystem/test/test_rffi.py b/rpython/rtyper/lltypesystem/test/test_rffi.py --- a/rpython/rtyper/lltypesystem/test/test_rffi.py +++ b/rpython/rtyper/lltypesystem/test/test_rffi.py @@ -916,3 +916,8 @@ assert buf[1] == 'a' assert buf[2] == 'r' assert buf[3] == '\x00' + +def test_wcharp2utf8n(): + w = 'hello\x00\x00\x00\x00' + u, i = wcharp2utf8n(w, len(w)) + assert i == len('hello') diff --git a/rpython/tool/leakfinder.py b/rpython/tool/leakfinder.py --- a/rpython/tool/leakfinder.py +++ b/rpython/tool/leakfinder.py @@ -1,5 +1,10 @@ import sys, gc -import cStringIO +try: + import cStringIO +except ImportError as e: + if sys.version_info.major > 2: + raise RuntimeError('use python 2 to run tests') + raise import traceback # Track allocations to detect memory leaks. From pypy.commits at gmail.com Wed Feb 13 03:21:42 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 00:21:42 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: minimize rpython difference to unicode-utf8 Message-ID: <5c63d396.1c69fb81.eb706.b430@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95994:2ae56b36c5ee Date: 2019-02-13 00:51 +0200 http://bitbucket.org/pypy/pypy/changeset/2ae56b36c5ee/ Log: minimize rpython difference to unicode-utf8 diff --git a/rpython/rlib/_os_support.py b/rpython/rlib/_os_support.py --- a/rpython/rlib/_os_support.py +++ b/rpython/rlib/_os_support.py @@ -64,8 +64,6 @@ assert path is not None if isinstance(path, unicode): return path - elif isinstance(path, str): - raise RuntimeError('str given where unicode expected') else: return path.as_unicode() diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -1273,8 +1273,6 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) - assert isinstance(path1, unicode) - assert isinstance(path2, unicode) if not win32traits.MoveFileEx(path1, path2, 0): raise rwin32.lastSavedWindowsError() @@ -1285,8 +1283,6 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) - assert isinstance(path1, unicode) - assert isinstance(path2, unicode) ret = win32traits.MoveFileEx(path1, path2, win32traits.MOVEFILE_REPLACE_EXISTING) if not ret: diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -418,7 +418,6 @@ def has_surrogates(utf8): # XXX write a faster version maybe - # XXX does not check for trailing lone surrogates, needs tests for ch in Utf8StringIterator(utf8): if 0xD800 <= ch <= 0xDBFF: return True From pypy.commits at gmail.com Wed Feb 13 03:25:00 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 00:25:00 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: update TODO Message-ID: <5c63d45c.1c69fb81.7b853.0bd9@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r95995:271c50c1e3bf Date: 2019-02-13 10:24 +0200 http://bitbucket.org/pypy/pypy/changeset/271c50c1e3bf/ Log: update TODO diff --git a/TODO b/TODO --- a/TODO +++ b/TODO @@ -4,17 +4,16 @@ * think about cost of utf8 list strategy (CF) * revisit why runicode import str_decode_utf_8_impl needed instead of runicode import str_decode_utf_8 -* revisit all places where we do utf8.decode('utf-8'), they should work +* revisit remaining places in win32 where we do utf8.decode('utf-8'), they should work directly with utf8 (can be converted via runicode.str_decode_utf_8 as well) - rutf8.utf8_encode_mbcs - unicodehelper.fsencode - _winreg.interp_winreg * remove 'assert not isinstance(*, unicode) * add a flag that prevents support for unicode in rpython and enable it in PyPy (CF, Armin) -* remove asserts from _WIN32 paths in rlib.rposix.re{name,place} * convert all realunicode_w to unicode_w after we flush out all old uses of unicode_w -* view all uses of W_Unicode.text_w, right now it is exactly W_Unicode.utf8_w. +* review all uses of W_Unicode.text_w, right now it is exactly W_Unicode.utf8_w. It shoud only return valid utf8 (see 0be26dc39a59 which broke translation on win32 and failed tests on linux64). Then we can use it in places like _socket.interp_func.getaddrinfo instead of space.encode_unicode_object(w_port, From pypy.commits at gmail.com Wed Feb 13 06:25:52 2019 From: pypy.commits at gmail.com (arigo) Date: Wed, 13 Feb 2019 03:25:52 -0800 (PST) Subject: [pypy-commit] cffi default: Add comments about this test failing occasionally on Windows Message-ID: <5c63fec0.1c69fb81.b3c8d.4906@mx.google.com> Author: Armin Rigo Branch: Changeset: r3211:262a22d485b7 Date: 2019-02-13 12:26 +0100 http://bitbucket.org/cffi/cffi/changeset/262a22d485b7/ Log: Add comments about this test failing occasionally on Windows diff --git a/testing/embedding/test_thread.py b/testing/embedding/test_thread.py --- a/testing/embedding/test_thread.py +++ b/testing/embedding/test_thread.py @@ -56,6 +56,11 @@ for j in range(10): output = self._take_out(output, "adding 40 and 2 and 100\n") output = self._take_out(output, "adding 1000, 200, 30, 4\n") + # Windows note: fails occasionally, but it looks like a Windows + # bug somehow. If I add fprintf(stderr, "foo\n") after the + # final printf("done\n") in thread3-test.c, then it gets printed, + # even though the "done\n" part is very occasionally missing + # from the output. assert output == ("starting\n" "prepADD2\n" "done\n") From pypy.commits at gmail.com Wed Feb 13 06:37:36 2019 From: pypy.commits at gmail.com (arigo) Date: Wed, 13 Feb 2019 03:37:36 -0800 (PST) Subject: [pypy-commit] cffi release-1.12: make release branch Message-ID: <5c640180.1c69fb81.5d43a.d528@mx.google.com> Author: Armin Rigo Branch: release-1.12 Changeset: r3212:4c6a0cf68db6 Date: 2019-02-13 12:37 +0100 http://bitbucket.org/cffi/cffi/changeset/4c6a0cf68db6/ Log: make release branch From pypy.commits at gmail.com Wed Feb 13 06:49:23 2019 From: pypy.commits at gmail.com (arigo) Date: Wed, 13 Feb 2019 03:49:23 -0800 (PST) Subject: [pypy-commit] cffi default: Found out a way that appears to fix the problems on Windows Message-ID: <5c640443.1c69fb81.cba0c.47b3@mx.google.com> Author: Armin Rigo Branch: Changeset: r3213:7eba738b73fb Date: 2019-02-13 12:49 +0100 http://bitbucket.org/cffi/cffi/changeset/7eba738b73fb/ Log: Found out a way that appears to fix the problems on Windows diff --git a/testing/embedding/test_thread.py b/testing/embedding/test_thread.py --- a/testing/embedding/test_thread.py +++ b/testing/embedding/test_thread.py @@ -56,11 +56,6 @@ for j in range(10): output = self._take_out(output, "adding 40 and 2 and 100\n") output = self._take_out(output, "adding 1000, 200, 30, 4\n") - # Windows note: fails occasionally, but it looks like a Windows - # bug somehow. If I add fprintf(stderr, "foo\n") after the - # final printf("done\n") in thread3-test.c, then it gets printed, - # even though the "done\n" part is very occasionally missing - # from the output. assert output == ("starting\n" "prepADD2\n" "done\n") diff --git a/testing/embedding/thread3-test.c b/testing/embedding/thread3-test.c --- a/testing/embedding/thread3-test.c +++ b/testing/embedding/thread3-test.c @@ -51,5 +51,6 @@ assert(status == 0); } printf("done\n"); + fflush(stdout); /* this is occasionally needed on Windows */ return 0; } From pypy.commits at gmail.com Wed Feb 13 06:55:47 2019 From: pypy.commits at gmail.com (arigo) Date: Wed, 13 Feb 2019 03:55:47 -0800 (PST) Subject: [pypy-commit] cffi release-1.12: hg merge default Message-ID: <5c6405c3.1c69fb81.ed68b.630f@mx.google.com> Author: Armin Rigo Branch: release-1.12 Changeset: r3214:648850aaf0ee Date: 2019-02-13 12:56 +0100 http://bitbucket.org/cffi/cffi/changeset/648850aaf0ee/ Log: hg merge default diff --git a/testing/embedding/test_thread.py b/testing/embedding/test_thread.py --- a/testing/embedding/test_thread.py +++ b/testing/embedding/test_thread.py @@ -56,11 +56,6 @@ for j in range(10): output = self._take_out(output, "adding 40 and 2 and 100\n") output = self._take_out(output, "adding 1000, 200, 30, 4\n") - # Windows note: fails occasionally, but it looks like a Windows - # bug somehow. If I add fprintf(stderr, "foo\n") after the - # final printf("done\n") in thread3-test.c, then it gets printed, - # even though the "done\n" part is very occasionally missing - # from the output. assert output == ("starting\n" "prepADD2\n" "done\n") diff --git a/testing/embedding/thread3-test.c b/testing/embedding/thread3-test.c --- a/testing/embedding/thread3-test.c +++ b/testing/embedding/thread3-test.c @@ -51,5 +51,6 @@ assert(status == 0); } printf("done\n"); + fflush(stdout); /* this is occasionally needed on Windows */ return 0; } From pypy.commits at gmail.com Wed Feb 13 07:08:46 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 13 Feb 2019 04:08:46 -0800 (PST) Subject: [pypy-commit] pypy py3.6: seems we never updated idlelib with the 3.6 changes! Message-ID: <5c6408ce.1c69fb81.feff6.1afa@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r95996:9387f96a5518 Date: 2019-02-13 13:07 +0100 http://bitbucket.org/pypy/pypy/changeset/9387f96a5518/ Log: seems we never updated idlelib with the 3.6 changes! diff too long, truncating to 2000 out of 7060 lines diff --git a/lib-python/3/idlelib/autocomplete.py b/lib-python/3/idlelib/autocomplete.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/autocomplete.py @@ -0,0 +1,232 @@ +"""autocomplete.py - An IDLE extension for automatically completing names. + +This extension can complete either attribute names or file names. It can pop +a window with all available names, for the user to select from. +""" +import os +import string +import sys + +# These constants represent the two different types of completions. +# They must be defined here so autocomple_w can import them. +COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1) + +from idlelib import autocomplete_w +from idlelib.config import idleConf +from idlelib.hyperparser import HyperParser +import __main__ + +# This string includes all chars that may be in an identifier. +# TODO Update this here and elsewhere. +ID_CHARS = string.ascii_letters + string.digits + "_" + +SEPS = os.sep +if os.altsep: # e.g. '/' on Windows... + SEPS += os.altsep + + +class AutoComplete: + + menudefs = [ + ('edit', [ + ("Show Completions", "<>"), + ]) + ] + + popupwait = idleConf.GetOption("extensions", "AutoComplete", + "popupwait", type="int", default=0) + + def __init__(self, editwin=None): + self.editwin = editwin + if editwin is not None: # not in subprocess or test + self.text = editwin.text + self.autocompletewindow = None + # id of delayed call, and the index of the text insert when + # the delayed call was issued. If _delayed_completion_id is + # None, there is no delayed call. + self._delayed_completion_id = None + self._delayed_completion_index = None + + def _make_autocomplete_window(self): + return autocomplete_w.AutoCompleteWindow(self.text) + + def _remove_autocomplete_window(self, event=None): + if self.autocompletewindow: + self.autocompletewindow.hide_window() + self.autocompletewindow = None + + def force_open_completions_event(self, event): + """Happens when the user really wants to open a completion list, even + if a function call is needed. + """ + self.open_completions(True, False, True) + + def try_open_completions_event(self, event): + """Happens when it would be nice to open a completion list, but not + really necessary, for example after a dot, so function + calls won't be made. + """ + lastchar = self.text.get("insert-1c") + if lastchar == ".": + self._open_completions_later(False, False, False, + COMPLETE_ATTRIBUTES) + elif lastchar in SEPS: + self._open_completions_later(False, False, False, + COMPLETE_FILES) + + def autocomplete_event(self, event): + """Happens when the user wants to complete his word, and if necessary, + open a completion list after that (if there is more than one + completion) + """ + if hasattr(event, "mc_state") and event.mc_state or\ + not self.text.get("insert linestart", "insert").strip(): + # A modifier was pressed along with the tab or + # there is only previous whitespace on this line, so tab. + return None + if self.autocompletewindow and self.autocompletewindow.is_active(): + self.autocompletewindow.complete() + return "break" + else: + opened = self.open_completions(False, True, True) + return "break" if opened else None + + def _open_completions_later(self, *args): + self._delayed_completion_index = self.text.index("insert") + if self._delayed_completion_id is not None: + self.text.after_cancel(self._delayed_completion_id) + self._delayed_completion_id = \ + self.text.after(self.popupwait, self._delayed_open_completions, + *args) + + def _delayed_open_completions(self, *args): + self._delayed_completion_id = None + if self.text.index("insert") == self._delayed_completion_index: + self.open_completions(*args) + + def open_completions(self, evalfuncs, complete, userWantsWin, mode=None): + """Find the completions and create the AutoCompleteWindow. + Return True if successful (no syntax error or so found). + if complete is True, then if there's nothing to complete and no + start of completion, won't open completions and return False. + If mode is given, will open a completion list only in this mode. + """ + # Cancel another delayed call, if it exists. + if self._delayed_completion_id is not None: + self.text.after_cancel(self._delayed_completion_id) + self._delayed_completion_id = None + + hp = HyperParser(self.editwin, "insert") + curline = self.text.get("insert linestart", "insert") + i = j = len(curline) + if hp.is_in_string() and (not mode or mode==COMPLETE_FILES): + # Find the beginning of the string + # fetch_completions will look at the file system to determine whether the + # string value constitutes an actual file name + # XXX could consider raw strings here and unescape the string value if it's + # not raw. + self._remove_autocomplete_window() + mode = COMPLETE_FILES + # Find last separator or string start + while i and curline[i-1] not in "'\"" + SEPS: + i -= 1 + comp_start = curline[i:j] + j = i + # Find string start + while i and curline[i-1] not in "'\"": + i -= 1 + comp_what = curline[i:j] + elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES): + self._remove_autocomplete_window() + mode = COMPLETE_ATTRIBUTES + while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127): + i -= 1 + comp_start = curline[i:j] + if i and curline[i-1] == '.': + hp.set_index("insert-%dc" % (len(curline)-(i-1))) + comp_what = hp.get_expression() + if not comp_what or \ + (not evalfuncs and comp_what.find('(') != -1): + return None + else: + comp_what = "" + else: + return None + + if complete and not comp_what and not comp_start: + return None + comp_lists = self.fetch_completions(comp_what, mode) + if not comp_lists[0]: + return None + self.autocompletewindow = self._make_autocomplete_window() + return not self.autocompletewindow.show_window( + comp_lists, "insert-%dc" % len(comp_start), + complete, mode, userWantsWin) + + def fetch_completions(self, what, mode): + """Return a pair of lists of completions for something. The first list + is a sublist of the second. Both are sorted. + + If there is a Python subprocess, get the comp. list there. Otherwise, + either fetch_completions() is running in the subprocess itself or it + was called in an IDLE EditorWindow before any script had been run. + + The subprocess environment is that of the most recently run script. If + two unrelated modules are being edited some calltips in the current + module may be inoperative if the module was not the last to run. + """ + try: + rpcclt = self.editwin.flist.pyshell.interp.rpcclt + except: + rpcclt = None + if rpcclt: + return rpcclt.remotecall("exec", "get_the_completion_list", + (what, mode), {}) + else: + if mode == COMPLETE_ATTRIBUTES: + if what == "": + namespace = __main__.__dict__.copy() + namespace.update(__main__.__builtins__.__dict__) + bigl = eval("dir()", namespace) + bigl.sort() + if "__all__" in bigl: + smalll = sorted(eval("__all__", namespace)) + else: + smalll = [s for s in bigl if s[:1] != '_'] + else: + try: + entity = self.get_entity(what) + bigl = dir(entity) + bigl.sort() + if "__all__" in bigl: + smalll = sorted(entity.__all__) + else: + smalll = [s for s in bigl if s[:1] != '_'] + except: + return [], [] + + elif mode == COMPLETE_FILES: + if what == "": + what = "." + try: + expandedpath = os.path.expanduser(what) + bigl = os.listdir(expandedpath) + bigl.sort() + smalll = [s for s in bigl if s[:1] != '.'] + except OSError: + return [], [] + + if not smalll: + smalll = bigl + return smalll, bigl + + def get_entity(self, name): + """Lookup name in a namespace spanning sys.modules and __main.dict__""" + namespace = sys.modules.copy() + namespace.update(__main__.__dict__) + return eval(name, namespace) + + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_autocomplete', verbosity=2) diff --git a/lib-python/3/idlelib/autoexpand.py b/lib-python/3/idlelib/autoexpand.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/autoexpand.py @@ -0,0 +1,105 @@ +'''Complete the current word before the cursor with words in the editor. + +Each menu selection or shortcut key selection replaces the word with a +different word with the same prefix. The search for matches begins +before the target and moves toward the top of the editor. It then starts +after the cursor and moves down. It then returns to the original word and +the cycle starts again. + +Changing the current text line or leaving the cursor in a different +place before requesting the next selection causes AutoExpand to reset +its state. + +This is an extension file and there is only one instance of AutoExpand. +''' +import re +import string + +###$ event <> +###$ win +###$ unix + +class AutoExpand: + + menudefs = [ + ('edit', [ + ('E_xpand Word', '<>'), + ]), + ] + + wordchars = string.ascii_letters + string.digits + "_" + + def __init__(self, editwin): + self.text = editwin.text + self.bell = self.text.bell + self.state = None + + def expand_word_event(self, event): + "Replace the current word with the next expansion." + curinsert = self.text.index("insert") + curline = self.text.get("insert linestart", "insert lineend") + if not self.state: + words = self.getwords() + index = 0 + else: + words, index, insert, line = self.state + if insert != curinsert or line != curline: + words = self.getwords() + index = 0 + if not words: + self.bell() + return "break" + word = self.getprevword() + self.text.delete("insert - %d chars" % len(word), "insert") + newword = words[index] + index = (index + 1) % len(words) + if index == 0: + self.bell() # Warn we cycled around + self.text.insert("insert", newword) + curinsert = self.text.index("insert") + curline = self.text.get("insert linestart", "insert lineend") + self.state = words, index, curinsert, curline + return "break" + + def getwords(self): + "Return a list of words that match the prefix before the cursor." + word = self.getprevword() + if not word: + return [] + before = self.text.get("1.0", "insert wordstart") + wbefore = re.findall(r"\b" + word + r"\w+\b", before) + del before + after = self.text.get("insert wordend", "end") + wafter = re.findall(r"\b" + word + r"\w+\b", after) + del after + if not wbefore and not wafter: + return [] + words = [] + dict = {} + # search backwards through words before + wbefore.reverse() + for w in wbefore: + if dict.get(w): + continue + words.append(w) + dict[w] = w + # search onwards through words after + for w in wafter: + if dict.get(w): + continue + words.append(w) + dict[w] = w + words.append(word) + return words + + def getprevword(self): + "Return the word prefix before the cursor." + line = self.text.get("insert linestart", "insert") + i = len(line) + while i > 0 and line[i-1] in self.wordchars: + i = i-1 + return line[i:] + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2) diff --git a/lib-python/3/idlelib/calltips.py b/lib-python/3/idlelib/calltips.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/calltips.py @@ -0,0 +1,175 @@ +"""calltips.py - An IDLE Extension to Jog Your Memory + +Call Tips are floating windows which display function, class, and method +parameter and docstring information when you type an opening parenthesis, and +which disappear when you type a closing parenthesis. + +""" +import inspect +import re +import sys +import textwrap +import types + +from idlelib import calltip_w +from idlelib.hyperparser import HyperParser +import __main__ + +class CallTips: + + menudefs = [ + ('edit', [ + ("Show call tip", "<>"), + ]) + ] + + def __init__(self, editwin=None): + if editwin is None: # subprocess and test + self.editwin = None + else: + self.editwin = editwin + self.text = editwin.text + self.active_calltip = None + self._calltip_window = self._make_tk_calltip_window + + def close(self): + self._calltip_window = None + + def _make_tk_calltip_window(self): + # See __init__ for usage + return calltip_w.CallTip(self.text) + + def _remove_calltip_window(self, event=None): + if self.active_calltip: + self.active_calltip.hidetip() + self.active_calltip = None + + def force_open_calltip_event(self, event): + "The user selected the menu entry or hotkey, open the tip." + self.open_calltip(True) + + def try_open_calltip_event(self, event): + """Happens when it would be nice to open a CallTip, but not really + necessary, for example after an opening bracket, so function calls + won't be made. + """ + self.open_calltip(False) + + def refresh_calltip_event(self, event): + if self.active_calltip and self.active_calltip.is_active(): + self.open_calltip(False) + + def open_calltip(self, evalfuncs): + self._remove_calltip_window() + + hp = HyperParser(self.editwin, "insert") + sur_paren = hp.get_surrounding_brackets('(') + if not sur_paren: + return + hp.set_index(sur_paren[0]) + expression = hp.get_expression() + if not expression: + return + if not evalfuncs and (expression.find('(') != -1): + return + argspec = self.fetch_tip(expression) + if not argspec: + return + self.active_calltip = self._calltip_window() + self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1]) + + def fetch_tip(self, expression): + """Return the argument list and docstring of a function or class. + + If there is a Python subprocess, get the calltip there. Otherwise, + either this fetch_tip() is running in the subprocess or it was + called in an IDLE running without the subprocess. + + The subprocess environment is that of the most recently run script. If + two unrelated modules are being edited some calltips in the current + module may be inoperative if the module was not the last to run. + + To find methods, fetch_tip must be fed a fully qualified name. + + """ + try: + rpcclt = self.editwin.flist.pyshell.interp.rpcclt + except AttributeError: + rpcclt = None + if rpcclt: + return rpcclt.remotecall("exec", "get_the_calltip", + (expression,), {}) + else: + return get_argspec(get_entity(expression)) + +def get_entity(expression): + """Return the object corresponding to expression evaluated + in a namespace spanning sys.modules and __main.dict__. + """ + if expression: + namespace = sys.modules.copy() + namespace.update(__main__.__dict__) + try: + return eval(expression, namespace) + except BaseException: + # An uncaught exception closes idle, and eval can raise any + # exception, especially if user classes are involved. + return None + +# The following are used in get_argspec and some in tests +_MAX_COLS = 85 +_MAX_LINES = 5 # enough for bytes +_INDENT = ' '*4 # for wrapped signatures +_first_param = re.compile(r'(?<=\()\w*\,?\s*') +_default_callable_argspec = "See source or doc" + + +def get_argspec(ob): + '''Return a string describing the signature of a callable object, or ''. + + For Python-coded functions and methods, the first line is introspected. + Delete 'self' parameter for classes (.__init__) and bound methods. + The next lines are the first lines of the doc string up to the first + empty line or _MAX_LINES. For builtins, this typically includes + the arguments in addition to the return value. + ''' + argspec = "" + try: + ob_call = ob.__call__ + except BaseException: + return argspec + if isinstance(ob, type): + fob = ob.__init__ + elif isinstance(ob_call, types.MethodType): + fob = ob_call + else: + fob = ob + if isinstance(fob, (types.FunctionType, types.MethodType)): + argspec = inspect.formatargspec(*inspect.getfullargspec(fob)) + if (isinstance(ob, (type, types.MethodType)) or + isinstance(ob_call, types.MethodType)): + argspec = _first_param.sub("", argspec) + + lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT) + if len(argspec) > _MAX_COLS else [argspec] if argspec else []) + + if isinstance(ob_call, types.MethodType): + doc = ob_call.__doc__ + else: + doc = getattr(ob, "__doc__", "") + if doc: + for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]: + line = line.strip() + if not line: + break + if len(line) > _MAX_COLS: + line = line[: _MAX_COLS - 3] + '...' + lines.append(line) + argspec = '\n'.join(lines) + if not argspec: + argspec = _default_callable_argspec + return argspec + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_calltips', verbosity=2) diff --git a/lib-python/3/idlelib/codecontext.py b/lib-python/3/idlelib/codecontext.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/codecontext.py @@ -0,0 +1,178 @@ +"""codecontext - Extension to display the block context above the edit window + +Once code has scrolled off the top of a window, it can be difficult to +determine which block you are in. This extension implements a pane at the top +of each IDLE edit window which provides block structure hints. These hints are +the lines which contain the block opening keywords, e.g. 'if', for the +enclosing block. The number of hint lines is determined by the numlines +variable in the codecontext section of config-extensions.def. Lines which do +not open blocks are not shown in the context hints pane. + +""" +import re +from sys import maxsize as INFINITY + +import tkinter +from tkinter.constants import TOP, LEFT, X, W, SUNKEN + +from idlelib.config import idleConf + +BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for", + "if", "try", "while", "with"} +UPDATEINTERVAL = 100 # millisec +FONTUPDATEINTERVAL = 1000 # millisec + +getspacesfirstword =\ + lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups() + +class CodeContext: + menudefs = [('options', [('!Code Conte_xt', '<>')])] + context_depth = idleConf.GetOption("extensions", "CodeContext", + "numlines", type="int", default=3) + bgcolor = idleConf.GetOption("extensions", "CodeContext", + "bgcolor", type="str", default="LightGray") + fgcolor = idleConf.GetOption("extensions", "CodeContext", + "fgcolor", type="str", default="Black") + def __init__(self, editwin): + self.editwin = editwin + self.text = editwin.text + self.textfont = self.text["font"] + self.label = None + # self.info is a list of (line number, indent level, line text, block + # keyword) tuples providing the block structure associated with + # self.topvisible (the linenumber of the line displayed at the top of + # the edit window). self.info[0] is initialized as a 'dummy' line which + # starts the toplevel 'block' of the module. + self.info = [(0, -1, "", False)] + self.topvisible = 1 + visible = idleConf.GetOption("extensions", "CodeContext", + "visible", type="bool", default=False) + if visible: + self.toggle_code_context_event() + self.editwin.setvar('<>', True) + # Start two update cycles, one for context lines, one for font changes. + self.text.after(UPDATEINTERVAL, self.timer_event) + self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) + + def toggle_code_context_event(self, event=None): + if not self.label: + # Calculate the border width and horizontal padding required to + # align the context with the text in the main Text widget. + # + # All values are passed through getint(), since some + # values may be pixel objects, which can't simply be added to ints. + widgets = self.editwin.text, self.editwin.text_frame + # Calculate the required vertical padding + padx = 0 + for widget in widgets: + padx += widget.tk.getint(widget.pack_info()['padx']) + padx += widget.tk.getint(widget.cget('padx')) + # Calculate the required border width + border = 0 + for widget in widgets: + border += widget.tk.getint(widget.cget('border')) + self.label = tkinter.Label(self.editwin.top, + text="\n" * (self.context_depth - 1), + anchor=W, justify=LEFT, + font=self.textfont, + bg=self.bgcolor, fg=self.fgcolor, + width=1, #don't request more than we get + padx=padx, border=border, + relief=SUNKEN) + # Pack the label widget before and above the text_frame widget, + # thus ensuring that it will appear directly above text_frame + self.label.pack(side=TOP, fill=X, expand=False, + before=self.editwin.text_frame) + else: + self.label.destroy() + self.label = None + idleConf.SetOption("extensions", "CodeContext", "visible", + str(self.label is not None)) + idleConf.SaveUserCfgFiles() + + def get_line_info(self, linenum): + """Get the line indent value, text, and any block start keyword + + If the line does not start a block, the keyword value is False. + The indentation of empty lines (or comment lines) is INFINITY. + + """ + text = self.text.get("%d.0" % linenum, "%d.end" % linenum) + spaces, firstword = getspacesfirstword(text) + opener = firstword in BLOCKOPENERS and firstword + if len(text) == len(spaces) or text[len(spaces)] == '#': + indent = INFINITY + else: + indent = len(spaces) + return indent, text, opener + + def get_context(self, new_topvisible, stopline=1, stopindent=0): + """Get context lines, starting at new_topvisible and working backwards. + + Stop when stopline or stopindent is reached. Return a tuple of context + data and the indent level at the top of the region inspected. + + """ + assert stopline > 0 + lines = [] + # The indentation level we are currently in: + lastindent = INFINITY + # For a line to be interesting, it must begin with a block opening + # keyword, and have less indentation than lastindent. + for linenum in range(new_topvisible, stopline-1, -1): + indent, text, opener = self.get_line_info(linenum) + if indent < lastindent: + lastindent = indent + if opener in ("else", "elif"): + # We also show the if statement + lastindent += 1 + if opener and linenum < new_topvisible and indent >= stopindent: + lines.append((linenum, indent, text, opener)) + if lastindent <= stopindent: + break + lines.reverse() + return lines, lastindent + + def update_code_context(self): + """Update context information and lines visible in the context pane. + + """ + new_topvisible = int(self.text.index("@0,0").split('.')[0]) + if self.topvisible == new_topvisible: # haven't scrolled + return + if self.topvisible < new_topvisible: # scroll down + lines, lastindent = self.get_context(new_topvisible, + self.topvisible) + # retain only context info applicable to the region + # between topvisible and new_topvisible: + while self.info[-1][1] >= lastindent: + del self.info[-1] + elif self.topvisible > new_topvisible: # scroll up + stopindent = self.info[-1][1] + 1 + # retain only context info associated + # with lines above new_topvisible: + while self.info[-1][0] >= new_topvisible: + stopindent = self.info[-1][1] + del self.info[-1] + lines, lastindent = self.get_context(new_topvisible, + self.info[-1][0]+1, + stopindent) + self.info.extend(lines) + self.topvisible = new_topvisible + # empty lines in context pane: + context_strings = [""] * max(0, self.context_depth - len(self.info)) + # followed by the context hint lines: + context_strings += [x[2] for x in self.info[-self.context_depth:]] + self.label["text"] = '\n'.join(context_strings) + + def timer_event(self): + if self.label: + self.update_code_context() + self.text.after(UPDATEINTERVAL, self.timer_event) + + def font_timer_event(self): + newtextfont = self.text["font"] + if self.label and newtextfont != self.textfont: + self.textfont = newtextfont + self.label["font"] = self.textfont + self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) diff --git a/lib-python/3/idlelib/configdialog.py b/lib-python/3/idlelib/configdialog.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/configdialog.py @@ -0,0 +1,1467 @@ +"""IDLE Configuration Dialog: support user customization of IDLE by GUI + +Customize font faces, sizes, and colorization attributes. Set indentation +defaults. Customize keybindings. Colorization and keybindings can be +saved as user defined sets. Select startup options including shell/editor +and default window size. Define additional help sources. + +Note that tab width in IDLE is currently fixed at eight due to Tk issues. +Refer to comments in EditorWindow autoindent code for details. + +""" +from tkinter import * +from tkinter.ttk import Scrollbar +import tkinter.colorchooser as tkColorChooser +import tkinter.font as tkFont +import tkinter.messagebox as tkMessageBox + +from idlelib.config import idleConf +from idlelib.config_key import GetKeysDialog +from idlelib.dynoption import DynOptionMenu +from idlelib import macosx +from idlelib.query import SectionName, HelpSource +from idlelib.tabbedpages import TabbedPageSet +from idlelib.textview import view_text + +class ConfigDialog(Toplevel): + + def __init__(self, parent, title='', _htest=False, _utest=False): + """ + _htest - bool, change box location when running htest + _utest - bool, don't wait_window when running unittest + """ + Toplevel.__init__(self, parent) + self.parent = parent + if _htest: + parent.instance_dict = {} + self.wm_withdraw() + + self.configure(borderwidth=5) + self.title(title or 'IDLE Preferences') + self.geometry( + "+%d+%d" % (parent.winfo_rootx() + 20, + parent.winfo_rooty() + (30 if not _htest else 150))) + #Theme Elements. Each theme element key is its display name. + #The first value of the tuple is the sample area tag name. + #The second value is the display name list sort index. + self.themeElements={ + 'Normal Text': ('normal', '00'), + 'Python Keywords': ('keyword', '01'), + 'Python Definitions': ('definition', '02'), + 'Python Builtins': ('builtin', '03'), + 'Python Comments': ('comment', '04'), + 'Python Strings': ('string', '05'), + 'Selected Text': ('hilite', '06'), + 'Found Text': ('hit', '07'), + 'Cursor': ('cursor', '08'), + 'Editor Breakpoint': ('break', '09'), + 'Shell Normal Text': ('console', '10'), + 'Shell Error Text': ('error', '11'), + 'Shell Stdout Text': ('stdout', '12'), + 'Shell Stderr Text': ('stderr', '13'), + } + self.ResetChangedItems() #load initial values in changed items dict + self.CreateWidgets() + self.resizable(height=FALSE, width=FALSE) + self.transient(parent) + self.grab_set() + self.protocol("WM_DELETE_WINDOW", self.Cancel) + self.tabPages.focus_set() + #key bindings for this dialog + #self.bind('', self.Cancel) #dismiss dialog, no save + #self.bind('', self.Apply) #apply changes, save + #self.bind('', self.Help) #context help + self.LoadConfigs() + self.AttachVarCallbacks() #avoid callbacks during LoadConfigs + + if not _utest: + self.wm_deiconify() + self.wait_window() + + def CreateWidgets(self): + self.tabPages = TabbedPageSet(self, + page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General', + 'Extensions']) + self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH) + self.CreatePageFontTab() + self.CreatePageHighlight() + self.CreatePageKeys() + self.CreatePageGeneral() + self.CreatePageExtensions() + self.create_action_buttons().pack(side=BOTTOM) + + def create_action_buttons(self): + if macosx.isAquaTk(): + # Changing the default padding on OSX results in unreadable + # text in the buttons + paddingArgs = {} + else: + paddingArgs = {'padx':6, 'pady':3} + outer = Frame(self, pady=2) + buttons = Frame(outer, pady=2) + for txt, cmd in ( + ('Ok', self.Ok), + ('Apply', self.Apply), + ('Cancel', self.Cancel), + ('Help', self.Help)): + Button(buttons, text=txt, command=cmd, takefocus=FALSE, + **paddingArgs).pack(side=LEFT, padx=5) + # add space above buttons + Frame(outer, height=2, borderwidth=0).pack(side=TOP) + buttons.pack(side=BOTTOM) + return outer + + def CreatePageFontTab(self): + parent = self.parent + self.fontSize = StringVar(parent) + self.fontBold = BooleanVar(parent) + self.fontName = StringVar(parent) + self.spaceNum = IntVar(parent) + self.editFont = tkFont.Font(parent, ('courier', 10, 'normal')) + + ##widget creation + #body frame + frame = self.tabPages.pages['Fonts/Tabs'].frame + #body section frames + frameFont = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ') + frameIndent = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ') + #frameFont + frameFontName = Frame(frameFont) + frameFontParam = Frame(frameFont) + labelFontNameTitle = Label( + frameFontName, justify=LEFT, text='Font Face :') + self.listFontName = Listbox( + frameFontName, height=5, takefocus=FALSE, exportselection=FALSE) + self.listFontName.bind( + '', self.OnListFontButtonRelease) + scrollFont = Scrollbar(frameFontName) + scrollFont.config(command=self.listFontName.yview) + self.listFontName.config(yscrollcommand=scrollFont.set) + labelFontSizeTitle = Label(frameFontParam, text='Size :') + self.optMenuFontSize = DynOptionMenu( + frameFontParam, self.fontSize, None, command=self.SetFontSample) + checkFontBold = Checkbutton( + frameFontParam, variable=self.fontBold, onvalue=1, + offvalue=0, text='Bold', command=self.SetFontSample) + frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1) + self.labelFontSample = Label( + frameFontSample, justify=LEFT, font=self.editFont, + text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]') + #frameIndent + frameIndentSize = Frame(frameIndent) + labelSpaceNumTitle = Label( + frameIndentSize, justify=LEFT, + text='Python Standard: 4 Spaces!') + self.scaleSpaceNum = Scale( + frameIndentSize, variable=self.spaceNum, + orient='horizontal', tickinterval=2, from_=2, to=16) + + #widget packing + #body + frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y) + #frameFont + frameFontName.pack(side=TOP, padx=5, pady=5, fill=X) + frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X) + labelFontNameTitle.pack(side=TOP, anchor=W) + self.listFontName.pack(side=LEFT, expand=TRUE, fill=X) + scrollFont.pack(side=LEFT, fill=Y) + labelFontSizeTitle.pack(side=LEFT, anchor=W) + self.optMenuFontSize.pack(side=LEFT, anchor=W) + checkFontBold.pack(side=LEFT, anchor=W, padx=20) + frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + self.labelFontSample.pack(expand=TRUE, fill=BOTH) + #frameIndent + frameIndentSize.pack(side=TOP, fill=X) + labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5) + self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X) + return frame + + def CreatePageHighlight(self): + parent = self.parent + self.builtinTheme = StringVar(parent) + self.customTheme = StringVar(parent) + self.fgHilite = BooleanVar(parent) + self.colour = StringVar(parent) + self.fontName = StringVar(parent) + self.themeIsBuiltin = BooleanVar(parent) + self.highlightTarget = StringVar(parent) + + ##widget creation + #body frame + frame = self.tabPages.pages['Highlighting'].frame + #body section frames + frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Custom Highlighting ') + frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Highlighting Theme ') + #frameCustom + self.textHighlightSample=Text( + frameCustom, relief=SOLID, borderwidth=1, + font=('courier', 12, ''), cursor='hand2', width=21, height=11, + takefocus=FALSE, highlightthickness=0, wrap=NONE) + text=self.textHighlightSample + text.bind('', lambda e: 'break') + text.bind('', lambda e: 'break') + textAndTags=( + ('#you can click here', 'comment'), ('\n', 'normal'), + ('#to choose items', 'comment'), ('\n', 'normal'), + ('def', 'keyword'), (' ', 'normal'), + ('func', 'definition'), ('(param):\n ', 'normal'), + ('"""string"""', 'string'), ('\n var0 = ', 'normal'), + ("'string'", 'string'), ('\n var1 = ', 'normal'), + ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), + ("'found'", 'hit'), ('\n var3 = ', 'normal'), + ('list', 'builtin'), ('(', 'normal'), + ('None', 'keyword'), (')\n', 'normal'), + (' breakpoint("line")', 'break'), ('\n\n', 'normal'), + (' error ', 'error'), (' ', 'normal'), + ('cursor |', 'cursor'), ('\n ', 'normal'), + ('shell', 'console'), (' ', 'normal'), + ('stdout', 'stdout'), (' ', 'normal'), + ('stderr', 'stderr'), ('\n', 'normal')) + for txTa in textAndTags: + text.insert(END, txTa[0], txTa[1]) + for element in self.themeElements: + def tem(event, elem=element): + event.widget.winfo_toplevel().highlightTarget.set(elem) + text.tag_bind( + self.themeElements[element][0], '', tem) + text.config(state=DISABLED) + self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1) + frameFgBg = Frame(frameCustom) + buttonSetColour = Button( + self.frameColourSet, text='Choose Colour for :', + command=self.GetColour, highlightthickness=0) + self.optMenuHighlightTarget = DynOptionMenu( + self.frameColourSet, self.highlightTarget, None, + highlightthickness=0) #, command=self.SetHighlightTargetBinding + self.radioFg = Radiobutton( + frameFgBg, variable=self.fgHilite, value=1, + text='Foreground', command=self.SetColourSampleBinding) + self.radioBg=Radiobutton( + frameFgBg, variable=self.fgHilite, value=0, + text='Background', command=self.SetColourSampleBinding) + self.fgHilite.set(1) + buttonSaveCustomTheme = Button( + frameCustom, text='Save as New Custom Theme', + command=self.SaveAsNewTheme) + #frameTheme + labelTypeTitle = Label(frameTheme, text='Select : ') + self.radioThemeBuiltin = Radiobutton( + frameTheme, variable=self.themeIsBuiltin, value=1, + command=self.SetThemeType, text='a Built-in Theme') + self.radioThemeCustom = Radiobutton( + frameTheme, variable=self.themeIsBuiltin, value=0, + command=self.SetThemeType, text='a Custom Theme') + self.optMenuThemeBuiltin = DynOptionMenu( + frameTheme, self.builtinTheme, None, command=None) + self.optMenuThemeCustom=DynOptionMenu( + frameTheme, self.customTheme, None, command=None) + self.buttonDeleteCustomTheme=Button( + frameTheme, text='Delete Custom Theme', + command=self.DeleteCustomTheme) + self.new_custom_theme = Label(frameTheme, bd=2) + + ##widget packing + #body + frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y) + #frameCustom + self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) + frameFgBg.pack(side=TOP, padx=5, pady=0) + self.textHighlightSample.pack( + side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4) + self.optMenuHighlightTarget.pack( + side=TOP, expand=TRUE, fill=X, padx=8, pady=3) + self.radioFg.pack(side=LEFT, anchor=E) + self.radioBg.pack(side=RIGHT, anchor=W) + buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5) + #frameTheme + labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5) + self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5) + self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2) + self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5) + self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) + self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5) + self.new_custom_theme.pack(side=TOP, fill=X, pady=5) + return frame + + def CreatePageKeys(self): + parent = self.parent + self.bindingTarget = StringVar(parent) + self.builtinKeys = StringVar(parent) + self.customKeys = StringVar(parent) + self.keysAreBuiltin = BooleanVar(parent) + self.keyBinding = StringVar(parent) + + ##widget creation + #body frame + frame = self.tabPages.pages['Keys'].frame + #body section frames + frameCustom = LabelFrame( + frame, borderwidth=2, relief=GROOVE, + text=' Custom Key Bindings ') + frameKeySets = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Key Set ') + #frameCustom + frameTarget = Frame(frameCustom) + labelTargetTitle = Label(frameTarget, text='Action - Key(s)') + scrollTargetY = Scrollbar(frameTarget) + scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL) + self.listBindings = Listbox( + frameTarget, takefocus=FALSE, exportselection=FALSE) + self.listBindings.bind('', self.KeyBindingSelected) + scrollTargetY.config(command=self.listBindings.yview) + scrollTargetX.config(command=self.listBindings.xview) + self.listBindings.config(yscrollcommand=scrollTargetY.set) + self.listBindings.config(xscrollcommand=scrollTargetX.set) + self.buttonNewKeys = Button( + frameCustom, text='Get New Keys for Selection', + command=self.GetNewKeys, state=DISABLED) + #frameKeySets + frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0) + for i in range(2)] + self.radioKeysBuiltin = Radiobutton( + frames[0], variable=self.keysAreBuiltin, value=1, + command=self.SetKeysType, text='Use a Built-in Key Set') + self.radioKeysCustom = Radiobutton( + frames[0], variable=self.keysAreBuiltin, value=0, + command=self.SetKeysType, text='Use a Custom Key Set') + self.optMenuKeysBuiltin = DynOptionMenu( + frames[0], self.builtinKeys, None, command=None) + self.optMenuKeysCustom = DynOptionMenu( + frames[0], self.customKeys, None, command=None) + self.buttonDeleteCustomKeys = Button( + frames[1], text='Delete Custom Key Set', + command=self.DeleteCustomKeys) + buttonSaveCustomKeys = Button( + frames[1], text='Save as New Custom Key Set', + command=self.SaveAsNewKeySet) + self.new_custom_keys = Label(frames[0], bd=2) + + ##widget packing + #body + frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH) + #frameCustom + self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5) + frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + #frame target + frameTarget.columnconfigure(0, weight=1) + frameTarget.rowconfigure(1, weight=1) + labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W) + self.listBindings.grid(row=1, column=0, sticky=NSEW) + scrollTargetY.grid(row=1, column=1, sticky=NS) + scrollTargetX.grid(row=2, column=0, sticky=EW) + #frameKeySets + self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS) + self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS) + self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW) + self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW) + self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5) + self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) + buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) + frames[0].pack(side=TOP, fill=BOTH, expand=True) + frames[1].pack(side=TOP, fill=X, expand=True, pady=2) + return frame + + def CreatePageGeneral(self): + parent = self.parent + self.winWidth = StringVar(parent) + self.winHeight = StringVar(parent) + self.startupEdit = IntVar(parent) + self.autoSave = IntVar(parent) + self.encoding = StringVar(parent) + self.userHelpBrowser = BooleanVar(parent) + self.helpBrowser = StringVar(parent) + + #widget creation + #body + frame = self.tabPages.pages['General'].frame + #body section frames + frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Startup Preferences ') + frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Autosave Preferences ') + frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE) + frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Additional Help Sources ') + #frameRun + labelRunChoiceTitle = Label(frameRun, text='At Startup') + self.radioStartupEdit = Radiobutton( + frameRun, variable=self.startupEdit, value=1, + text="Open Edit Window") + self.radioStartupShell = Radiobutton( + frameRun, variable=self.startupEdit, value=0, + text='Open Shell Window') + #frameSave + labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ') + self.radioSaveAsk = Radiobutton( + frameSave, variable=self.autoSave, value=0, + text="Prompt to Save") + self.radioSaveAuto = Radiobutton( + frameSave, variable=self.autoSave, value=1, + text='No Prompt') + #frameWinSize + labelWinSizeTitle = Label( + frameWinSize, text='Initial Window Size (in characters)') + labelWinWidthTitle = Label(frameWinSize, text='Width') + self.entryWinWidth = Entry( + frameWinSize, textvariable=self.winWidth, width=3) + labelWinHeightTitle = Label(frameWinSize, text='Height') + self.entryWinHeight = Entry( + frameWinSize, textvariable=self.winHeight, width=3) + #frameHelp + frameHelpList = Frame(frameHelp) + frameHelpListButtons = Frame(frameHelpList) + scrollHelpList = Scrollbar(frameHelpList) + self.listHelp = Listbox( + frameHelpList, height=5, takefocus=FALSE, + exportselection=FALSE) + scrollHelpList.config(command=self.listHelp.yview) + self.listHelp.config(yscrollcommand=scrollHelpList.set) + self.listHelp.bind('', self.HelpSourceSelected) + self.buttonHelpListEdit = Button( + frameHelpListButtons, text='Edit', state=DISABLED, + width=8, command=self.HelpListItemEdit) + self.buttonHelpListAdd = Button( + frameHelpListButtons, text='Add', + width=8, command=self.HelpListItemAdd) + self.buttonHelpListRemove = Button( + frameHelpListButtons, text='Remove', state=DISABLED, + width=8, command=self.HelpListItemRemove) + + #widget packing + #body + frameRun.pack(side=TOP, padx=5, pady=5, fill=X) + frameSave.pack(side=TOP, padx=5, pady=5, fill=X) + frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X) + frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + #frameRun + labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5) + self.radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5) + #frameSave + labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5) + self.radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5) + #frameWinSize + labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + self.entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5) + labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5) + self.entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5) + labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5) + #frameHelp + frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y) + frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y) + self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) + self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5) + self.buttonHelpListAdd.pack(side=TOP, anchor=W) + self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5) + return frame + + def AttachVarCallbacks(self): + self.fontSize.trace_add('write', self.VarChanged_font) + self.fontName.trace_add('write', self.VarChanged_font) + self.fontBold.trace_add('write', self.VarChanged_font) + self.spaceNum.trace_add('write', self.VarChanged_spaceNum) + self.colour.trace_add('write', self.VarChanged_colour) + self.builtinTheme.trace_add('write', self.VarChanged_builtinTheme) + self.customTheme.trace_add('write', self.VarChanged_customTheme) + self.themeIsBuiltin.trace_add('write', self.VarChanged_themeIsBuiltin) + self.highlightTarget.trace_add('write', self.VarChanged_highlightTarget) + self.keyBinding.trace_add('write', self.VarChanged_keyBinding) + self.builtinKeys.trace_add('write', self.VarChanged_builtinKeys) + self.customKeys.trace_add('write', self.VarChanged_customKeys) + self.keysAreBuiltin.trace_add('write', self.VarChanged_keysAreBuiltin) + self.winWidth.trace_add('write', self.VarChanged_winWidth) + self.winHeight.trace_add('write', self.VarChanged_winHeight) + self.startupEdit.trace_add('write', self.VarChanged_startupEdit) + self.autoSave.trace_add('write', self.VarChanged_autoSave) + self.encoding.trace_add('write', self.VarChanged_encoding) + + def remove_var_callbacks(self): + "Remove callbacks to prevent memory leaks." + for var in ( + self.fontSize, self.fontName, self.fontBold, + self.spaceNum, self.colour, self.builtinTheme, + self.customTheme, self.themeIsBuiltin, self.highlightTarget, + self.keyBinding, self.builtinKeys, self.customKeys, + self.keysAreBuiltin, self.winWidth, self.winHeight, + self.startupEdit, self.autoSave, self.encoding,): + var.trace_remove('write', var.trace_info()[0][1]) + + def VarChanged_font(self, *params): + '''When one font attribute changes, save them all, as they are + not independent from each other. In particular, when we are + overriding the default font, we need to write out everything. + ''' + value = self.fontName.get() + self.AddChangedItem('main', 'EditorWindow', 'font', value) + value = self.fontSize.get() + self.AddChangedItem('main', 'EditorWindow', 'font-size', value) + value = self.fontBold.get() + self.AddChangedItem('main', 'EditorWindow', 'font-bold', value) + + def VarChanged_spaceNum(self, *params): + value = self.spaceNum.get() + self.AddChangedItem('main', 'Indent', 'num-spaces', value) + + def VarChanged_colour(self, *params): + self.OnNewColourSet() + + def VarChanged_builtinTheme(self, *params): + oldthemes = ('IDLE Classic', 'IDLE New') + value = self.builtinTheme.get() + if value not in oldthemes: + if idleConf.GetOption('main', 'Theme', 'name') not in oldthemes: + self.AddChangedItem('main', 'Theme', 'name', oldthemes[0]) + self.AddChangedItem('main', 'Theme', 'name2', value) + self.new_custom_theme.config(text='New theme, see Help', + fg='#500000') + else: + self.AddChangedItem('main', 'Theme', 'name', value) + self.AddChangedItem('main', 'Theme', 'name2', '') + self.new_custom_theme.config(text='', fg='black') + self.PaintThemeSample() + + def VarChanged_customTheme(self, *params): + value = self.customTheme.get() + if value != '- no custom themes -': + self.AddChangedItem('main', 'Theme', 'name', value) + self.PaintThemeSample() + + def VarChanged_themeIsBuiltin(self, *params): + value = self.themeIsBuiltin.get() + self.AddChangedItem('main', 'Theme', 'default', value) + if value: + self.VarChanged_builtinTheme() + else: + self.VarChanged_customTheme() + + def VarChanged_highlightTarget(self, *params): + self.SetHighlightTarget() + + def VarChanged_keyBinding(self, *params): + value = self.keyBinding.get() + keySet = self.customKeys.get() + event = self.listBindings.get(ANCHOR).split()[0] + if idleConf.IsCoreBinding(event): + #this is a core keybinding + self.AddChangedItem('keys', keySet, event, value) + else: #this is an extension key binding + extName = idleConf.GetExtnNameForEvent(event) + extKeybindSection = extName + '_cfgBindings' + self.AddChangedItem('extensions', extKeybindSection, event, value) + + def VarChanged_builtinKeys(self, *params): + oldkeys = ( + 'IDLE Classic Windows', + 'IDLE Classic Unix', + 'IDLE Classic Mac', + 'IDLE Classic OSX', + ) + value = self.builtinKeys.get() + if value not in oldkeys: + if idleConf.GetOption('main', 'Keys', 'name') not in oldkeys: + self.AddChangedItem('main', 'Keys', 'name', oldkeys[0]) + self.AddChangedItem('main', 'Keys', 'name2', value) + self.new_custom_keys.config(text='New key set, see Help', + fg='#500000') + else: + self.AddChangedItem('main', 'Keys', 'name', value) + self.AddChangedItem('main', 'Keys', 'name2', '') + self.new_custom_keys.config(text='', fg='black') + self.LoadKeysList(value) + + def VarChanged_customKeys(self, *params): + value = self.customKeys.get() + if value != '- no custom keys -': + self.AddChangedItem('main', 'Keys', 'name', value) + self.LoadKeysList(value) + + def VarChanged_keysAreBuiltin(self, *params): + value = self.keysAreBuiltin.get() + self.AddChangedItem('main', 'Keys', 'default', value) + if value: + self.VarChanged_builtinKeys() + else: + self.VarChanged_customKeys() + + def VarChanged_winWidth(self, *params): + value = self.winWidth.get() + self.AddChangedItem('main', 'EditorWindow', 'width', value) + + def VarChanged_winHeight(self, *params): + value = self.winHeight.get() + self.AddChangedItem('main', 'EditorWindow', 'height', value) + + def VarChanged_startupEdit(self, *params): + value = self.startupEdit.get() + self.AddChangedItem('main', 'General', 'editor-on-startup', value) + + def VarChanged_autoSave(self, *params): + value = self.autoSave.get() + self.AddChangedItem('main', 'General', 'autosave', value) + + def VarChanged_encoding(self, *params): + value = self.encoding.get() + self.AddChangedItem('main', 'EditorWindow', 'encoding', value) + + def ResetChangedItems(self): + #When any config item is changed in this dialog, an entry + #should be made in the relevant section (config type) of this + #dictionary. The key should be the config file section name and the + #value a dictionary, whose key:value pairs are item=value pairs for + #that config file section. + self.changedItems = {'main':{}, 'highlight':{}, 'keys':{}, + 'extensions':{}} + + def AddChangedItem(self, typ, section, item, value): + value = str(value) #make sure we use a string + if section not in self.changedItems[typ]: + self.changedItems[typ][section] = {} + self.changedItems[typ][section][item] = value + + def GetDefaultItems(self): + dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} + for configType in dItems: + sections = idleConf.GetSectionList('default', configType) + for section in sections: + dItems[configType][section] = {} + options = idleConf.defaultCfg[configType].GetOptionList(section) + for option in options: + dItems[configType][section][option] = ( + idleConf.defaultCfg[configType].Get(section, option)) + return dItems + + def SetThemeType(self): + if self.themeIsBuiltin.get(): + self.optMenuThemeBuiltin.config(state=NORMAL) + self.optMenuThemeCustom.config(state=DISABLED) + self.buttonDeleteCustomTheme.config(state=DISABLED) + else: + self.optMenuThemeBuiltin.config(state=DISABLED) + self.radioThemeCustom.config(state=NORMAL) + self.optMenuThemeCustom.config(state=NORMAL) + self.buttonDeleteCustomTheme.config(state=NORMAL) + + def SetKeysType(self): + if self.keysAreBuiltin.get(): + self.optMenuKeysBuiltin.config(state=NORMAL) + self.optMenuKeysCustom.config(state=DISABLED) + self.buttonDeleteCustomKeys.config(state=DISABLED) + else: + self.optMenuKeysBuiltin.config(state=DISABLED) + self.radioKeysCustom.config(state=NORMAL) + self.optMenuKeysCustom.config(state=NORMAL) + self.buttonDeleteCustomKeys.config(state=NORMAL) + + def GetNewKeys(self): + listIndex = self.listBindings.index(ANCHOR) + binding = self.listBindings.get(listIndex) + bindName = binding.split()[0] #first part, up to first space + if self.keysAreBuiltin.get(): + currentKeySetName = self.builtinKeys.get() + else: + currentKeySetName = self.customKeys.get() + currentBindings = idleConf.GetCurrentKeySet() + if currentKeySetName in self.changedItems['keys']: #unsaved changes + keySetChanges = self.changedItems['keys'][currentKeySetName] + for event in keySetChanges: + currentBindings[event] = keySetChanges[event].split() + currentKeySequences = list(currentBindings.values()) + newKeys = GetKeysDialog(self, 'Get New Keys', bindName, + currentKeySequences).result + if newKeys: #new keys were specified + if self.keysAreBuiltin.get(): #current key set is a built-in + message = ('Your changes will be saved as a new Custom Key Set.' + ' Enter a name for your new Custom Key Set below.') + newKeySet = self.GetNewKeysName(message) + if not newKeySet: #user cancelled custom key set creation + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + return + else: #create new custom key set based on previously active key set + self.CreateNewKeySet(newKeySet) + self.listBindings.delete(listIndex) + self.listBindings.insert(listIndex, bindName+' - '+newKeys) + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + self.keyBinding.set(newKeys) + else: + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + + def GetNewKeysName(self, message): + usedNames = (idleConf.GetSectionList('user', 'keys') + + idleConf.GetSectionList('default', 'keys')) + newKeySet = SectionName( + self, 'New Custom Key Set', message, usedNames).result + return newKeySet + + def SaveAsNewKeySet(self): + newKeysName = self.GetNewKeysName('New Key Set Name:') + if newKeysName: + self.CreateNewKeySet(newKeysName) + + def KeyBindingSelected(self, event): + self.buttonNewKeys.config(state=NORMAL) + + def CreateNewKeySet(self, newKeySetName): + #creates new custom key set based on the previously active key set, + #and makes the new key set active + if self.keysAreBuiltin.get(): + prevKeySetName = self.builtinKeys.get() + else: + prevKeySetName = self.customKeys.get() + prevKeys = idleConf.GetCoreKeys(prevKeySetName) + newKeys = {} + for event in prevKeys: #add key set to changed items + eventName = event[2:-2] #trim off the angle brackets + binding = ' '.join(prevKeys[event]) + newKeys[eventName] = binding + #handle any unsaved changes to prev key set + if prevKeySetName in self.changedItems['keys']: + keySetChanges = self.changedItems['keys'][prevKeySetName] + for event in keySetChanges: + newKeys[event] = keySetChanges[event] + #save the new theme + self.SaveNewKeySet(newKeySetName, newKeys) + #change gui over to the new key set + customKeyList = idleConf.GetSectionList('user', 'keys') + customKeyList.sort() + self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName) + self.keysAreBuiltin.set(0) + self.SetKeysType() + + def LoadKeysList(self, keySetName): + reselect = 0 + newKeySet = 0 + if self.listBindings.curselection(): + reselect = 1 + listIndex = self.listBindings.index(ANCHOR) + keySet = idleConf.GetKeySet(keySetName) + bindNames = list(keySet.keys()) + bindNames.sort() + self.listBindings.delete(0, END) + for bindName in bindNames: + key = ' '.join(keySet[bindName]) #make key(s) into a string + bindName = bindName[2:-2] #trim off the angle brackets + if keySetName in self.changedItems['keys']: + #handle any unsaved changes to this key set + if bindName in self.changedItems['keys'][keySetName]: + key = self.changedItems['keys'][keySetName][bindName] + self.listBindings.insert(END, bindName+' - '+key) + if reselect: + self.listBindings.see(listIndex) + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + + def DeleteCustomKeys(self): + keySetName=self.customKeys.get() + delmsg = 'Are you sure you wish to delete the key set %r ?' + if not tkMessageBox.askyesno( + 'Delete Key Set', delmsg % keySetName, parent=self): + return + self.DeactivateCurrentConfig() + #remove key set from config + idleConf.userCfg['keys'].remove_section(keySetName) + if keySetName in self.changedItems['keys']: + del(self.changedItems['keys'][keySetName]) + #write changes + idleConf.userCfg['keys'].Save() + #reload user key set list + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + if not itemList: + self.radioKeysCustom.config(state=DISABLED) + self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -') + else: + self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) + #revert to default key set + self.keysAreBuiltin.set(idleConf.defaultCfg['main'] + .Get('Keys', 'default')) + self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name') + or idleConf.default_keys()) + #user can't back out of these changes, they must be applied now + self.SaveAllChangedConfigs() + self.ActivateConfigChanges() + self.SetKeysType() + + def DeleteCustomTheme(self): + themeName = self.customTheme.get() + delmsg = 'Are you sure you wish to delete the theme %r ?' + if not tkMessageBox.askyesno( + 'Delete Theme', delmsg % themeName, parent=self): + return + self.DeactivateCurrentConfig() + #remove theme from config + idleConf.userCfg['highlight'].remove_section(themeName) + if themeName in self.changedItems['highlight']: + del(self.changedItems['highlight'][themeName]) + #write changes + idleConf.userCfg['highlight'].Save() + #reload user theme list + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + if not itemList: + self.radioThemeCustom.config(state=DISABLED) + self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -') + else: + self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) + #revert to default theme + self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) + self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) + #user can't back out of these changes, they must be applied now + self.SaveAllChangedConfigs() + self.ActivateConfigChanges() + self.SetThemeType() + + def GetColour(self): + target = self.highlightTarget.get() + prevColour = self.frameColourSet.cget('bg') + rgbTuplet, colourString = tkColorChooser.askcolor( + parent=self, title='Pick new colour for : '+target, + initialcolor=prevColour) + if colourString and (colourString != prevColour): + #user didn't cancel, and they chose a new colour + if self.themeIsBuiltin.get(): #current theme is a built-in + message = ('Your changes will be saved as a new Custom Theme. ' + 'Enter a name for your new Custom Theme below.') + newTheme = self.GetNewThemeName(message) + if not newTheme: #user cancelled custom theme creation + return + else: #create new custom theme based on previously active theme + self.CreateNewTheme(newTheme) + self.colour.set(colourString) + else: #current theme is user defined + self.colour.set(colourString) + + def OnNewColourSet(self): + newColour=self.colour.get() + self.frameColourSet.config(bg=newColour) #set sample + plane ='foreground' if self.fgHilite.get() else 'background' + sampleElement = self.themeElements[self.highlightTarget.get()][0] + self.textHighlightSample.tag_config(sampleElement, **{plane:newColour}) + theme = self.customTheme.get() + themeElement = sampleElement + '-' + plane + self.AddChangedItem('highlight', theme, themeElement, newColour) + + def GetNewThemeName(self, message): + usedNames = (idleConf.GetSectionList('user', 'highlight') + + idleConf.GetSectionList('default', 'highlight')) + newTheme = SectionName( + self, 'New Custom Theme', message, usedNames).result + return newTheme + + def SaveAsNewTheme(self): + newThemeName = self.GetNewThemeName('New Theme Name:') + if newThemeName: + self.CreateNewTheme(newThemeName) + + def CreateNewTheme(self, newThemeName): + #creates new custom theme based on the previously active theme, + #and makes the new theme active + if self.themeIsBuiltin.get(): + themeType = 'default' + themeName = self.builtinTheme.get() + else: + themeType = 'user' + themeName = self.customTheme.get() + newTheme = idleConf.GetThemeDict(themeType, themeName) + #apply any of the old theme's unsaved changes to the new theme + if themeName in self.changedItems['highlight']: + themeChanges = self.changedItems['highlight'][themeName] + for element in themeChanges: + newTheme[element] = themeChanges[element] + #save the new theme + self.SaveNewTheme(newThemeName, newTheme) + #change gui over to the new theme + customThemeList = idleConf.GetSectionList('user', 'highlight') + customThemeList.sort() + self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName) + self.themeIsBuiltin.set(0) + self.SetThemeType() + + def OnListFontButtonRelease(self, event): + font = self.listFontName.get(ANCHOR) + self.fontName.set(font.lower()) + self.SetFontSample() + + def SetFontSample(self, event=None): + fontName = self.fontName.get() + fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL + newFont = (fontName, self.fontSize.get(), fontWeight) + self.labelFontSample.config(font=newFont) + self.textHighlightSample.configure(font=newFont) + + def SetHighlightTarget(self): + if self.highlightTarget.get() == 'Cursor': #bg not possible + self.radioFg.config(state=DISABLED) + self.radioBg.config(state=DISABLED) + self.fgHilite.set(1) + else: #both fg and bg can be set + self.radioFg.config(state=NORMAL) + self.radioBg.config(state=NORMAL) + self.fgHilite.set(1) + self.SetColourSample() + + def SetColourSampleBinding(self, *args): + self.SetColourSample() + + def SetColourSample(self): + #set the colour smaple area + tag = self.themeElements[self.highlightTarget.get()][0] + plane = 'foreground' if self.fgHilite.get() else 'background' + colour = self.textHighlightSample.tag_cget(tag, plane) + self.frameColourSet.config(bg=colour) + + def PaintThemeSample(self): + if self.themeIsBuiltin.get(): #a default theme + theme = self.builtinTheme.get() + else: #a user theme + theme = self.customTheme.get() + for elementTitle in self.themeElements: + element = self.themeElements[elementTitle][0] + colours = idleConf.GetHighlight(theme, element) + if element == 'cursor': #cursor sample needs special painting + colours['background'] = idleConf.GetHighlight( + theme, 'normal', fgBg='bg') + #handle any unsaved changes to this theme + if theme in self.changedItems['highlight']: + themeDict = self.changedItems['highlight'][theme] + if element + '-foreground' in themeDict: + colours['foreground'] = themeDict[element + '-foreground'] + if element + '-background' in themeDict: + colours['background'] = themeDict[element + '-background'] + self.textHighlightSample.tag_config(element, **colours) + self.SetColourSample() + + def HelpSourceSelected(self, event): + self.SetHelpListButtonStates() + + def SetHelpListButtonStates(self): + if self.listHelp.size() < 1: #no entries in list + self.buttonHelpListEdit.config(state=DISABLED) + self.buttonHelpListRemove.config(state=DISABLED) + else: #there are some entries + if self.listHelp.curselection(): #there currently is a selection + self.buttonHelpListEdit.config(state=NORMAL) + self.buttonHelpListRemove.config(state=NORMAL) + else: #there currently is not a selection + self.buttonHelpListEdit.config(state=DISABLED) + self.buttonHelpListRemove.config(state=DISABLED) + + def HelpListItemAdd(self): + helpSource = HelpSource(self, 'New Help Source', + ).result + if helpSource: + self.userHelpList.append((helpSource[0], helpSource[1])) + self.listHelp.insert(END, helpSource[0]) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def HelpListItemEdit(self): + itemIndex = self.listHelp.index(ANCHOR) + helpSource = self.userHelpList[itemIndex] + newHelpSource = HelpSource( + self, 'Edit Help Source', + menuitem=helpSource[0], + filepath=helpSource[1], + ).result + if newHelpSource and newHelpSource != helpSource: + self.userHelpList[itemIndex] = newHelpSource + self.listHelp.delete(itemIndex) + self.listHelp.insert(itemIndex, newHelpSource[0]) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def HelpListItemRemove(self): + itemIndex = self.listHelp.index(ANCHOR) + del(self.userHelpList[itemIndex]) + self.listHelp.delete(itemIndex) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def UpdateUserHelpChangedItems(self): + "Clear and rebuild the HelpFiles section in self.changedItems" + self.changedItems['main']['HelpFiles'] = {} + for num in range(1, len(self.userHelpList) + 1): + self.AddChangedItem( + 'main', 'HelpFiles', str(num), + ';'.join(self.userHelpList[num-1][:2])) + + def LoadFontCfg(self): + ##base editor font selection list + fonts = list(tkFont.families(self)) + fonts.sort() + for font in fonts: + self.listFontName.insert(END, font) + configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow') + fontName = configuredFont[0].lower() + fontSize = configuredFont[1] + fontBold = configuredFont[2]=='bold' + self.fontName.set(fontName) + lc_fonts = [s.lower() for s in fonts] + try: + currentFontIndex = lc_fonts.index(fontName) + self.listFontName.see(currentFontIndex) + self.listFontName.select_set(currentFontIndex) + self.listFontName.select_anchor(currentFontIndex) + except ValueError: + pass + ##font size dropdown + self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13', + '14', '16', '18', '20', '22', + '25', '29', '34', '40'), fontSize ) + ##fontWeight + self.fontBold.set(fontBold) + ##font sample + self.SetFontSample() + + def LoadTabCfg(self): + ##indent sizes + spaceNum = idleConf.GetOption( + 'main', 'Indent', 'num-spaces', default=4, type='int') + self.spaceNum.set(spaceNum) + + def LoadThemeCfg(self): + ##current theme type radiobutton + self.themeIsBuiltin.set(idleConf.GetOption( + 'main', 'Theme', 'default', type='bool', default=1)) + ##currently set theme + currentOption = idleConf.CurrentTheme() + ##load available theme option menus + if self.themeIsBuiltin.get(): #default theme selected + itemList = idleConf.GetSectionList('default', 'highlight') + itemList.sort() + self.optMenuThemeBuiltin.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + if not itemList: + self.radioThemeCustom.config(state=DISABLED) + self.customTheme.set('- no custom themes -') + else: + self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) + else: #user theme selected + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + self.optMenuThemeCustom.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('default', 'highlight') + itemList.sort() + self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0]) + self.SetThemeType() + ##load theme element option menu + themeNames = list(self.themeElements.keys()) + themeNames.sort(key=lambda x: self.themeElements[x][1]) + self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0]) + self.PaintThemeSample() + self.SetHighlightTarget() + + def LoadKeyCfg(self): + ##current keys type radiobutton + self.keysAreBuiltin.set(idleConf.GetOption( + 'main', 'Keys', 'default', type='bool', default=1)) + ##currently set keys + currentOption = idleConf.CurrentKeys() + ##load available keyset option menus + if self.keysAreBuiltin.get(): #default theme selected + itemList = idleConf.GetSectionList('default', 'keys') + itemList.sort() + self.optMenuKeysBuiltin.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + if not itemList: + self.radioKeysCustom.config(state=DISABLED) + self.customKeys.set('- no custom keys -') + else: + self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) + else: #user key set selected + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + self.optMenuKeysCustom.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('default', 'keys') + itemList.sort() + self.optMenuKeysBuiltin.SetMenu(itemList, idleConf.default_keys()) + self.SetKeysType() + ##load keyset element list + keySetName = idleConf.CurrentKeys() + self.LoadKeysList(keySetName) + + def LoadGeneralCfg(self): + #startup state + self.startupEdit.set(idleConf.GetOption( + 'main', 'General', 'editor-on-startup', default=1, type='bool')) + #autosave state + self.autoSave.set(idleConf.GetOption( + 'main', 'General', 'autosave', default=0, type='bool')) + #initial window size + self.winWidth.set(idleConf.GetOption( + 'main', 'EditorWindow', 'width', type='int')) + self.winHeight.set(idleConf.GetOption( + 'main', 'EditorWindow', 'height', type='int')) + # default source encoding + self.encoding.set(idleConf.GetOption( + 'main', 'EditorWindow', 'encoding', default='none')) + # additional help sources + self.userHelpList = idleConf.GetAllExtraHelpSourcesList() + for helpItem in self.userHelpList: + self.listHelp.insert(END, helpItem[0]) + self.SetHelpListButtonStates() + + def LoadConfigs(self): + """ + load configuration from default and user config files and populate + the widgets on the config dialog pages. + """ + ### fonts / tabs page + self.LoadFontCfg() + self.LoadTabCfg() + ### highlighting page + self.LoadThemeCfg() + ### keys page + self.LoadKeyCfg() + ### general page + self.LoadGeneralCfg() + # note: extension page handled separately + + def SaveNewKeySet(self, keySetName, keySet): + """ + save a newly created core key set. + keySetName - string, the name of the new key set + keySet - dictionary containing the new key set + """ + if not idleConf.userCfg['keys'].has_section(keySetName): + idleConf.userCfg['keys'].add_section(keySetName) + for event in keySet: + value = keySet[event] + idleConf.userCfg['keys'].SetOption(keySetName, event, value) + + def SaveNewTheme(self, themeName, theme): + """ + save a newly created theme. + themeName - string, the name of the new theme + theme - dictionary containing the new theme + """ + if not idleConf.userCfg['highlight'].has_section(themeName): + idleConf.userCfg['highlight'].add_section(themeName) + for element in theme: + value = theme[element] + idleConf.userCfg['highlight'].SetOption(themeName, element, value) + + def SetUserValue(self, configType, section, item, value): + if idleConf.defaultCfg[configType].has_option(section, item): + if idleConf.defaultCfg[configType].Get(section, item) == value: + #the setting equals a default setting, remove it from user cfg + return idleConf.userCfg[configType].RemoveOption(section, item) + #if we got here set the option + return idleConf.userCfg[configType].SetOption(section, item, value) + + def SaveAllChangedConfigs(self): + "Save configuration changes to the user config file." + idleConf.userCfg['main'].Save() + for configType in self.changedItems: + cfgTypeHasChanges = False + for section in self.changedItems[configType]: + if section == 'HelpFiles': + #this section gets completely replaced + idleConf.userCfg['main'].remove_section('HelpFiles') + cfgTypeHasChanges = True + for item in self.changedItems[configType][section]: + value = self.changedItems[configType][section][item] + if self.SetUserValue(configType, section, item, value): + cfgTypeHasChanges = True + if cfgTypeHasChanges: + idleConf.userCfg[configType].Save() + for configType in ['keys', 'highlight']: + # save these even if unchanged! + idleConf.userCfg[configType].Save() + self.ResetChangedItems() #clear the changed items dict + self.save_all_changed_extensions() # uses a different mechanism + + def DeactivateCurrentConfig(self): + #Before a config is saved, some cleanup of current + #config must be done - remove the previous keybindings + winInstances = self.parent.instance_dict.keys() + for instance in winInstances: + instance.RemoveKeybindings() + + def ActivateConfigChanges(self): + "Dynamically apply configuration changes" + winInstances = self.parent.instance_dict.keys() + for instance in winInstances: + instance.ResetColorizer() + instance.ResetFont() + instance.set_notabs_indentwidth() + instance.ApplyKeybindings() + instance.reset_help_menu_entries() + + def Cancel(self): + self.destroy() + + def Ok(self): + self.Apply() + self.destroy() + + def Apply(self): + self.DeactivateCurrentConfig() + self.SaveAllChangedConfigs() + self.ActivateConfigChanges() + + def Help(self): + page = self.tabPages._current_page + view_text(self, title='Help for IDLE preferences', + text=help_common+help_pages.get(page, '')) + + def CreatePageExtensions(self): + """Part of the config dialog used for configuring IDLE extensions. + + This code is generic - it works for any and all IDLE extensions. + + IDLE extensions save their configuration options using idleConf. + This code reads the current configuration using idleConf, supplies a + GUI interface to change the configuration values, and saves the + changes using idleConf. + + Not all changes take effect immediately - some may require restarting IDLE. + This depends on each extension's implementation. + + All values are treated as text, and it is up to the user to supply + reasonable values. The only exception to this are the 'enable*' options, + which are boolean, and can be toggled with a True/False button. + """ + parent = self.parent + frame = self.tabPages.pages['Extensions'].frame + self.ext_defaultCfg = idleConf.defaultCfg['extensions'] + self.ext_userCfg = idleConf.userCfg['extensions'] + self.is_int = self.register(is_int) + self.load_extensions() + # create widgets - a listbox shows all available extensions, with the + # controls for the extension selected in the listbox to the right + self.extension_names = StringVar(self) + frame.rowconfigure(0, weight=1) + frame.columnconfigure(2, weight=1) + self.extension_list = Listbox(frame, listvariable=self.extension_names, + selectmode='browse') + self.extension_list.bind('<>', self.extension_selected) + scroll = Scrollbar(frame, command=self.extension_list.yview) + self.extension_list.yscrollcommand=scroll.set + self.details_frame = LabelFrame(frame, width=250, height=250) + self.extension_list.grid(column=0, row=0, sticky='nws') + scroll.grid(column=1, row=0, sticky='ns') + self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0]) + frame.configure(padx=10, pady=10) + self.config_frame = {} + self.current_extension = None + + self.outerframe = self # TEMPORARY + self.tabbed_page_set = self.extension_list # TEMPORARY + + # create the frame holding controls for each extension + ext_names = '' + for ext_name in sorted(self.extensions): + self.create_extension_frame(ext_name) + ext_names = ext_names + '{' + ext_name + '} ' + self.extension_names.set(ext_names) + self.extension_list.selection_set(0) + self.extension_selected(None) + + def load_extensions(self): + "Fill self.extensions with data from the default and user configs." + self.extensions = {} + for ext_name in idleConf.GetExtensions(active_only=False): + self.extensions[ext_name] = [] + + for ext_name in self.extensions: + opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name)) + + # bring 'enable' options to the beginning of the list + enables = [opt_name for opt_name in opt_list From pypy.commits at gmail.com Wed Feb 13 08:42:49 2019 From: pypy.commits at gmail.com (arigo) Date: Wed, 13 Feb 2019 05:42:49 -0800 (PST) Subject: [pypy-commit] cffi release-1.12: update md5/sha Message-ID: <5c641ed9.1c69fb81.45b91.d143@mx.google.com> Author: Armin Rigo Branch: release-1.12 Changeset: r3215:170fb3d1f0b3 Date: 2019-02-13 14:42 +0100 http://bitbucket.org/cffi/cffi/changeset/170fb3d1f0b3/ Log: update md5/sha diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -54,11 +54,11 @@ * Checksums of the "source" package version 1.12.0: - - MD5: ... + - MD5: 8e3c92fb5a7b5e32390882dd8ed32275 - - SHA: ... + - SHA: 270cc6e3b4873c617647d05aaa6fd1302a1f0071 - - SHA256: ... + - SHA256: 08090454ff236239e583a9119d0502a6b9817594c0a3714dd1d8593f2350ba11 * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` From pypy.commits at gmail.com Wed Feb 13 08:42:51 2019 From: pypy.commits at gmail.com (arigo) Date: Wed, 13 Feb 2019 05:42:51 -0800 (PST) Subject: [pypy-commit] cffi release-1.12: Added tag v1.12.0 for changeset 170fb3d1f0b3 Message-ID: <5c641edb.1c69fb81.13425.7b1e@mx.google.com> Author: Armin Rigo Branch: release-1.12 Changeset: r3216:36b42008e2fc Date: 2019-02-13 14:42 +0100 http://bitbucket.org/cffi/cffi/changeset/36b42008e2fc/ Log: Added tag v1.12.0 for changeset 170fb3d1f0b3 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -17,3 +17,4 @@ 1aafccb9255dbb36f8e785b65624e39628cee63a v1.11.3 e08abd4703fef26f036e82255f4070277a9e03bd v1.11.4 48416163071ed48300c3ae4358cc7fd841912413 v1.11.5 +170fb3d1f0b30d32c9794ea40dbb51836dc7d947 v1.12.0 From pypy.commits at gmail.com Wed Feb 13 08:42:52 2019 From: pypy.commits at gmail.com (arigo) Date: Wed, 13 Feb 2019 05:42:52 -0800 (PST) Subject: [pypy-commit] cffi default: hg merge release-1.12 Message-ID: <5c641edc.1c69fb81.934f3.af38@mx.google.com> Author: Armin Rigo Branch: Changeset: r3217:6ab3ee780c72 Date: 2019-02-13 14:43 +0100 http://bitbucket.org/cffi/cffi/changeset/6ab3ee780c72/ Log: hg merge release-1.12 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -17,3 +17,4 @@ 1aafccb9255dbb36f8e785b65624e39628cee63a v1.11.3 e08abd4703fef26f036e82255f4070277a9e03bd v1.11.4 48416163071ed48300c3ae4358cc7fd841912413 v1.11.5 +170fb3d1f0b30d32c9794ea40dbb51836dc7d947 v1.12.0 diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -54,11 +54,11 @@ * Checksums of the "source" package version 1.12.0: - - MD5: ... + - MD5: 8e3c92fb5a7b5e32390882dd8ed32275 - - SHA: ... + - SHA: 270cc6e3b4873c617647d05aaa6fd1302a1f0071 - - SHA256: ... + - SHA256: 08090454ff236239e583a9119d0502a6b9817594c0a3714dd1d8593f2350ba11 * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` From pypy.commits at gmail.com Wed Feb 13 09:17:35 2019 From: pypy.commits at gmail.com (stevie_92) Date: Wed, 13 Feb 2019 06:17:35 -0800 (PST) Subject: [pypy-commit] pypy cpyext-gc-cycle: Added interface for rawrefcount finalizers to the gc Message-ID: <5c6426ff.1c69fb81.ed3c0.67dd@mx.google.com> Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r95997:a316d3f47e9f Date: 2019-02-13 15:16 +0100 http://bitbucket.org/pypy/pypy/changeset/a316d3f47e9f/ Log: Added interface for rawrefcount finalizers to the gc Added support for rawrefcount finalizers to dot-tests Potentially dead cross-heap cycles are kept alive until the following collection cycle (still missing optimization for cycles without finalizers) 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 @@ -1187,6 +1187,9 @@ state.C._PyPy_pyobj_as_gc = rffi.llexternal( '_PyPy_pyobj_as_gc', [GCHdr_PyObject], PyGC_HeadPtr, compilation_info=eci, _nowrapper=True) + state.C._PyPy_finalizer_type = rffi.llexternal( + '_PyPy_finalizer_type', [PyGC_HeadPtr], lltype.Signed, + compilation_info=eci, _nowrapper=True) def init_function(func): 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 @@ -438,6 +438,7 @@ PyAPI_FUNC(PyGC_Head *) _PyPy_init_pyobj_list(); PyAPI_FUNC(GCHdr_PyObject *) _PyPy_gc_as_pyobj(PyGC_Head *); PyAPI_FUNC(PyGC_Head *) _PyPy_pyobj_as_gc(GCHdr_PyObject *); +PyAPI_FUNC(Py_ssize_t) _PyPy_finalizer_type(PyGC_Head *); #ifdef __cplusplus } diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c --- a/pypy/module/cpyext/src/object.c +++ b/pypy/module/cpyext/src/object.c @@ -61,6 +61,12 @@ } } +Py_ssize_t +_PyPy_finalizer_type(PyGC_Head *g) +{ + return 0; +} + void _Py_Dealloc(PyObject *obj) { diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py --- a/pypy/module/cpyext/state.py +++ b/pypy/module/cpyext/state.py @@ -174,7 +174,8 @@ llhelper(rawrefcount.RAWREFCOUNT_TRAVERSE, self.tp_traverse), pypyobj_list, - self.C._PyPy_gc_as_pyobj, self.C._PyPy_pyobj_as_gc) + self.C._PyPy_gc_as_pyobj, self.C._PyPy_pyobj_as_gc, + self.C._PyPy_finalizer_type) self.builder.attach_all(space) setup_new_method_def(space) diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py --- a/rpython/memory/gc/incminimark.py +++ b/rpython/memory/gc/incminimark.py @@ -3011,6 +3011,8 @@ PYOBJ_HDR_PTR)) RAWREFCOUNT_PYOBJ_AS_GC = lltype.Ptr(lltype.FuncType([PYOBJ_HDR_PTR], PYOBJ_GC_HDR_PTR)) + RAWREFCOUNT_FINALIZER_TYPE = lltype.Ptr(lltype.FuncType([PYOBJ_GC_HDR_PTR], + lltype.Signed)) def _pyobj(self, pyobjaddr): return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR) @@ -3018,7 +3020,7 @@ return llmemory.cast_adr_to_ptr(pygchdraddr, self.PYOBJ_GC_HDR_PTR) def rawrefcount_init(self, dealloc_trigger_callback, tp_traverse, - pyobj_list, gc_as_pyobj, pyobj_as_gc): + pyobj_list, gc_as_pyobj, pyobj_as_gc, finalizer_type): # see pypy/doc/discussion/rawrefcount.rst if not self.rrc_enabled: self.rrc_p_list_young = self.AddressStack() @@ -3041,6 +3043,7 @@ self.rrc_pyobj_garbage_list.c_gc_prev = self.rrc_pyobj_garbage_list self.rrc_gc_as_pyobj = gc_as_pyobj self.rrc_pyobj_as_gc = pyobj_as_gc + self.rrc_finalizer_type = finalizer_type self.rrc_enabled = True def check_no_more_rawrefcount_state(self): @@ -3268,13 +3271,19 @@ # TODO: pypy objects def _rrc_major_trace(self, pyobject, ignore): - pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject)) - if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR): - rc = pygchdr.c_gc_refs - else: - rc = self._pyobj(pyobject).c_ob_refcnt + from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY + from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT + # + # TODO: optimization: if no finalizers are found the cyclic rc + # TODO: can be used instead of the real rc, because the objects + # TODO: cannot be resurrected anyway + # pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject)) + # if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR): + # rc = pygchdr.c_gc_refs + # else: + rc = self._pyobj(pyobject).c_ob_refcnt - if rc == 0: + if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT: # or rc == 0 pass # the corresponding object may die else: # force the corresponding object to be alive diff --git a/rpython/memory/gc/test/dot/free_finalizer_nocycle.dot b/rpython/memory/gc/test/dot/free_finalizer_nocycle.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_finalizer_nocycle.dot @@ -0,0 +1,13 @@ +digraph G { + "a" [type=P, alive=n]; + "b" [type=B, alive=n]; + "c" [type=C, alive=n, finalizer=modern]; + "d" [type=C, alive=n]; + "e" [type=B, alive=n]; + "f" [type=P, alive=n]; + "a" -> "b"; + "b" -> "c"; + "c" -> "d"; + "d" -> "e"; + "e" -> "f"; +} diff --git a/rpython/memory/gc/test/dot/free_finalizer_simple.dot b/rpython/memory/gc/test/dot/free_finalizer_simple.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/free_finalizer_simple.dot @@ -0,0 +1,14 @@ +digraph G { + "a" [type=P, alive=n]; + "b" [type=B, alive=n]; + "c" [type=C, alive=n, finalizer=modern]; + "d" [type=C, alive=n]; + "e" [type=B, alive=n]; + "f" [type=P, alive=n]; + "a" -> "b"; + "b" -> "c"; + "c" -> "d"; + "d" -> "e"; + "e" -> "f"; + "f" -> "a"; +} diff --git a/rpython/memory/gc/test/dot/keep_finalizer_simple.dot b/rpython/memory/gc/test/dot/keep_finalizer_simple.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/keep_finalizer_simple.dot @@ -0,0 +1,14 @@ +digraph G { + "a" [type=P, alive=y]; + "b" [type=B, alive=y]; + "c" [type=C, alive=y, finalizer=modern, resurrect=d]; + "d" [type=C, alive=y]; + "e" [type=B, alive=y]; + "f" [type=P, alive=y]; + "a" -> "b"; + "b" -> "c"; + "c" -> "d"; + "d" -> "e"; + "e" -> "f"; + "f" -> "a"; +} diff --git a/rpython/memory/gc/test/dot/partial_free_finalizer_nocycle.dot b/rpython/memory/gc/test/dot/partial_free_finalizer_nocycle.dot new file mode 100644 --- /dev/null +++ b/rpython/memory/gc/test/dot/partial_free_finalizer_nocycle.dot @@ -0,0 +1,13 @@ +digraph G { + "a" [type=P, alive=n]; + "b" [type=B, alive=n]; + "c" [type=C, alive=n, finalizer=modern, resurrect=d]; + "d" [type=C, alive=y]; + "e" [type=B, alive=y]; + "f" [type=P, alive=y]; + "a" -> "b"; + "b" -> "c"; + "c" -> "d"; + "d" -> "e"; + "e" -> "f"; +} diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py --- a/rpython/memory/gc/test/test_rawrefcount.py +++ b/rpython/memory/gc/test/test_rawrefcount.py @@ -39,6 +39,9 @@ def rawrefcount_pyobj_as_gc(pyobj): return self.gcobjs[self.pyobjs.index(pyobj)] + def rawrefcount_finalizer_type(gc): + return 0 + self.pyobj_list = lltype.malloc(PYOBJ_GC_HDR_PTR.TO, flavor='raw', immortal=True) self.pyobj_list.c_gc_next = self.pyobj_list @@ -47,7 +50,8 @@ rawrefcount_tp_traverse, llmemory.cast_ptr_to_adr(self.pyobj_list), rawrefcount_gc_as_pyobj, - rawrefcount_pyobj_as_gc) + rawrefcount_pyobj_as_gc, + rawrefcount_finalizer_type) def _collect(self, major, expected_trigger=0): if major: @@ -392,6 +396,8 @@ @py.test.mark.parametrize("file", dot_files) def test_dots(self, file): from rpython.memory.gc.test.dot import pydot + from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY + from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT class Node: def __init__(self, info): @@ -421,10 +427,12 @@ self.info = info class NodeInfo: - def __init__(self, type, alive, ext_refcnt): + def __init__(self, type, alive, ext_refcnt, finalizer, resurrect): self.type = type self.alive = alive self.ext_refcnt = ext_refcnt + self.finalizer = finalizer + self.resurrect = resurrect path = os.path.join(self.dot_dir, file) g = pydot.graph_from_dot_file(path)[0] @@ -439,7 +447,9 @@ alive = attr['alive'] == "y" rooted = attr['rooted'] == "y" if 'rooted' in attr else False ext_refcnt = int(attr['ext_refcnt']) if 'ext_refcnt' in attr else 0 - info = NodeInfo(type, alive, ext_refcnt) + finalizer = attr['finalizer'] if 'finalizer' in attr else None + resurrect = attr['resurrect'] if 'resurrect' in attr else None + info = NodeInfo(type, alive, ext_refcnt, finalizer, resurrect) if type == "C": r, raddr, check_alive = self._rawrefcount_pyobj() r.c_ob_refcnt += ext_refcnt @@ -490,11 +500,7 @@ self.gc.rrc_tp_traverse(source.r, append, None) assert len(dests_target) == 0 - # do collection - self.gc.collect() - - self.gc.rrc_invoke_callback() - if self.trigger <> []: + def cleanup(): # do cleanup after collection (clear all dead pyobjects) def decref_children(pyobj): self.gc.rrc_tp_traverse(pyobj, decref, None) @@ -513,19 +519,26 @@ next_dead = self.gc.rawrefcount_next_dead() while next_dead <> llmemory.NULL: - pyobj = llmemory.cast_adr_to_ptr(next_dead, self.gc.PYOBJ_HDR_PTR) + pyobj = llmemory.cast_adr_to_ptr(next_dead, + self.gc.PYOBJ_HDR_PTR) + print "nextdead:", pyobj, "refcnt:", pyobj.c_ob_refcnt decref(pyobj, None) next_dead = self.gc.rawrefcount_next_dead() + # TODO: call finalizers here and during the next collection it + # will be checked if the CI is really trash + next_dead = self.gc.rawrefcount_cyclic_garbage_head() while next_dead <> llmemory.NULL: - pyobj = llmemory.cast_adr_to_ptr(next_dead, self.gc.PYOBJ_HDR_PTR) + pyobj = llmemory.cast_adr_to_ptr(next_dead, + self.gc.PYOBJ_HDR_PTR) pyobj.c_ob_refcnt += 1 def clear(pyobj_to, pyobj_from): refs = self.pyobj_refs[self.pyobjs.index(pyobj_from)] refs.remove(pyobj_to) decref(pyobj_to, None) + self.gc.rrc_tp_traverse(pyobj, clear, pyobj) decref(pyobj, None) @@ -537,6 +550,21 @@ self.gc.rawrefcount_cyclic_garbage_remove() next_dead = self.gc.rawrefcount_cyclic_garbage_head() + # do a collection to find cyclic isolates + self.gc.collect() + + self.gc.rrc_invoke_callback() + if self.trigger <> []: + cleanup() + + # now do another collection, to clean up cyclic trash + # TODO: maybe optimize, so that we don't need another major collection + self.gc.collect() + + self.gc.rrc_invoke_callback() + if self.trigger <> []: + cleanup() + # check livelihood of objects, according to graph for name in nodes: n = nodes[name] diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py --- a/rpython/memory/gctransform/framework.py +++ b/rpython/memory/gctransform/framework.py @@ -478,7 +478,8 @@ [s_gc, SomePtr(GCClass.RAWREFCOUNT_DEALLOC_TRIGGER), SomePtr(GCClass.RAWREFCOUNT_TRAVERSE), SomeAddress(), SomePtr(GCClass.RAWREFCOUNT_GC_AS_PYOBJ), - SomePtr(GCClass.RAWREFCOUNT_PYOBJ_AS_GC)], + SomePtr(GCClass.RAWREFCOUNT_PYOBJ_AS_GC), + SomePtr(GCClass.RAWREFCOUNT_FINALIZER_TYPE)], annmodel.s_None) self.rawrefcount_create_link_pypy_ptr = getfn( GCClass.rawrefcount_create_link_pypy, @@ -1319,7 +1320,8 @@ self.pop_roots(hop, livevars) def gct_gc_rawrefcount_init(self, hop): - [v_fnptr, v_fnptr2, v_pyobj_list, v_fnptr3, v_fnptr4] = hop.spaceop.args + [v_fnptr, v_fnptr2, v_pyobj_list, v_fnptr3, v_fnptr4, + v_fnptr5] = hop.spaceop.args assert v_fnptr.concretetype == self.GCClass.RAWREFCOUNT_DEALLOC_TRIGGER assert v_fnptr2.concretetype == self.GCClass.RAWREFCOUNT_TRAVERSE # TODO add assert for v_pyobj_list, improve asserts (types not same but equal) @@ -1327,7 +1329,7 @@ # assert v_fnptr4.concretetype == self.GCClass.RAWREFCOUNT_PYOBJ_AS_GC hop.genop("direct_call", [self.rawrefcount_init_ptr, self.c_const_gc, v_fnptr, - v_fnptr2, v_pyobj_list, v_fnptr3, v_fnptr4]) + v_fnptr2, v_pyobj_list, v_fnptr3, v_fnptr4, v_fnptr5]) def gct_gc_rawrefcount_create_link_pypy(self, hop): [v_gcobj, v_pyobject] = hop.spaceop.args diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py --- a/rpython/rlib/rawrefcount.py +++ b/rpython/rlib/rawrefcount.py @@ -286,19 +286,22 @@ _about_ = init def compute_result_annotation(self, s_dealloc_callback, s_tp_traverse, - s_pyobj_list, s_as_gc, s_as_pyobj): + s_pyobj_list, s_as_gc, s_as_pyobj, + a_finalizer_type): from rpython.rtyper.llannotation import SomePtr assert isinstance(s_dealloc_callback, SomePtr) # ll-ptr-to-function assert isinstance(s_tp_traverse, SomePtr) assert isinstance(s_as_gc, SomePtr) assert isinstance(s_as_pyobj, SomePtr) + assert isinstance(a_finalizer_type, SomePtr) def specialize_call(self, hop): hop.exception_cannot_occur() v_dealloc_callback, v_tp_traverse, v_pyobj_list, v_as_gc, \ - v_as_pyobj = hop.inputargs(*hop.args_r) + v_as_pyobj, v_finalizer_type = hop.inputargs(*hop.args_r) hop.genop('gc_rawrefcount_init', [v_dealloc_callback, v_tp_traverse, - v_pyobj_list, v_as_gc, v_as_pyobj]) + v_pyobj_list, v_as_gc, v_as_pyobj, + v_finalizer_type]) class Entry(ExtRegistryEntry): From pypy.commits at gmail.com Wed Feb 13 09:51:26 2019 From: pypy.commits at gmail.com (rlamy) Date: Wed, 13 Feb 2019 06:51:26 -0800 (PST) Subject: [pypy-commit] pypy default: Update ctypes description Message-ID: <5c642eee.1c69fb81.63de6.4b82@mx.google.com> Author: Ronan Lamy Branch: Changeset: r95998:18ed8b023dea Date: 2019-02-13 14:45 +0000 http://bitbucket.org/pypy/pypy/changeset/18ed8b023dea/ Log: Update ctypes description diff --git a/pypy/doc/extending.rst b/pypy/doc/extending.rst --- a/pypy/doc/extending.rst +++ b/pypy/doc/extending.rst @@ -45,16 +45,13 @@ with the `CPython ctypes`_ version. It works for large examples, such as pyglet. PyPy's implementation is not strictly 100% compatible with CPython, but close enough for most cases. - -We also used to provide ``ctypes-configure`` for some API-level access. -This is now viewed as a precursor of CFFI, which you should use instead. More (but older) information is available :doc:`here `. Also, ctypes' performance is not as good as CFFI's. .. _CPython ctypes: http://docs.python.org/library/ctypes.html PyPy implements ctypes as pure Python code around two built-in modules -called ``_ffi`` and ``_rawffi``, which give a very low-level binding to +called ``_rawffi`` and ``_rawffi.alt``, which give a very low-level binding to the C library libffi_. Nowadays it is not recommended to use directly these two modules. From pypy.commits at gmail.com Wed Feb 13 09:51:28 2019 From: pypy.commits at gmail.com (rlamy) Date: Wed, 13 Feb 2019 06:51:28 -0800 (PST) Subject: [pypy-commit] pypy default: Update link to IRC logs Message-ID: <5c642ef0.1c69fb81.4218f.df97@mx.google.com> Author: Ronan Lamy Branch: Changeset: r95999:6b234ecc047d Date: 2019-02-13 14:46 +0000 http://bitbucket.org/pypy/pypy/changeset/6b234ecc047d/ Log: Update link to IRC logs diff --git a/pypy/doc/index.rst b/pypy/doc/index.rst --- a/pypy/doc/index.rst +++ b/pypy/doc/index.rst @@ -103,7 +103,7 @@ the `development mailing list`_. .. _#pypy on irc.freenode.net: irc://irc.freenode.net/pypy -.. _here: https://botbot.me/freenode/pypy/ +.. _here: https://quodlibet.duckdns.org/irc/pypy/latest.log.html#irc-end .. _Development mailing list: http://mail.python.org/mailman/listinfo/pypy-dev .. _Commit mailing list: http://mail.python.org/mailman/listinfo/pypy-commit .. _Development bug/feature tracker: https://bitbucket.org/pypy/pypy/issues From pypy.commits at gmail.com Wed Feb 13 14:18:14 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 13 Feb 2019 11:18:14 -0800 (PST) Subject: [pypy-commit] pypy default: try to fix s390x Message-ID: <5c646d76.1c69fb81.74c1f.0cf6@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96000:8ad869bf0714 Date: 2019-02-13 20:17 +0100 http://bitbucket.org/pypy/pypy/changeset/8ad869bf0714/ Log: try to fix s390x diff --git a/rpython/jit/backend/zarch/regalloc.py b/rpython/jit/backend/zarch/regalloc.py --- a/rpython/jit/backend/zarch/regalloc.py +++ b/rpython/jit/backend/zarch/regalloc.py @@ -316,13 +316,13 @@ orig_var_even = reverse_mapping[even] if orig_var_even in forbidden_vars: continue # duh! - self._sync_var(orig_var_even) + self._sync_var_to_stack(orig_var_even) del self.reg_bindings[orig_var_even] elif which_to_spill == SPILL_ODD: orig_var_odd = reverse_mapping[odd] if orig_var_odd in forbidden_vars: continue # duh! - self._sync_var(orig_var_odd) + self._sync_var_to_stack(orig_var_odd) del self.reg_bindings[orig_var_odd] # well, we got away with a single spill :) @@ -344,10 +344,10 @@ continue if orig_var_even is not None: - self._sync_var(orig_var_even) + self._sync_var_to_stack(orig_var_even) del self.reg_bindings[orig_var_even] if orig_var_odd is not None: - self._sync_var(orig_var_odd) + self._sync_var_to_stack(orig_var_odd) del self.reg_bindings[orig_var_odd] self.reg_bindings[even_var] = even @@ -371,7 +371,7 @@ forbidden_vars, odd) else: # old even var is not forbidden, sync it and be done with it - self._sync_var(old_even_var) + self._sync_var_to_stack(old_even_var) del self.reg_bindings[old_even_var] del reverse_mapping[odd] if old_odd_var: @@ -379,7 +379,7 @@ self._relocate_forbidden_variable(odd, old_odd_var, reverse_mapping, forbidden_vars, even) else: - self._sync_var(old_odd_var) + self._sync_var_to_stack(old_odd_var) del self.reg_bindings[old_odd_var] del reverse_mapping[odd] @@ -406,7 +406,7 @@ candidate_var = reverse_mapping.get(candidate, None) if not candidate_var or candidate_var not in forbidden_vars: if candidate_var is not None: - self._sync_var(candidate_var) + self._sync_var_to_stack(candidate_var) del self.reg_bindings[candidate_var] del reverse_mapping[candidate] self.assembler.regalloc_mov(reg, candidate) From pypy.commits at gmail.com Wed Feb 13 17:07:08 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 14:07:08 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8: document branch to be merged Message-ID: <5c64950c.1c69fb81.1a9e3.662b@mx.google.com> Author: Matti Picus Branch: unicode-utf8 Changeset: r96001:2418468b1574 Date: 2019-02-13 23:06 +0200 http://bitbucket.org/pypy/pypy/changeset/2418468b1574/ Log: document branch to be merged 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 @@ -28,3 +28,7 @@ .. branch: regalloc-playgrounds Improve register allocation in the JIT. + +.. branch: unicode-utf8 + +Use utf8 internally to represent unicode, with the goal of never using rpython-level unicode From pypy.commits at gmail.com Wed Feb 13 17:07:09 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 14:07:09 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8: close branch Message-ID: <5c64950d.1c69fb81.313e8.1ed3@mx.google.com> Author: Matti Picus Branch: unicode-utf8 Changeset: r96002:98fe98e6e00d Date: 2019-02-13 23:09 +0200 http://bitbucket.org/pypy/pypy/changeset/98fe98e6e00d/ Log: close branch From pypy.commits at gmail.com Wed Feb 13 17:07:18 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 14:07:18 -0800 (PST) Subject: [pypy-commit] pypy default: merge unicode-utf8 into default Message-ID: <5c649516.1c69fb81.f21e.1f1f@mx.google.com> Author: Matti Picus Branch: Changeset: r96003:ba081fb468f4 Date: 2019-02-13 23:11 +0200 http://bitbucket.org/pypy/pypy/changeset/ba081fb468f4/ Log: merge unicode-utf8 into default diff too long, truncating to 2000 out of 15164 lines diff --git a/TODO b/TODO new file mode 100644 --- /dev/null +++ b/TODO @@ -0,0 +1,4 @@ +* find a better way to run "find" without creating the index storage, + if one is not already readily available (understand cost now, improve after merge) +* improve performance of splitlines +* think about cost of utf8 list strategy (Armin and CF) diff --git a/lib-python/2.7/test/test_memoryio.py b/lib-python/2.7/test/test_memoryio.py --- a/lib-python/2.7/test/test_memoryio.py +++ b/lib-python/2.7/test/test_memoryio.py @@ -712,6 +712,7 @@ # XXX: For the Python version of io.StringIO, this is highly # dependent on the encoding used for the underlying buffer. + @support.cpython_only def test_widechar(self): buf = self.buftype("\U0002030a\U00020347") memio = self.ioclass(buf) 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 @@ -29,7 +29,11 @@ Improve register allocation in the JIT. - .. branch: promote-unicode Implement rlib.jit.promote_unicode to complement promote_string + +.. branch: unicode-utf8 + +Use utf8 internally to represent unicode, with the goal of never using rpython-level unicode + diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py --- a/pypy/interpreter/argument.py +++ b/pypy/interpreter/argument.py @@ -535,24 +535,26 @@ if num_remainingkwds == 1: for i in range(len(keywords)): if i not in kwds_mapping: - name = keywords[i] - if name is None: - # We'll assume it's unicode. Encode it. - # Careful, I *think* it should not be possible to - # get an IndexError here but you never know. - try: - if keyword_names_w is None: - raise IndexError - # note: negative-based indexing from the end - w_name = keyword_names_w[i - len(keywords)] - except IndexError: + name = '?' + # We'll assume it's unicode. Encode it. + # Careful, I *think* it should not be possible to + # get an IndexError here but you never know. + try: + if keyword_names_w is None: + raise IndexError + # note: negative-based indexing from the end + w_name = keyword_names_w[i - len(keywords)] + except IndexError: + if keywords is None: name = '?' else: - w_enc = space.newtext(space.sys.defaultencoding) - w_err = space.newtext("replace") - w_name = space.call_method(w_name, "encode", w_enc, - w_err) - name = space.text_w(w_name) + name = keywords[i] + else: + w_enc = space.newtext(space.sys.defaultencoding) + w_err = space.newtext("replace") + w_name = space.call_method(w_name, "encode", w_enc, + w_err) + name = space.text_w(w_name) break self.kwd_name = name diff --git a/pypy/interpreter/astcompiler/optimize.py b/pypy/interpreter/astcompiler/optimize.py --- a/pypy/interpreter/astcompiler/optimize.py +++ b/pypy/interpreter/astcompiler/optimize.py @@ -5,7 +5,7 @@ from pypy.tool import stdlib_opcode as ops from pypy.interpreter.error import OperationError from rpython.rlib.unroll import unrolling_iterable -from rpython.rlib.runicode import MAXUNICODE +from rpython.rlib.rutf8 import MAXUNICODE from rpython.rlib.objectmodel import specialize diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py --- a/pypy/interpreter/astcompiler/test/test_compiler.py +++ b/pypy/interpreter/astcompiler/test/test_compiler.py @@ -975,9 +975,6 @@ class AppTestCompiler: - def setup_class(cls): - cls.w_maxunicode = cls.space.wrap(sys.maxunicode) - def test_docstring_not_loaded(self): import StringIO, dis, sys ns = {} @@ -1027,7 +1024,7 @@ import sys d = {} exec '# -*- coding: utf-8 -*-\n\nu = u"\xf0\x9f\x92\x8b"' in d - if sys.maxunicode > 65535 and self.maxunicode > 65535: + if sys.maxunicode > 65535: expected_length = 1 else: expected_length = 2 diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -3,7 +3,7 @@ from rpython.rlib.cache import Cache from rpython.tool.uid import HUGEVAL_BYTES -from rpython.rlib import jit, types +from rpython.rlib import jit, types, rutf8 from rpython.rlib.debug import make_sure_not_resized from rpython.rlib.objectmodel import (we_are_translated, newlist_hint, compute_unique_id, specialize, not_rpython) @@ -283,7 +283,10 @@ def str_w(self, space): self._typed_unwrap_error(space, "string") - def unicode_w(self, space): + def utf8_w(self, space): + self._typed_unwrap_error(space, "unicode") + + def convert_to_w_unicode(self, space): self._typed_unwrap_error(space, "unicode") def bytearray_list_of_chars_w(self, space): @@ -1103,7 +1106,7 @@ """ return None - def listview_unicode(self, w_list): + def listview_utf8(self, w_list): """ Return a list of unwrapped unicode out of a list of unicode. If the argument is not a list or does not contain only unicode, return None. May return None anyway. @@ -1133,8 +1136,15 @@ def newlist_bytes(self, list_s): return self.newlist([self.newbytes(s) for s in list_s]) - def newlist_unicode(self, list_u): - return self.newlist([self.newunicode(u) for u in list_u]) + def newlist_utf8(self, list_u, is_ascii): + l_w = [None] * len(list_u) + for i, item in enumerate(list_u): + if not is_ascii: + length = rutf8.check_utf8(item, True) + else: + length = len(item) + l_w[i] = self.newutf8(item, length) + return self.newlist(l_w) def newlist_int(self, list_i): return self.newlist([self.newint(i) for i in list_i]) @@ -1661,6 +1671,8 @@ # needed because CPython has the same issue. (Well, it's # unclear if there is any use at all for getting the bytes in # the unicode buffer.) + if self.isinstance_w(w_obj, self.w_unicode): + return w_obj.charbuf_w(self) try: return self.bytes_w(w_obj) except OperationError as e: @@ -1802,27 +1814,38 @@ raise oefmt(self.w_TypeError, "argument must be a string") return self.bytes_w(w_obj) - @specialize.argtype(1) - def unicode_w(self, w_obj): - assert w_obj is not None - return w_obj.unicode_w(self) + def utf8_w(self, w_obj): + return w_obj.utf8_w(self) + + def convert_to_w_unicode(self, w_obj): + return w_obj.convert_to_w_unicode(self) def unicode0_w(self, w_obj): "Like unicode_w, but rejects strings with NUL bytes." from rpython.rlib import rstring - result = w_obj.unicode_w(self) + result = w_obj.utf8_w(self).decode('utf8') if u'\x00' in result: raise oefmt(self.w_TypeError, "argument must be a unicode string without NUL " "characters") return rstring.assert_str0(result) - def realunicode_w(self, w_obj): - # Like unicode_w(), but only works if w_obj is really of type - # 'unicode'. On Python 3 this is the same as unicode_w(). + def convert_arg_to_w_unicode(self, w_obj, strict=None): + # XXX why convert_to_w_unicode does something slightly different? + from pypy.objspace.std.unicodeobject import W_UnicodeObject + assert not hasattr(self, 'is_fake_objspace') + return W_UnicodeObject.convert_arg_to_w_unicode(self, w_obj, strict) + + def utf8_len_w(self, w_obj): + w_obj = self.convert_arg_to_w_unicode(w_obj) + return w_obj._utf8, w_obj._len() + + def realutf8_w(self, w_obj): + # Like utf8_w(), but only works if w_obj is really of type + # 'unicode'. On Python 3 this is the same as utf8_w(). if not self.isinstance_w(w_obj, self.w_unicode): raise oefmt(self.w_TypeError, "argument must be a unicode") - return self.unicode_w(w_obj) + return self.utf8_w(w_obj) def bool_w(self, w_obj): # Unwraps a bool, also accepting an int for compatibility. @@ -2187,7 +2210,7 @@ 'float_w', 'uint_w', 'bigint_w', - 'unicode_w', + 'utf8_w', 'unwrap', 'is_true', 'is_w', diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py --- a/pypy/interpreter/gateway.py +++ b/pypy/interpreter/gateway.py @@ -160,6 +160,9 @@ def visit_text0(self, el, app_sig): self.checked_space_method(el, app_sig) + def visit_utf8(self, el, app_sig): + self.checked_space_method(el, app_sig) + def visit_fsencode(self, el, app_sig): self.checked_space_method(el, app_sig) @@ -304,6 +307,9 @@ def visit_text0(self, typ): self.run_args.append("space.text0_w(%s)" % (self.scopenext(),)) + def visit_utf8(self, typ): + self.run_args.append("space.utf8_w(%s)" % (self.scopenext(),)) + def visit_fsencode(self, typ): self.run_args.append("space.fsencode_w(%s)" % (self.scopenext(),)) @@ -469,6 +475,9 @@ def visit_text0(self, typ): self.unwrap.append("space.text0_w(%s)" % (self.nextarg(),)) + def visit_utf8(self, typ): + self.unwrap.append("space.utf8_w(%s)" % (self.nextarg(),)) + def visit_fsencode(self, typ): self.unwrap.append("space.fsencode_w(%s)" % (self.nextarg(),)) @@ -533,10 +542,10 @@ def int_unwrapping_space_method(typ): - assert typ in (int, str, float, unicode, r_longlong, r_uint, r_ulonglong, bool) + assert typ in (int, str, float, r_longlong, r_uint, r_ulonglong, bool) if typ is r_int is r_longlong: return 'gateway_r_longlong_w' - elif typ in (str, unicode, bool): + elif typ in (str, bool): return typ.__name__ + '_w' else: return 'gateway_' + typ.__name__ + '_w' diff --git a/pypy/interpreter/pyparser/parsestring.py b/pypy/interpreter/pyparser/parsestring.py --- a/pypy/interpreter/pyparser/parsestring.py +++ b/pypy/interpreter/pyparser/parsestring.py @@ -1,3 +1,4 @@ +from rpython.rlib import rutf8 from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter import unicodehelper from rpython.rlib.rstring import StringBuilder @@ -51,18 +52,20 @@ 'unmatched triple quotes in literal') q -= 2 - if unicode_literal: # XXX Py_UnicodeFlag is ignored for now + if unicode_literal: if encoding is None or encoding == "iso-8859-1": # 'unicode_escape' expects latin-1 bytes, string is ready. assert 0 <= ps <= q substr = s[ps:q] else: + unicodehelper.check_utf8_or_raise(space, s, ps, q) substr = decode_unicode_utf8(space, s, ps, q) if rawmode: - v = unicodehelper.decode_raw_unicode_escape(space, substr) + r = unicodehelper.decode_raw_unicode_escape(space, substr) else: - v = unicodehelper.decode_unicode_escape(space, substr) - return space.newunicode(v) + r = unicodehelper.decode_unicode_escape(space, substr) + v, length = r + return space.newutf8(v, length) need_encoding = (encoding is not None and encoding != "utf-8" and encoding != "utf8" and @@ -71,7 +74,8 @@ substr = s[ps : q] if rawmode or '\\' not in s[ps:]: if need_encoding: - w_u = space.newunicode(unicodehelper.decode_utf8(space, substr)) + lgt = unicodehelper.check_utf8_or_raise(space, substr) + w_u = space.newutf8(substr, lgt) w_v = unicodehelper.encode(space, w_u, encoding) return w_v else: @@ -101,15 +105,12 @@ # the backslash we just wrote, we emit "\u005c" # instead. lis.append("u005c") - if ord(s[ps]) & 0x80: # XXX inefficient - w, ps = decode_utf8(space, s, ps, end) - for c in w: - # The equivalent of %08x, which is not supported by RPython. - # 7 zeroes are enough for the unicode range, and the - # result still fits in 32-bit. - hexa = hex(ord(c) + 0x10000000) - lis.append('\\U0') - lis.append(hexa[3:]) # Skip 0x and the leading 1 + if ord(s[ps]) & 0x80: + cp = rutf8.codepoint_at_pos(s, ps) + hexa = hex(cp + 0x10000000) + lis.append('\\U0') + lis.append(hexa[3:]) # Skip 0x and the leading 1 + ps = rutf8.next_codepoint_pos(s, ps) else: lis.append(s[ps]) ps += 1 @@ -215,20 +216,29 @@ ch >= 'A' and ch <= 'F') -def decode_utf8(space, s, ps, end): +def check_utf8(space, s, ps, end): assert ps >= 0 pt = ps # while (s < end && *s != '\\') s++; */ /* inefficient for u".." while ps < end and ord(s[ps]) & 0x80: ps += 1 - u = unicodehelper.decode_utf8(space, s[pt:ps]) - return u, ps + try: + rutf8.check_utf8(s, True, pt, ps) + except rutf8.CheckError as e: + lgt, flag = rutf8.check_utf8(s, True, pt, e.pos) + unicodehelper.decode_error_handler(space)('strict', 'utf8', + 'invalid utf-8', s, pt + lgt, pt + lgt + 1) + return s[pt:ps] def decode_utf8_recode(space, s, ps, end, recode_encoding): - u, ps = decode_utf8(space, s, ps, end) - w_v = unicodehelper.encode(space, space.newunicode(u), recode_encoding) + p = ps + while p < end and ord(s[p]) & 0x80: + p += 1 + lgt = unicodehelper.check_utf8_or_raise(space, s, ps, p) + w_v = unicodehelper.encode(space, space.newutf8(s[ps:p], lgt), + recode_encoding) v = space.bytes_w(w_v) - return v, ps + return v, p def raise_app_valueerror(space, msg): raise OperationError(space.w_ValueError, space.newtext(msg)) diff --git a/pypy/interpreter/pyparser/test/test_parsestring.py b/pypy/interpreter/pyparser/test/test_parsestring.py --- a/pypy/interpreter/pyparser/test/test_parsestring.py +++ b/pypy/interpreter/pyparser/test/test_parsestring.py @@ -10,7 +10,7 @@ assert space.str_w(w_ret) == value elif isinstance(value, unicode): assert space.type(w_ret) == space.w_unicode - assert space.unicode_w(w_ret) == value + assert space.utf8_w(w_ret).decode('utf8') == value else: assert False @@ -50,7 +50,7 @@ s = "u'\x81'" s = s.decode("koi8-u").encode("utf8") w_ret = parsestring.parsestr(self.space, 'koi8-u', s) - ret = space.unwrap(w_ret) + ret = w_ret._utf8.decode('utf8') assert ret == eval("# -*- coding: koi8-u -*-\nu'\x81'") def test_unicode_literals(self): @@ -102,7 +102,4 @@ def test_decode_unicode_utf8(self): buf = parsestring.decode_unicode_utf8(self.space, 'u"\xf0\x9f\x92\x8b"', 2, 6) - if sys.maxunicode == 65535: - assert buf == r"\U0000d83d\U0000dc8b" - else: - assert buf == r"\U0001f48b" + assert buf == r"\U0001f48b" diff --git a/pypy/interpreter/test/test_argument.py b/pypy/interpreter/test/test_argument.py --- a/pypy/interpreter/test/test_argument.py +++ b/pypy/interpreter/test/test_argument.py @@ -54,6 +54,9 @@ pass class DummySpace(object): + class sys: + defaultencoding = 'utf-8' + def newtuple(self, items): return tuple(items) diff --git a/pypy/interpreter/test/test_gateway.py b/pypy/interpreter/test/test_gateway.py --- a/pypy/interpreter/test/test_gateway.py +++ b/pypy/interpreter/test/test_gateway.py @@ -535,25 +535,33 @@ w_app_g3_r = space.wrap(app_g3_r) raises(gateway.OperationError,space.call_function,w_app_g3_r,w(1.0)) - def test_interp2app_unwrap_spec_unicode(self): + def test_interp2app_unwrap_spec_utf8(self): space = self.space w = space.wrap - def g3_u(space, uni): - return space.wrap(len(uni)) + def g3_u(space, utf8): + return space.wrap(utf8) app_g3_u = gateway.interp2app_temp(g3_u, unwrap_spec=[gateway.ObjSpace, - unicode]) + 'utf8']) w_app_g3_u = space.wrap(app_g3_u) + encoded = u"gęść".encode('utf8') assert self.space.eq_w( - space.call_function(w_app_g3_u, w(u"foo")), - w(3)) + space.call_function(w_app_g3_u, w(u"gęść")), + w(encoded)) assert self.space.eq_w( - space.call_function(w_app_g3_u, w("baz")), - w(3)) + space.call_function(w_app_g3_u, w("foo")), + w("foo")) raises(gateway.OperationError, space.call_function, w_app_g3_u, w(None)) raises(gateway.OperationError, space.call_function, w_app_g3_u, w(42)) + # XXX this part of the test seems wrong, why would "\x80" fail? + # w_ascii = space.appexec([], """(): + # import sys + # return sys.getdefaultencoding() == 'ascii'""") + # if space.is_true(w_ascii): + # raises(gateway.OperationError, space.call_function, w_app_g3_u, + # w("\x80")) def test_interp2app_unwrap_spec_unwrapper(self): space = self.space diff --git a/pypy/interpreter/test/test_objspace.py b/pypy/interpreter/test/test_objspace.py --- a/pypy/interpreter/test/test_objspace.py +++ b/pypy/interpreter/test/test_objspace.py @@ -216,9 +216,7 @@ space = self.space w = space.wrap assert space.text0_w(w("123")) == "123" - exc = space.raises_w(space.w_TypeError, space.text0_w, w("123\x004")) - assert space.unicode0_w(w(u"123")) == u"123" - exc = space.raises_w(space.w_TypeError, space.unicode0_w, w(u"123\x004")) + space.raises_w(space.w_TypeError, space.text0_w, w("123\x004")) def test_getindex_w(self): w_instance1 = self.space.appexec([], """(): diff --git a/pypy/interpreter/test/test_unicodehelper.py b/pypy/interpreter/test/test_unicodehelper.py --- a/pypy/interpreter/test/test_unicodehelper.py +++ b/pypy/interpreter/test/test_unicodehelper.py @@ -1,53 +1,93 @@ import pytest +try: + from hypothesis import given, strategies + HAS_HYPOTHESIS = True +except ImportError: + HAS_HYPOTHESIS = False import struct import sys -from pypy.interpreter.unicodehelper import ( - encode_utf8, decode_utf8, unicode_encode_utf_32_be) -class FakeSpace: - pass +from rpython.rlib import rutf8 -def test_encode_utf8(): - space = FakeSpace() - assert encode_utf8(space, u"abc") == "abc" - assert encode_utf8(space, u"\u1234") == "\xe1\x88\xb4" - assert encode_utf8(space, u"\ud800") == "\xed\xa0\x80" - assert encode_utf8(space, u"\udc00") == "\xed\xb0\x80" - # for the following test, go to lengths to avoid CPython's optimizer - # and .pyc file storage, which collapse the two surrogates into one - c = u"\udc00" - assert encode_utf8(space, u"\ud800" + c) == "\xf0\x90\x80\x80" +from pypy.interpreter.unicodehelper import str_decode_utf8 +from pypy.interpreter.unicodehelper import utf8_encode_ascii, str_decode_ascii +from pypy.interpreter import unicodehelper as uh +from pypy.module._codecs.interp_codecs import CodecState + +def decode_utf8(u): + return str_decode_utf8(u, True, "strict", None) def test_decode_utf8(): - space = FakeSpace() - assert decode_utf8(space, "abc") == u"abc" - assert decode_utf8(space, "\xe1\x88\xb4") == u"\u1234" - assert decode_utf8(space, "\xed\xa0\x80") == u"\ud800" - assert decode_utf8(space, "\xed\xb0\x80") == u"\udc00" - got = decode_utf8(space, "\xed\xa0\x80\xed\xb0\x80") - assert map(ord, got) == [0xd800, 0xdc00] - got = decode_utf8(space, "\xf0\x90\x80\x80") - if sys.maxunicode > 65535: - assert map(ord, got) == [0x10000] - else: - assert map(ord, got) == [55296, 56320] + assert decode_utf8("abc") == ("abc", 3, 3) + assert decode_utf8("\xe1\x88\xb4") == ("\xe1\x88\xb4", 3, 1) + assert decode_utf8("\xed\xa0\x80") == ("\xed\xa0\x80", 3, 1) + assert decode_utf8("\xed\xb0\x80") == ("\xed\xb0\x80", 3, 1) + assert decode_utf8("\xed\xa0\x80\xed\xb0\x80") == ( + "\xed\xa0\x80\xed\xb0\x80", 6, 2) + assert decode_utf8("\xf0\x90\x80\x80") == ("\xf0\x90\x80\x80", 4, 1) - at pytest.mark.parametrize('unich', [u"\ud800", u"\udc80"]) -def test_utf32_surrogates(unich): - assert (unicode_encode_utf_32_be(unich, 1, None) == - struct.pack('>i', ord(unich))) - with pytest.raises(UnicodeEncodeError): - unicode_encode_utf_32_be(unich, 1, None, allow_surrogates=False) +def test_utf8_encode_ascii(): + assert utf8_encode_ascii("abc", "??", "??") == "abc" + def eh(errors, encoding, reason, p, start, end): + lst.append((errors, encoding, p, start, end)) + return "", end + lst = [] + input = u"\u1234".encode("utf8") + assert utf8_encode_ascii(input, "??", eh) == "" + assert lst == [("??", "ascii", input, 0, 1)] + lst = [] + input = u"\u1234\u5678abc\u8765\u4321".encode("utf8") + assert utf8_encode_ascii(input, "??", eh) == "abc" + assert lst == [("??", "ascii", input, 0, 2), + ("??", "ascii", input, 5, 7)] - def replace_with(ru, rs): - def errorhandler(errors, enc, msg, u, startingpos, endingpos): - if errors == 'strict': - raise UnicodeEncodeError(enc, u, startingpos, endingpos, msg) - return ru, rs, endingpos - return unicode_encode_utf_32_be( - u"<%s>" % unich, 3, None, - errorhandler, allow_surrogates=False) +if HAS_HYPOTHESIS: + @given(strategies.text()) + def test_utf8_encode_ascii_2(u): + def eh(errors, encoding, reason, p, start, end): + return "?" * (end - start), end - assert replace_with(u'rep', None) == u''.encode('utf-32-be') - assert (replace_with(None, '\xca\xfe\xca\xfe') == - '\x00\x00\x00<\xca\xfe\xca\xfe\x00\x00\x00>') + assert utf8_encode_ascii(u.encode("utf8"), + "replace", eh) == u.encode("ascii", "replace") + +def test_str_decode_ascii(): + assert str_decode_ascii("abc", "??", True, "??") == ("abc", 3, 3) + def eh(errors, encoding, reason, p, start, end): + lst.append((errors, encoding, p, start, end)) + return u"\u1234\u5678".encode("utf8"), end + lst = [] + input = "\xe8" + exp = u"\u1234\u5678".encode("utf8") + assert str_decode_ascii(input, "??", True, eh) == (exp, 1, 2) + assert lst == [("??", "ascii", input, 0, 1)] + lst = [] + input = "\xe8\xe9abc\xea\xeb" + assert str_decode_ascii(input, "??", True, eh) == ( + exp + exp + "abc" + exp + exp, 7, 11) + assert lst == [("??", "ascii", input, 0, 1), + ("??", "ascii", input, 1, 2), + ("??", "ascii", input, 5, 6), + ("??", "ascii", input, 6, 7)] +if HAS_HYPOTHESIS: + @given(strategies.text()) + def test_unicode_raw_escape(u): + r = uh.utf8_encode_raw_unicode_escape(u.encode("utf8"), 'strict', None) + assert r == u.encode("raw-unicode-escape") + + @given(strategies.text()) + def test_unicode_escape(u): + r = uh.utf8_encode_unicode_escape(u.encode("utf8"), "strict", None) + assert r == u.encode("unicode-escape") + +def test_encode_decimal(space): + assert uh.unicode_encode_decimal(u' 12, 34 ', None) == ' 12, 34 ' + with pytest.raises(ValueError): + uh.unicode_encode_decimal(u' 12, \u1234 '.encode('utf8'), None) + state = space.fromcache(CodecState) + handler = state.encode_error_handler + assert uh.unicode_encode_decimal( + u'u\u1234\u1235v'.encode('utf8'), 'replace', handler) == 'u??v' + + result = uh.unicode_encode_decimal( + u'12\u1234'.encode('utf8'), 'xmlcharrefreplace', handler) + assert result == '12ሴ' diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -1,11 +1,12 @@ +import sys + +from pypy.interpreter.error import OperationError, oefmt from rpython.rlib.objectmodel import specialize -from rpython.rlib.rarithmetic import intmask -from rpython.rlib.rstring import StringBuilder, UnicodeBuilder -from rpython.rlib import runicode -from rpython.rlib.runicode import ( - default_unicode_error_encode, default_unicode_error_decode, - MAXUNICODE, BYTEORDER, BYTEORDER2, UNICHR) -from pypy.interpreter.error import OperationError +from rpython.rlib.rstring import StringBuilder +from rpython.rlib import rutf8 +from rpython.rlib.rarithmetic import r_uint, intmask +from rpython.rtyper.lltypesystem import rffi +from pypy.module.unicodedata import unicodedb @specialize.memo() def decode_error_handler(space): @@ -20,90 +21,982 @@ space.newtext(msg)])) return raise_unicode_exception_decode +def decode_never_raise(errors, encoding, msg, s, startingpos, endingpos): + assert startingpos >= 0 + ux = ['\ux' + hex(ord(x))[2:].upper() for x in s[startingpos:endingpos]] + return ''.join(ux), endingpos, 'b' + @specialize.memo() def encode_error_handler(space): # Fast version of the "strict" errors handler. - def raise_unicode_exception_encode(errors, encoding, msg, u, + def raise_unicode_exception_encode(errors, encoding, msg, utf8, startingpos, endingpos): + u_len = rutf8.get_utf8_length(utf8) raise OperationError(space.w_UnicodeEncodeError, space.newtuple([space.newtext(encoding), - space.newunicode(u), + space.newutf8(utf8, u_len), space.newint(startingpos), space.newint(endingpos), space.newtext(msg)])) return raise_unicode_exception_encode +def default_error_encode( + errors, encoding, msg, u, startingpos, endingpos): + """A default handler, for tests""" + assert endingpos >= 0 + if errors == 'replace': + return '?', endingpos + if errors == 'ignore': + return '', endingpos + raise ValueError + # ____________________________________________________________ +_WIN32 = sys.platform == 'win32' +_MACOSX = sys.platform == 'darwin' + def encode(space, w_data, encoding=None, errors='strict'): from pypy.objspace.std.unicodeobject import encode_object return encode_object(space, w_data, encoding, errors) -# These functions take and return unwrapped rpython strings and unicodes + +def _has_surrogate(u): + for c in u: + if 0xD800 <= ord(c) <= 0xDFFF: + return True + return False + +# These functions take and return unwrapped rpython strings def decode_unicode_escape(space, string): from pypy.module._codecs import interp_codecs state = space.fromcache(interp_codecs.CodecState) unicodedata_handler = state.get_unicodedata_handler(space) - result, consumed = runicode.str_decode_unicode_escape( - string, len(string), "strict", - final=True, errorhandler=decode_error_handler(space), - unicodedata_handler=unicodedata_handler) - return result + result_utf8, consumed, length = str_decode_unicode_escape( + string, "strict", + final=True, + errorhandler=decode_error_handler(space), + ud_handler=unicodedata_handler) + return result_utf8, length def decode_raw_unicode_escape(space, string): - result, consumed = runicode.str_decode_raw_unicode_escape( - string, len(string), "strict", + result_utf8, consumed, lgt = str_decode_raw_unicode_escape( + string, "strict", final=True, errorhandler=decode_error_handler(space)) - return result + return result_utf8, lgt -def decode_utf8(space, string): +def check_ascii_or_raise(space, string): + try: + rutf8.check_ascii(string) + except rutf8.CheckError as e: + decode_error_handler(space)('strict', 'ascii', + 'ordinal not in range(128)', string, + e.pos, e.pos + 1) + assert False, "unreachable" + +def check_utf8_or_raise(space, string, start=0, end=-1): # Surrogates are accepted and not treated specially at all. # If there happen to be two 3-bytes encoding a pair of surrogates, # you still get two surrogate unicode characters in the result. # These are the Python2 rules; Python3 differs. - result, consumed = runicode.str_decode_utf_8( - string, len(string), "strict", - final=True, errorhandler=decode_error_handler(space), - allow_surrogates=True) - return result + try: + length = rutf8.check_utf8(string, True, start, end) + except rutf8.CheckError as e: + # convert position into unicode position + lgt = rutf8.check_utf8(string, True, start, stop=e.pos) + decode_error_handler(space)('strict', 'utf8', 'invalid utf-8', string, + start + lgt, start + lgt + 1) + assert False, "unreachable" + return length -def encode_utf8(space, uni): - # Note that this function never raises UnicodeEncodeError, - # since surrogates are allowed, either paired or lone. - # A paired surrogate is considered like the non-BMP character - # it stands for. These are the Python2 rules; Python3 differs. +def str_decode_ascii(s, errors, final, errorhandler): + try: + rutf8.check_ascii(s) + return s, len(s), len(s) + except rutf8.CheckError: + return _str_decode_ascii_slowpath(s, errors, final, errorhandler) + +def _str_decode_ascii_slowpath(s, errors, final, errorhandler): + i = 0 + res = StringBuilder() + while i < len(s): + ch = s[i] + if ord(ch) > 0x7F: + r, i = errorhandler(errors, 'ascii', 'ordinal not in range(128)', + s, i, i + 1) + res.append(r) + else: + res.append(ch) + i += 1 + ress = res.build() + lgt = rutf8.check_utf8(ress, True) + return ress, len(s), lgt + +def str_decode_latin_1(s, errors, final, errorhandler): + try: + rutf8.check_ascii(s) + return s, len(s), len(s) + except rutf8.CheckError: + return _str_decode_latin_1_slowpath(s, errors, final, errorhandler) + +def _str_decode_latin_1_slowpath(s, errors, final, errorhandler): + res = StringBuilder(len(s)) + i = 0 + while i < len(s): + if ord(s[i]) > 0x7F: + while i < len(s) and ord(s[i]) > 0x7F: + rutf8.unichr_as_utf8_append(res, ord(s[i])) + i += 1 + else: + start = i + end = i + 1 + while end < len(s) and ord(s[end]) <= 0x7F: + end += 1 + res.append_slice(s, start, end) + i = end + # cannot be ASCII, cannot have surrogates, I believe + return res.build(), len(s), len(s) + +def utf8_encode_latin_1(s, errors, errorhandler): + try: + rutf8.check_ascii(s) + return s + except rutf8.CheckError: + return _utf8_encode_latin_1_slowpath(s, errors, errorhandler) + +def _utf8_encode_latin_1_slowpath(s, errors, errorhandler): + size = len(s) + result = StringBuilder(size) + index = 0 + pos = 0 + while pos < size: + ch = rutf8.codepoint_at_pos(s, pos) + if ch <= 0xFF: + result.append(chr(ch)) + index += 1 + pos = rutf8.next_codepoint_pos(s, pos) + else: + startindex = index + pos = rutf8.next_codepoint_pos(s, pos) + index += 1 + while pos < size and rutf8.codepoint_at_pos(s, pos) > 0xFF: + pos = rutf8.next_codepoint_pos(s, pos) + index += 1 + msg = "ordinal not in range(256)" + res_8, newindex = errorhandler( + errors, 'latin1', msg, s, startindex, index) + for cp in rutf8.Utf8StringIterator(res_8): + if cp > 0xFF: + errorhandler("strict", 'latin1', msg, s, startindex, index) + result.append(chr(cp)) + if index != newindex: # Should be uncommon + index = newindex + pos = rutf8._pos_at_index(s, newindex) + return result.build() + +def utf8_encode_ascii(s, errors, errorhandler): + """ Don't be confused - this is a slowpath for errors e.g. "ignore" + or an obscure errorhandler + """ + size = len(s) + result = StringBuilder(size) + index = 0 + pos = 0 + while pos < size: + ch = rutf8.codepoint_at_pos(s, pos) + if ch <= 0x7F: + result.append(chr(ch)) + index += 1 + pos = rutf8.next_codepoint_pos(s, pos) + else: + startindex = index + pos = rutf8.next_codepoint_pos(s, pos) + index += 1 + while pos < size and rutf8.codepoint_at_pos(s, pos) > 0x7F: + pos = rutf8.next_codepoint_pos(s, pos) + index += 1 + msg = "ordinal not in range(128)" + res_8, newindex = errorhandler( + errors, 'ascii', msg, s, startindex, index) + for cp in rutf8.Utf8StringIterator(res_8): + if cp > 0x7F: + errorhandler("strict", 'ascii', msg, s, startindex, index) + result.append(chr(cp)) + if index != newindex: # Should be uncommon + index = newindex + pos = rutf8._pos_at_index(s, newindex) + return result.build() + +if sys.platform == 'win32': + def utf8_encode_mbcs(s, errors, errorhandler): + from rpython.rlib import runicode + s = s.decode('utf-8') + slen = len(s) + res = runicode.unicode_encode_mbcs(s, slen, errors, errorhandler) + return res + + def str_decode_mbcs(s, errors, final, errorhandler): + from rpython.rlib import runicode + slen = len(s) + res, size = runicode.str_decode_mbcs(s, slen, final=final, errors=errors, + errorhandler=errorhandler) + return res.encode('utf8'), size, len(res) + +def str_decode_utf8(s, errors, final, errorhandler): + """ Same as checking for the valid utf8, but we know the utf8 is not + valid so we're trying to either raise or pack stuff with error handler. + The key difference is that this is call_may_force + """ + slen = len(s) + res = StringBuilder(slen) + pos = 0 + end = len(s) + suppressing = False # we are in a chain of "bad" unicode, only emit one fix + while pos < end: + ordch1 = ord(s[pos]) + # fast path for ASCII + if ordch1 <= 0x7F: + pos += 1 + res.append(chr(ordch1)) + suppressing = False + continue + + if ordch1 <= 0xC1: + r, pos = errorhandler(errors, "utf8", "invalid start byte", + s, pos, pos + 1) + if not suppressing: + res.append(r) + continue + + pos += 1 + + if ordch1 <= 0xDF: + if pos >= end: + if not final: + pos -= 1 + break + r, pos = errorhandler(errors, "utf8", "unexpected end of data", + s, pos - 1, pos) + if not suppressing: + res.append(r) + continue + ordch2 = ord(s[pos]) + + if rutf8._invalid_byte_2_of_2(ordch2): + r, pos = errorhandler(errors, "utf8", "invalid continuation byte", + s, pos - 1, pos) + if not suppressing: + res.append(r) + continue + # 110yyyyy 10zzzzzz -> 00000000 00000yyy yyzzzzzz + pos += 1 + res.append(chr(ordch1)) + res.append(chr(ordch2)) + continue + + if ordch1 <= 0xEF: + if (pos + 2) > end: + if not final: + pos -= 1 + break + r, pos = errorhandler(errors, "utf8", "unexpected end of data", + s, pos - 1, pos) + res.append(r) + suppressing = True + continue + ordch2 = ord(s[pos]) + ordch3 = ord(s[pos + 1]) + + if rutf8._invalid_byte_2_of_3(ordch1, ordch2, True): + r, pos = errorhandler(errors, "utf8", "invalid continuation byte", + s, pos - 1, pos) + if not suppressing: + res.append(r) + continue + elif rutf8._invalid_byte_3_of_3(ordch3): + r, pos = errorhandler(errors, "utf8", "invalid continuation byte", + s, pos - 1, pos + 1) + if not suppressing: + res.append(r) + continue + pos += 2 + + # 1110xxxx 10yyyyyy 10zzzzzz -> 00000000 xxxxyyyy yyzzzzzz + res.append(chr(ordch1)) + res.append(chr(ordch2)) + res.append(chr(ordch3)) + suppressing = False + continue + + if ordch1 <= 0xF4: + if (pos + 3) > end: + if not final: + pos -= 1 + break + r, pos = errorhandler(errors, "utf8", "unexpected end of data", + s, pos - 1, pos) + res.append(r) + suppressing = True + continue + ordch2 = ord(s[pos]) + ordch3 = ord(s[pos + 1]) + ordch4 = ord(s[pos + 2]) + + if rutf8._invalid_byte_2_of_4(ordch1, ordch2): + r, pos = errorhandler(errors, "utf8", "invalid continuation byte", + s, pos - 1, pos) + if not suppressing: + res.append(r) + continue + elif rutf8._invalid_byte_3_of_4(ordch3): + r, pos = errorhandler(errors, "utf8", "invalid continuation byte", + s, pos - 1, pos + 1) + res.append(r) + continue + elif rutf8._invalid_byte_4_of_4(ordch4): + r, pos = errorhandler(errors, "utf8", "invalid continuation byte", + s, pos - 1, pos + 2) + if not suppressing: + res.append(r) + continue + + pos += 3 + # 11110www 10xxxxxx 10yyyyyy 10zzzzzz -> 000wwwxx xxxxyyyy yyzzzzzz + res.append(chr(ordch1)) + res.append(chr(ordch2)) + res.append(chr(ordch3)) + res.append(chr(ordch4)) + suppressing = False + continue + + r, pos = errorhandler(errors, "utf8", "invalid start byte", + s, pos - 1, pos) + if not suppressing: + res.append(r) + + r = res.build() + return r, pos, rutf8.check_utf8(r, True) + +hexdigits = "0123456789ABCDEFabcdef" + +def hexescape(builder, s, pos, digits, + encoding, errorhandler, message, errors): + chr = 0 + if pos + digits > len(s): + endinpos = pos + while endinpos < len(s) and s[endinpos] in hexdigits: + endinpos += 1 + res, pos = errorhandler( + errors, encoding, message, s, pos - 2, endinpos) + builder.append(res) + else: + try: + chr = int(s[pos:pos + digits], 16) + except ValueError: + endinpos = pos + while s[endinpos] in hexdigits: + endinpos += 1 + res, pos = errorhandler( + errors, encoding, message, s, pos - 2, endinpos) + builder.append(res) + else: + # when we get here, chr is a 32-bit unicode character + try: + builder.append_code(chr) + pos += digits + except ValueError: + message = "illegal Unicode character" + res, pos = errorhandler( + errors, encoding, message, s, pos - 2, pos + digits) + builder.append(res) + return pos + +def str_decode_unicode_escape(s, errors, final, errorhandler, ud_handler): + size = len(s) + if size == 0: + return '', 0, 0 + + builder = rutf8.Utf8StringBuilder(size) + pos = 0 + while pos < size: + ch = s[pos] + + # Non-escape characters are interpreted as Unicode ordinals + if ch != '\\': + if ord(ch) > 0x7F: + builder.append_code(ord(ch)) + else: + builder.append(ch) + pos += 1 + continue + + # - Escapes + pos += 1 + if pos >= size: + message = "\\ at end of string" + res, pos = errorhandler(errors, "unicodeescape", + message, s, pos - 1, size) + builder.append(res) + continue + + ch = s[pos] + pos += 1 + # \x escapes + if ch == '\n': + pass + elif ch == '\\': + builder.append_char('\\') + elif ch == '\'': + builder.append_char('\'') + elif ch == '\"': + builder.append_char('\"') + elif ch == 'b': + builder.append_char('\b') + elif ch == 'f': + builder.append_char('\f') + elif ch == 't': + builder.append_char('\t') + elif ch == 'n': + builder.append_char('\n') + elif ch == 'r': + builder.append_char('\r') + elif ch == 'v': + builder.append_char('\v') + elif ch == 'a': + builder.append_char('\a') + elif '0' <= ch <= '7': + x = ord(ch) - ord('0') + if pos < size: + ch = s[pos] + if '0' <= ch <= '7': + pos += 1 + x = (x << 3) + ord(ch) - ord('0') + if pos < size: + ch = s[pos] + if '0' <= ch <= '7': + pos += 1 + x = (x << 3) + ord(ch) - ord('0') + if x > 0x7F: + builder.append_code(x) + else: + builder.append_char(chr(x)) + # hex escapes + # \xXX + elif ch == 'x': + digits = 2 + message = "truncated \\xXX escape" + pos = hexescape(builder, s, pos, digits, + "unicodeescape", errorhandler, message, errors) + # \uXXXX + elif ch == 'u': + digits = 4 + message = "truncated \\uXXXX escape" + pos = hexescape(builder, s, pos, digits, + "unicodeescape", errorhandler, message, errors) + # \UXXXXXXXX + elif ch == 'U': + digits = 8 + message = "truncated \\UXXXXXXXX escape" + pos = hexescape(builder, s, pos, digits, + "unicodeescape", errorhandler, message, errors) + # \N{name} + elif ch == 'N' and ud_handler is not None: + message = "malformed \\N character escape" + look = pos + + if look < size and s[look] == '{': + # look for the closing brace + while look < size and s[look] != '}': + look += 1 + if look < size and s[look] == '}': + # found a name. look it up in the unicode database + message = "unknown Unicode character name" + name = s[pos + 1:look] + code = ud_handler.call(name) + if code < 0: + res, pos = errorhandler( + errors, "unicodeescape", message, + s, pos - 1, look + 1) + builder.append(res) + continue + pos = look + 1 + builder.append_code(code) + else: + res, pos = errorhandler(errors, "unicodeescape", + message, s, pos - 1, look + 1) + builder.append(res) + else: + res, pos = errorhandler(errors, "unicodeescape", + message, s, pos - 1, look + 1) + builder.append(res) + else: + builder.append_char('\\') + builder.append_code(ord(ch)) + + return builder.build(), pos, builder.getlength() + +def wcharpsize2utf8(space, wcharp, size): + """Safe version of rffi.wcharpsize2utf8. + + Raises app-level ValueError if any wchar value is outside the valid + codepoint range. + """ + try: + return rffi.wcharpsize2utf8(wcharp, size) + except ValueError: + raise oefmt(space.w_ValueError, + "character is not in range [U+0000; U+10ffff]") + + +# ____________________________________________________________ +# Raw unicode escape + +def str_decode_raw_unicode_escape(s, errors, final=False, + errorhandler=None): + size = len(s) + if size == 0: + return '', 0, 0 + + builder = rutf8.Utf8StringBuilder(size) + pos = 0 + while pos < size: + ch = s[pos] + + # Non-escape characters are interpreted as Unicode ordinals + if ch != '\\': + builder.append_code(ord(ch)) + pos += 1 + continue + + # \u-escapes are only interpreted iff the number of leading + # backslashes is odd + bs = pos + while pos < size: + pos += 1 + if pos == size or s[pos] != '\\': + break + builder.append_char('\\') + + # we have a backslash at the end of the string, stop here + if pos >= size: + builder.append_char('\\') + break + + if ((pos - bs) & 1 == 0 or pos >= size or + (s[pos] != 'u' and s[pos] != 'U')): + builder.append_char('\\') + builder.append_code(ord(s[pos])) + pos += 1 + continue + + digits = 4 if s[pos] == 'u' else 8 + message = "truncated \\uXXXX" + pos += 1 + pos = hexescape(builder, s, pos, digits, + "rawunicodeescape", errorhandler, message, errors) + + return builder.build(), pos, builder.getlength() + +_utf8_encode_unicode_escape = rutf8.make_utf8_escape_function() + + +TABLE = '0123456789abcdef' + +def raw_unicode_escape_helper(result, char): + if char >= 0x10000 or char < 0: + result.append("\\U") + zeros = 8 + elif char >= 0x100: + result.append("\\u") + zeros = 4 + else: + result.append("\\x") + zeros = 2 + for i in range(zeros-1, -1, -1): + result.append(TABLE[(char >> (4 * i)) & 0x0f]) + +def utf8_encode_raw_unicode_escape(s, errors, errorhandler): + # errorhandler is not used: this function cannot cause Unicode errors + size = len(s) + if size == 0: + return '' + result = StringBuilder(size) + pos = 0 + while pos < size: + oc = rutf8.codepoint_at_pos(s, pos) + + if oc < 0x100: + result.append(chr(oc)) + else: + raw_unicode_escape_helper(result, oc) + pos = rutf8.next_codepoint_pos(s, pos) + + return result.build() + + +def utf8_encode_unicode_escape(s, errors, errorhandler): + return _utf8_encode_unicode_escape(s) + +# ____________________________________________________________ +# utf-7 + +# Three simple macros defining base-64 + +def _utf7_IS_BASE64(oc): + "Is c a base-64 character?" + c = chr(oc) + return c.isalnum() or c == '+' or c == '/' +def _utf7_TO_BASE64(n): + "Returns the base-64 character of the bottom 6 bits of n" + return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[n & 0x3f] +def _utf7_FROM_BASE64(c): + "given that c is a base-64 character, what is its base-64 value?" + if c >= 'a': + return ord(c) - 71 + elif c >= 'A': + return ord(c) - 65 + elif c >= '0': + return ord(c) + 4 + elif c == '+': + return 62 + else: # c == '/' + return 63 + +def _utf7_DECODE_DIRECT(oc): + return oc <= 127 and oc != ord('+') + +# The UTF-7 encoder treats ASCII characters differently according to +# whether they are Set D, Set O, Whitespace, or special (i.e. none of +# the above). See RFC2152. This array identifies these different +# sets: +# 0 : "Set D" +# alphanumeric and '(),-./:? +# 1 : "Set O" +# !"#$%&*;<=>@[]^_`{|} +# 2 : "whitespace" +# ht nl cr sp +# 3 : special (must be base64 encoded) +# everything else (i.e. +\~ and non-printing codes 0-8 11-12 14-31 127) + +utf7_category = [ +# nul soh stx etx eot enq ack bel bs ht nl vt np cr so si + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, +# dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, +# sp ! " # $ % & ' ( ) * + , - . / + 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 3, 0, 0, 0, 0, +# 0 1 2 3 4 5 6 7 8 9 : ; < = > ? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, +# @ A B C D E F G H I J K L M N O + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +# P Q R S T U V W X Y Z [ \ ] ^ _ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1, 1, 1, +# ` a b c d e f g h i j k l m n o + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +# p q r s t u v w x y z { | } ~ del + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 3, 3, +] + +# ENCODE_DIRECT: this character should be encoded as itself. The +# answer depends on whether we are encoding set O as itself, and also +# on whether we are encoding whitespace as itself. RFC2152 makes it +# clear that the answers to these questions vary between +# applications, so this code needs to be flexible. + +def _utf7_ENCODE_DIRECT(oc, directO, directWS): + return(oc < 128 and oc > 0 and + (utf7_category[oc] == 0 or + (directWS and utf7_category[oc] == 2) or + (directO and utf7_category[oc] == 1))) + +def _utf7_ENCODE_CHAR(result, oc, base64bits, base64buffer): + if oc >= 0x10000: + # code first surrogate + base64bits += 16 + base64buffer = (base64buffer << 16) | 0xd800 | ((oc-0x10000) >> 10) + while base64bits >= 6: + result.append(_utf7_TO_BASE64(base64buffer >> (base64bits-6))) + base64bits -= 6 + # prepare second surrogate + oc = 0xDC00 | ((oc-0x10000) & 0x3FF) + base64bits += 16 + base64buffer = (base64buffer << 16) | oc + while base64bits >= 6: + result.append(_utf7_TO_BASE64(base64buffer >> (base64bits-6))) + base64bits -= 6 + return base64bits, base64buffer + +def str_decode_utf_7(s, errors, final=False, + errorhandler=None): + size = len(s) + if size == 0: + return '', 0, 0 + + inShift = False + base64bits = 0 + base64buffer = 0 + surrogate = 0 + outsize = 0 + + result = StringBuilder(size) + pos = 0 + shiftOutStartPos = 0 + startinpos = 0 + while pos < size: + ch = s[pos] + + if inShift: # in a base-64 section + if _utf7_IS_BASE64(ord(ch)): #consume a base-64 character + base64buffer = (base64buffer << 6) | _utf7_FROM_BASE64(ch) + assert base64buffer >= 0 + base64bits += 6 + pos += 1 + + if base64bits >= 16: + # enough bits for a UTF-16 value + outCh = base64buffer >> (base64bits - 16) + assert outCh >= 0 + base64bits -= 16 + base64buffer &= (1 << base64bits) - 1 # clear high bits + assert outCh <= 0xffff + if surrogate: + # expecting a second surrogate + if outCh >= 0xDC00 and outCh <= 0xDFFF: + code = (((surrogate & 0x3FF)<<10) | + (outCh & 0x3FF)) + 0x10000 + rutf8.unichr_as_utf8_append(result, code) + outsize += 1 + surrogate = 0 + continue + else: + rutf8.unichr_as_utf8_append(result, surrogate, + allow_surrogates=True) + outsize += 1 + surrogate = 0 + # Not done with outCh: falls back to next line + if outCh >= 0xD800 and outCh <= 0xDBFF: + # first surrogate + surrogate = outCh + else: + outsize += 1 + assert outCh >= 0 + rutf8.unichr_as_utf8_append(result, outCh, True) + + else: + # now leaving a base-64 section + inShift = False + + if base64bits > 0: # left-over bits + if base64bits >= 6: + # We've seen at least one base-64 character + pos += 1 + msg = "partial character in shift sequence" + res, pos = errorhandler(errors, 'utf7', + msg, s, pos-1, pos) + reslen = rutf8.check_utf8(res, True) + outsize += reslen + result.append(res) + continue + else: + # Some bits remain; they should be zero + if base64buffer != 0: + pos += 1 + msg = "non-zero padding bits in shift sequence" + res, pos = errorhandler(errors, 'utf7', + msg, s, pos-1, pos) + reslen = rutf8.check_utf8(res, True) + outsize += reslen + result.append(res) + continue + + if surrogate and _utf7_DECODE_DIRECT(ord(ch)): + outsize += 1 + rutf8.unichr_as_utf8_append(result, surrogate, True) + surrogate = 0 + + if ch == '-': + # '-' is absorbed; other terminating characters are + # preserved + pos += 1 + + elif ch == '+': + startinpos = pos + pos += 1 # consume '+' + if pos < size and s[pos] == '-': # '+-' encodes '+' + pos += 1 + result.append('+') + outsize += 1 + else: # begin base64-encoded section + inShift = 1 + surrogate = 0 + shiftOutStartPos = result.getlength() + base64bits = 0 + base64buffer = 0 + + elif _utf7_DECODE_DIRECT(ord(ch)): # character decodes at itself + result.append(ch) + outsize += 1 + pos += 1 + else: + startinpos = pos + pos += 1 + msg = "unexpected special character" + res, pos = errorhandler(errors, 'utf7', msg, s, pos-1, pos) + reslen = rutf8.check_utf8(res, True) + outsize += reslen + result.append(res) + + # end of string + final_length = result.getlength() + if inShift and final: # in shift sequence, no more to follow + # if we're in an inconsistent state, that's an error + inShift = 0 + if (surrogate or + base64bits >= 6 or + (base64bits > 0 and base64buffer != 0)): + msg = "unterminated shift sequence" + res, pos = errorhandler(errors, 'utf7', msg, s, shiftOutStartPos, pos) + reslen = rutf8.check_utf8(res, True) + outsize += reslen + result.append(res) + final_length = result.getlength() + elif inShift: + pos = startinpos + final_length = shiftOutStartPos # back off output + + assert final_length >= 0 + return result.build()[:final_length], pos, outsize + +def utf8_encode_utf_7(s, errors, errorhandler): + size = len(s) + if size == 0: + return '' + result = StringBuilder(size) + + encodeSetO = encodeWhiteSpace = False + + inShift = False + base64bits = 0 + base64buffer = 0 + + pos = 0 + while pos < size: + oc = rutf8.codepoint_at_pos(s, pos) + if not inShift: + if oc == ord('+'): + result.append('+-') + elif _utf7_ENCODE_DIRECT(oc, not encodeSetO, not encodeWhiteSpace): + result.append(chr(oc)) + else: + result.append('+') + inShift = True + base64bits, base64buffer = _utf7_ENCODE_CHAR( + result, oc, base64bits, base64buffer) + else: + if _utf7_ENCODE_DIRECT(oc, not encodeSetO, not encodeWhiteSpace): + # shifting out + if base64bits: # output remaining bits + result.append(_utf7_TO_BASE64(base64buffer << (6-base64bits))) + base64buffer = 0 + base64bits = 0 + + inShift = False + ## Characters not in the BASE64 set implicitly unshift the + ## sequence so no '-' is required, except if the character is + ## itself a '-' + if _utf7_IS_BASE64(oc) or oc == ord('-'): + result.append('-') + result.append(chr(oc)) + else: + base64bits, base64buffer = _utf7_ENCODE_CHAR( + result, oc, base64bits, base64buffer) + pos = rutf8.next_codepoint_pos(s, pos) + + if base64bits: + result.append(_utf7_TO_BASE64(base64buffer << (6 - base64bits))) + if inShift: + result.append('-') + + return result.build() + + at specialize.memo() +def _encode_unicode_error_handler(space): + # Fast version of the "strict" errors handler. + from rpython.rlib import runicode + def raise_unicode_exception_encode(errors, encoding, msg, uni, + startingpos, endingpos): + assert isinstance(uni, unicode) + u_len = len(uni) + utf8 = runicode.unicode_encode_utf8sp(uni, u_len) + raise OperationError(space.w_UnicodeEncodeError, + space.newtuple([space.newtext(encoding), + space.newtext(utf8, u_len), + space.newint(startingpos), + space.newint(endingpos), + space.newtext(msg)])) + return u'', None, 0 + return raise_unicode_exception_encode + + +def encode_utf8(space, uni, allow_surrogates=False): + # Note that Python3 tends to forbid *all* surrogates in utf-8. + # If allow_surrogates=True, then revert to the Python 2 behavior + # which never raises UnicodeEncodeError. Surrogate pairs are then + # allowed, either paired or lone. A paired surrogate is considered + # like the non-BMP character it stands for. See also *_utf8sp(). + from rpython.rlib import runicode + assert isinstance(uni, unicode) return runicode.unicode_encode_utf_8( uni, len(uni), "strict", - errorhandler=None, - allow_surrogates=True) + errorhandler=_encode_unicode_error_handler(space), + allow_surrogates=allow_surrogates) + +def encode_utf8sp(space, uni, allow_surrogates=True): + xxx + # Surrogate-preserving utf-8 encoding. Any surrogate character + # turns into its 3-bytes encoding, whether it is paired or not. + # This should always be reversible, and the reverse is + # decode_utf8sp(). + from rpython.rlib import runicode + return runicode.unicode_encode_utf8sp(uni, len(uni)) + +def decode_utf8sp(space, string): + # Surrogate-preserving utf-8 decoding. Assuming there is no + # encoding error, it should always be reversible, and the reverse is + # encode_utf8sp(). + return str_decode_utf8(string, "string", True, decode_never_raise, + allow_surrogates=True) + # ____________________________________________________________ # utf-16 -def str_decode_utf_16(s, size, errors, final=True, +BYTEORDER = sys.byteorder +BYTEORDER2 = BYTEORDER[0] + 'e' # either "le" or "be" +assert BYTEORDER2 in ('le', 'be') + +def str_decode_utf_16(s, errors, final=True, errorhandler=None): - result, length, byteorder = str_decode_utf_16_helper(s, size, errors, final, + result, c, lgt, _ = str_decode_utf_16_helper(s, errors, final, errorhandler, "native") - return result, length + return result, c, lgt -def str_decode_utf_16_be(s, size, errors, final=True, +def str_decode_utf_16_be(s, errors, final=True, + errorhandler=None): + result, c, lgt, _ = str_decode_utf_16_helper(s, errors, final, + errorhandler, "big") + return result, c, lgt + +def str_decode_utf_16_le(s, errors, final=True, errorhandler=None): - result, length, byteorder = str_decode_utf_16_helper(s, size, errors, final, - errorhandler, "big") - return result, length + result, c, lgt, _ = str_decode_utf_16_helper(s, errors, final, + errorhandler, "little") + return result, c, lgt -def str_decode_utf_16_le(s, size, errors, final=True, - errorhandler=None): - result, length, byteorder = str_decode_utf_16_helper(s, size, errors, final, - errorhandler, "little") - return result, length - -def str_decode_utf_16_helper(s, size, errors, final=True, +def str_decode_utf_16_helper(s, errors, final=True, errorhandler=None, byteorder="native", public_encoding_name='utf16'): - if errorhandler is None: - errorhandler = default_unicode_error_decode + size = len(s) bo = 0 if BYTEORDER == 'little': @@ -140,7 +1033,7 @@ else: bo = 1 if size == 0: - return u'', 0, bo + return '', 0, 0, bo if bo == -1: # force little endian ihi = 1 @@ -151,7 +1044,7 @@ ihi = 0 ilo = 1 - result = UnicodeBuilder(size // 2) + result = StringBuilder(size // 2) #XXX I think the errors are not correctly handled here while pos < size: @@ -168,7 +1061,7 @@ ch = (ord(s[pos + ihi]) << 8) | ord(s[pos + ilo]) pos += 2 if ch < 0xD800 or ch > 0xDFFF: - result.append(unichr(ch)) + rutf8.unichr_as_utf8_append(result, ch) continue # UTF-16 code pair: if len(s) - pos < 2: @@ -185,12 +1078,8 @@ ch2 = (ord(s[pos+ihi]) << 8) | ord(s[pos+ilo]) pos += 2 if 0xDC00 <= ch2 <= 0xDFFF: - if MAXUNICODE < 65536: - result.append(unichr(ch)) - result.append(unichr(ch2)) - else: - result.append(UNICHR((((ch & 0x3FF)<<10) | - (ch2 & 0x3FF)) + 0x10000)) + ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000 + rutf8.unichr_as_utf8_append(result, ch) continue else: r, pos = errorhandler(errors, public_encoding_name, @@ -202,7 +1091,9 @@ "illegal encoding", s, pos - 2, pos) result.append(r) - return result.build(), pos, bo + r = result.build() + lgt = rutf8.check_utf8(r, True) + return result.build(), pos, lgt, bo def _STORECHAR(result, CH, byteorder): hi = chr(((CH) >> 8) & 0xff) @@ -214,13 +1105,12 @@ result.append(hi) result.append(lo) -def unicode_encode_utf_16_helper(s, size, errors, +def unicode_encode_utf_16_helper(s, errors, errorhandler=None, allow_surrogates=True, byteorder='little', public_encoding_name='utf16'): - if errorhandler is None: - errorhandler = default_unicode_error_encode + size = len(s) if size == 0: if byteorder == 'native': result = StringBuilder(2) @@ -234,9 +1124,9 @@ byteorder = BYTEORDER pos = 0 + index = 0 while pos < size: - ch = ord(s[pos]) - pos += 1 + ch = rutf8.codepoint_at_pos(s, pos) if ch < 0xD800: _STORECHAR(result, ch, byteorder) @@ -246,78 +1136,76 @@ elif ch >= 0xE000 or allow_surrogates: _STORECHAR(result, ch, byteorder) else: - ru, rs, pos = errorhandler(errors, public_encoding_name, - 'surrogates not allowed', - s, pos-1, pos) - if rs is not None: - # py3k only - if len(rs) % 2 != 0: - errorhandler('strict', public_encoding_name, - 'surrogates not allowed', - s, pos-1, pos) - result.append(rs) - continue - for ch in ru: - if ord(ch) < 0xD800: - _STORECHAR(result, ord(ch), byteorder) + res_8, newindex = errorhandler( + errors, public_encoding_name, 'surrogates not allowed', + s, pos, pos+1) + for cp in rutf8.Utf8StringIterator(res_8): + if cp < 0xD800: + _STORECHAR(result, cp, byteorder) else: errorhandler('strict', public_encoding_name, 'surrogates not allowed', - s, pos-1, pos) + s, pos, pos+1) + if index != newindex: # Should be uncommon + index = newindex + pos = rutf8._pos_at_index(s, newindex) continue + pos = rutf8.next_codepoint_pos(s, pos) + index += 1 + return result.build() -def unicode_encode_utf_16(s, size, errors, +def utf8_encode_utf_16(s, errors, errorhandler=None, allow_surrogates=True): - return unicode_encode_utf_16_helper(s, size, errors, errorhandler, + return unicode_encode_utf_16_helper(s, errors, errorhandler, allow_surrogates, "native") -def unicode_encode_utf_16_be(s, size, errors, +def utf8_encode_utf_16_be(s, errors, errorhandler=None, allow_surrogates=True): - return unicode_encode_utf_16_helper(s, size, errors, errorhandler, + return unicode_encode_utf_16_helper(s, errors, errorhandler, allow_surrogates, "big") -def unicode_encode_utf_16_le(s, size, errors, +def utf8_encode_utf_16_le(s, errors, errorhandler=None, allow_surrogates=True): - return unicode_encode_utf_16_helper(s, size, errors, errorhandler, + return unicode_encode_utf_16_helper(s, errors, errorhandler, allow_surrogates, "little") - # ____________________________________________________________ # utf-32 -def str_decode_utf_32(s, size, errors, final=True, +def str_decode_utf_32(s, errors, final=True, errorhandler=None): - result, length, byteorder = str_decode_utf_32_helper( - s, size, errors, final, errorhandler, "native") - return result, length + result, c, lgt, _ = str_decode_utf_32_helper(s, errors, final, + errorhandler, "native") + return result, c, lgt -def str_decode_utf_32_be(s, size, errors, final=True, +def str_decode_utf_32_be(s, errors, final=True, errorhandler=None): - result, length, byteorder = str_decode_utf_32_helper( - s, size, errors, final, errorhandler, "big") - return result, length + result, c, lgt, _ = str_decode_utf_32_helper(s, errors, final, + errorhandler, "big") + return result, c, lgt -def str_decode_utf_32_le(s, size, errors, final=True, +def str_decode_utf_32_le(s, errors, final=True, errorhandler=None): - result, length, byteorder = str_decode_utf_32_helper( - s, size, errors, final, errorhandler, "little") - return result, length + result, c, lgt, _ = str_decode_utf_32_helper(s, errors, final, + errorhandler, "little") + return result, c, lgt -BOM32_DIRECT = intmask(0x0000FEFF) +BOM32_DIRECT = intmask(0x0000FEFF) BOM32_REVERSE = intmask(0xFFFE0000) -def str_decode_utf_32_helper(s, size, errors, final=True, - errorhandler=None, +def str_decode_utf_32_helper(s, errors, final, + errorhandler, byteorder="native", - public_encoding_name='utf32'): - if errorhandler is None: - errorhandler = default_unicode_error_decode + public_encoding_name='utf32', + allow_surrogates=True): + assert errorhandler is not None bo = 0 + size = len(s) if BYTEORDER == 'little': iorder = [0, 1, 2, 3] @@ -353,7 +1241,7 @@ else: bo = 1 if size == 0: - return u'', 0, bo + return '', 0, 0, bo if bo == -1: # force little endian iorder = [0, 1, 2, 3] @@ -361,7 +1249,7 @@ # force big endian iorder = [3, 2, 1, 0] - result = UnicodeBuilder(size // 4) + result = StringBuilder(size // 4) while pos < size: # remaining bytes at the end? (size should be divisible by 4) @@ -376,22 +1264,26 @@ break continue ch = ((ord(s[pos + iorder[3]]) << 24) | (ord(s[pos + iorder[2]]) << 16) | - (ord(s[pos + iorder[1]]) << 8) | ord(s[pos + iorder[0]])) - if ch >= 0x110000: + (ord(s[pos + iorder[1]]) << 8) | ord(s[pos + iorder[0]])) + if not allow_surrogates and 0xD800 <= ch <= 0xDFFF: + r, pos = errorhandler(errors, public_encoding_name, + "code point in surrogate code point " + "range(0xd800, 0xe000)", + s, pos, pos + 4) + result.append(r) + continue + elif ch >= 0x110000: r, pos = errorhandler(errors, public_encoding_name, "codepoint not in range(0x110000)", s, pos, len(s)) result.append(r) continue - if MAXUNICODE < 65536 and ch >= 0x10000: - ch -= 0x10000L - result.append(unichr(0xD800 + (ch >> 10))) - result.append(unichr(0xDC00 + (ch & 0x03FF))) - else: - result.append(UNICHR(ch)) + rutf8.unichr_as_utf8_append(result, ch, allow_surrogates=allow_surrogates) pos += 4 - return result.build(), pos, bo + r = result.build() + lgt = rutf8.check_utf8(r, True) + return r, pos, lgt, bo def _STORECHAR32(result, CH, byteorder): c0 = chr(((CH) >> 24) & 0xff) @@ -409,13 +1301,12 @@ result.append(c2) result.append(c3) -def unicode_encode_utf_32_helper(s, size, errors, +def unicode_encode_utf_32_helper(s, errors, errorhandler=None, allow_surrogates=True, byteorder='little', public_encoding_name='utf32'): - if errorhandler is None: - errorhandler = default_unicode_error_encode + size = len(s) if size == 0: if byteorder == 'native': result = StringBuilder(4) @@ -429,50 +1320,253 @@ byteorder = BYTEORDER pos = 0 + index = 0 while pos < size: - ch = ord(s[pos]) - pos += 1 - ch2 = 0 + ch = rutf8.codepoint_at_pos(s, pos) + pos = rutf8.next_codepoint_pos(s, pos) if not allow_surrogates and 0xD800 <= ch < 0xE000: - ru, rs, pos = errorhandler( + res_8, newindex = errorhandler( errors, public_encoding_name, 'surrogates not allowed', s, pos - 1, pos) - if rs is not None: - # py3k only - if len(rs) % 4 != 0: + for ch in rutf8.Utf8StringIterator(res_8): + if ch < 0xD800: + _STORECHAR32(result, ch, byteorder) + else: errorhandler( 'strict', public_encoding_name, 'surrogates not allowed', s, pos - 1, pos) - result.append(rs) - continue - for ch in ru: - if ord(ch) < 0xD800: - _STORECHAR32(result, ord(ch), byteorder) - else: - errorhandler( - 'strict', public_encoding_name, - 'surrogates not allowed', s, pos - 1, pos) + if index != newindex: # Should be uncommon + index = newindex + pos = rutf8._pos_at_index(s, newindex) continue - if 0xD800 <= ch < 0xDC00 and MAXUNICODE < 65536 and pos < size: - ch2 = ord(s[pos]) - if 0xDC00 <= ch2 < 0xE000: - ch = (((ch & 0x3FF) << 10) | (ch2 & 0x3FF)) + 0x10000 - pos += 1 _STORECHAR32(result, ch, byteorder) + index += 1 return result.build() -def unicode_encode_utf_32(s, size, errors, +def utf8_encode_utf_32(s, errors, errorhandler=None, allow_surrogates=True): - return unicode_encode_utf_32_helper(s, size, errors, errorhandler, + return unicode_encode_utf_32_helper(s, errors, errorhandler, allow_surrogates, "native") -def unicode_encode_utf_32_be(s, size, errors, +def utf8_encode_utf_32_be(s, errors, errorhandler=None, allow_surrogates=True): - return unicode_encode_utf_32_helper(s, size, errors, errorhandler, + return unicode_encode_utf_32_helper(s, errors, errorhandler, allow_surrogates, "big") -def unicode_encode_utf_32_le(s, size, errors, From pypy.commits at gmail.com Wed Feb 13 17:07:20 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 14:07:20 -0800 (PST) Subject: [pypy-commit] pypy unicode-utf8-py3: close branch Message-ID: <5c649518.1c69fb81.dc821.2066@mx.google.com> Author: Matti Picus Branch: unicode-utf8-py3 Changeset: r96004:e6b04cfc1802 Date: 2019-02-13 23:13 +0200 http://bitbucket.org/pypy/pypy/changeset/e6b04cfc1802/ Log: close branch From pypy.commits at gmail.com Wed Feb 13 17:07:27 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 14:07:27 -0800 (PST) Subject: [pypy-commit] pypy py3.5: merge unicode-utf8-py3 into py3.5 Message-ID: <5c64951f.1c69fb81.9b579.2728@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r96005:0bbb64dc7f98 Date: 2019-02-13 23:13 +0200 http://bitbucket.org/pypy/pypy/changeset/0bbb64dc7f98/ Log: merge unicode-utf8-py3 into py3.5 diff too long, truncating to 2000 out of 25065 lines diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -61,3 +61,9 @@ 9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +c8805ee6d7846ca2722b106eeaa2f128c699aba3 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +928a4f70d3de7d17449456946154c5da6e600162 release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +fb40f7a5524c77b80e6c468e087d621610137261 release-pypy3.6-v7.0.0 diff --git a/TODO b/TODO new file mode 100644 --- /dev/null +++ b/TODO @@ -0,0 +1,20 @@ +* find a better way to run "find" without creating the index storage, if one + if one is not already readily available (understand cost now, improve after merge) +* improve performance of splitlines (CF) +* think about cost of utf8 list strategy (CF) +* revisit why runicode import str_decode_utf_8_impl needed instead of runicode + import str_decode_utf_8 +* revisit remaining places in win32 where we do utf8.decode('utf-8'), they should work + directly with utf8 (can be converted via runicode.str_decode_utf_8 as well) + - rutf8.utf8_encode_mbcs + - unicodehelper.fsencode + - _winreg.interp_winreg +* remove 'assert not isinstance(*, unicode) +* add a flag that prevents support for unicode in rpython and enable it in PyPy (CF, Armin) +* convert all realunicode_w to unicode_w after we flush out all old uses of + unicode_w +* review all uses of W_Unicode.text_w, right now it is exactly W_Unicode.utf8_w. + It shoud only return valid utf8 (see 0be26dc39a59 which broke translation on + win32 and failed tests on linux64). Then we can use it in places like + _socket.interp_func.getaddrinfo instead of space.encode_unicode_object(w_port, + 'utf-8', 'strict') diff --git a/pypy/TODO b/pypy/TODO --- a/pypy/TODO +++ b/pypy/TODO @@ -1,6 +1,3 @@ -... - - antocuni's older TODO: * run coverage against the parser/astbuilder/astcompiler: it's probably full of @@ -11,3 +8,5 @@ * re-enable BUILD_LIST_FROM_ARG: see the comment in astcompiler/codegen.py in ast.ListComp.build_container + +* review use of std_decode_utf8, we probably do not want to be using it diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst --- a/pypy/doc/release-v7.0.0.rst +++ b/pypy/doc/release-v7.0.0.rst @@ -39,7 +39,7 @@ The utf8 branch that changes internal representation of unicode to utf8 did not make it into the release, so there is still more goodness coming. -You can download the v6.0 releases here: +You can download the v7.0 releases here: http://pypy.org/download.html 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 @@ -5,6 +5,11 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f +.. branch: zlib-copying-third-time-a-charm + +Make sure zlib decompressobjs have their streams deallocated immediately +on flush. + .. branch: zlib-copying-redux Fix calling copy on already-flushed compressobjs. @@ -15,7 +20,11 @@ as they do on CPython. -.. math-improvements +.. branch: math-improvements Improve performance of long operations where one of the operands fits into -an int. \ No newline at end of file +an int. + +.. branch: regalloc-playgrounds + +Improve register allocation in the JIT. diff --git a/pypy/doc/whatsnew-pypy2-5.10.0.rst b/pypy/doc/whatsnew-pypy2-5.10.0.rst --- a/pypy/doc/whatsnew-pypy2-5.10.0.rst +++ b/pypy/doc/whatsnew-pypy2-5.10.0.rst @@ -1,42 +1,42 @@ -========================== -What's new in PyPy2.7 5.10 -========================== - -.. this is a revision shortly after release-pypy2.7-v5.9.0 -.. startrev:d56dadcef996 - - -.. branch: cppyy-packaging - -Cleanup and improve cppyy packaging - -.. branch: docs-osx-brew-openssl - -.. branch: keep-debug-symbols - -Add a smartstrip tool, which can optionally keep the debug symbols in a -separate file, instead of just stripping them away. Use it in packaging - -.. branch: bsd-patches - -Fix failures on FreeBSD, contributed by David Naylor as patches on the issue -tracker (issues 2694, 2695, 2696, 2697) - -.. branch: run-extra-tests - -Run extra_tests/ in buildbot - -.. branch: vmprof-0.4.10 - -Upgrade the _vmprof backend to vmprof 0.4.10 - -.. branch: fix-vmprof-stacklet-switch -.. branch: fix-vmprof-stacklet-switch-2 - -Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) - -.. branch: win32-vcvars - -.. branch: rdict-fast-hash - -Make it possible to declare that the hash function of an r_dict is fast in RPython. +========================== +What's new in PyPy2.7 5.10 +========================== + +.. this is a revision shortly after release-pypy2.7-v5.9.0 +.. startrev:d56dadcef996 + + +.. branch: cppyy-packaging + +Cleanup and improve cppyy packaging + +.. branch: docs-osx-brew-openssl + +.. branch: keep-debug-symbols + +Add a smartstrip tool, which can optionally keep the debug symbols in a +separate file, instead of just stripping them away. Use it in packaging + +.. branch: bsd-patches + +Fix failures on FreeBSD, contributed by David Naylor as patches on the issue +tracker (issues 2694, 2695, 2696, 2697) + +.. branch: run-extra-tests + +Run extra_tests/ in buildbot + +.. branch: vmprof-0.4.10 + +Upgrade the _vmprof backend to vmprof 0.4.10 + +.. branch: fix-vmprof-stacklet-switch +.. branch: fix-vmprof-stacklet-switch-2 + +Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) + +.. branch: win32-vcvars + +.. branch: rdict-fast-hash + +Make it possible to declare that the hash function of an r_dict is fast in RPython. diff --git a/pypy/doc/whatsnew-pypy2-6.0.0.rst b/pypy/doc/whatsnew-pypy2-6.0.0.rst --- a/pypy/doc/whatsnew-pypy2-6.0.0.rst +++ b/pypy/doc/whatsnew-pypy2-6.0.0.rst @@ -1,132 +1,128 @@ -=========================== -What's new in PyPy2.7 5.10+ -=========================== - -.. this is a revision shortly after release-pypy2.7-v5.10.0 -.. startrev: 6b024edd9d12 - -.. branch: cpyext-avoid-roundtrip - -Big refactoring of some cpyext code, which avoids a lot of nonsense when -calling C from Python and vice-versa: the result is a big speedup in -function/method calls, up to 6 times faster. - -.. branch: cpyext-datetime2 - -Support ``tzinfo`` field on C-API datetime objects, fixes latest pandas HEAD - - -.. branch: mapdict-size-limit - -Fix a corner case of mapdict: When an instance is used like a dict (using -``setattr`` and ``getattr``, or ``.__dict__``) and a lot of attributes are -added, then the performance using mapdict is linear in the number of -attributes. This is now fixed (by switching to a regular dict after 80 -attributes). - - -.. branch: cpyext-faster-arg-passing - -When using cpyext, improve the speed of passing certain objects from PyPy to C -code, most notably None, True, False, types, all instances of C-defined types. -Before, a dict lookup was needed every time such an object crossed over, now it -is just a field read. - - -.. branch: 2634_datetime_timedelta_performance - -Improve datetime + timedelta performance. - -.. branch: memory-accounting - -Improve way to describe memory - -.. branch: msvc14 - -Allow compilaiton with Visual Studio 2017 compiler suite on windows - -.. branch: winapi - -Update _winapi and internal _winbase_cffi (via _winbase_build) for python 3 - -.. branch: refactor-slots - -Refactor cpyext slots. - - -.. branch: call-loopinvariant-into-bridges - -Speed up branchy code that does a lot of function inlining by saving one call -to read the TLS in most bridges. - -.. branch: rpython-sprint - -Refactor in rpython signatures - -.. branch: cpyext-tls-operror2 - -Store error state thread-locally in executioncontext, fixes issue #2764 - -.. branch: cpyext-fast-typecheck - -Optimize `Py*_Check` for `Bool`, `Float`, `Set`. Also refactor and simplify -`W_PyCWrapperObject` which is used to call slots from the C-API, greatly -improving microbenchmarks in https://github.com/antocuni/cpyext-benchmarks - - -.. branch: fix-sre-problems - -Fix two (unrelated) JIT bugs manifesting in the re module: - -- green fields are broken and were thus disabled, plus their usage removed from - the _sre implementation - -- in rare "trace is too long" situations, the JIT could break behaviour - arbitrarily. - -.. branch: jit-hooks-can-be-disabled - -Be more efficient about JIT hooks. Make it possible for the frontend to declare -that jit hooks are currently not enabled at all. in that case, the list of ops -does not have to be created in the case of the on_abort hook (which is -expensive). - - -.. branch: pyparser-improvements - -Improve speed of Python parser, improve ParseError messages slightly. - -.. branch: ioctl-arg-size - -Work around possible bugs in upstream ioctl users, like CPython allocate at -least 1024 bytes for the arg in calls to ``ioctl(fd, request, arg)``. Fixes -issue #2776 - -.. branch: cpyext-subclass-setattr - -Fix for python-level classes that inherit from C-API types, previously the -`w_obj` was not necessarily preserved throughout the lifetime of the `pyobj` -which led to cases where instance attributes were lost. Fixes issue #2793 - - -.. branch: pyparser-improvements-2 - -Improve line offsets that are reported by SyntaxError. Improve error messages -for a few situations, including mismatched parenthesis. - -.. branch: issue2752 - -Fix a rare GC bug that was introduced more than one year ago, but was -not diagnosed before issue #2752. - -.. branch: gc-hooks - -Introduce GC hooks, as documented in doc/gc_info.rst - -.. branch: gc-hook-better-timestamp - -Improve GC hooks - -.. branch: cppyy-packaging - -Update backend to 0.6.0 and support exceptions through wrappers +=========================== +What's new in PyPy2.7 5.10+ +=========================== + +.. this is a revision shortly after release-pypy2.7-v5.10.0 +.. startrev: 6b024edd9d12 + +.. branch: cpyext-avoid-roundtrip + +Big refactoring of some cpyext code, which avoids a lot of nonsense when +calling C from Python and vice-versa: the result is a big speedup in +function/method calls, up to 6 times faster. + +.. branch: cpyext-datetime2 + +Support ``tzinfo`` field on C-API datetime objects, fixes latest pandas HEAD + + +.. branch: mapdict-size-limit + +Fix a corner case of mapdict: When an instance is used like a dict (using +``setattr`` and ``getattr``, or ``.__dict__``) and a lot of attributes are +added, then the performance using mapdict is linear in the number of +attributes. This is now fixed (by switching to a regular dict after 80 +attributes). + + +.. branch: cpyext-faster-arg-passing + +When using cpyext, improve the speed of passing certain objects from PyPy to C +code, most notably None, True, False, types, all instances of C-defined types. +Before, a dict lookup was needed every time such an object crossed over, now it +is just a field read. + + +.. branch: 2634_datetime_timedelta_performance + +Improve datetime + timedelta performance. + +.. branch: memory-accounting + +Improve way to describe memory + +.. branch: msvc14 + +Allow compilaiton with Visual Studio 2017 compiler suite on windows + +.. branch: refactor-slots + +Refactor cpyext slots. + + +.. branch: call-loopinvariant-into-bridges + +Speed up branchy code that does a lot of function inlining by saving one call +to read the TLS in most bridges. + +.. branch: rpython-sprint + +Refactor in rpython signatures + +.. branch: cpyext-tls-operror2 + +Store error state thread-locally in executioncontext, fixes issue #2764 + +.. branch: cpyext-fast-typecheck + +Optimize `Py*_Check` for `Bool`, `Float`, `Set`. Also refactor and simplify +`W_PyCWrapperObject` which is used to call slots from the C-API, greatly +improving microbenchmarks in https://github.com/antocuni/cpyext-benchmarks + + +.. branch: fix-sre-problems + +Fix two (unrelated) JIT bugs manifesting in the re module: + +- green fields are broken and were thus disabled, plus their usage removed from + the _sre implementation + +- in rare "trace is too long" situations, the JIT could break behaviour + arbitrarily. + +.. branch: jit-hooks-can-be-disabled + +Be more efficient about JIT hooks. Make it possible for the frontend to declare +that jit hooks are currently not enabled at all. in that case, the list of ops +does not have to be created in the case of the on_abort hook (which is +expensive). + + +.. branch: pyparser-improvements + +Improve speed of Python parser, improve ParseError messages slightly. + +.. branch: ioctl-arg-size + +Work around possible bugs in upstream ioctl users, like CPython allocate at +least 1024 bytes for the arg in calls to ``ioctl(fd, request, arg)``. Fixes +issue #2776 + +.. branch: cpyext-subclass-setattr + +Fix for python-level classes that inherit from C-API types, previously the +`w_obj` was not necessarily preserved throughout the lifetime of the `pyobj` +which led to cases where instance attributes were lost. Fixes issue #2793 + + +.. branch: pyparser-improvements-2 + +Improve line offsets that are reported by SyntaxError. Improve error messages +for a few situations, including mismatched parenthesis. + +.. branch: issue2752 + +Fix a rare GC bug that was introduced more than one year ago, but was +not diagnosed before issue #2752. + +.. branch: gc-hooks + +Introduce GC hooks, as documented in doc/gc_info.rst + +.. branch: gc-hook-better-timestamp + +Improve GC hooks + +.. branch: cppyy-packaging + +Update backend to 0.6.0 and support exceptions through wrappers diff --git a/pypy/doc/whatsnew-pypy2-7.0.0.rst b/pypy/doc/whatsnew-pypy2-7.0.0.rst --- a/pypy/doc/whatsnew-pypy2-7.0.0.rst +++ b/pypy/doc/whatsnew-pypy2-7.0.0.rst @@ -1,69 +1,69 @@ -========================== -What's new in PyPy2.7 6.0+ -========================== - -.. this is a revision shortly after release-pypy-6.0.0 -.. startrev: e50e11af23f1 - -.. branch: cppyy-packaging - -Main items: vastly better template resolution and improved performance. In -detail: upgrade to backend 1.4, improved handling of templated methods and -functions (in particular automatic deduction of types), improved pythonization -interface, range of compatibility fixes for Python3, free functions now take -fast libffi path when possible, moves for strings (incl. from Python str), -easier/faster handling of std::vector by numpy, improved and faster object -identity preservation - -.. branch: socket_default_timeout_blockingness - -Make sure 'blocking-ness' of socket is set along with default timeout - -.. branch: crypt_h - -Include crypt.h for crypt() on Linux - -.. branch: gc-more-logging - -Log additional gc-minor and gc-collect-step info in the PYPYLOG - -.. branch: reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb - - -.. branch: pyparser-improvements-3 - -Small refactorings in the Python parser. - -.. branch: fix-readme-typo - -.. branch: avoid_shell_injection_in_shutil - -Backport CPython fix for possible shell injection issue in `distutils.spawn`, -https://bugs.python.org/issue34540 - -.. branch: cffi_dlopen_unicode - -Enable use of unicode file names in `dlopen` - -.. branch: rlock-in-rpython - -Backport CPython fix for `thread.RLock` - - -.. branch: expose-gc-time - -Make GC hooks measure time in seconds (as opposed to an opaque unit). - -.. branch: cleanup-test_lib_pypy - -Update most test_lib_pypy/ tests and move them to extra_tests/. - -.. branch: gc-disable - -Make it possible to manually manage the GC by using a combination of -gc.disable() and gc.collect_step(). Make sure to write a proper release -announcement in which we explain that existing programs could leak memory if -they run for too much time between a gc.disable()/gc.enable() +========================== +What's new in PyPy2.7 6.0+ +========================== + +.. this is a revision shortly after release-pypy-6.0.0 +.. startrev: e50e11af23f1 + +.. branch: cppyy-packaging + +Main items: vastly better template resolution and improved performance. In +detail: upgrade to backend 1.4, improved handling of templated methods and +functions (in particular automatic deduction of types), improved pythonization +interface, range of compatibility fixes for Python3, free functions now take +fast libffi path when possible, moves for strings (incl. from Python str), +easier/faster handling of std::vector by numpy, improved and faster object +identity preservation + +.. branch: socket_default_timeout_blockingness + +Make sure 'blocking-ness' of socket is set along with default timeout + +.. branch: crypt_h + +Include crypt.h for crypt() on Linux + +.. branch: gc-more-logging + +Log additional gc-minor and gc-collect-step info in the PYPYLOG + +.. branch: reverse-debugger + +The reverse-debugger branch has been merged. For more information, see +https://bitbucket.org/pypy/revdb + + +.. branch: pyparser-improvements-3 + +Small refactorings in the Python parser. + +.. branch: fix-readme-typo + +.. branch: avoid_shell_injection_in_shutil + +Backport CPython fix for possible shell injection issue in `distutils.spawn`, +https://bugs.python.org/issue34540 + +.. branch: cffi_dlopen_unicode + +Enable use of unicode file names in `dlopen` + +.. branch: rlock-in-rpython + +Backport CPython fix for `thread.RLock` + + +.. branch: expose-gc-time + +Make GC hooks measure time in seconds (as opposed to an opaque unit). + +.. branch: cleanup-test_lib_pypy + +Update most test_lib_pypy/ tests and move them to extra_tests/. + +.. branch: gc-disable + +Make it possible to manually manage the GC by using a combination of +gc.disable() and gc.collect_step(). Make sure to write a proper release +announcement in which we explain that existing programs could leak memory if +they run for too much time between a gc.disable()/gc.enable() diff --git a/pypy/doc/whatsnew-pypy3-5.10.0.rst b/pypy/doc/whatsnew-pypy3-5.10.0.rst --- a/pypy/doc/whatsnew-pypy3-5.10.0.rst +++ b/pypy/doc/whatsnew-pypy3-5.10.0.rst @@ -1,21 +1,7 @@ -========================= -What's new in PyPy3 5.9+ -========================= - -.. this is the revision after release-pypy3.5-5.9 -.. startrev: be41e3ac0a29 - -.. branch: sched_yield -Add sched_yield posix attribute - -.. branch: py3.5-appexec -Raise if space.is_true(space.appexec()) used in app level tests, fix tests -that did this - -.. branch: py3.5-mac-embedding -Download and patch dependencies when building cffi-based stdlib modules - -.. branch: os_lockf - -.. branch: py3.5-xattr -Add posix.*attr() functions +======================== +What's new in PyPy3 7.0+ +======================== + +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c + diff --git a/pypy/doc/whatsnew-pypy3-6.0.0.rst b/pypy/doc/whatsnew-pypy3-6.0.0.rst --- a/pypy/doc/whatsnew-pypy3-6.0.0.rst +++ b/pypy/doc/whatsnew-pypy3-6.0.0.rst @@ -1,28 +1,7 @@ -========================= -What's new in PyPy3 5.10+ -========================= +======================== +What's new in PyPy3 7.0+ +======================== -.. this is the revision after release-pypy3.5-v5.10 -.. startrev: 34c63fba0bba +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c -.. branch: hroncok/fix-typeerror-str-does-not-support-the-b-1514414905375 - -Fix for bytestrings in console repl - -.. branch: py3-winreg - -Update winreg module to use unicode, wide-strings - -.. branch: cpyext-py3-instancemethod-attributes - -Add missing ``__doc__``, ``__module__``, ``__name__`` attributes to -``instancemethod`` - -.. branch: winapi - -Update support for _winapi cffi module for python3 - -.. branch: py3.5-refactor-slots - -Refactor cpyext slots. - diff --git a/pypy/doc/whatsnew-pypy3-7.0.0.rst b/pypy/doc/whatsnew-pypy3-7.0.0.rst --- a/pypy/doc/whatsnew-pypy3-7.0.0.rst +++ b/pypy/doc/whatsnew-pypy3-7.0.0.rst @@ -5,15 +5,10 @@ .. this is the revision after release-pypy3.5-v6.0 .. startrev: 580e3e26cd32 -.. branch: hroncok/fix-multiprocessing-regression-on-newer--1524656522151 +.. branch: unicode-utf8 -Fix multiprocessing regression on newer glibcs +Use utf-8 internally to represent unicode strings -.. branch: py3.5-user-site-impl +.. branch: unicode-utf8-py3 -Use implementation-specific site directories in sysconfig like in Python2 - -.. branch: py3.5-reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb +Use utf-8 internally to represent unicode strings diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -83,7 +83,7 @@ ## con.interact() except OperationError as e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return 1 finally: @@ -91,7 +91,7 @@ space.finish() except OperationError as e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return 1 return exitcode @@ -148,7 +148,7 @@ except OperationError as e: if verbose: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return rffi.cast(rffi.INT, -1) finally: @@ -202,7 +202,7 @@ """) except OperationError as e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return -1 return 0 diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py --- a/pypy/interpreter/argument.py +++ b/pypy/interpreter/argument.py @@ -596,6 +596,10 @@ except IndexError: name = '?' else: + w_enc = space.newtext(space.sys.defaultencoding) + w_err = space.newtext("replace") + w_name = space.call_method(w_name, "encode", w_enc, + w_err) name = space.text_w(w_name) break self.kwd_name = name diff --git a/pypy/interpreter/astcompiler/astbuilder.py b/pypy/interpreter/astcompiler/astbuilder.py --- a/pypy/interpreter/astcompiler/astbuilder.py +++ b/pypy/interpreter/astcompiler/astbuilder.py @@ -58,6 +58,7 @@ self.space = space self.compile_info = compile_info self.root_node = n + # used in f-strings self.recursive_parser = recursive_parser def build_ast(self): diff --git a/pypy/interpreter/astcompiler/fstring.py b/pypy/interpreter/astcompiler/fstring.py --- a/pypy/interpreter/astcompiler/fstring.py +++ b/pypy/interpreter/astcompiler/fstring.py @@ -3,6 +3,7 @@ from pypy.interpreter import error from pypy.interpreter import unicodehelper from rpython.rlib.rstring import StringBuilder +from rpython.rlib.rutf8 import codepoints_in_utf8 def add_constant_string(astbuilder, joined_pieces, w_string, atom_node): @@ -21,10 +22,8 @@ joined_pieces.append(node(w_string, atom_node.get_lineno(), atom_node.get_column())) -def f_constant_string(astbuilder, joined_pieces, u, atom_node): - space = astbuilder.space - add_constant_string(astbuilder, joined_pieces, space.newunicode(u), - atom_node) +def f_constant_string(astbuilder, joined_pieces, w_u, atom_node): + add_constant_string(astbuilder, joined_pieces, w_u, atom_node) def f_string_compile(astbuilder, source, atom_node): # Note: a f-string is kept as a single literal up to here. @@ -259,20 +258,20 @@ i += 1 fstr.current_index = i + space = astbuilder.space literal = builder.build() + lgt = codepoints_in_utf8(literal) if not fstr.raw_mode and '\\' in literal: - space = astbuilder.space literal = parsestring.decode_unicode_utf8(space, literal, 0, len(literal)) - return unicodehelper.decode_unicode_escape(space, literal) - else: - return literal.decode('utf-8') + literal, lgt, pos = unicodehelper.decode_unicode_escape(space, literal) + return space.newtext(literal, lgt) def fstring_find_literal_and_expr(astbuilder, fstr, atom_node, rec): - # Return a tuple with the next literal part, and optionally the + # Return a tuple with the next literal part as a W_Unicode, and optionally the # following expression node. Updates the current index inside 'fstr'. - literal = fstring_find_literal(astbuilder, fstr, atom_node, rec) + w_u = fstring_find_literal(astbuilder, fstr, atom_node, rec) s = fstr.unparsed i = fstr.current_index @@ -284,7 +283,7 @@ # We must now be the start of an expression, on a '{'. assert s[i] == '{' expr = fstring_find_expr(astbuilder, fstr, atom_node, rec) - return literal, expr + return w_u, expr def parse_f_string(astbuilder, joined_pieces, fstr, atom_node, rec=0): @@ -303,11 +302,11 @@ "really the case", atom_node) while True: - literal, expr = fstring_find_literal_and_expr(astbuilder, fstr, + w_u, expr = fstring_find_literal_and_expr(astbuilder, fstr, atom_node, rec) # add the literal part - f_constant_string(astbuilder, joined_pieces, literal, atom_node) + f_constant_string(astbuilder, joined_pieces, w_u, atom_node) if expr is None: break # We're done with this f-string. diff --git a/pypy/interpreter/astcompiler/misc.py b/pypy/interpreter/astcompiler/misc.py --- a/pypy/interpreter/astcompiler/misc.py +++ b/pypy/interpreter/astcompiler/misc.py @@ -112,7 +112,7 @@ # only intern identifier-like strings from pypy.objspace.std.unicodeobject import _isidentifier if (space.is_w(space.type(w_const), space.w_unicode) and - _isidentifier(space.unicode_w(w_const))): + _isidentifier(space.utf8_w(w_const))): return space.new_interned_w_str(w_const) return w_const diff --git a/pypy/interpreter/astcompiler/optimize.py b/pypy/interpreter/astcompiler/optimize.py --- a/pypy/interpreter/astcompiler/optimize.py +++ b/pypy/interpreter/astcompiler/optimize.py @@ -5,7 +5,7 @@ from pypy.tool import stdlib_opcode as ops from pypy.interpreter.error import OperationError from rpython.rlib.unroll import unrolling_iterable -from rpython.rlib.runicode import MAXUNICODE +from rpython.rlib.rutf8 import MAXUNICODE from rpython.rlib.objectmodel import specialize @@ -326,7 +326,7 @@ # produce compatible pycs. if (self.space.isinstance_w(w_obj, self.space.w_unicode) and self.space.isinstance_w(w_const, self.space.w_unicode)): - #unistr = self.space.unicode_w(w_const) + #unistr = self.space.utf8_w(w_const) #if len(unistr) == 1: # ch = ord(unistr[0]) #else: diff --git a/pypy/interpreter/astcompiler/test/test_astbuilder.py b/pypy/interpreter/astcompiler/test/test_astbuilder.py --- a/pypy/interpreter/astcompiler/test/test_astbuilder.py +++ b/pypy/interpreter/astcompiler/test/test_astbuilder.py @@ -902,7 +902,7 @@ def test_flufl(self): source = "x <> y" - raises(SyntaxError, self.get_ast, source) + py.test.raises(SyntaxError, self.get_ast, source) comp = self.get_first_expr(source, flags=consts.CO_FUTURE_BARRY_AS_BDFL) assert isinstance(comp, ast.Compare) @@ -1130,7 +1130,7 @@ s = self.get_first_expr("b'hi' b' implicitly' b' extra'") assert isinstance(s, ast.Bytes) assert space.eq_w(s.s, space.newbytes("hi implicitly extra")) - raises(SyntaxError, self.get_first_expr, "b'hello' 'world'") + py.test.raises(SyntaxError, self.get_first_expr, "b'hello' 'world'") sentence = u"Die Männer ärgern sich!" source = u"# coding: utf-7\nstuff = '%s'" % (sentence,) info = pyparse.CompileInfo("", "exec") @@ -1325,8 +1325,8 @@ assert isinstance(if2, ast.Name) def test_cpython_issue12983(self): - raises(SyntaxError, self.get_ast, r"""b'\x'""") - raises(SyntaxError, self.get_ast, r"""b'\x0'""") + py.test.raises(SyntaxError, self.get_ast, r"""b'\x'""") + py.test.raises(SyntaxError, self.get_ast, r"""b'\x0'""") def test_matmul(self): mod = self.get_ast("a @ b") diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py --- a/pypy/interpreter/astcompiler/test/test_compiler.py +++ b/pypy/interpreter/astcompiler/test/test_compiler.py @@ -1,5 +1,6 @@ from __future__ import division import py, sys +from pytest import raises from pypy.interpreter.astcompiler import codegen, astbuilder, symtable, optimize from pypy.interpreter.pyparser import pyparse from pypy.interpreter.pyparser.test import expressions @@ -76,7 +77,7 @@ space = self.space pyco_expr = PyCode._from_code(space, co_expr) w_res = pyco_expr.exec_host_bytecode(w_dict, w_dict) - res = space.str_w(space.repr(w_res)) + res = space.text_w(space.repr(w_res)) expected_repr = self.get_py3_repr(expected) if isinstance(expected, float): # Float representation can vary a bit between interpreter @@ -1249,7 +1250,6 @@ def test_revdb_metavar(self): from pypy.interpreter.reverse_debugging import dbstate, setup_revdb - self.space.config.translation.reverse_debugger = True self.space.reverse_debugging = True try: setup_revdb(self.space) @@ -1264,9 +1264,6 @@ class AppTestCompiler: - def setup_class(cls): - cls.w_maxunicode = cls.space.wrap(sys.maxunicode) - def test_docstring_not_loaded(self): import io, dis, sys ns = {} @@ -1428,7 +1425,7 @@ ''', d) return d['f'](5) """) - assert 'generator' in space.str_w(space.repr(w_generator)) + assert 'generator' in space.text_w(space.repr(w_generator)) def test_folding_of_list_constants(self): for source in ( diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -3,7 +3,7 @@ from rpython.rlib.cache import Cache from rpython.tool.uid import HUGEVAL_BYTES -from rpython.rlib import jit, types +from rpython.rlib import jit, types, rutf8 from rpython.rlib.debug import make_sure_not_resized from rpython.rlib.objectmodel import (we_are_translated, newlist_hint, compute_unique_id, specialize, not_rpython) @@ -80,10 +80,10 @@ def getname(self, space): try: - return space.unicode_w(space.getattr(self, space.newtext('__name__'))) + return space.utf8_w(space.getattr(self, space.newtext('__name__'))) except OperationError as e: if e.match(space, space.w_TypeError) or e.match(space, space.w_AttributeError): - return u'?' + return '?' raise def getaddrstring(self, space): @@ -105,9 +105,9 @@ w_id = space.rshift(w_id, w_4) return ''.join(addrstring) - def getrepr(self, space, info, moreinfo=u''): - addrstring = unicode(self.getaddrstring(space)) - return space.newunicode(u"<%s at 0x%s%s>" % (info, addrstring, moreinfo)) + def getrepr(self, space, info, moreinfo=''): + addrstring = self.getaddrstring(space) + return space.newtext("<%s at 0x%s%s>" % (info, addrstring, moreinfo)) def getslotvalue(self, index): raise NotImplementedError @@ -245,11 +245,14 @@ def bytes_w(self, space): self._typed_unwrap_error(space, "bytes") - def unicode_w(self, space): - self._typed_unwrap_error(space, "string") + def text_w(self, space): + self._typed_unwrap_error(space, "unicode") - def text_w(self, space): - self._typed_unwrap_error(space, "string") + def utf8_w(self, space): + self._typed_unwrap_error(space, "unicode") + + def convert_to_w_unicode(self, space): + self._typed_unwrap_error(space, "unicode") def bytearray_list_of_chars_w(self, space): self._typed_unwrap_error(space, "bytearray") @@ -420,7 +423,7 @@ self.builtin_modules = {} self.reloading_modules = {} - self.interned_strings = make_weak_value_dictionary(self, unicode, W_Root) + self.interned_strings = make_weak_value_dictionary(self, str, W_Root) self.actionflag = ActionFlag() # changed by the signal module self.check_signal_action = None # changed by the signal module make_finalizer_queue(W_Root, self) @@ -781,12 +784,12 @@ def setitem_str(self, w_obj, key, w_value): # key is a "text", i.e. a byte string (in python3 it - # represents a utf-8-encoded unicode) + # represents a valid utf-8-encoded unicode) return self.setitem(w_obj, self.newtext(key), w_value) def finditem_str(self, w_obj, key): # key is a "text", i.e. a byte string (in python3 it - # represents a utf-8-encoded unicode) + # represents a valid utf-8-encoded unicode) return self.finditem(w_obj, self.newtext(key)) def finditem(self, w_obj, w_key): @@ -820,9 +823,9 @@ def new_interned_w_str(self, w_u): assert isinstance(w_u, W_Root) # and is not None - u = self.unicode_w(w_u) + u = self.utf8_w(w_u) if not we_are_translated(): - assert type(u) is unicode + assert type(u) is str w_u1 = self.interned_strings.get(u) if w_u1 is None: w_u1 = w_u @@ -835,12 +838,11 @@ # returns a "text" object (ie str in python2 and unicode in python3) if not we_are_translated(): assert type(s) is str - u = s.decode('utf-8') - w_s1 = self.interned_strings.get(u) + w_s1 = self.interned_strings.get(s) if w_s1 is None: - w_s1 = self.newunicode(u) + w_s1 = self.newtext(s) if self._side_effects_ok(): - self.interned_strings.set(u, w_s1) + self.interned_strings.set(s, w_s1) return w_s1 def _revdb_startup(self): @@ -879,11 +881,7 @@ # interface for marshal_impl if not we_are_translated(): assert type(s) is str - try: - u = s.decode('utf-8') - except UnicodeDecodeError: - return None - return self.interned_strings.get(u) # may be None + return self.interned_strings.get(s) # may be None @specialize.arg(1) def descr_self_interp_w(self, RequiredClass, w_obj): @@ -1066,7 +1064,7 @@ """ return None - def listview_unicode(self, w_list): + def listview_utf8(self, w_list): """ Return a list of unwrapped unicode out of a list of unicode. If the argument is not a list or does not contain only unicode, return None. May return None anyway. @@ -1096,8 +1094,15 @@ def newlist_bytes(self, list_s): return self.newlist([self.newbytes(s) for s in list_s]) - def newlist_unicode(self, list_u): - return self.newlist([self.newunicode(u) for u in list_u]) + def newlist_utf8(self, list_u, is_ascii): + l_w = [None] * len(list_u) + for i, item in enumerate(list_u): + if not is_ascii: + length = rutf8.check_utf8(item, True) + else: + length = len(item) + l_w[i] = self.newutf8(item, length) + return self.newlist(l_w) def newlist_int(self, list_i): return self.newlist([self.newint(i) for i in list_i]) @@ -1595,6 +1600,8 @@ else: assert False + if self.isinstance_w(w_obj, self.w_unicode): + return w_obj.charbuf_w(self) def text_or_none_w(self, w_obj): return None if self.is_none(w_obj) else self.text_w(w_obj) @@ -1617,18 +1624,22 @@ an utf-8 encoded rpython string. """ assert w_obj is not None + if not self.isinstance_w(w_obj, self.w_unicode): + w_obj._typed_unwrap_error(self, "unicode") return w_obj.text_w(self) @not_rpython # tests only; should be replaced with bytes_w or text_w def str_w(self, w_obj): """ - if w_obj is unicode, call text_w() (i.e., return the UTF-8-nosg + if w_obj is unicode, call utf8_w() (i.e., return the UTF-8-nosg encoded string). Else, call bytes_w(). We should kill str_w completely and manually substitute it with text_w/bytes_w at all call sites. It remains for now for tests only. """ + XXX # deprecated, leaving in place for clear errors if self.isinstance_w(w_obj, self.w_unicode): + # XXX lo text_w, but better to deprecate str_w than to fix this return w_obj.text_w(self) else: return w_obj.bytes_w(self) @@ -1711,23 +1722,38 @@ assert w_obj is not None return w_obj.float_w(self, allow_conversion) - @specialize.argtype(1) - def unicode_w(self, w_obj): - assert w_obj is not None - return w_obj.unicode_w(self) + def utf8_w(self, w_obj): + return w_obj.utf8_w(self) - def unicode0_w(self, w_obj): - "Like unicode_w, but rejects strings with NUL bytes." + def utf8_0_w(self, w_obj): + "Like utf_w, but rejects strings with NUL bytes." from rpython.rlib import rstring - result = w_obj.unicode_w(self) - if u'\x00' in result: + result = w_obj.utf8_w(self) + if '\x00' in result: + raise oefmt(self.w_TypeError, + "argument must be a string without NUL " + "characters") + return rstring.assert_str0(result) + + def convert_to_w_unicode(self, w_obj): + return w_obj.convert_to_w_unicode(self) + + def realunicode_w(self, w_obj): + from pypy.interpreter.unicodehelper import decode_utf8sp + utf8 = self.utf8_w(w_obj) + return decode_utf8sp(self, utf8)[0].decode('utf8') + + def utf8_0_w(self, w_obj): + "Like utf8_w, but rejects strings with NUL bytes." + from rpython.rlib import rstring + result = w_obj.utf8_w(self) + if '\x00' in result: raise oefmt(self.w_ValueError, - "argument must be a unicode string without NUL " + "argument must be a utf8 string without NUL " "characters") return rstring.assert_str0(result) realtext_w = text_w # Python 2 compatibility - realunicode_w = unicode_w def fsencode(space, w_obj): from pypy.interpreter.unicodehelper import fsencode @@ -1742,6 +1768,27 @@ w_obj = self.fsencode(w_obj) return self.bytesbuf0_w(w_obj) + def convert_arg_to_w_unicode(self, w_obj, strict=None): + # XXX why convert_to_w_unicode does something slightly different? + from pypy.objspace.std.unicodeobject import W_UnicodeObject + # for z_translation tests + if hasattr(self, 'is_fake_objspace'): return self.newtext("foobar") + return W_UnicodeObject.convert_arg_to_w_unicode(self, w_obj, strict) + + def utf8_len_w(self, w_obj): + w_obj = self.convert_arg_to_w_unicode(w_obj) + return w_obj._utf8, w_obj._len() + + def realutf8_w(self, w_obj): + # Like utf8_w(), but only works if w_obj is really of type + # 'unicode'. On Python 3 this is the same as utf8_w(). + from pypy.objspace.std.unicodeobject import W_UnicodeObject + # for z_translation tests + if hasattr(self, 'is_fake_objspace'): return self.newtext("foobar") + if not isinstance(w_obj, W_UnicodeObject): + raise oefmt(self.w_TypeError, "argument must be a unicode") + return self.utf8_w(w_obj) + def bytesbuf0_w(self, w_obj): # Like bytes0_w(), but also accept a read-only buffer. from rpython.rlib import rstring @@ -1759,7 +1806,7 @@ def fsdecode_w(self, w_obj): if self.isinstance_w(w_obj, self.w_bytes): w_obj = self.fsdecode(w_obj) - return self.unicode0_w(w_obj) + return self.utf8_w(w_obj) def bool_w(self, w_obj): # Unwraps a bool, also accepting an int for compatibility. @@ -2087,7 +2134,7 @@ 'float_w', 'uint_w', 'bigint_w', - 'unicode_w', + 'utf8_w', 'unwrap', 'is_true', 'is_w', diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -9,8 +9,7 @@ from rpython.rlib.objectmodel import we_are_translated, specialize from rpython.rlib.objectmodel import dont_inline, not_rpython from rpython.rlib import rstack, rstackovf -from rpython.rlib import rwin32 -from rpython.rlib import runicode +from rpython.rlib import rwin32, rutf8 from pypy.interpreter import debug @@ -21,7 +20,8 @@ def strerror(errno): """Translate an error code to a unicode message string.""" from pypy.module._codecs.locale import str_decode_locale_surrogateescape - return str_decode_locale_surrogateescape(os.strerror(errno)) + utf8, lgt = str_decode_locale_surrogateescape(os.strerror(errno)) + return utf8, lgt class OperationError(Exception): """Interpreter-level exception that signals an exception that should be @@ -72,7 +72,7 @@ space = getattr(self.w_type, 'space', None) if space is not None: if self.__class__ is not OperationError and s is None: - s = self._compute_value(space) + s, lgt = self._compute_value(space) try: s = space.text_w(s) except Exception: @@ -306,8 +306,8 @@ def get_w_value(self, space): w_value = self._w_value if w_value is None: - value = self._compute_value(space) - self._w_value = w_value = space.newunicode(value) + value, lgt = self._compute_value(space) + self._w_value = w_value = space.newtext(value, lgt) return w_value def _compute_value(self, space): @@ -472,16 +472,7 @@ assert len(formats) > 0, "unsupported: no % command found" return tuple(parts), tuple(formats) -def _decode_utf8(string): - # when building the error message, don't crash if the byte string - # provided is not valid UTF-8 - assert isinstance(string, str) - result, consumed = runicode.str_decode_utf_8( - string, len(string), "replace", final=True) - return result - def get_operrcls2(valuefmt): - valuefmt = valuefmt.decode('ascii') strings, formats = decompose_valuefmt(valuefmt) assert len(strings) == len(formats) + 1 try: @@ -501,30 +492,49 @@ def _compute_value(self, space): lst = [None] * (len(formats) + len(formats) + 1) + lgt = 0 for i, fmt, attr in entries: lst[i + i] = self.xstrings[i] + lgt += len(self.xstrings[i]) value = getattr(self, attr) if fmt == 'd': - result = str(value).decode('ascii') + result = str(value) + lgt += len(result) elif fmt == 'R': - result = space.unicode_w(space.repr(value)) + result = space.utf8_w(space.repr(value)) + lgt += len(result) elif fmt == 'S': - result = space.unicode_w(space.str(value)) + result = space.utf8_w(space.str(value)) + lgt += len(result) elif fmt == 'T': - result = _decode_utf8(space.type(value).name) + result = space.type(value).name + lgt += len(result) elif fmt == 'N': result = value.getname(space) + lgt += len(result) elif fmt == '8': - result = _decode_utf8(value) + # u'str\uxxxx' -> 'str\xXX\xXX' -> u"'str\xXX\xXX'" + from pypy.interpreter import unicodehelper + result, _lgt, pos = unicodehelper.str_decode_utf8( + value, 'replace', True, + unicodehelper.decode_never_raise, True) + lgt += _lgt + elif isinstance(value, unicode): + # 's' + result = str(value.encode('utf-8')) + lgt += len(value) else: - if isinstance(value, unicode): - result = value - else: - result = _decode_utf8(str(value)) + result = str(value) + try: + lgt += rutf8.check_utf8(result, True) + except rutf8.CheckError as e: + lgt -= e.pos lst[i + i + 1] = result lst[-1] = self.xstrings[-1] - return u''.join(lst) - # + lgt += len(self.xstrings[-1]) + retval = ''.join(lst) + return retval, lgt + _fmtcache2[formats] = OpErrFmt return OpErrFmt, strings @@ -534,7 +544,7 @@ self.setup(w_type) def _compute_value(self, space): - return self._value.decode('utf-8') + return self._value, len(self._value) def async(self, space): # also matches a RuntimeError("maximum rec.") if the stack is @@ -565,8 +575,8 @@ %8 - The result of arg.decode('utf-8') %N - The result of w_arg.getname(space) - %R - The result of space.unicode_w(space.repr(w_arg)) - %S - The result of space.unicode_w(space.str(w_arg)) + %R - The result of space.utf8_w(space.repr(w_arg)) + %S - The result of space.utf8_w(space.str(w_arg)) %T - The result of space.type(w_arg).name """ @@ -621,12 +631,13 @@ if rwin32.WIN32 and isinstance(e, WindowsError): winerror = e.winerror try: - msg = rwin32.FormatErrorW(winerror) + msg, lgt = rwin32.FormatErrorW(winerror) except ValueError: - msg = u'Windows Error %d' % winerror + msg = 'Windows Error %d' % winerror + lgt = len(msg) w_errno = space.w_None w_winerror = space.newint(winerror) - w_msg = space.newunicode(msg) + w_msg = space.newtext(msg, lgt) else: errno = e.errno if errno == EINTR: @@ -635,12 +646,13 @@ return None try: - msg = strerror(errno) + msg, lgt = strerror(errno) except ValueError: - msg = u'error %d' % errno + msg = 'error %d' % errno + lgt = len(msg) w_errno = space.newint(errno) w_winerror = space.w_None - w_msg = space.newunicode(msg) + w_msg = space.newtext(msg, lgt) if w_filename is None: w_filename = space.w_None @@ -670,9 +682,9 @@ eintr_retry=eintr_retry) def exception_from_errno(space, w_type, errno): - msg = strerror(errno) + msg, lgt = strerror(errno) w_error = space.call_function(w_type, space.newint(errno), - space.newunicode(msg)) + space.newtext(msg, lgt)) return OperationError(w_type, w_error) def exception_from_saved_errno(space, w_type): diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py --- a/pypy/interpreter/function.py +++ b/pypy/interpreter/function.py @@ -45,7 +45,8 @@ closure=None, w_ann=None, forcename=None, qualname=None): self.space = space self.name = forcename or code.co_name - self.qualname = qualname or self.name.decode('utf-8') + self.qualname = qualname or self.name + assert isinstance(self.qualname, str) self.w_doc = None # lazily read from code.getdocstring() self.code = code # Code instance self.w_func_globals = w_globals # the globals dictionary @@ -255,7 +256,7 @@ return self.call_args(__args__) def descr_function_repr(self): - return self.getrepr(self.space, u'function %s' % self.qualname) + return self.getrepr(self.space, 'function %s' % self.qualname) def _cleanup_(self): @@ -313,7 +314,7 @@ tup_base = [] tup_state = [ space.newtext(self.name), - space.newunicode(self.qualname), + space.newtext(self.qualname), w_doc, self.code, w_func_globals, @@ -337,7 +338,7 @@ self.space = space self.name = space.text_w(w_name) - self.qualname = space.unicode_w(w_qualname) + self.qualname = space.utf8_w(w_qualname) self.code = space.interp_w(Code, w_code) if not space.is_w(w_closure, space.w_None): from pypy.interpreter.nestedscope import Cell @@ -430,11 +431,11 @@ "__name__ must be set to a string object") def fget_func_qualname(self, space): - return space.newunicode(self.qualname) + return space.newtext(self.qualname) def fset_func_qualname(self, space, w_name): try: - self.qualname = space.unicode_w(w_name) + self.qualname = space.realutf8_w(w_name) except OperationError as e: if e.match(space, space.w_TypeError): raise oefmt(space.w_TypeError, @@ -549,14 +550,14 @@ name = self.w_function.getname(self.space) else: try: - name = space.unicode_w(w_name) + name = space.utf8_w(w_name) except OperationError as e: if not e.match(space, space.w_TypeError): raise - name = u'?' - objrepr = space.unicode_w(space.repr(self.w_instance)) - s = u'' % (name, objrepr) - return space.newunicode(s) + name = '?' + objrepr = space.utf8_w(space.repr(self.w_instance)) + s = b'' % (name, objrepr) + return space.newtext(s) def descr_method_getattribute(self, w_attr): space = self.space @@ -598,7 +599,7 @@ else: w_builtins = space.getbuiltinmodule('builtins') new_inst = space.getattr(w_builtins, space.newtext('getattr')) - tup = [w_instance, space.newunicode(w_function.getname(space))] + tup = [w_instance, space.newtext(w_function.getname(space))] return space.newtuple([new_inst, space.newtuple(tup)]) @@ -699,7 +700,7 @@ return self.space.newtext('' % (self.name,)) def descr__reduce__(self, space): - return space.newunicode(self.qualname) + return space.newtext(self.qualname) def is_builtin_code(w_func): from pypy.interpreter.gateway import BuiltinCode diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py --- a/pypy/interpreter/gateway.py +++ b/pypy/interpreter/gateway.py @@ -174,6 +174,9 @@ def visit_unicode(self, el, app_sig): self.checked_space_method(el, app_sig) + def visit_utf8(self, el, app_sig): + self.checked_space_method(el, app_sig) + def visit_fsencode(self, el, app_sig): self.checked_space_method(el, app_sig) @@ -324,7 +327,10 @@ self.run_args.append("space.text0_w(%s)" % (self.scopenext(),)) def visit_unicode(self, typ): - self.run_args.append("space.unicode_w(%s)" % (self.scopenext(),)) + self.run_args.append("space.realunicode_w(%s)" % (self.scopenext(),)) + + def visit_utf8(self, typ): + self.run_args.append("space.utf8_w(%s)" % (self.scopenext(),)) def visit_fsencode(self, typ): self.run_args.append("space.fsencode_w(%s)" % (self.scopenext(),)) @@ -492,11 +498,14 @@ self.unwrap.append("space.text_w(%s)" % (self.nextarg(),)) def visit_unicode(self, typ): - self.unwrap.append("space.unicode_w(%s)" % (self.nextarg(),)) + self.unwrap.append("space.realunicode_w(%s)" % (self.nextarg(),)) def visit_text0(self, typ): self.unwrap.append("space.text0_w(%s)" % (self.nextarg(),)) + def visit_utf8(self, typ): + self.unwrap.append("space.utf8_w(%s)" % (self.nextarg(),)) + def visit_fsencode(self, typ): self.unwrap.append("space.fsencode_w(%s)" % (self.nextarg(),)) @@ -567,8 +576,10 @@ assert typ in (int, str, float, unicode, r_longlong, r_uint, r_ulonglong, bool) if typ is r_int is r_longlong: return 'gateway_r_longlong_w' - elif typ in (str, unicode): - return typ.__name__ + '_w' + elif typ is str: + return 'utf8_w' + elif typ is unicode: + return 'realunicode_w' elif typ is bool: # For argument clinic's "bool" specifier: accept any object, and # convert it to a boolean value. If you don't want this @@ -1113,7 +1124,7 @@ kw_defs_w = [] for name, w_def in sorted(alldefs_w.items()): assert name in sig.kwonlyargnames - w_name = space.newunicode(name.decode('utf-8')) + w_name = space.newtext(name) kw_defs_w.append((w_name, w_def)) return defs_w, kw_defs_w diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -38,14 +38,12 @@ # 'qualname' is a unicode string if self._qualname is not None: return self._qualname - return self.get_name().decode('utf-8') + return self.get_name() def descr__repr__(self, space): addrstring = self.getaddrstring(space) - return space.newunicode(u"<%s object %s at 0x%s>" % - (unicode(self.KIND), - self.get_qualname(), - unicode(addrstring))) + return space.newtext("<%s object %s at 0x%s>" % + (self.KIND, self.get_qualname(), addrstring)) def descr_send(self, w_arg): """send(arg) -> send 'arg' into generator/coroutine, @@ -215,7 +213,7 @@ e2.record_context(space, space.getexecutioncontext()) raise e2 else: - space.warn(space.newunicode(u"generator '%s' raised StopIteration" + space.warn(space.newtext("generator '%s' raised StopIteration" % self.get_qualname()), space.w_PendingDeprecationWarning) @@ -308,11 +306,11 @@ "__name__ must be set to a string object") def descr__qualname__(self, space): - return space.newunicode(self.get_qualname()) + return space.newtext(self.get_qualname()) def descr_set__qualname__(self, space, w_name): try: - self._qualname = space.unicode_w(w_name) + self._qualname = space.utf8_w(w_name) except OperationError as e: if e.match(space, space.w_TypeError): raise oefmt(space.w_TypeError, @@ -399,8 +397,8 @@ self.frame is not None and \ self.frame.last_instr == -1: space = self.space - msg = u"coroutine '%s' was never awaited" % self.get_qualname() - space.warn(space.newunicode(msg), space.w_RuntimeWarning) + msg = "coroutine '%s' was never awaited" % self.get_qualname() + space.warn(space.newtext(msg), space.w_RuntimeWarning) GeneratorOrCoroutine._finalize_(self) diff --git a/pypy/interpreter/mixedmodule.py b/pypy/interpreter/mixedmodule.py --- a/pypy/interpreter/mixedmodule.py +++ b/pypy/interpreter/mixedmodule.py @@ -130,7 +130,7 @@ bltin.w_module = self.w_name func._builtinversion_ = bltin bltin.name = name - bltin.qualname = bltin.name.decode('utf-8') + bltin.qualname = bltin.name w_value = bltin space.setitem(self.w_dict, w_name, w_value) return w_value diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py --- a/pypy/interpreter/pycode.py +++ b/pypy/interpreter/pycode.py @@ -206,7 +206,7 @@ self.co_filename = '/%s' % (basename,) self.w_filename = self.space.newfilename(self.co_filename) - co_names = property(lambda self: [self.space.str_w(w_name) for w_name in self.co_names_w]) # for trace + co_names = property(lambda self: [self.space.text_w(w_name) for w_name in self.co_names_w]) # for trace def signature(self): return self._signature @@ -452,8 +452,8 @@ def repr(self, space): space = self.space # co_name should be an identifier - name = self.co_name.decode('utf-8') - fn = space.unicode_w(self.w_filename) - return space.newunicode(u'' % ( - name, unicode(self.getaddrstring(space)), fn, + name = self.co_name + fn = space.utf8_w(self.w_filename) + return space.newtext(b'' % ( + name, self.getaddrstring(space), fn, -1 if self.co_firstlineno == 0 else self.co_firstlineno)) diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py --- a/pypy/interpreter/pyopcode.py +++ b/pypy/interpreter/pyopcode.py @@ -1081,8 +1081,8 @@ try: w_pkgname = space.getattr( w_module, space.newtext('__name__')) - w_fullname = space.newunicode(u'%s.%s' % - (space.unicode_w(w_pkgname), space.unicode_w(w_name))) + w_fullname = space.newtext(b'%s.%s' % + (space.utf8_w(w_pkgname), space.utf8_w(w_name))) return space.getitem(space.sys.get('modules'), w_fullname) except OperationError: raise oefmt( @@ -1333,7 +1333,7 @@ def _make_function(self, oparg, freevars=None): space = self.space w_qualname = self.popvalue() - qualname = self.space.unicode_w(w_qualname) + qualname = self.space.utf8_w(w_qualname) w_codeobj = self.popvalue() codeobj = self.space.interp_w(PyCode, w_codeobj) if freevars is not None: @@ -1628,7 +1628,7 @@ if (oparg & consts.FVS_MASK) == consts.FVS_HAVE_SPEC: w_spec = self.popvalue() else: - w_spec = space.newunicode(u'') + w_spec = space.newtext('') w_value = self.popvalue() # conversion = oparg & consts.FVC_MASK @@ -1649,9 +1649,9 @@ lst = [] for i in range(itemcount-1, -1, -1): w_item = self.peekvalue(i) - lst.append(space.unicode_w(w_item)) + lst.append(space.utf8_w(w_item)) self.dropvalues(itemcount) - w_res = space.newunicode(u''.join(lst)) + w_res = space.newtext(''.join(lst)) self.pushvalue(w_res) def _revdb_load_var(self, oparg): diff --git a/pypy/interpreter/pyparser/error.py b/pypy/interpreter/pyparser/error.py --- a/pypy/interpreter/pyparser/error.py +++ b/pypy/interpreter/pyparser/error.py @@ -29,20 +29,24 @@ except: # we can't allow any exceptions here! return None""") elif self.text is not None: - from rpython.rlib.runicode import str_decode_utf_8 + from rpython.rlib.runicode import str_decode_utf_8_impl # self.text may not be UTF-8 in case of decoding errors. # adjust the encoded text offset to a decoded offset # XXX do the right thing about continuation lines, which # XXX are their own fun, sometimes giving offset > # XXX len(self.text) for example (right now, avoid crashing) + def replace_error_handler(errors, encoding, msg, s, startpos, endpos): + # must return unicode + return u'\ufffd', endpos if offset > len(self.text): offset = len(self.text) - text, _ = str_decode_utf_8(self.text, offset, 'replace') + text, _ = str_decode_utf_8_impl(self.text, offset, + 'replace', False, replace_error_handler, True) offset = len(text) if len(self.text) != offset: - text, _ = str_decode_utf_8(self.text, len(self.text), - 'replace') - w_text = space.newunicode(text) + text, _ = str_decode_utf_8_impl(self.text, len(self.text), + 'replace', False, replace_error_handler, True) + w_text = space.newtext(text.encode('utf8'), len(text)) return space.newtuple([ space.newtext(self.msg), space.newtuple([ diff --git a/pypy/interpreter/pyparser/parsestring.py b/pypy/interpreter/pyparser/parsestring.py --- a/pypy/interpreter/pyparser/parsestring.py +++ b/pypy/interpreter/pyparser/parsestring.py @@ -1,4 +1,5 @@ # coding: utf-8 +from rpython.rlib import rutf8 from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter import unicodehelper @@ -91,9 +92,11 @@ if encoding is None: substr = s[ps:q] else: + unicodehelper.check_utf8_or_raise(space, s, ps, q) substr = decode_unicode_utf8(space, s, ps, q) - v = unicodehelper.decode_unicode_escape(space, substr) - return space.newunicode(v) + r = unicodehelper.decode_unicode_escape(space, substr) + v, length, pos = r + return space.newutf8(v, length) assert 0 <= ps <= q substr = s[ps : q] @@ -111,8 +114,8 @@ elif saw_f: return W_FString(substr, rawmode) else: - v = unicodehelper.decode_utf8(space, substr) - return space.newunicode(v) + v = unicodehelper.str_decode_utf8(substr, 'strict', True, None) + return space.newtext(*v) v = PyString_DecodeEscape(space, substr, 'strict', encoding) return space.newbytes(v) @@ -135,15 +138,12 @@ # the backslash we just wrote, we emit "\u005c" # instead. lis.append("u005c") - if ord(s[ps]) & 0x80: # XXX inefficient - w, ps = decode_utf8(space, s, ps, end) - for c in w: - # The equivalent of %08x, which is not supported by RPython. - # 7 zeroes are enough for the unicode range, and the - # result still fits in 32-bit. - hexa = hex(ord(c) + 0x10000000) - lis.append('\\U0') - lis.append(hexa[3:]) # Skip 0x and the leading 1 + if ord(s[ps]) & 0x80: + cp = rutf8.codepoint_at_pos(s, ps) + hexa = hex(cp + 0x10000000) + lis.append('\\U0') + lis.append(hexa[3:]) # Skip 0x and the leading 1 + ps = rutf8.next_codepoint_pos(s, ps) else: lis.append(s[ps]) ps += 1 @@ -250,20 +250,29 @@ ch >= 'A' and ch <= 'F') -def decode_utf8(space, s, ps, end): +def check_utf8(space, s, ps, end): assert ps >= 0 pt = ps # while (s < end && *s != '\\') s++; */ /* inefficient for u".." while ps < end and ord(s[ps]) & 0x80: ps += 1 - u = unicodehelper.decode_utf8(space, s[pt:ps]) - return u, ps + try: + rutf8.check_utf8(s, True, pt, ps) + except rutf8.CheckError as e: + lgt, flag = rutf8.check_utf8(s, True, pt, e.pos) + unicodehelper.decode_error_handler(space)('strict', 'utf8', + 'invalid utf-8', s, pt + lgt, pt + lgt + 1) + return s[pt:ps] def decode_utf8_recode(space, s, ps, end, recode_encoding): - u, ps = decode_utf8(space, s, ps, end) - w_v = unicodehelper.encode(space, space.newunicode(u), recode_encoding) + p = ps + while p < end and ord(s[p]) & 0x80: + p += 1 + lgt = unicodehelper.check_utf8_or_raise(space, s, ps, p) + w_v = unicodehelper.encode(space, space.newutf8(s[ps:p], lgt), + recode_encoding) v = space.bytes_w(w_v) - return v, ps + return v, p def raise_app_valueerror(space, msg): raise OperationError(space.w_ValueError, space.newtext(msg)) diff --git a/pypy/interpreter/pyparser/pytokenizer.py b/pypy/interpreter/pyparser/pytokenizer.py --- a/pypy/interpreter/pyparser/pytokenizer.py +++ b/pypy/interpreter/pyparser/pytokenizer.py @@ -6,6 +6,7 @@ from pypy.interpreter.pyparser.pytokenize import tabsize, alttabsize, whiteSpaceDFA, \ triple_quoted, endDFAs, single_quoted, pseudoDFA from pypy.interpreter.astcompiler import consts +from rpython.rlib import rutf8 NAMECHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_' NUMCHARS = '0123456789' @@ -46,14 +47,9 @@ def verify_utf8(token): - for c in token: - if ord(c) >= 0x80: - break - else: - return True try: - u = token.decode('utf-8') - except UnicodeDecodeError: + rutf8.check_utf8(token, False) + except rutf8.CheckError: return False return True @@ -69,17 +65,12 @@ def verify_identifier(token): # 1=ok; 0=not an identifier; -1=bad utf-8 - for c in token: - if ord(c) >= 0x80: - break - else: - return 1 try: - u = token.decode('utf-8') - except UnicodeDecodeError: + rutf8.check_utf8(token, False) + except rutf8.CheckError: return -1 from pypy.objspace.std.unicodeobject import _isidentifier - return _isidentifier(u) + return _isidentifier(token) DUMMY_DFA = automata.DFA([], []) diff --git a/pypy/interpreter/pyparser/test/test_parsestring.py b/pypy/interpreter/pyparser/test/test_parsestring.py --- a/pypy/interpreter/pyparser/test/test_parsestring.py +++ b/pypy/interpreter/pyparser/test/test_parsestring.py @@ -10,7 +10,7 @@ assert space.bytes_w(w_ret) == value elif isinstance(value, unicode): assert space.type(w_ret) == space.w_unicode - assert space.unicode_w(w_ret) == value + assert space.utf8_w(w_ret).decode('utf8') == value else: assert False @@ -61,7 +61,7 @@ s = "u'\x81'" s = s.decode("koi8-u").encode("utf8")[1:] w_ret = parsestring.parsestr(self.space, 'koi8-u', s) - ret = space.unwrap(w_ret) + ret = w_ret._utf8.decode('utf8') assert ret == eval("# -*- coding: koi8-u -*-\nu'\x81'") def test_unicode_pep414(self): @@ -112,14 +112,14 @@ space = self.space s = '"""' + '\\' + '\n"""' w_ret = parsestring.parsestr(space, None, s) - assert space.str_w(w_ret) == '' + assert space.text_w(w_ret) == '' def test_bug1(self): space = self.space expected = ['x', ' ', chr(0xc3), chr(0xa9), ' ', '\n'] input = ["'", 'x', ' ', chr(0xc3), chr(0xa9), ' ', chr(92), 'n', "'"] w_ret = parsestring.parsestr(space, 'utf8', ''.join(input)) - assert space.str_w(w_ret) == ''.join(expected) + assert space.text_w(w_ret) == ''.join(expected) def test_wide_unicode_in_source(self): if sys.maxunicode == 65535: @@ -131,7 +131,4 @@ def test_decode_unicode_utf8(self): buf = parsestring.decode_unicode_utf8(self.space, 'u"\xf0\x9f\x92\x8b"', 2, 6) - if sys.maxunicode == 65535: - assert buf == r"\U0000d83d\U0000dc8b" - else: - assert buf == r"\U0001f48b" + assert buf == r"\U0001f48b" diff --git a/pypy/interpreter/test/test_appinterp.py b/pypy/interpreter/test/test_appinterp.py --- a/pypy/interpreter/test/test_appinterp.py +++ b/pypy/interpreter/test/test_appinterp.py @@ -155,7 +155,7 @@ w_mymod2 = MyModule(space2, space2.wrap('mymod')) w_str = space1.getattr(w_mymod1, space1.wrap("hi")) - assert space1.str_w(w_str) == "hello" + assert space1.text_w(w_str) == "hello" class TestMixedModuleUnfreeze: spaceconfig = dict(usemodules=('_socket',)) diff --git a/pypy/interpreter/test/test_argument.py b/pypy/interpreter/test/test_argument.py --- a/pypy/interpreter/test/test_argument.py +++ b/pypy/interpreter/test/test_argument.py @@ -55,6 +55,9 @@ pass class DummySpace(object): + class sys: + defaultencoding = 'utf-8' + def newtuple(self, items): return tuple(items) @@ -92,16 +95,15 @@ def getitem(self, obj, key): return obj[key] - def wrap(self, obj): + def wrap(self, obj, lgt=-1): return obj newtext = wrap - newunicode = wrap def text_w(self, s): - return self.unicode_w(s).encode('utf-8') + return self.utf8_w(s) - def unicode_w(self, s): - return unicode(s) + def utf8_w(self, s): + return s def len(self, x): return len(x) @@ -135,7 +137,7 @@ def type(self, obj): class Type: def getname(self, space): - return unicode(type(obj).__name__) + return type(obj).__name__ return Type() @@ -343,14 +345,14 @@ def test_unwrap_error(self): space = DummySpace() valuedummy = object() - def unicode_w(w): + def utf8_w(w): if w is None: raise OperationError(TypeError, None) if w is valuedummy: raise OperationError(ValueError, None) - return str(w) - space.unicode_w = unicode_w - space.text_w = unicode_w + return bytes(w, 'utf-8') + space.utf8_w = utf8_w + space.text_w = utf8_w excinfo = py.test.raises(OperationError, Arguments, space, [], ["a"], [1], w_starstararg={None: 1}) assert excinfo.value.w_type is TypeError @@ -672,14 +674,14 @@ try: Arguments(space, [], w_stararg=space.wrap(42)) except OperationError as e: - msg = space.str_w(space.str(e.get_w_value(space))) + msg = space.text_w(space.str(e.get_w_value(space))) assert msg == "argument after * must be an iterable, not int" else: assert 0, "did not raise" try: Arguments(space, [], w_starstararg=space.wrap(42)) except OperationError as e: - msg = space.str_w(space.str(e.get_w_value(space))) + msg = space.text_w(space.str(e.get_w_value(space))) assert msg == "argument after ** must be a mapping, not int" else: assert 0, "did not raise" @@ -838,7 +840,6 @@ From pypy.commits at gmail.com Wed Feb 13 17:07:29 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 14:07:29 -0800 (PST) Subject: [pypy-commit] pypy py3.5: merge default into branch Message-ID: <5c649521.1c69fb81.3e808.1c47@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r96006:f468bb5ec141 Date: 2019-02-13 23:18 +0200 http://bitbucket.org/pypy/pypy/changeset/f468bb5ec141/ Log: merge default into branch diff --git a/pypy/doc/extending.rst b/pypy/doc/extending.rst --- a/pypy/doc/extending.rst +++ b/pypy/doc/extending.rst @@ -45,16 +45,13 @@ with the `CPython ctypes`_ version. It works for large examples, such as pyglet. PyPy's implementation is not strictly 100% compatible with CPython, but close enough for most cases. - -We also used to provide ``ctypes-configure`` for some API-level access. -This is now viewed as a precursor of CFFI, which you should use instead. More (but older) information is available :doc:`here `. Also, ctypes' performance is not as good as CFFI's. .. _CPython ctypes: http://docs.python.org/library/ctypes.html PyPy implements ctypes as pure Python code around two built-in modules -called ``_ffi`` and ``_rawffi``, which give a very low-level binding to +called ``_rawffi`` and ``_rawffi.alt``, which give a very low-level binding to the C library libffi_. Nowadays it is not recommended to use directly these two modules. diff --git a/pypy/doc/index.rst b/pypy/doc/index.rst --- a/pypy/doc/index.rst +++ b/pypy/doc/index.rst @@ -103,7 +103,7 @@ the `development mailing list`_. .. _#pypy on irc.freenode.net: irc://irc.freenode.net/pypy -.. _here: https://botbot.me/freenode/pypy/ +.. _here: https://quodlibet.duckdns.org/irc/pypy/latest.log.html#irc-end .. _Development mailing list: http://mail.python.org/mailman/listinfo/pypy-dev .. _Commit mailing list: http://mail.python.org/mailman/listinfo/pypy-commit .. _Development bug/feature tracker: https://bitbucket.org/pypy/pypy/issues diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst --- a/pypy/doc/release-v7.0.0.rst +++ b/pypy/doc/release-v7.0.0.rst @@ -19,11 +19,12 @@ Until we can work with downstream providers to distribute builds with PyPy, we have made packages for some common packages `available as wheels`_. -The GC `hooks`_ , which can be used to gain more insights into its +The `GC hooks`_ , which can be used to gain more insights into its performance, has been improved and it is now possible to manually manage the GC by using a combination of ``gc.disable`` and ``gc.collect_step``. See the `GC blog post`_. +.. _`GC hooks`: http://doc.pypy.org/en/latest/gc_info.html#semi-manual-gc-management We updated the `cffi`_ module included in PyPy to version 1.12, and the `cppyy`_ backend to 1.4. Please use these to wrap your C and C++ code, @@ -49,7 +50,7 @@ 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 +and `RPython`_ documentation improvements, tweaking popular modules to run on pypy, or general `help`_ with making RPython's JIT even better. .. _`PyPy`: index.html 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 @@ -25,6 +25,15 @@ Improve performance of long operations where one of the operands fits into an int. -.. branch: regalloc-playgrounds +.. branch: regalloc-playground Improve register allocation in the JIT. + +.. branch: promote-unicode + +Implement rlib.jit.promote_unicode to complement promote_string + +.. branch: unicode-utf8 + +Use utf8 internally to represent unicode, with the goal of never using rpython-level unicode + diff --git a/pypy/module/zlib/test/test_zlib.py b/pypy/module/zlib/test/test_zlib.py --- a/pypy/module/zlib/test/test_zlib.py +++ b/pypy/module/zlib/test/test_zlib.py @@ -2,8 +2,8 @@ Tests for the zlib module. """ +import pypy import py -import pypy try: import zlib @@ -15,7 +15,7 @@ from pypy.module.zlib import interp_zlib from rpython.rlib import rzlib except ImportError: - import py; py.test.skip("no zlib C library on this machine") + py.test.skip("no zlib C library on this machine") class AppTestZlib(object): @@ -403,6 +403,7 @@ assert (d1 + from_copy) == (d1 + from_compressor) + @py.test.mark.skipif(rzlib.ZLIB_VERSION == '1.2.8', reason='does not error check') def test_cannot_copy_compressor_with_stream_in_inconsistent_state(self): if self.runappdirect: skip("can't run with -A") compressor = self.zlib.compressobj() diff --git a/rpython/jit/backend/arm/regalloc.py b/rpython/jit/backend/arm/regalloc.py --- a/rpython/jit/backend/arm/regalloc.py +++ b/rpython/jit/backend/arm/regalloc.py @@ -305,9 +305,8 @@ operations = cpu.gc_ll_descr.rewrite_assembler(cpu, operations, allgcrefs) # compute longevity of variables - longevity, last_real_usage = compute_vars_longevity(inputargs, operations) + longevity = compute_vars_longevity(inputargs, operations) self.longevity = longevity - self.last_real_usage = last_real_usage fm = self.frame_manager asm = self.assembler self.vfprm = VFPRegisterManager(longevity, fm, asm) @@ -1062,7 +1061,7 @@ position = self.rm.position for arg in inputargs: assert not isinstance(arg, Const) - if self.last_real_usage.get(arg, -1) <= position: + if self.longevity[arg].is_last_real_use_before(position): self.force_spill_var(arg) # diff --git a/rpython/jit/backend/llsupport/assembler.py b/rpython/jit/backend/llsupport/assembler.py --- a/rpython/jit/backend/llsupport/assembler.py +++ b/rpython/jit/backend/llsupport/assembler.py @@ -77,6 +77,20 @@ self._debug = False self.loop_run_counters = [] + # XXX register allocation statistics to be removed later + self.num_moves_calls = 0 + self.num_moves_jump = 0 + self.num_spills = 0 + self.num_spills_to_existing = 0 + self.num_reloads = 0 + + self.preamble_num_moves_calls = 0 + self.preamble_num_moves_jump = 0 + self.preamble_num_spills = 0 + self.preamble_num_spills_to_existing = 0 + self.preamble_num_reloads = 0 + + def stitch_bridge(self, faildescr, target): raise NotImplementedError diff --git a/rpython/jit/backend/x86/assembler.py b/rpython/jit/backend/x86/assembler.py --- a/rpython/jit/backend/x86/assembler.py +++ b/rpython/jit/backend/x86/assembler.py @@ -90,19 +90,6 @@ self.target_tokens_currently_compiling = {} self.frame_depth_to_patch = [] - # XXX register allocation statistics to be removed later - self.num_moves_calls = 0 - self.num_moves_jump = 0 - self.num_spills = 0 - self.num_spills_to_existing = 0 - self.num_reloads = 0 - - self.preamble_num_moves_calls = 0 - self.preamble_num_moves_jump = 0 - self.preamble_num_spills = 0 - self.preamble_num_spills_to_existing = 0 - self.preamble_num_reloads = 0 - def teardown(self): self.pending_guard_tokens = None diff --git a/rpython/jit/backend/x86/test/test_regalloc.py b/rpython/jit/backend/x86/test/test_regalloc.py --- a/rpython/jit/backend/x86/test/test_regalloc.py +++ b/rpython/jit/backend/x86/test/test_regalloc.py @@ -419,4 +419,6 @@ # 1 because lifetime of i172 does not end at the int_xor # 1 ptr to save before call # 3 for argument shuffling - assert len(self.filter_log_moves()) == 11 + + # XXX there is an additional mov, find out why! + assert len(self.filter_log_moves()) == 12 diff --git a/rpython/jit/backend/zarch/regalloc.py b/rpython/jit/backend/zarch/regalloc.py --- a/rpython/jit/backend/zarch/regalloc.py +++ b/rpython/jit/backend/zarch/regalloc.py @@ -316,13 +316,13 @@ orig_var_even = reverse_mapping[even] if orig_var_even in forbidden_vars: continue # duh! - self._sync_var(orig_var_even) + self._sync_var_to_stack(orig_var_even) del self.reg_bindings[orig_var_even] elif which_to_spill == SPILL_ODD: orig_var_odd = reverse_mapping[odd] if orig_var_odd in forbidden_vars: continue # duh! - self._sync_var(orig_var_odd) + self._sync_var_to_stack(orig_var_odd) del self.reg_bindings[orig_var_odd] # well, we got away with a single spill :) @@ -344,10 +344,10 @@ continue if orig_var_even is not None: - self._sync_var(orig_var_even) + self._sync_var_to_stack(orig_var_even) del self.reg_bindings[orig_var_even] if orig_var_odd is not None: - self._sync_var(orig_var_odd) + self._sync_var_to_stack(orig_var_odd) del self.reg_bindings[orig_var_odd] self.reg_bindings[even_var] = even @@ -371,7 +371,7 @@ forbidden_vars, odd) else: # old even var is not forbidden, sync it and be done with it - self._sync_var(old_even_var) + self._sync_var_to_stack(old_even_var) del self.reg_bindings[old_even_var] del reverse_mapping[odd] if old_odd_var: @@ -379,7 +379,7 @@ self._relocate_forbidden_variable(odd, old_odd_var, reverse_mapping, forbidden_vars, even) else: - self._sync_var(old_odd_var) + self._sync_var_to_stack(old_odd_var) del self.reg_bindings[old_odd_var] del reverse_mapping[odd] @@ -406,7 +406,7 @@ candidate_var = reverse_mapping.get(candidate, None) if not candidate_var or candidate_var not in forbidden_vars: if candidate_var is not None: - self._sync_var(candidate_var) + self._sync_var_to_stack(candidate_var) del self.reg_bindings[candidate_var] del reverse_mapping[candidate] self.assembler.regalloc_mov(reg, candidate) diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py --- a/rpython/jit/codewriter/jtransform.py +++ b/rpython/jit/codewriter/jtransform.py @@ -596,6 +596,23 @@ op1 = SpaceOperation('str_guard_value', [op.args[0], c, descr], op.result) return [SpaceOperation('-live-', [], None), op1, None] + if (hints.get('promote_unicode') and + op.args[0].concretetype is not lltype.Void): + U = lltype.Ptr(rstr.UNICODE) + assert op.args[0].concretetype == U + self._register_extra_helper(EffectInfo.OS_UNIEQ_NONNULL, + "str.eq_nonnull", + [U, U], + lltype.Signed, + EffectInfo.EF_ELIDABLE_CANNOT_RAISE) + descr, p = self.callcontrol.callinfocollection.callinfo_for_oopspec( + EffectInfo.OS_UNIEQ_NONNULL) + # XXX this is fairly ugly way of creating a constant, + # however, callinfocollection has no better interface + c = Constant(p.adr.ptr, lltype.typeOf(p.adr.ptr)) + op1 = SpaceOperation('str_guard_value', [op.args[0], c, descr], + op.result) + return [SpaceOperation('-live-', [], None), op1, None] if hints.get('force_virtualizable'): return SpaceOperation('hint_force_virtualizable', [op.args[0]], None) if hints.get('force_no_const'): # for tests only diff --git a/rpython/jit/codewriter/test/test_jtransform.py b/rpython/jit/codewriter/test/test_jtransform.py --- a/rpython/jit/codewriter/test/test_jtransform.py +++ b/rpython/jit/codewriter/test/test_jtransform.py @@ -94,7 +94,7 @@ return True return False def callinfo_for_oopspec(self, oopspecindex): - assert oopspecindex == effectinfo.EffectInfo.OS_STREQ_NONNULL + # assert oopspecindex == effectinfo.EffectInfo.OS_STREQ_NONNULL class c: class adr: ptr = 1 @@ -1129,6 +1129,21 @@ assert op1.result == v2 assert op0.opname == '-live-' +def test_unicode_promote(): + PUNICODE = lltype.Ptr(rstr.UNICODE) + v1 = varoftype(PUNICODE) + v2 = varoftype(PUNICODE) + op = SpaceOperation('hint', + [v1, Constant({'promote_unicode': True}, lltype.Void)], + v2) + tr = Transformer(FakeCPU(), FakeBuiltinCallControl()) + op0, op1, _ = tr.rewrite_operation(op) + assert op1.opname == 'str_guard_value' + assert op1.args[0] == v1 + assert op1.args[2] == 'calldescr' + assert op1.result == v2 + assert op0.opname == '-live-' + def test_double_promote_str(): PSTR = lltype.Ptr(rstr.STR) v1 = varoftype(PSTR) diff --git a/rpython/jit/metainterp/test/test_string.py b/rpython/jit/metainterp/test/test_string.py --- a/rpython/jit/metainterp/test/test_string.py +++ b/rpython/jit/metainterp/test/test_string.py @@ -3,7 +3,7 @@ from rpython.jit.metainterp.test.support import LLJitMixin from rpython.rlib.debug import debug_print from rpython.rlib.jit import (JitDriver, dont_look_inside, we_are_jitted, - promote_string) + promote_string, promote_unicode) from rpython.rlib.rstring import StringBuilder, UnicodeBuilder @@ -518,6 +518,19 @@ self.meta_interp(f, [0]) self.check_resops(call_r=2, call_i=5) + def test_promote_unicode(self): + driver = JitDriver(greens = [], reds = ['n']) + + def f(n): + while n < 21: + driver.jit_merge_point(n=n) + promote_unicode(unicode(str(n % 3))) + n += 1 + return 0 + + self.meta_interp(f, [0]) + self.check_resops(call_r=4, call_i=5) + def test_join_chars(self): jitdriver = JitDriver(reds=['a', 'b', 'c', 'i'], greens=[]) _str = self._str diff --git a/rpython/memory/gc/test/test_direct.py b/rpython/memory/gc/test/test_direct.py --- a/rpython/memory/gc/test/test_direct.py +++ b/rpython/memory/gc/test/test_direct.py @@ -781,7 +781,7 @@ def large_malloc(): # malloc an object which is large enough to trigger a major collection threshold = self.gc.next_major_collection_threshold - self.malloc(VAR, int(threshold/8)) + self.malloc(VAR, int(threshold/4)) summary = debuglog.summary() debuglog.reset() return summary diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py --- a/rpython/rlib/jit.py +++ b/rpython/rlib/jit.py @@ -84,6 +84,7 @@ * promote - promote the argument from a variable into a constant * promote_string - same, but promote string by *value* + * promote_unicode - same, but promote unicode string by *value* * access_directly - directly access a virtualizable, as a structure and don't treat it as a virtualizable * fresh_virtualizable - means that virtualizable was just allocated. @@ -126,6 +127,9 @@ def promote_string(x): return hint(x, promote_string=True) +def promote_unicode(x): + return hint(x, promote_unicode=True) + def dont_look_inside(func): """ Make sure the JIT does not trace inside decorated function (it becomes a call instead) diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -254,7 +254,7 @@ 'sched.h', 'grp.h', 'dirent.h', 'sys/stat.h', 'fcntl.h', 'signal.h', 'sys/utsname.h', _ptyh] - if sys.platform.startswith('linux'): + if sys.platform.startswith('linux') or sys.platform.startswith('gnu'): includes.append('sys/sysmacros.h') if sys.platform.startswith('freebsd') or sys.platform.startswith('openbsd'): includes.append('sys/ttycom.h') diff --git a/rpython/rlib/test/test_rzlib.py b/rpython/rlib/test/test_rzlib.py --- a/rpython/rlib/test/test_rzlib.py +++ b/rpython/rlib/test/test_rzlib.py @@ -274,7 +274,7 @@ rzlib.deflateEnd(copied) assert bytes1 + bytes_copy == compressed - + at py.test.mark.skipif(rzlib.ZLIB_VERSION == '1.2.8', reason='does not error check') def test_unsuccessful_compress_copy(): """ Errors during unsuccesful deflateCopy operations raise RZlibErrors. From pypy.commits at gmail.com Wed Feb 13 17:07:31 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 14:07:31 -0800 (PST) Subject: [pypy-commit] pypy py3.5: fix whatsnew Message-ID: <5c649523.1c69fb81.dc527.2010@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r96007:1c6f81f3c5cf Date: 2019-02-13 23:21 +0200 http://bitbucket.org/pypy/pypy/changeset/1c6f81f3c5cf/ Log: fix whatsnew 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 @@ -5,3 +5,6 @@ .. this is the revision after release-pypy3.5-v7.0 .. startrev: 9d2fa7c63b7c +.. branch: unicode-utf8-py3 + +Use utf8 instead of rpython-level unicode From pypy.commits at gmail.com Wed Feb 13 17:31:36 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 14:31:36 -0800 (PST) Subject: [pypy-commit] pypy py3.5: backport 3.6 changes Message-ID: <5c649ac8.1c69fb81.5bcb7.2cd7@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r96008:a66a6d79084f Date: 2019-02-14 00:30 +0200 http://bitbucket.org/pypy/pypy/changeset/a66a6d79084f/ Log: backport 3.6 changes diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -136,11 +136,12 @@ from pypy.module._codecs import interp_codecs state = space.fromcache(interp_codecs.CodecState) unicodedata_handler = state.get_unicodedata_handler(space) - return str_decode_unicode_escape( + s, blen, ulen, first_escape_error_char = str_decode_unicode_escape( string, "strict", final=True, errorhandler=state.decode_error_handler, ud_handler=unicodedata_handler) + return s, blen, ulen def decode_raw_unicode_escape(space, string): return str_decode_raw_unicode_escape( @@ -501,10 +502,11 @@ def str_decode_unicode_escape(s, errors, final, errorhandler, ud_handler): size = len(s) if size == 0: - return '', 0, 0 + return '', 0, 0, None builder = rutf8.Utf8StringBuilder(size) pos = 0 + first_escape_error_char = None while pos < size: ch = s[pos] @@ -619,8 +621,9 @@ else: builder.append_char('\\') builder.append_code(ord(ch)) + first_escape_error_char = ch - return builder.build(), builder.getlength(), pos + return builder.build(), builder.getlength(), pos, first_escape_error_char def wcharpsize2utf8(space, wcharp, size): """Safe version of rffi.wcharpsize2utf8. diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -960,7 +960,7 @@ unicode_name_handler = state.get_unicodedata_handler(space) - result, lgt, u_len = unicodehelper.str_decode_unicode_escape( + result, lgt, u_len, first_escape_error_char = unicodehelper.str_decode_unicode_escape( string, errors, final, state.decode_error_handler, unicode_name_handler) From pypy.commits at gmail.com Wed Feb 13 17:52:23 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 14:52:23 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix merge Message-ID: <5c649fa7.1c69fb81.7fcb5.1f16@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96010:c3059a6a2591 Date: 2019-02-14 00:51 +0200 http://bitbucket.org/pypy/pypy/changeset/c3059a6a2591/ Log: fix merge diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -8,9 +8,6 @@ from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault from pypy.interpreter import unicodehelper -from pypy.interpreter.unicodehelper import ( - unicode_encode_utf_8_impl, - str_decode_unicode_escape) from pypy.module.unicodedata import unicodedb From pypy.commits at gmail.com Wed Feb 13 17:52:21 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 13 Feb 2019 14:52:21 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge py3.5 into branch Message-ID: <5c649fa5.1c69fb81.9228c.6ae0@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96009:9a4153f0307e Date: 2019-02-14 00:39 +0200 http://bitbucket.org/pypy/pypy/changeset/9a4153f0307e/ Log: merge py3.5 into branch diff too long, truncating to 2000 out of 27730 lines diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -58,3 +58,12 @@ 3f6eaa010fce78cc7973bdc1dfdb95970f08fed2 release-pypy3.5-v5.10.1 ab0b9caf307db6592905a80b8faffd69b39005b8 release-pypy2.7-v6.0.0 fdd60ed87e941677e8ea11acf9f1819466521bf2 release-pypy3.5-v6.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +c8805ee6d7846ca2722b106eeaa2f128c699aba3 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +928a4f70d3de7d17449456946154c5da6e600162 release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +fb40f7a5524c77b80e6c468e087d621610137261 release-pypy3.6-v7.0.0 diff --git a/TODO b/TODO new file mode 100644 --- /dev/null +++ b/TODO @@ -0,0 +1,20 @@ +* find a better way to run "find" without creating the index storage, if one + if one is not already readily available (understand cost now, improve after merge) +* improve performance of splitlines (CF) +* think about cost of utf8 list strategy (CF) +* revisit why runicode import str_decode_utf_8_impl needed instead of runicode + import str_decode_utf_8 +* revisit remaining places in win32 where we do utf8.decode('utf-8'), they should work + directly with utf8 (can be converted via runicode.str_decode_utf_8 as well) + - rutf8.utf8_encode_mbcs + - unicodehelper.fsencode + - _winreg.interp_winreg +* remove 'assert not isinstance(*, unicode) +* add a flag that prevents support for unicode in rpython and enable it in PyPy (CF, Armin) +* convert all realunicode_w to unicode_w after we flush out all old uses of + unicode_w +* review all uses of W_Unicode.text_w, right now it is exactly W_Unicode.utf8_w. + It shoud only return valid utf8 (see 0be26dc39a59 which broke translation on + win32 and failed tests on linux64). Then we can use it in places like + _socket.interp_func.getaddrinfo instead of space.encode_unicode_object(w_port, + 'utf-8', 'strict') diff --git a/pypy/TODO b/pypy/TODO --- a/pypy/TODO +++ b/pypy/TODO @@ -1,6 +1,3 @@ -... - - antocuni's older TODO: * run coverage against the parser/astbuilder/astcompiler: it's probably full of @@ -11,3 +8,5 @@ * re-enable BUILD_LIST_FROM_ARG: see the comment in astcompiler/codegen.py in ast.ListComp.build_container + +* review use of std_decode_utf8, we probably do not want to be using it diff --git a/pypy/doc/extending.rst b/pypy/doc/extending.rst --- a/pypy/doc/extending.rst +++ b/pypy/doc/extending.rst @@ -45,16 +45,13 @@ with the `CPython ctypes`_ version. It works for large examples, such as pyglet. PyPy's implementation is not strictly 100% compatible with CPython, but close enough for most cases. - -We also used to provide ``ctypes-configure`` for some API-level access. -This is now viewed as a precursor of CFFI, which you should use instead. More (but older) information is available :doc:`here `. Also, ctypes' performance is not as good as CFFI's. .. _CPython ctypes: http://docs.python.org/library/ctypes.html PyPy implements ctypes as pure Python code around two built-in modules -called ``_ffi`` and ``_rawffi``, which give a very low-level binding to +called ``_rawffi`` and ``_rawffi.alt``, which give a very low-level binding to the C library libffi_. Nowadays it is not recommended to use directly these two modules. diff --git a/pypy/doc/index.rst b/pypy/doc/index.rst --- a/pypy/doc/index.rst +++ b/pypy/doc/index.rst @@ -103,7 +103,7 @@ the `development mailing list`_. .. _#pypy on irc.freenode.net: irc://irc.freenode.net/pypy -.. _here: https://botbot.me/freenode/pypy/ +.. _here: https://quodlibet.duckdns.org/irc/pypy/latest.log.html#irc-end .. _Development mailing list: http://mail.python.org/mailman/listinfo/pypy-dev .. _Commit mailing list: http://mail.python.org/mailman/listinfo/pypy-commit .. _Development bug/feature tracker: https://bitbucket.org/pypy/pypy/issues diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst --- a/pypy/doc/release-v7.0.0.rst +++ b/pypy/doc/release-v7.0.0.rst @@ -19,11 +19,12 @@ Until we can work with downstream providers to distribute builds with PyPy, we have made packages for some common packages `available as wheels`_. -The GC `hooks`_ , which can be used to gain more insights into its +The `GC hooks`_ , which can be used to gain more insights into its performance, has been improved and it is now possible to manually manage the GC by using a combination of ``gc.disable`` and ``gc.collect_step``. See the `GC blog post`_. +.. _`GC hooks`: http://doc.pypy.org/en/latest/gc_info.html#semi-manual-gc-management We updated the `cffi`_ module included in PyPy to version 1.12, and the `cppyy`_ backend to 1.4. Please use these to wrap your C and C++ code, @@ -39,7 +40,7 @@ The utf8 branch that changes internal representation of unicode to utf8 did not make it into the release, so there is still more goodness coming. -You can download the v6.0 releases here: +You can download the v7.0 releases here: http://pypy.org/download.html @@ -49,7 +50,7 @@ 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 +and `RPython`_ documentation improvements, tweaking popular modules to run on pypy, or general `help`_ with making RPython's JIT even better. .. _`PyPy`: index.html 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 @@ -4,3 +4,31 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f + +.. branch: zlib-copying-third-time-a-charm + +Make sure zlib decompressobjs have their streams deallocated immediately +on flush. + +.. branch: zlib-copying-redux + +Fix calling copy on already-flushed compressobjs. + + + +.. branch: math-improvements + +Improve performance of long operations where one of the operands fits into +an int. + +.. branch: regalloc-playground + +Improve register allocation in the JIT. + +.. branch: promote-unicode + +Implement rlib.jit.promote_unicode to complement promote_string + +.. branch: unicode-utf8 + +Use utf8 internally to represent unicode, with the goal of never using rpython-level unicode diff --git a/pypy/doc/whatsnew-pypy2-5.10.0.rst b/pypy/doc/whatsnew-pypy2-5.10.0.rst --- a/pypy/doc/whatsnew-pypy2-5.10.0.rst +++ b/pypy/doc/whatsnew-pypy2-5.10.0.rst @@ -1,42 +1,42 @@ -========================== -What's new in PyPy2.7 5.10 -========================== - -.. this is a revision shortly after release-pypy2.7-v5.9.0 -.. startrev:d56dadcef996 - - -.. branch: cppyy-packaging - -Cleanup and improve cppyy packaging - -.. branch: docs-osx-brew-openssl - -.. branch: keep-debug-symbols - -Add a smartstrip tool, which can optionally keep the debug symbols in a -separate file, instead of just stripping them away. Use it in packaging - -.. branch: bsd-patches - -Fix failures on FreeBSD, contributed by David Naylor as patches on the issue -tracker (issues 2694, 2695, 2696, 2697) - -.. branch: run-extra-tests - -Run extra_tests/ in buildbot - -.. branch: vmprof-0.4.10 - -Upgrade the _vmprof backend to vmprof 0.4.10 - -.. branch: fix-vmprof-stacklet-switch -.. branch: fix-vmprof-stacklet-switch-2 - -Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) - -.. branch: win32-vcvars - -.. branch: rdict-fast-hash - -Make it possible to declare that the hash function of an r_dict is fast in RPython. +========================== +What's new in PyPy2.7 5.10 +========================== + +.. this is a revision shortly after release-pypy2.7-v5.9.0 +.. startrev:d56dadcef996 + + +.. branch: cppyy-packaging + +Cleanup and improve cppyy packaging + +.. branch: docs-osx-brew-openssl + +.. branch: keep-debug-symbols + +Add a smartstrip tool, which can optionally keep the debug symbols in a +separate file, instead of just stripping them away. Use it in packaging + +.. branch: bsd-patches + +Fix failures on FreeBSD, contributed by David Naylor as patches on the issue +tracker (issues 2694, 2695, 2696, 2697) + +.. branch: run-extra-tests + +Run extra_tests/ in buildbot + +.. branch: vmprof-0.4.10 + +Upgrade the _vmprof backend to vmprof 0.4.10 + +.. branch: fix-vmprof-stacklet-switch +.. branch: fix-vmprof-stacklet-switch-2 + +Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) + +.. branch: win32-vcvars + +.. branch: rdict-fast-hash + +Make it possible to declare that the hash function of an r_dict is fast in RPython. diff --git a/pypy/doc/whatsnew-pypy2-6.0.0.rst b/pypy/doc/whatsnew-pypy2-6.0.0.rst --- a/pypy/doc/whatsnew-pypy2-6.0.0.rst +++ b/pypy/doc/whatsnew-pypy2-6.0.0.rst @@ -1,132 +1,128 @@ -=========================== -What's new in PyPy2.7 5.10+ -=========================== - -.. this is a revision shortly after release-pypy2.7-v5.10.0 -.. startrev: 6b024edd9d12 - -.. branch: cpyext-avoid-roundtrip - -Big refactoring of some cpyext code, which avoids a lot of nonsense when -calling C from Python and vice-versa: the result is a big speedup in -function/method calls, up to 6 times faster. - -.. branch: cpyext-datetime2 - -Support ``tzinfo`` field on C-API datetime objects, fixes latest pandas HEAD - - -.. branch: mapdict-size-limit - -Fix a corner case of mapdict: When an instance is used like a dict (using -``setattr`` and ``getattr``, or ``.__dict__``) and a lot of attributes are -added, then the performance using mapdict is linear in the number of -attributes. This is now fixed (by switching to a regular dict after 80 -attributes). - - -.. branch: cpyext-faster-arg-passing - -When using cpyext, improve the speed of passing certain objects from PyPy to C -code, most notably None, True, False, types, all instances of C-defined types. -Before, a dict lookup was needed every time such an object crossed over, now it -is just a field read. - - -.. branch: 2634_datetime_timedelta_performance - -Improve datetime + timedelta performance. - -.. branch: memory-accounting - -Improve way to describe memory - -.. branch: msvc14 - -Allow compilaiton with Visual Studio 2017 compiler suite on windows - -.. branch: winapi - -Update _winapi and internal _winbase_cffi (via _winbase_build) for python 3 - -.. branch: refactor-slots - -Refactor cpyext slots. - - -.. branch: call-loopinvariant-into-bridges - -Speed up branchy code that does a lot of function inlining by saving one call -to read the TLS in most bridges. - -.. branch: rpython-sprint - -Refactor in rpython signatures - -.. branch: cpyext-tls-operror2 - -Store error state thread-locally in executioncontext, fixes issue #2764 - -.. branch: cpyext-fast-typecheck - -Optimize `Py*_Check` for `Bool`, `Float`, `Set`. Also refactor and simplify -`W_PyCWrapperObject` which is used to call slots from the C-API, greatly -improving microbenchmarks in https://github.com/antocuni/cpyext-benchmarks - - -.. branch: fix-sre-problems - -Fix two (unrelated) JIT bugs manifesting in the re module: - -- green fields are broken and were thus disabled, plus their usage removed from - the _sre implementation - -- in rare "trace is too long" situations, the JIT could break behaviour - arbitrarily. - -.. branch: jit-hooks-can-be-disabled - -Be more efficient about JIT hooks. Make it possible for the frontend to declare -that jit hooks are currently not enabled at all. in that case, the list of ops -does not have to be created in the case of the on_abort hook (which is -expensive). - - -.. branch: pyparser-improvements - -Improve speed of Python parser, improve ParseError messages slightly. - -.. branch: ioctl-arg-size - -Work around possible bugs in upstream ioctl users, like CPython allocate at -least 1024 bytes for the arg in calls to ``ioctl(fd, request, arg)``. Fixes -issue #2776 - -.. branch: cpyext-subclass-setattr - -Fix for python-level classes that inherit from C-API types, previously the -`w_obj` was not necessarily preserved throughout the lifetime of the `pyobj` -which led to cases where instance attributes were lost. Fixes issue #2793 - - -.. branch: pyparser-improvements-2 - -Improve line offsets that are reported by SyntaxError. Improve error messages -for a few situations, including mismatched parenthesis. - -.. branch: issue2752 - -Fix a rare GC bug that was introduced more than one year ago, but was -not diagnosed before issue #2752. - -.. branch: gc-hooks - -Introduce GC hooks, as documented in doc/gc_info.rst - -.. branch: gc-hook-better-timestamp - -Improve GC hooks - -.. branch: cppyy-packaging - -Update backend to 0.6.0 and support exceptions through wrappers +=========================== +What's new in PyPy2.7 5.10+ +=========================== + +.. this is a revision shortly after release-pypy2.7-v5.10.0 +.. startrev: 6b024edd9d12 + +.. branch: cpyext-avoid-roundtrip + +Big refactoring of some cpyext code, which avoids a lot of nonsense when +calling C from Python and vice-versa: the result is a big speedup in +function/method calls, up to 6 times faster. + +.. branch: cpyext-datetime2 + +Support ``tzinfo`` field on C-API datetime objects, fixes latest pandas HEAD + + +.. branch: mapdict-size-limit + +Fix a corner case of mapdict: When an instance is used like a dict (using +``setattr`` and ``getattr``, or ``.__dict__``) and a lot of attributes are +added, then the performance using mapdict is linear in the number of +attributes. This is now fixed (by switching to a regular dict after 80 +attributes). + + +.. branch: cpyext-faster-arg-passing + +When using cpyext, improve the speed of passing certain objects from PyPy to C +code, most notably None, True, False, types, all instances of C-defined types. +Before, a dict lookup was needed every time such an object crossed over, now it +is just a field read. + + +.. branch: 2634_datetime_timedelta_performance + +Improve datetime + timedelta performance. + +.. branch: memory-accounting + +Improve way to describe memory + +.. branch: msvc14 + +Allow compilaiton with Visual Studio 2017 compiler suite on windows + +.. branch: refactor-slots + +Refactor cpyext slots. + + +.. branch: call-loopinvariant-into-bridges + +Speed up branchy code that does a lot of function inlining by saving one call +to read the TLS in most bridges. + +.. branch: rpython-sprint + +Refactor in rpython signatures + +.. branch: cpyext-tls-operror2 + +Store error state thread-locally in executioncontext, fixes issue #2764 + +.. branch: cpyext-fast-typecheck + +Optimize `Py*_Check` for `Bool`, `Float`, `Set`. Also refactor and simplify +`W_PyCWrapperObject` which is used to call slots from the C-API, greatly +improving microbenchmarks in https://github.com/antocuni/cpyext-benchmarks + + +.. branch: fix-sre-problems + +Fix two (unrelated) JIT bugs manifesting in the re module: + +- green fields are broken and were thus disabled, plus their usage removed from + the _sre implementation + +- in rare "trace is too long" situations, the JIT could break behaviour + arbitrarily. + +.. branch: jit-hooks-can-be-disabled + +Be more efficient about JIT hooks. Make it possible for the frontend to declare +that jit hooks are currently not enabled at all. in that case, the list of ops +does not have to be created in the case of the on_abort hook (which is +expensive). + + +.. branch: pyparser-improvements + +Improve speed of Python parser, improve ParseError messages slightly. + +.. branch: ioctl-arg-size + +Work around possible bugs in upstream ioctl users, like CPython allocate at +least 1024 bytes for the arg in calls to ``ioctl(fd, request, arg)``. Fixes +issue #2776 + +.. branch: cpyext-subclass-setattr + +Fix for python-level classes that inherit from C-API types, previously the +`w_obj` was not necessarily preserved throughout the lifetime of the `pyobj` +which led to cases where instance attributes were lost. Fixes issue #2793 + + +.. branch: pyparser-improvements-2 + +Improve line offsets that are reported by SyntaxError. Improve error messages +for a few situations, including mismatched parenthesis. + +.. branch: issue2752 + +Fix a rare GC bug that was introduced more than one year ago, but was +not diagnosed before issue #2752. + +.. branch: gc-hooks + +Introduce GC hooks, as documented in doc/gc_info.rst + +.. branch: gc-hook-better-timestamp + +Improve GC hooks + +.. branch: cppyy-packaging + +Update backend to 0.6.0 and support exceptions through wrappers diff --git a/pypy/doc/whatsnew-pypy2-7.0.0.rst b/pypy/doc/whatsnew-pypy2-7.0.0.rst --- a/pypy/doc/whatsnew-pypy2-7.0.0.rst +++ b/pypy/doc/whatsnew-pypy2-7.0.0.rst @@ -1,73 +1,73 @@ -========================== -What's new in PyPy2.7 6.0+ -========================== - -.. this is a revision shortly after release-pypy-6.0.0 -.. startrev: e50e11af23f1 - -.. branch: cppyy-packaging - -Main items: vastly better template resolution and improved performance. In -detail: upgrade to backend 1.4, improved handling of templated methods and -functions (in particular automatic deduction of types), improved pythonization -interface, range of compatibility fixes for Python3, free functions now take -fast libffi path when possible, moves for strings (incl. from Python str), -easier/faster handling of std::vector by numpy, improved and faster object -identity preservation - -.. branch: socket_default_timeout_blockingness - -Make sure 'blocking-ness' of socket is set along with default timeout - -.. branch: crypt_h - -Include crypt.h for crypt() on Linux - -.. branch: gc-more-logging - -Log additional gc-minor and gc-collect-step info in the PYPYLOG - -.. branch: reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb - - -.. branch: pyparser-improvements-3 - -Small refactorings in the Python parser. - -.. branch: fix-readme-typo - -.. branch: py3.6-wordcode - -implement new wordcode instruction encoding on the 3.6 branch - -.. branch: socket_default_timeout_blockingness - -Backport CPython fix for possible shell injection issue in `distutils.spawn`, -https://bugs.python.org/issue34540 - -.. branch: cffi_dlopen_unicode - -Enable use of unicode file names in `dlopen` - -.. branch: rlock-in-rpython - -Backport CPython fix for `thread.RLock` - - -.. branch: expose-gc-time - -Make GC hooks measure time in seconds (as opposed to an opaque unit). - -.. branch: cleanup-test_lib_pypy - -Update most test_lib_pypy/ tests and move them to extra_tests/. - -.. branch: gc-disable - -Make it possible to manually manage the GC by using a combination of -gc.disable() and gc.collect_step(). Make sure to write a proper release -announcement in which we explain that existing programs could leak memory if -they run for too much time between a gc.disable()/gc.enable() +========================== +What's new in PyPy2.7 6.0+ +========================== + +.. this is a revision shortly after release-pypy-6.0.0 +.. startrev: e50e11af23f1 + +.. branch: cppyy-packaging + +Main items: vastly better template resolution and improved performance. In +detail: upgrade to backend 1.4, improved handling of templated methods and +functions (in particular automatic deduction of types), improved pythonization +interface, range of compatibility fixes for Python3, free functions now take +fast libffi path when possible, moves for strings (incl. from Python str), +easier/faster handling of std::vector by numpy, improved and faster object +identity preservation + +.. branch: socket_default_timeout_blockingness + +Make sure 'blocking-ness' of socket is set along with default timeout + +.. branch: crypt_h + +Include crypt.h for crypt() on Linux + +.. branch: gc-more-logging + +Log additional gc-minor and gc-collect-step info in the PYPYLOG + +.. branch: reverse-debugger + +The reverse-debugger branch has been merged. For more information, see +https://bitbucket.org/pypy/revdb + + +.. branch: pyparser-improvements-3 + +Small refactorings in the Python parser. + +.. branch: fix-readme-typo + +.. branch: py3.6-wordcode + +implement new wordcode instruction encoding on the 3.6 branch + +.. branch: socket_default_timeout_blockingness + +Backport CPython fix for possible shell injection issue in `distutils.spawn`, +https://bugs.python.org/issue34540 + +.. branch: cffi_dlopen_unicode + +Enable use of unicode file names in `dlopen` + +.. branch: rlock-in-rpython + +Backport CPython fix for `thread.RLock` + + +.. branch: expose-gc-time + +Make GC hooks measure time in seconds (as opposed to an opaque unit). + +.. branch: cleanup-test_lib_pypy + +Update most test_lib_pypy/ tests and move them to extra_tests/. + +.. branch: gc-disable + +Make it possible to manually manage the GC by using a combination of +gc.disable() and gc.collect_step(). Make sure to write a proper release +announcement in which we explain that existing programs could leak memory if +they run for too much time between a gc.disable()/gc.enable() diff --git a/pypy/doc/whatsnew-pypy3-5.10.0.rst b/pypy/doc/whatsnew-pypy3-5.10.0.rst --- a/pypy/doc/whatsnew-pypy3-5.10.0.rst +++ b/pypy/doc/whatsnew-pypy3-5.10.0.rst @@ -1,21 +1,7 @@ -========================= -What's new in PyPy3 5.9+ -========================= - -.. this is the revision after release-pypy3.5-5.9 -.. startrev: be41e3ac0a29 - -.. branch: sched_yield -Add sched_yield posix attribute - -.. branch: py3.5-appexec -Raise if space.is_true(space.appexec()) used in app level tests, fix tests -that did this - -.. branch: py3.5-mac-embedding -Download and patch dependencies when building cffi-based stdlib modules - -.. branch: os_lockf - -.. branch: py3.5-xattr -Add posix.*attr() functions +======================== +What's new in PyPy3 7.0+ +======================== + +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c + diff --git a/pypy/doc/whatsnew-pypy3-6.0.0.rst b/pypy/doc/whatsnew-pypy3-6.0.0.rst --- a/pypy/doc/whatsnew-pypy3-6.0.0.rst +++ b/pypy/doc/whatsnew-pypy3-6.0.0.rst @@ -1,28 +1,7 @@ -========================= -What's new in PyPy3 5.10+ -========================= +======================== +What's new in PyPy3 7.0+ +======================== -.. this is the revision after release-pypy3.5-v5.10 -.. startrev: 34c63fba0bba +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c -.. branch: hroncok/fix-typeerror-str-does-not-support-the-b-1514414905375 - -Fix for bytestrings in console repl - -.. branch: py3-winreg - -Update winreg module to use unicode, wide-strings - -.. branch: cpyext-py3-instancemethod-attributes - -Add missing ``__doc__``, ``__module__``, ``__name__`` attributes to -``instancemethod`` - -.. branch: winapi - -Update support for _winapi cffi module for python3 - -.. branch: py3.5-refactor-slots - -Refactor cpyext slots. - diff --git a/pypy/doc/whatsnew-pypy3-7.0.0.rst b/pypy/doc/whatsnew-pypy3-7.0.0.rst --- a/pypy/doc/whatsnew-pypy3-7.0.0.rst +++ b/pypy/doc/whatsnew-pypy3-7.0.0.rst @@ -5,21 +5,16 @@ .. this is the revision after release-pypy3.5-v6.0 .. startrev: 580e3e26cd32 -.. branch: hroncok/fix-multiprocessing-regression-on-newer--1524656522151 +.. branch: unicode-utf8 -Fix multiprocessing regression on newer glibcs +Use utf-8 internally to represent unicode strings -.. branch: py3.5-user-site-impl +.. branch: unicode-utf8-py3 -Use implementation-specific site directories in sysconfig like in Python2 +Use utf-8 internally to represent unicode strings .. branch: alex_gaynor/remove-an-unneeded-call-into-openssl-th-1526429141011 Remove an unneeded call into OpenSSL, from cpython https://github.com/python/cpython/pull/6887 - -.. branch: py3.5-reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb 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 @@ -4,3 +4,7 @@ .. this is the revision after release-pypy3.5-v7.0 .. startrev: 9d2fa7c63b7c + +.. branch: unicode-utf8-py3 + +Use utf8 instead of rpython-level unicode \ No newline at end of file diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -83,7 +83,7 @@ ## con.interact() except OperationError as e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return 1 finally: @@ -91,7 +91,7 @@ space.finish() except OperationError as e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return 1 return exitcode @@ -148,7 +148,7 @@ except OperationError as e: if verbose: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return rffi.cast(rffi.INT, -1) finally: @@ -202,7 +202,7 @@ """) except OperationError as e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return -1 return 0 diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py --- a/pypy/interpreter/argument.py +++ b/pypy/interpreter/argument.py @@ -596,6 +596,10 @@ except IndexError: name = '?' else: + w_enc = space.newtext(space.sys.defaultencoding) + w_err = space.newtext("replace") + w_name = space.call_method(w_name, "encode", w_enc, + w_err) name = space.text_w(w_name) break self.kwd_name = name diff --git a/pypy/interpreter/astcompiler/astbuilder.py b/pypy/interpreter/astcompiler/astbuilder.py --- a/pypy/interpreter/astcompiler/astbuilder.py +++ b/pypy/interpreter/astcompiler/astbuilder.py @@ -58,6 +58,7 @@ self.space = space self.compile_info = compile_info self.root_node = n + # used in f-strings self.recursive_parser = recursive_parser def build_ast(self): diff --git a/pypy/interpreter/astcompiler/fstring.py b/pypy/interpreter/astcompiler/fstring.py --- a/pypy/interpreter/astcompiler/fstring.py +++ b/pypy/interpreter/astcompiler/fstring.py @@ -3,6 +3,7 @@ from pypy.interpreter import error from pypy.interpreter import unicodehelper from rpython.rlib.rstring import StringBuilder +from rpython.rlib.rutf8 import codepoints_in_utf8 def add_constant_string(astbuilder, joined_pieces, w_string, atom_node): @@ -21,10 +22,8 @@ joined_pieces.append(node(w_string, atom_node.get_lineno(), atom_node.get_column())) -def f_constant_string(astbuilder, joined_pieces, u, atom_node): - space = astbuilder.space - add_constant_string(astbuilder, joined_pieces, space.newunicode(u), - atom_node) +def f_constant_string(astbuilder, joined_pieces, w_u, atom_node): + add_constant_string(astbuilder, joined_pieces, w_u, atom_node) def f_string_compile(astbuilder, source, atom_node): # Note: a f-string is kept as a single literal up to here. @@ -259,20 +258,20 @@ i += 1 fstr.current_index = i + space = astbuilder.space literal = builder.build() + lgt = codepoints_in_utf8(literal) if not fstr.raw_mode and '\\' in literal: - space = astbuilder.space literal = parsestring.decode_unicode_utf8(space, literal, 0, len(literal)) - return unicodehelper.decode_unicode_escape(space, literal) - else: - return literal.decode('utf-8') + literal, lgt, pos = unicodehelper.decode_unicode_escape(space, literal) + return space.newtext(literal, lgt) def fstring_find_literal_and_expr(astbuilder, fstr, atom_node, rec): - # Return a tuple with the next literal part, and optionally the + # Return a tuple with the next literal part as a W_Unicode, and optionally the # following expression node. Updates the current index inside 'fstr'. - literal = fstring_find_literal(astbuilder, fstr, atom_node, rec) + w_u = fstring_find_literal(astbuilder, fstr, atom_node, rec) s = fstr.unparsed i = fstr.current_index @@ -284,7 +283,7 @@ # We must now be the start of an expression, on a '{'. assert s[i] == '{' expr = fstring_find_expr(astbuilder, fstr, atom_node, rec) - return literal, expr + return w_u, expr def parse_f_string(astbuilder, joined_pieces, fstr, atom_node, rec=0): @@ -303,11 +302,11 @@ "really the case", atom_node) while True: - literal, expr = fstring_find_literal_and_expr(astbuilder, fstr, + w_u, expr = fstring_find_literal_and_expr(astbuilder, fstr, atom_node, rec) # add the literal part - f_constant_string(astbuilder, joined_pieces, literal, atom_node) + f_constant_string(astbuilder, joined_pieces, w_u, atom_node) if expr is None: break # We're done with this f-string. diff --git a/pypy/interpreter/astcompiler/misc.py b/pypy/interpreter/astcompiler/misc.py --- a/pypy/interpreter/astcompiler/misc.py +++ b/pypy/interpreter/astcompiler/misc.py @@ -116,7 +116,7 @@ # only intern identifier-like strings from pypy.objspace.std.unicodeobject import _isidentifier if (space.is_w(space.type(w_const), space.w_unicode) and - _isidentifier(space.unicode_w(w_const))): + _isidentifier(space.utf8_w(w_const))): return space.new_interned_w_str(w_const) return w_const diff --git a/pypy/interpreter/astcompiler/optimize.py b/pypy/interpreter/astcompiler/optimize.py --- a/pypy/interpreter/astcompiler/optimize.py +++ b/pypy/interpreter/astcompiler/optimize.py @@ -5,7 +5,7 @@ from pypy.tool import stdlib_opcode as ops from pypy.interpreter.error import OperationError from rpython.rlib.unroll import unrolling_iterable -from rpython.rlib.runicode import MAXUNICODE +from rpython.rlib.rutf8 import MAXUNICODE from rpython.rlib.objectmodel import specialize @@ -329,7 +329,7 @@ # produce compatible pycs. if (self.space.isinstance_w(w_obj, self.space.w_unicode) and self.space.isinstance_w(w_const, self.space.w_unicode)): - #unistr = self.space.unicode_w(w_const) + #unistr = self.space.utf8_w(w_const) #if len(unistr) == 1: # ch = ord(unistr[0]) #else: diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py --- a/pypy/interpreter/astcompiler/test/test_compiler.py +++ b/pypy/interpreter/astcompiler/test/test_compiler.py @@ -1,5 +1,6 @@ from __future__ import division import py, sys +from pytest import raises from pypy.interpreter.astcompiler import codegen, astbuilder, symtable, optimize from pypy.interpreter.pyparser import pyparse from pypy.interpreter.pyparser.test import expressions @@ -75,7 +76,7 @@ space = self.space pyco_expr = space.createcompiler().compile(evalexpr, '', 'eval', 0) w_res = space.exec_(pyco_expr, w_dict, w_dict) - res = space.str_w(space.repr(w_res)) + res = space.text_w(space.repr(w_res)) expected_repr = self.get_py3_repr(expected) if isinstance(expected, float): # Float representation can vary a bit between interpreter @@ -1255,7 +1256,6 @@ def test_revdb_metavar(self): from pypy.interpreter.reverse_debugging import dbstate, setup_revdb - self.space.config.translation.reverse_debugger = True self.space.reverse_debugging = True try: setup_revdb(self.space) @@ -1270,9 +1270,6 @@ class AppTestCompiler: - def setup_class(cls): - cls.w_maxunicode = cls.space.wrap(sys.maxunicode) - def test_docstring_not_loaded(self): import io, dis, sys ns = {} @@ -1442,7 +1439,7 @@ ''', d) return d['f'](5) """) - assert 'generator' in space.str_w(space.repr(w_generator)) + assert 'generator' in space.text_w(space.repr(w_generator)) def test_folding_of_list_constants(self): for source in ( diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -3,7 +3,7 @@ from rpython.rlib.cache import Cache from rpython.tool.uid import HUGEVAL_BYTES -from rpython.rlib import jit, types +from rpython.rlib import jit, types, rutf8 from rpython.rlib.debug import make_sure_not_resized from rpython.rlib.objectmodel import (we_are_translated, newlist_hint, compute_unique_id, specialize, not_rpython) @@ -80,10 +80,10 @@ def getname(self, space): try: - return space.unicode_w(space.getattr(self, space.newtext('__name__'))) + return space.utf8_w(space.getattr(self, space.newtext('__name__'))) except OperationError as e: if e.match(space, space.w_TypeError) or e.match(space, space.w_AttributeError): - return u'?' + return '?' raise def getaddrstring(self, space): @@ -105,9 +105,9 @@ w_id = space.rshift(w_id, w_4) return ''.join(addrstring) - def getrepr(self, space, info, moreinfo=u''): - addrstring = unicode(self.getaddrstring(space)) - return space.newunicode(u"<%s at 0x%s%s>" % (info, addrstring, moreinfo)) + def getrepr(self, space, info, moreinfo=''): + addrstring = self.getaddrstring(space) + return space.newtext("<%s at 0x%s%s>" % (info, addrstring, moreinfo)) def getslotvalue(self, index): raise NotImplementedError @@ -245,11 +245,14 @@ def bytes_w(self, space): self._typed_unwrap_error(space, "bytes") - def unicode_w(self, space): - self._typed_unwrap_error(space, "string") + def text_w(self, space): + self._typed_unwrap_error(space, "unicode") - def text_w(self, space): - self._typed_unwrap_error(space, "string") + def utf8_w(self, space): + self._typed_unwrap_error(space, "unicode") + + def convert_to_w_unicode(self, space): + self._typed_unwrap_error(space, "unicode") def bytearray_list_of_chars_w(self, space): self._typed_unwrap_error(space, "bytearray") @@ -423,7 +426,7 @@ self.builtin_modules = {} self.reloading_modules = {} - self.interned_strings = make_weak_value_dictionary(self, unicode, W_Root) + self.interned_strings = make_weak_value_dictionary(self, str, W_Root) self.actionflag = ActionFlag() # changed by the signal module self.check_signal_action = None # changed by the signal module make_finalizer_queue(W_Root, self) @@ -784,12 +787,12 @@ def setitem_str(self, w_obj, key, w_value): # key is a "text", i.e. a byte string (in python3 it - # represents a utf-8-encoded unicode) + # represents a valid utf-8-encoded unicode) return self.setitem(w_obj, self.newtext(key), w_value) def finditem_str(self, w_obj, key): # key is a "text", i.e. a byte string (in python3 it - # represents a utf-8-encoded unicode) + # represents a valid utf-8-encoded unicode) return self.finditem(w_obj, self.newtext(key)) def finditem(self, w_obj, w_key): @@ -823,9 +826,9 @@ def new_interned_w_str(self, w_u): assert isinstance(w_u, W_Root) # and is not None - u = self.unicode_w(w_u) + u = self.utf8_w(w_u) if not we_are_translated(): - assert type(u) is unicode + assert type(u) is str w_u1 = self.interned_strings.get(u) if w_u1 is None: w_u1 = w_u @@ -838,12 +841,11 @@ # returns a "text" object (ie str in python2 and unicode in python3) if not we_are_translated(): assert type(s) is str - u = s.decode('utf-8') - w_s1 = self.interned_strings.get(u) + w_s1 = self.interned_strings.get(s) if w_s1 is None: - w_s1 = self.newunicode(u) + w_s1 = self.newtext(s) if self._side_effects_ok(): - self.interned_strings.set(u, w_s1) + self.interned_strings.set(s, w_s1) return w_s1 def _revdb_startup(self): @@ -882,11 +884,7 @@ # interface for marshal_impl if not we_are_translated(): assert type(s) is str - try: - u = s.decode('utf-8') - except UnicodeDecodeError: - return None - return self.interned_strings.get(u) # may be None + return self.interned_strings.get(s) # may be None @specialize.arg(1) def descr_self_interp_w(self, RequiredClass, w_obj): @@ -1069,7 +1067,7 @@ """ return None - def listview_unicode(self, w_list): + def listview_utf8(self, w_list): """ Return a list of unwrapped unicode out of a list of unicode. If the argument is not a list or does not contain only unicode, return None. May return None anyway. @@ -1099,8 +1097,15 @@ def newlist_bytes(self, list_s): return self.newlist([self.newbytes(s) for s in list_s]) - def newlist_unicode(self, list_u): - return self.newlist([self.newunicode(u) for u in list_u]) + def newlist_utf8(self, list_u, is_ascii): + l_w = [None] * len(list_u) + for i, item in enumerate(list_u): + if not is_ascii: + length = rutf8.check_utf8(item, True) + else: + length = len(item) + l_w[i] = self.newutf8(item, length) + return self.newlist(l_w) def newlist_int(self, list_i): return self.newlist([self.newint(i) for i in list_i]) @@ -1598,6 +1603,8 @@ else: assert False + if self.isinstance_w(w_obj, self.w_unicode): + return w_obj.charbuf_w(self) def text_or_none_w(self, w_obj): return None if self.is_none(w_obj) else self.text_w(w_obj) @@ -1620,18 +1627,22 @@ an utf-8 encoded rpython string. """ assert w_obj is not None + if not self.isinstance_w(w_obj, self.w_unicode): + w_obj._typed_unwrap_error(self, "unicode") return w_obj.text_w(self) @not_rpython # tests only; should be replaced with bytes_w or text_w def str_w(self, w_obj): """ - if w_obj is unicode, call text_w() (i.e., return the UTF-8-nosg + if w_obj is unicode, call utf8_w() (i.e., return the UTF-8-nosg encoded string). Else, call bytes_w(). We should kill str_w completely and manually substitute it with text_w/bytes_w at all call sites. It remains for now for tests only. """ + XXX # deprecated, leaving in place for clear errors if self.isinstance_w(w_obj, self.w_unicode): + # XXX lo text_w, but better to deprecate str_w than to fix this return w_obj.text_w(self) else: return w_obj.bytes_w(self) @@ -1714,23 +1725,38 @@ assert w_obj is not None return w_obj.float_w(self, allow_conversion) - @specialize.argtype(1) - def unicode_w(self, w_obj): - assert w_obj is not None - return w_obj.unicode_w(self) + def utf8_w(self, w_obj): + return w_obj.utf8_w(self) - def unicode0_w(self, w_obj): - "Like unicode_w, but rejects strings with NUL bytes." + def utf8_0_w(self, w_obj): + "Like utf_w, but rejects strings with NUL bytes." from rpython.rlib import rstring - result = w_obj.unicode_w(self) - if u'\x00' in result: + result = w_obj.utf8_w(self) + if '\x00' in result: + raise oefmt(self.w_TypeError, + "argument must be a string without NUL " + "characters") + return rstring.assert_str0(result) + + def convert_to_w_unicode(self, w_obj): + return w_obj.convert_to_w_unicode(self) + + def realunicode_w(self, w_obj): + from pypy.interpreter.unicodehelper import decode_utf8sp + utf8 = self.utf8_w(w_obj) + return decode_utf8sp(self, utf8)[0].decode('utf8') + + def utf8_0_w(self, w_obj): + "Like utf8_w, but rejects strings with NUL bytes." + from rpython.rlib import rstring + result = w_obj.utf8_w(self) + if '\x00' in result: raise oefmt(self.w_ValueError, - "argument must be a unicode string without NUL " + "argument must be a utf8 string without NUL " "characters") return rstring.assert_str0(result) realtext_w = text_w # Python 2 compatibility - realunicode_w = unicode_w def fsencode(space, w_obj): from pypy.interpreter.unicodehelper import fsencode @@ -1755,6 +1781,27 @@ w_obj = self.fsencode(w_obj) return self.bytesbuf0_w(w_obj) + def convert_arg_to_w_unicode(self, w_obj, strict=None): + # XXX why convert_to_w_unicode does something slightly different? + from pypy.objspace.std.unicodeobject import W_UnicodeObject + # for z_translation tests + if hasattr(self, 'is_fake_objspace'): return self.newtext("foobar") + return W_UnicodeObject.convert_arg_to_w_unicode(self, w_obj, strict) + + def utf8_len_w(self, w_obj): + w_obj = self.convert_arg_to_w_unicode(w_obj) + return w_obj._utf8, w_obj._len() + + def realutf8_w(self, w_obj): + # Like utf8_w(), but only works if w_obj is really of type + # 'unicode'. On Python 3 this is the same as utf8_w(). + from pypy.objspace.std.unicodeobject import W_UnicodeObject + # for z_translation tests + if hasattr(self, 'is_fake_objspace'): return self.newtext("foobar") + if not isinstance(w_obj, W_UnicodeObject): + raise oefmt(self.w_TypeError, "argument must be a unicode") + return self.utf8_w(w_obj) + def bytesbuf0_w(self, w_obj): # Like bytes0_w(), but also accept a read-only buffer. from rpython.rlib import rstring @@ -1777,7 +1824,7 @@ w_obj = fspath(self, w_obj) else: w_obj = self.fsdecode(w_obj) - return self.unicode0_w(w_obj) + return self.utf8_w(w_obj) def bool_w(self, w_obj): # Unwraps a bool, also accepting an int for compatibility. @@ -2105,7 +2152,7 @@ 'float_w', 'uint_w', 'bigint_w', - 'unicode_w', + 'utf8_w', 'unwrap', 'is_true', 'is_w', diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -9,8 +9,7 @@ from rpython.rlib.objectmodel import we_are_translated, specialize from rpython.rlib.objectmodel import dont_inline, not_rpython from rpython.rlib import rstack, rstackovf -from rpython.rlib import rwin32 -from rpython.rlib import runicode +from rpython.rlib import rwin32, rutf8 from pypy.interpreter import debug @@ -21,7 +20,8 @@ def strerror(errno): """Translate an error code to a unicode message string.""" from pypy.module._codecs.locale import str_decode_locale_surrogateescape - return str_decode_locale_surrogateescape(os.strerror(errno)) + utf8, lgt = str_decode_locale_surrogateescape(os.strerror(errno)) + return utf8, lgt class OperationError(Exception): """Interpreter-level exception that signals an exception that should be @@ -72,7 +72,7 @@ space = getattr(self.w_type, 'space', None) if space is not None: if self.__class__ is not OperationError and s is None: - s = self._compute_value(space) + s, lgt = self._compute_value(space) try: s = space.text_w(s) except Exception: @@ -306,8 +306,8 @@ def get_w_value(self, space): w_value = self._w_value if w_value is None: - value = self._compute_value(space) - self._w_value = w_value = space.newunicode(value) + value, lgt = self._compute_value(space) + self._w_value = w_value = space.newtext(value, lgt) return w_value def _compute_value(self, space): @@ -478,16 +478,7 @@ assert len(formats) > 0, "unsupported: no % command found" return tuple(parts), tuple(formats) -def _decode_utf8(string): - # when building the error message, don't crash if the byte string - # provided is not valid UTF-8 - assert isinstance(string, str) - result, consumed = runicode.str_decode_utf_8( - string, len(string), "replace", final=True) - return result - def get_operrcls2(valuefmt): - valuefmt = valuefmt.decode('ascii') strings, formats = decompose_valuefmt(valuefmt) assert len(strings) == len(formats) + 1 try: @@ -507,30 +498,49 @@ def _compute_value(self, space): lst = [None] * (len(formats) + len(formats) + 1) + lgt = 0 for i, fmt, attr in entries: lst[i + i] = self.xstrings[i] + lgt += len(self.xstrings[i]) value = getattr(self, attr) if fmt == 'd': - result = str(value).decode('ascii') + result = str(value) + lgt += len(result) elif fmt == 'R': - result = space.unicode_w(space.repr(value)) + result = space.utf8_w(space.repr(value)) + lgt += len(result) elif fmt == 'S': - result = space.unicode_w(space.str(value)) + result = space.utf8_w(space.str(value)) + lgt += len(result) elif fmt == 'T': - result = _decode_utf8(space.type(value).name) + result = space.type(value).name + lgt += len(result) elif fmt == 'N': result = value.getname(space) + lgt += len(result) elif fmt == '8': - result = _decode_utf8(value) + # u'str\uxxxx' -> 'str\xXX\xXX' -> u"'str\xXX\xXX'" + from pypy.interpreter import unicodehelper + result, _lgt, pos = unicodehelper.str_decode_utf8( + value, 'replace', True, + unicodehelper.decode_never_raise, True) + lgt += _lgt + elif isinstance(value, unicode): + # 's' + result = str(value.encode('utf-8')) + lgt += len(value) else: - if isinstance(value, unicode): - result = value - else: - result = _decode_utf8(str(value)) + result = str(value) + try: + lgt += rutf8.check_utf8(result, True) + except rutf8.CheckError as e: + lgt -= e.pos lst[i + i + 1] = result lst[-1] = self.xstrings[-1] - return u''.join(lst) - # + lgt += len(self.xstrings[-1]) + retval = ''.join(lst) + return retval, lgt + _fmtcache2[formats] = OpErrFmt return OpErrFmt, strings @@ -540,7 +550,7 @@ self.setup(w_type) def _compute_value(self, space): - return self._value.decode('utf-8') + return self._value, len(self._value) def async(self, space): # also matches a RuntimeError("maximum rec.") if the stack is @@ -571,8 +581,8 @@ %8 - The result of arg.decode('utf-8') %N - The result of w_arg.getname(space) - %R - The result of space.unicode_w(space.repr(w_arg)) - %S - The result of space.unicode_w(space.str(w_arg)) + %R - The result of space.utf8_w(space.repr(w_arg)) + %S - The result of space.utf8_w(space.str(w_arg)) %T - The result of space.type(w_arg).name """ @@ -627,12 +637,13 @@ if rwin32.WIN32 and isinstance(e, WindowsError): winerror = e.winerror try: - msg = rwin32.FormatErrorW(winerror) + msg, lgt = rwin32.FormatErrorW(winerror) except ValueError: - msg = u'Windows Error %d' % winerror + msg = 'Windows Error %d' % winerror + lgt = len(msg) w_errno = space.w_None w_winerror = space.newint(winerror) - w_msg = space.newunicode(msg) + w_msg = space.newtext(msg, lgt) else: errno = e.errno if errno == EINTR: @@ -641,12 +652,13 @@ return None try: - msg = strerror(errno) + msg, lgt = strerror(errno) except ValueError: - msg = u'error %d' % errno + msg = 'error %d' % errno + lgt = len(msg) w_errno = space.newint(errno) w_winerror = space.w_None - w_msg = space.newunicode(msg) + w_msg = space.newtext(msg, lgt) if w_filename is None: w_filename = space.w_None @@ -676,9 +688,9 @@ eintr_retry=eintr_retry) def exception_from_errno(space, w_type, errno): - msg = strerror(errno) + msg, lgt = strerror(errno) w_error = space.call_function(w_type, space.newint(errno), - space.newunicode(msg)) + space.newtext(msg, lgt)) return OperationError(w_type, w_error) def exception_from_saved_errno(space, w_type): diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py --- a/pypy/interpreter/function.py +++ b/pypy/interpreter/function.py @@ -45,7 +45,8 @@ closure=None, w_ann=None, forcename=None, qualname=None): self.space = space self.name = forcename or code.co_name - self.qualname = qualname or self.name.decode('utf-8') + self.qualname = qualname or self.name + assert isinstance(self.qualname, str) self.w_doc = None # lazily read from code.getdocstring() self.code = code # Code instance self.w_func_globals = w_globals # the globals dictionary @@ -255,7 +256,7 @@ return self.call_args(__args__) def descr_function_repr(self): - return self.getrepr(self.space, u'function %s' % self.qualname) + return self.getrepr(self.space, 'function %s' % self.qualname) def _cleanup_(self): @@ -313,7 +314,7 @@ tup_base = [] tup_state = [ space.newtext(self.name), - space.newunicode(self.qualname), + space.newtext(self.qualname), w_doc, self.code, w_func_globals, @@ -337,7 +338,7 @@ self.space = space self.name = space.text_w(w_name) - self.qualname = space.unicode_w(w_qualname) + self.qualname = space.utf8_w(w_qualname) self.code = space.interp_w(Code, w_code) if not space.is_w(w_closure, space.w_None): from pypy.interpreter.nestedscope import Cell @@ -430,11 +431,11 @@ "__name__ must be set to a string object") def fget_func_qualname(self, space): - return space.newunicode(self.qualname) + return space.newtext(self.qualname) def fset_func_qualname(self, space, w_name): try: - self.qualname = space.unicode_w(w_name) + self.qualname = space.realutf8_w(w_name) except OperationError as e: if e.match(space, space.w_TypeError): raise oefmt(space.w_TypeError, @@ -549,14 +550,14 @@ name = self.w_function.getname(self.space) else: try: - name = space.unicode_w(w_name) + name = space.utf8_w(w_name) except OperationError as e: if not e.match(space, space.w_TypeError): raise - name = u'?' - objrepr = space.unicode_w(space.repr(self.w_instance)) - s = u'' % (name, objrepr) - return space.newunicode(s) + name = '?' + objrepr = space.utf8_w(space.repr(self.w_instance)) + s = b'' % (name, objrepr) + return space.newtext(s) def descr_method_getattribute(self, w_attr): space = self.space @@ -598,7 +599,7 @@ else: w_builtins = space.getbuiltinmodule('builtins') new_inst = space.getattr(w_builtins, space.newtext('getattr')) - tup = [w_instance, space.newunicode(w_function.getname(space))] + tup = [w_instance, space.newtext(w_function.getname(space))] return space.newtuple([new_inst, space.newtuple(tup)]) @@ -699,7 +700,7 @@ return self.space.newtext('' % (self.name,)) def descr__reduce__(self, space): - return space.newunicode(self.qualname) + return space.newtext(self.qualname) def is_builtin_code(w_func): from pypy.interpreter.gateway import BuiltinCode diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py --- a/pypy/interpreter/gateway.py +++ b/pypy/interpreter/gateway.py @@ -174,6 +174,9 @@ def visit_unicode(self, el, app_sig): self.checked_space_method(el, app_sig) + def visit_utf8(self, el, app_sig): + self.checked_space_method(el, app_sig) + def visit_fsencode(self, el, app_sig): self.checked_space_method(el, app_sig) @@ -324,7 +327,10 @@ self.run_args.append("space.text0_w(%s)" % (self.scopenext(),)) def visit_unicode(self, typ): - self.run_args.append("space.unicode_w(%s)" % (self.scopenext(),)) + self.run_args.append("space.realunicode_w(%s)" % (self.scopenext(),)) + + def visit_utf8(self, typ): + self.run_args.append("space.utf8_w(%s)" % (self.scopenext(),)) def visit_fsencode(self, typ): self.run_args.append("space.fsencode_w(%s)" % (self.scopenext(),)) @@ -492,11 +498,14 @@ self.unwrap.append("space.text_w(%s)" % (self.nextarg(),)) def visit_unicode(self, typ): - self.unwrap.append("space.unicode_w(%s)" % (self.nextarg(),)) + self.unwrap.append("space.realunicode_w(%s)" % (self.nextarg(),)) def visit_text0(self, typ): self.unwrap.append("space.text0_w(%s)" % (self.nextarg(),)) + def visit_utf8(self, typ): + self.unwrap.append("space.utf8_w(%s)" % (self.nextarg(),)) + def visit_fsencode(self, typ): self.unwrap.append("space.fsencode_w(%s)" % (self.nextarg(),)) @@ -567,8 +576,10 @@ assert typ in (int, str, float, unicode, r_longlong, r_uint, r_ulonglong, bool) if typ is r_int is r_longlong: return 'gateway_r_longlong_w' - elif typ in (str, unicode): - return typ.__name__ + '_w' + elif typ is str: + return 'utf8_w' + elif typ is unicode: + return 'realunicode_w' elif typ is bool: # For argument clinic's "bool" specifier: accept any object, and # convert it to a boolean value. If you don't want this @@ -1113,7 +1124,7 @@ kw_defs_w = [] for name, w_def in sorted(alldefs_w.items()): assert name in sig.kwonlyargnames - w_name = space.newunicode(name.decode('utf-8')) + w_name = space.newtext(name) kw_defs_w.append((w_name, w_def)) return defs_w, kw_defs_w diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -38,14 +38,12 @@ # 'qualname' is a unicode string if self._qualname is not None: return self._qualname - return self.get_name().decode('utf-8') + return self.get_name() def descr__repr__(self, space): addrstring = self.getaddrstring(space) - return space.newunicode(u"<%s object %s at 0x%s>" % - (self.KIND_U, - self.get_qualname(), - unicode(addrstring))) + return space.newtext("<%s object %s at 0x%s>" % + (self.KIND, self.get_qualname(), addrstring)) def descr_send(self, w_arg): """send(arg) -> send 'arg' into generator/coroutine, @@ -229,7 +227,7 @@ e2.chain_exceptions_from_cause(space, e) raise e2 else: - space.warn(space.newunicode(u"generator '%s' raised StopIteration" + space.warn(space.newtext("generator '%s' raised StopIteration" % self.get_qualname()), space.w_DeprecationWarning) @@ -329,11 +327,11 @@ "__name__ must be set to a string object") def descr__qualname__(self, space): - return space.newunicode(self.get_qualname()) + return space.newtext(self.get_qualname()) def descr_set__qualname__(self, space, w_name): try: - self._qualname = space.unicode_w(w_name) + self._qualname = space.utf8_w(w_name) except OperationError as e: if e.match(space, space.w_TypeError): raise oefmt(space.w_TypeError, @@ -422,8 +420,8 @@ self.frame is not None and \ self.frame.last_instr == -1: space = self.space - msg = u"coroutine '%s' was never awaited" % self.get_qualname() - space.warn(space.newunicode(msg), space.w_RuntimeWarning) + msg = "coroutine '%s' was never awaited" % self.get_qualname() + space.warn(space.newtext(msg), space.w_RuntimeWarning) GeneratorOrCoroutine._finalize_(self) diff --git a/pypy/interpreter/mixedmodule.py b/pypy/interpreter/mixedmodule.py --- a/pypy/interpreter/mixedmodule.py +++ b/pypy/interpreter/mixedmodule.py @@ -130,7 +130,7 @@ bltin.w_module = self.w_name func._builtinversion_ = bltin bltin.name = name - bltin.qualname = bltin.name.decode('utf-8') + bltin.qualname = bltin.name w_value = bltin space.setitem(self.w_dict, w_name, w_value) return w_value diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py --- a/pypy/interpreter/pycode.py +++ b/pypy/interpreter/pycode.py @@ -206,7 +206,7 @@ self.co_filename = '/%s' % (basename,) self.w_filename = self.space.newfilename(self.co_filename) - co_names = property(lambda self: [self.space.str_w(w_name) for w_name in self.co_names_w]) # for trace + co_names = property(lambda self: [self.space.text_w(w_name) for w_name in self.co_names_w]) # for trace def signature(self): return self._signature @@ -449,8 +449,8 @@ def repr(self, space): space = self.space # co_name should be an identifier - name = self.co_name.decode('utf-8') - fn = space.unicode_w(self.w_filename) - return space.newunicode(u'' % ( - name, unicode(self.getaddrstring(space)), fn, + name = self.co_name + fn = space.utf8_w(self.w_filename) + return space.newtext(b'' % ( + name, self.getaddrstring(space), fn, -1 if self.co_firstlineno == 0 else self.co_firstlineno)) diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py --- a/pypy/interpreter/pyopcode.py +++ b/pypy/interpreter/pyopcode.py @@ -1099,8 +1099,8 @@ try: w_pkgname = space.getattr( w_module, space.newtext('__name__')) - w_fullname = space.newunicode(u'%s.%s' % - (space.unicode_w(w_pkgname), space.unicode_w(w_name))) + w_fullname = space.newtext(b'%s.%s' % + (space.utf8_w(w_pkgname), space.utf8_w(w_name))) return space.getitem(space.sys.get('modules'), w_fullname) except OperationError: raise oefmt( @@ -1355,7 +1355,7 @@ def MAKE_FUNCTION(self, oparg, next_instr): space = self.space w_qualname = self.popvalue() - qualname = self.space.unicode_w(w_qualname) + qualname = self.space.utf8_w(w_qualname) w_codeobj = self.popvalue() codeobj = self.space.interp_w(PyCode, w_codeobj) assert 0 <= oparg <= 0x0F @@ -1659,7 +1659,7 @@ if (oparg & consts.FVS_MASK) == consts.FVS_HAVE_SPEC: w_spec = self.popvalue() else: - w_spec = space.newunicode(u'') + w_spec = space.newtext('') w_value = self.popvalue() # conversion = oparg & consts.FVC_MASK @@ -1680,9 +1680,9 @@ lst = [] for i in range(itemcount-1, -1, -1): w_item = self.peekvalue(i) - lst.append(space.unicode_w(w_item)) + lst.append(space.utf8_w(w_item)) self.dropvalues(itemcount) - w_res = space.newunicode(u''.join(lst)) + w_res = space.newtext(''.join(lst)) self.pushvalue(w_res) def _revdb_load_var(self, oparg): diff --git a/pypy/interpreter/pyparser/error.py b/pypy/interpreter/pyparser/error.py --- a/pypy/interpreter/pyparser/error.py +++ b/pypy/interpreter/pyparser/error.py @@ -29,20 +29,24 @@ except: # we can't allow any exceptions here! return None""") elif self.text is not None: - from rpython.rlib.runicode import str_decode_utf_8 + from rpython.rlib.runicode import str_decode_utf_8_impl # self.text may not be UTF-8 in case of decoding errors. # adjust the encoded text offset to a decoded offset # XXX do the right thing about continuation lines, which # XXX are their own fun, sometimes giving offset > # XXX len(self.text) for example (right now, avoid crashing) + def replace_error_handler(errors, encoding, msg, s, startpos, endpos): + # must return unicode + return u'\ufffd', endpos if offset > len(self.text): offset = len(self.text) - text, _ = str_decode_utf_8(self.text, offset, 'replace') + text, _ = str_decode_utf_8_impl(self.text, offset, + 'replace', False, replace_error_handler, True) offset = len(text) if len(self.text) != offset: - text, _ = str_decode_utf_8(self.text, len(self.text), - 'replace') - w_text = space.newunicode(text) + text, _ = str_decode_utf_8_impl(self.text, len(self.text), + 'replace', False, replace_error_handler, True) + w_text = space.newtext(text.encode('utf8'), len(text)) return space.newtuple([ space.newtext(self.msg), space.newtuple([ diff --git a/pypy/interpreter/pyparser/parsestring.py b/pypy/interpreter/pyparser/parsestring.py --- a/pypy/interpreter/pyparser/parsestring.py +++ b/pypy/interpreter/pyparser/parsestring.py @@ -1,4 +1,5 @@ # coding: utf-8 +from rpython.rlib import rutf8 from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter import unicodehelper @@ -91,9 +92,11 @@ if encoding is None: substr = s[ps:q] else: + unicodehelper.check_utf8_or_raise(space, s, ps, q) substr = decode_unicode_utf8(space, s, ps, q) - v = unicodehelper.decode_unicode_escape(space, substr) - return space.newunicode(v) + r = unicodehelper.decode_unicode_escape(space, substr) + v, length, pos = r + return space.newutf8(v, length) assert 0 <= ps <= q substr = s[ps : q] @@ -111,8 +114,8 @@ elif saw_f: return W_FString(substr, rawmode) else: - v = unicodehelper.decode_utf8(space, substr) - return space.newunicode(v) + v = unicodehelper.str_decode_utf8(substr, 'strict', True, None) + return space.newtext(*v) v, first_escape_error_char = PyString_DecodeEscape( space, substr, 'strict', encoding) @@ -137,15 +140,12 @@ # the backslash we just wrote, we emit "\u005c" # instead. lis.append("u005c") - if ord(s[ps]) & 0x80: # XXX inefficient - w, ps = decode_utf8(space, s, ps, end) - for c in w: - # The equivalent of %08x, which is not supported by RPython. - # 7 zeroes are enough for the unicode range, and the - # result still fits in 32-bit. - hexa = hex(ord(c) + 0x10000000) - lis.append('\\U0') - lis.append(hexa[3:]) # Skip 0x and the leading 1 + if ord(s[ps]) & 0x80: + cp = rutf8.codepoint_at_pos(s, ps) + hexa = hex(cp + 0x10000000) + lis.append('\\U0') + lis.append(hexa[3:]) # Skip 0x and the leading 1 + ps = rutf8.next_codepoint_pos(s, ps) else: lis.append(s[ps]) ps += 1 @@ -262,20 +262,29 @@ ch >= 'A' and ch <= 'F') -def decode_utf8(space, s, ps, end): +def check_utf8(space, s, ps, end): assert ps >= 0 pt = ps # while (s < end && *s != '\\') s++; */ /* inefficient for u".." while ps < end and ord(s[ps]) & 0x80: ps += 1 - u = unicodehelper.decode_utf8(space, s[pt:ps]) - return u, ps + try: + rutf8.check_utf8(s, True, pt, ps) + except rutf8.CheckError as e: + lgt, flag = rutf8.check_utf8(s, True, pt, e.pos) + unicodehelper.decode_error_handler(space)('strict', 'utf8', + 'invalid utf-8', s, pt + lgt, pt + lgt + 1) + return s[pt:ps] def decode_utf8_recode(space, s, ps, end, recode_encoding): - u, ps = decode_utf8(space, s, ps, end) - w_v = unicodehelper.encode(space, space.newunicode(u), recode_encoding) + p = ps + while p < end and ord(s[p]) & 0x80: + p += 1 + lgt = unicodehelper.check_utf8_or_raise(space, s, ps, p) + w_v = unicodehelper.encode(space, space.newutf8(s[ps:p], lgt), + recode_encoding) v = space.bytes_w(w_v) - return v, ps + return v, p def raise_app_valueerror(space, msg): raise OperationError(space.w_ValueError, space.newtext(msg)) diff --git a/pypy/interpreter/pyparser/pytokenizer.py b/pypy/interpreter/pyparser/pytokenizer.py --- a/pypy/interpreter/pyparser/pytokenizer.py +++ b/pypy/interpreter/pyparser/pytokenizer.py @@ -6,6 +6,7 @@ from pypy.interpreter.pyparser.pytokenize import tabsize, alttabsize, whiteSpaceDFA, \ triple_quoted, endDFAs, single_quoted, pseudoDFA from pypy.interpreter.astcompiler import consts +from rpython.rlib import rutf8 NAMECHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_' NUMCHARS = '0123456789' @@ -46,14 +47,9 @@ def verify_utf8(token): - for c in token: - if ord(c) >= 0x80: - break - else: - return True try: - u = token.decode('utf-8') - except UnicodeDecodeError: + rutf8.check_utf8(token, False) + except rutf8.CheckError: return False return True @@ -69,17 +65,12 @@ def verify_identifier(token): # 1=ok; 0=not an identifier; -1=bad utf-8 - for c in token: - if ord(c) >= 0x80: - break - else: - return 1 try: - u = token.decode('utf-8') - except UnicodeDecodeError: + rutf8.check_utf8(token, False) + except rutf8.CheckError: return -1 from pypy.objspace.std.unicodeobject import _isidentifier - return _isidentifier(u) + return _isidentifier(token) DUMMY_DFA = automata.DFA([], []) diff --git a/pypy/interpreter/pyparser/test/test_parsestring.py b/pypy/interpreter/pyparser/test/test_parsestring.py --- a/pypy/interpreter/pyparser/test/test_parsestring.py +++ b/pypy/interpreter/pyparser/test/test_parsestring.py @@ -10,7 +10,7 @@ assert space.bytes_w(w_ret) == value elif isinstance(value, unicode): assert space.type(w_ret) == space.w_unicode - assert space.unicode_w(w_ret) == value + assert space.utf8_w(w_ret).decode('utf8') == value else: assert False @@ -61,7 +61,7 @@ s = "u'\x81'" s = s.decode("koi8-u").encode("utf8")[1:] w_ret = parsestring.parsestr(self.space, 'koi8-u', s) - ret = space.unwrap(w_ret) + ret = w_ret._utf8.decode('utf8') assert ret == eval("# -*- coding: koi8-u -*-\nu'\x81'") def test_unicode_pep414(self): @@ -112,14 +112,14 @@ space = self.space s = '"""' + '\\' + '\n"""' w_ret = parsestring.parsestr(space, None, s) - assert space.str_w(w_ret) == '' + assert space.text_w(w_ret) == '' def test_bug1(self): space = self.space expected = ['x', ' ', chr(0xc3), chr(0xa9), ' ', '\n'] input = ["'", 'x', ' ', chr(0xc3), chr(0xa9), ' ', chr(92), 'n', "'"] w_ret = parsestring.parsestr(space, 'utf8', ''.join(input)) - assert space.str_w(w_ret) == ''.join(expected) + assert space.text_w(w_ret) == ''.join(expected) def test_wide_unicode_in_source(self): if sys.maxunicode == 65535: @@ -131,7 +131,4 @@ def test_decode_unicode_utf8(self): buf = parsestring.decode_unicode_utf8(self.space, 'u"\xf0\x9f\x92\x8b"', 2, 6) - if sys.maxunicode == 65535: - assert buf == r"\U0000d83d\U0000dc8b" - else: - assert buf == r"\U0001f48b" + assert buf == r"\U0001f48b" diff --git a/pypy/interpreter/test/test_appinterp.py b/pypy/interpreter/test/test_appinterp.py --- a/pypy/interpreter/test/test_appinterp.py +++ b/pypy/interpreter/test/test_appinterp.py @@ -155,7 +155,7 @@ w_mymod2 = MyModule(space2, space2.wrap('mymod')) w_str = space1.getattr(w_mymod1, space1.wrap("hi")) - assert space1.str_w(w_str) == "hello" + assert space1.text_w(w_str) == "hello" class TestMixedModuleUnfreeze: spaceconfig = dict(usemodules=('_socket',)) diff --git a/pypy/interpreter/test/test_argument.py b/pypy/interpreter/test/test_argument.py --- a/pypy/interpreter/test/test_argument.py +++ b/pypy/interpreter/test/test_argument.py @@ -55,6 +55,9 @@ pass class DummySpace(object): + class sys: + defaultencoding = 'utf-8' + def newtuple(self, items): return tuple(items) @@ -92,16 +95,15 @@ def getitem(self, obj, key): return obj[key] - def wrap(self, obj): + def wrap(self, obj, lgt=-1): return obj newtext = wrap - newunicode = wrap From pypy.commits at gmail.com Thu Feb 14 03:26:45 2019 From: pypy.commits at gmail.com (mattip) Date: Thu, 14 Feb 2019 00:26:45 -0800 (PST) Subject: [pypy-commit] pypy default: release notes for hypothesis 4.0.0: "average_size was a no-op, is removed Message-ID: <5c652645.1c69fb81.f561f.510f@mx.google.com> Author: Matti Picus Branch: Changeset: r96011:271763ca6c71 Date: 2019-02-14 09:45 +0200 http://bitbucket.org/pypy/pypy/changeset/271763ca6c71/ Log: release notes for hypothesis 4.0.0: "average_size was a no-op, is removed diff --git a/rpython/rlib/test/test_rutf8.py b/rpython/rlib/test/test_rutf8.py --- a/rpython/rlib/test/test_rutf8.py +++ b/rpython/rlib/test/test_rutf8.py @@ -130,7 +130,7 @@ assert (rutf8.codepoint_position_at_index(u.encode('utf8'), index, i) == len(u[:i].encode('utf8'))) - at given(strategies.text(average_size=140)) + at given(strategies.text()) @example(u'x' * 64 * 5) @example(u'x' * (64 * 5 - 1)) def test_codepoint_index_at_byte_position(u): From pypy.commits at gmail.com Fri Feb 15 05:24:52 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 15 Feb 2019 02:24:52 -0800 (PST) Subject: [pypy-commit] pypy default: fix test, logic was backwards Message-ID: <5c669374.1c69fb81.3e9ff.630f@mx.google.com> Author: Matti Picus Branch: Changeset: r96012:98b12ab55f09 Date: 2019-02-15 12:21 +0200 http://bitbucket.org/pypy/pypy/changeset/98b12ab55f09/ Log: fix test, logic was backwards diff --git a/rpython/rlib/test/test_rutf8.py b/rpython/rlib/test/test_rutf8.py --- a/rpython/rlib/test/test_rutf8.py +++ b/rpython/rlib/test/test_rutf8.py @@ -152,7 +152,7 @@ @example([u'\ud800', u'\udc00']) def test_surrogate_in_utf8(unichars): uni = ''.join([u.encode('utf8') for u in unichars]) - result = rutf8.surrogate_in_utf8(uni) < 0 + result = rutf8.surrogate_in_utf8(uni) >= 0 expected = any(uch for uch in unichars if u'\ud800' <= uch <= u'\udfff') assert result == expected From pypy.commits at gmail.com Fri Feb 15 05:24:54 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 15 Feb 2019 02:24:54 -0800 (PST) Subject: [pypy-commit] pypy py3.5: merge default into branch Message-ID: <5c669376.1c69fb81.de77d.3ad3@mx.google.com> Author: Matti Picus Branch: py3.5 Changeset: r96013:c27524ca09c9 Date: 2019-02-15 12:22 +0200 http://bitbucket.org/pypy/pypy/changeset/c27524ca09c9/ Log: merge default into branch diff --git a/rpython/rlib/test/test_rutf8.py b/rpython/rlib/test/test_rutf8.py --- a/rpython/rlib/test/test_rutf8.py +++ b/rpython/rlib/test/test_rutf8.py @@ -130,7 +130,7 @@ assert (rutf8.codepoint_position_at_index(u.encode('utf8'), index, i) == len(u[:i].encode('utf8'))) - at given(strategies.text(average_size=140)) + at given(strategies.text()) @example(u'x' * 64 * 5) @example(u'x' * (64 * 5 - 1)) def test_codepoint_index_at_byte_position(u): @@ -152,7 +152,7 @@ @example([u'\ud800', u'\udc00']) def test_surrogate_in_utf8(unichars): uni = ''.join([u.encode('utf8') for u in unichars]) - result = rutf8.surrogate_in_utf8(uni) < 0 + result = rutf8.surrogate_in_utf8(uni) >= 0 expected = any(uch for uch in unichars if u'\ud800' <= uch <= u'\udfff') assert result == expected From pypy.commits at gmail.com Fri Feb 15 05:24:56 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 15 Feb 2019 02:24:56 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge py3.5 into branch Message-ID: <5c669378.1c69fb81.2aaab.f87f@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96014:b2fbc6986462 Date: 2019-02-15 12:23 +0200 http://bitbucket.org/pypy/pypy/changeset/b2fbc6986462/ Log: merge py3.5 into branch diff --git a/rpython/rlib/test/test_rutf8.py b/rpython/rlib/test/test_rutf8.py --- a/rpython/rlib/test/test_rutf8.py +++ b/rpython/rlib/test/test_rutf8.py @@ -130,7 +130,7 @@ assert (rutf8.codepoint_position_at_index(u.encode('utf8'), index, i) == len(u[:i].encode('utf8'))) - at given(strategies.text(average_size=140)) + at given(strategies.text()) @example(u'x' * 64 * 5) @example(u'x' * (64 * 5 - 1)) def test_codepoint_index_at_byte_position(u): @@ -152,7 +152,7 @@ @example([u'\ud800', u'\udc00']) def test_surrogate_in_utf8(unichars): uni = ''.join([u.encode('utf8') for u in unichars]) - result = rutf8.surrogate_in_utf8(uni) < 0 + result = rutf8.surrogate_in_utf8(uni) >= 0 expected = any(uch for uch in unichars if u'\ud800' <= uch <= u'\udfff') assert result == expected From pypy.commits at gmail.com Fri Feb 15 05:36:32 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 15 Feb 2019 02:36:32 -0800 (PST) Subject: [pypy-commit] buildbot default: disable arm32 cross build and dead win32 slaves, build py3.6 instead of py3.5 Message-ID: <5c669630.1c69fb81.c021e.15f1@mx.google.com> Author: Matti Picus Branch: Changeset: r1066:56a9d50aa3b3 Date: 2019-02-15 12:36 +0200 http://bitbucket.org/pypy/buildbot/changeset/56a9d50aa3b3/ Log: disable arm32 cross build and dead win32 slaves, build py3.6 instead of py3.5 diff --git a/bot2/pypybuildbot/arm_master.py b/bot2/pypybuildbot/arm_master.py --- a/bot2/pypybuildbot/arm_master.py +++ b/bot2/pypybuildbot/arm_master.py @@ -244,28 +244,36 @@ }, # Translation Builders for ARM {"name": BUILDLINUXARM, - "slavenames": ['hhu-cross-armel'], + "slavenames": [ + #'hhu-cross-armel', + ], "builddir": BUILDLINUXARM, "factory": pypyCrossTranslationFactoryARM, "category": 'linux-armel', "locks": [ARMCrossLock.access('counting')], }, {"name": BUILDJITLINUXARM, - "slavenames": ['hhu-cross-armel'], + "slavenames": [ + # 'hhu-cross-armel', + ], "builddir": BUILDJITLINUXARM, "factory": pypyJITCrossTranslationFactoryARM, "category": 'linux-armel', "locks": [ARMCrossLock.access('counting')], }, {"name": BUILDLINUXARMHF_RASPBIAN, - "slavenames": ['hhu-cross-raspbianhf'], + "slavenames": [ + #'hhu-cross-raspbianhf', + ], "builddir": BUILDLINUXARMHF_RASPBIAN, "factory": pypyCrossTranslationFactoryRaspbianHF, "category": 'linux-armhf', "locks": [ARMCrossLock.access('counting')], }, {"name": BUILDJITLINUXARMHF_RASPBIAN, - "slavenames": ['hhu-cross-raspbianhf'], + "slavenames": [ + #'hhu-cross-raspbianhf', + ], "builddir": BUILDJITLINUXARMHF_RASPBIAN, "factory": pypyJITCrossTranslationFactoryRaspbianHF, "category": 'linux-armhf', diff --git a/bot2/pypybuildbot/master.py b/bot2/pypybuildbot/master.py --- a/bot2/pypybuildbot/master.py +++ b/bot2/pypybuildbot/master.py @@ -292,14 +292,14 @@ # linux tests LINUX32OWN, # on bencher4_32, uses all cores LINUX64OWN, # on bencher4, uses all cores - WIN32OWN, # on allegro_win32, SalsaSalsa + WIN32OWN, # on SalsaSalsa JITLINUX32, # on bencher4_32, uses 1 core JITLINUX64, # on bencher4, uses 1 core #APPLVLLINUX32, # on bencher4_32, uses 1 core #APPLVLLINUX64, # on bencher4, uses 1 core # other platforms #MACOSX32, # on minime - JITWIN32, # on allegro_win32, SalsaSalsa + JITWIN32, # on SalsaSalsa #JITFREEBSD764, # on headless #JITFREEBSD864, # on ananke #JITFREEBSD964, # on tavendo @@ -313,7 +313,7 @@ Nightly("nightly-0-01", [ LINUX32RPYTHON, # on bencher4_32, uses all cores LINUX64RPYTHON, # on bencher4, uses all cores - WIN32RPYTHON, # on allegro_win32, SalsaSalsa + WIN32RPYTHON, # on SalsaSalsa LINUX_S390XRPYTHON, ], branch='default', hour=0, minute=0, onlyIfChanged=True, fileIsImportant=isRPython, @@ -335,7 +335,7 @@ ]), Triggerable("NUMPYWIN_scheduler", [ - #NUMPY_WIN, # on allegro_win32, SalsaSalsa + #NUMPY_WIN, # on SalsaSalsa ]), #Nightly("nightly-3-01-py3.5", [LINUX64, JITLINUX64,], @@ -347,8 +347,8 @@ LINUX64OWN, # on bencher4, uses all cores JITLINUX64, # on bencher4, uses 1 core JITMACOSX64, # on xerxes - JITWIN32, # on allegro_win32, SalsaSalsa - ], branch="py3.5", hour=3, minute=0, + JITWIN32, # on SalsaSalsa + ], branch="py3.6", hour=3, minute=0, #onlyIfChanged=True, ), @@ -541,28 +541,28 @@ # 'category' : 'mac64', # }, {"name": WIN32OWN, - "slavenames": ["SalsaSalsa", "allegro_win32", "anubis64"], + "slavenames": ["SalsaSalsa", "anubis64"], "builddir": WIN32OWN, "factory": pypyOwnTestFactoryWin, "locks": [WinSlaveLock.access('counting')], "category": 'win32', }, {"name": WIN32RPYTHON, - "slavenames": ["SalsaSalsa", "allegro_win32", "anubis64"], + "slavenames": ["SalsaSalsa", "anubis64"], "builddir": WIN32RPYTHON, "factory": pypyRPythonTestFactoryWin, "locks": [WinSlaveLock.access('counting')], "category": 'win32', }, {"name": APPLVLWIN32, - "slavenames": ["SalsaSalsa", "allegro_win32"], + "slavenames": ["SalsaSalsa", ], "builddir": APPLVLWIN32, "factory": pypyTranslatedAppLevelTestFactoryWin, "locks": [WinSlaveLock.access('counting')], "category": "win32", }, {"name" : JITWIN32, - "slavenames": ["SalsaSalsa", "allegro_win32", "anubis64"], + "slavenames": ["SalsaSalsa", "anubis64"], 'builddir' : JITWIN32, 'factory' : pypyJITTranslatedTestFactoryWin, "locks": [WinSlaveLock.access('counting')], @@ -584,7 +584,7 @@ "locks": [Bencher4Lock.access('counting')], }, {'name': NUMPY_WIN, - 'slavenames': ["allegro_win32", "SalsaSalsa"], + 'slavenames': ["SalsaSalsa"], 'builddir': NUMPY_WIN, 'factory': pypyNumpyCompatabilityWin, "locks": [WinSlaveLock.access('counting')], From pypy.commits at gmail.com Fri Feb 15 06:08:40 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 15 Feb 2019 03:08:40 -0800 (PST) Subject: [pypy-commit] buildbot default: disable differently Message-ID: <5c669db8.1c69fb81.6ad8e.d21a@mx.google.com> Author: Matti Picus Branch: Changeset: r1067:5181b149af2c Date: 2019-02-15 13:08 +0200 http://bitbucket.org/pypy/buildbot/changeset/5181b149af2c/ Log: disable differently diff --git a/bot2/pypybuildbot/arm_master.py b/bot2/pypybuildbot/arm_master.py --- a/bot2/pypybuildbot/arm_master.py +++ b/bot2/pypybuildbot/arm_master.py @@ -115,10 +115,10 @@ JITBACKENDONLYLINUXARMEL, JITBACKENDONLYLINUXARMHF, JITBACKENDONLYLINUXARMHF_v7, - BUILDLINUXARM, - BUILDJITLINUXARM, - BUILDLINUXARMHF_RASPBIAN, - BUILDJITLINUXARMHF_RASPBIAN, + #BUILDLINUXARM, + #BUILDJITLINUXARM, + #BUILDLINUXARMHF_RASPBIAN, + #BUILDJITLINUXARMHF_RASPBIAN, ] schedulers = [ @@ -134,8 +134,8 @@ ), Nightly("nightly-arm-3-00-py3.5", [ - BUILDJITLINUXARM, # on hhu-cross-armel, uses 1 core - BUILDJITLINUXARMHF_RASPBIAN, # on hhu-cross-raspbianhf, uses 1 core + #BUILDJITLINUXARM, # on hhu-cross-armel, uses 1 core + #BUILDJITLINUXARMHF_RASPBIAN, # on hhu-cross-raspbianhf, uses 1 core ], branch="py3.5", hour=3, minute=0, #onlyIfChanged=True, ), @@ -244,36 +244,28 @@ }, # Translation Builders for ARM {"name": BUILDLINUXARM, - "slavenames": [ - #'hhu-cross-armel', - ], + "slavenames": ['hhu-cross-armel'], "builddir": BUILDLINUXARM, "factory": pypyCrossTranslationFactoryARM, "category": 'linux-armel', "locks": [ARMCrossLock.access('counting')], }, {"name": BUILDJITLINUXARM, - "slavenames": [ - # 'hhu-cross-armel', - ], + "slavenames": ['hhu-cross-armel'], "builddir": BUILDJITLINUXARM, "factory": pypyJITCrossTranslationFactoryARM, "category": 'linux-armel', "locks": [ARMCrossLock.access('counting')], }, {"name": BUILDLINUXARMHF_RASPBIAN, - "slavenames": [ - #'hhu-cross-raspbianhf', - ], + "slavenames": ['hhu-cross-raspbianhf'], "builddir": BUILDLINUXARMHF_RASPBIAN, "factory": pypyCrossTranslationFactoryRaspbianHF, "category": 'linux-armhf', "locks": [ARMCrossLock.access('counting')], }, {"name": BUILDJITLINUXARMHF_RASPBIAN, - "slavenames": [ - #'hhu-cross-raspbianhf', - ], + "slavenames": ['hhu-cross-raspbianhf'], "builddir": BUILDJITLINUXARMHF_RASPBIAN, "factory": pypyJITCrossTranslationFactoryRaspbianHF, "category": 'linux-armhf', From pypy.commits at gmail.com Fri Feb 15 06:11:58 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 15 Feb 2019 03:11:58 -0800 (PST) Subject: [pypy-commit] buildbot default: disable here too Message-ID: <5c669e7e.1c69fb81.cd48c.bd75@mx.google.com> Author: Matti Picus Branch: Changeset: r1068:50073fc35d4e Date: 2019-02-15 13:11 +0200 http://bitbucket.org/pypy/buildbot/changeset/50073fc35d4e/ Log: disable here too diff --git a/bot2/pypybuildbot/arm_master.py b/bot2/pypybuildbot/arm_master.py --- a/bot2/pypybuildbot/arm_master.py +++ b/bot2/pypybuildbot/arm_master.py @@ -242,33 +242,33 @@ 'category': 'linux-armhf', "locks": [ARMBoardLock.access('counting')], }, - # Translation Builders for ARM - {"name": BUILDLINUXARM, - "slavenames": ['hhu-cross-armel'], - "builddir": BUILDLINUXARM, - "factory": pypyCrossTranslationFactoryARM, - "category": 'linux-armel', - "locks": [ARMCrossLock.access('counting')], - }, - {"name": BUILDJITLINUXARM, - "slavenames": ['hhu-cross-armel'], - "builddir": BUILDJITLINUXARM, - "factory": pypyJITCrossTranslationFactoryARM, - "category": 'linux-armel', - "locks": [ARMCrossLock.access('counting')], - }, - {"name": BUILDLINUXARMHF_RASPBIAN, - "slavenames": ['hhu-cross-raspbianhf'], - "builddir": BUILDLINUXARMHF_RASPBIAN, - "factory": pypyCrossTranslationFactoryRaspbianHF, - "category": 'linux-armhf', - "locks": [ARMCrossLock.access('counting')], - }, - {"name": BUILDJITLINUXARMHF_RASPBIAN, - "slavenames": ['hhu-cross-raspbianhf'], - "builddir": BUILDJITLINUXARMHF_RASPBIAN, - "factory": pypyJITCrossTranslationFactoryRaspbianHF, - "category": 'linux-armhf', - "locks": [ARMCrossLock.access('counting')], - }, + # Translation Builders for ARM - disabled + # {"name": BUILDLINUXARM, + # "slavenames": ['hhu-cross-armel'], + # "builddir": BUILDLINUXARM, + # "factory": pypyCrossTranslationFactoryARM, + # "category": 'linux-armel', + # "locks": [ARMCrossLock.access('counting')], + # }, + # {"name": BUILDJITLINUXARM, + # "slavenames": ['hhu-cross-armel'], + # "builddir": BUILDJITLINUXARM, + # "factory": pypyJITCrossTranslationFactoryARM, + # "category": 'linux-armel', + # "locks": [ARMCrossLock.access('counting')], + # }, + # {"name": BUILDLINUXARMHF_RASPBIAN, + # "slavenames": ['hhu-cross-raspbianhf'], + # "builddir": BUILDLINUXARMHF_RASPBIAN, + # "factory": pypyCrossTranslationFactoryRaspbianHF, + # "category": 'linux-armhf', + # "locks": [ARMCrossLock.access('counting')], + # }, + # {"name": BUILDJITLINUXARMHF_RASPBIAN, + # "slavenames": ['hhu-cross-raspbianhf'], + # "builddir": BUILDJITLINUXARMHF_RASPBIAN, + # "factory": pypyJITCrossTranslationFactoryRaspbianHF, + # "category": 'linux-armhf', + # "locks": [ARMCrossLock.access('counting')], + # }, ] From pypy.commits at gmail.com Fri Feb 15 06:13:15 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 15 Feb 2019 03:13:15 -0800 (PST) Subject: [pypy-commit] buildbot default: missed these too Message-ID: <5c669ecb.1c69fb81.bdc43.ca7c@mx.google.com> Author: Matti Picus Branch: Changeset: r1069:726d580f241a Date: 2019-02-15 13:12 +0200 http://bitbucket.org/pypy/buildbot/changeset/726d580f241a/ Log: missed these too diff --git a/bot2/pypybuildbot/arm_master.py b/bot2/pypybuildbot/arm_master.py --- a/bot2/pypybuildbot/arm_master.py +++ b/bot2/pypybuildbot/arm_master.py @@ -123,8 +123,8 @@ schedulers = [ Nightly("nighly-arm-0-00", [ - BUILDJITLINUXARM, # on hhu-cross-armel, uses 1 core - BUILDJITLINUXARMHF_RASPBIAN, # on hhu-cross-raspbianhf, uses 1 core + #BUILDJITLINUXARM, # on hhu-cross-armel, uses 1 core + #BUILDJITLINUXARMHF_RASPBIAN, # on hhu-cross-raspbianhf, uses 1 core #BUILDLINUXARM, # on hhu-cross-armel, uses 1 core #BUILDLINUXARMHF_RASPBIAN, # on hhu-cross-raspbianhf, uses 1 core From pypy.commits at gmail.com Fri Feb 15 06:45:49 2019 From: pypy.commits at gmail.com (arigo) Date: Fri, 15 Feb 2019 03:45:49 -0800 (PST) Subject: [pypy-commit] pypy default: fix rsre_core.py (hard to test for, as it was working in non-translated tests but accidentally killing the second half of the method after translation) Message-ID: <5c66a66d.1c69fb81.77197.4401@mx.google.com> Author: Armin Rigo Branch: Changeset: r96015:8d12729e7465 Date: 2019-02-15 12:45 +0100 http://bitbucket.org/pypy/pypy/changeset/8d12729e7465/ Log: fix rsre_core.py (hard to test for, as it was working in non- translated tests but accidentally killing the second half of the method after translation) diff --git a/rpython/rlib/rsre/rsre_core.py b/rpython/rlib/rsre/rsre_core.py --- a/rpython/rlib/rsre/rsre_core.py +++ b/rpython/rlib/rsre/rsre_core.py @@ -151,7 +151,10 @@ # The following methods are provided to be overriden in # Utf8MatchContext. The non-utf8 implementation is provided # by the FixedMatchContext abstract subclass, in order to use - # the same @not_rpython safety trick as above. + # the same @not_rpython safety trick as above. If you get a + # "not_rpython" error during translation, either consider + # calling the methods xxx_indirect() instead of xxx(), or if + # applicable add the @specializectx decorator. ZERO = 0 @not_rpython def next(self, position): @@ -460,8 +463,7 @@ ptr = self.start_ptr if not self.next_char_ok(ctx, pattern, ptr, self.ppos3): return - assert not isinstance(ctx, AbstractMatchContext) - self.start_ptr = ctx.next(ptr) + self.start_ptr = ctx.next_indirect(ptr) return self.find_first_result(ctx, pattern) def next_char_ok(self, ctx, pattern, ptr, ppos): From pypy.commits at gmail.com Fri Feb 15 06:47:39 2019 From: pypy.commits at gmail.com (arigo) Date: Fri, 15 Feb 2019 03:47:39 -0800 (PST) Subject: [pypy-commit] pypy pread/pwrite: close branch, which I think was abandoned Message-ID: <5c66a6db.1c69fb81.b7895.076b@mx.google.com> Author: Armin Rigo Branch: pread/pwrite Changeset: r96016:d417efd34983 Date: 2019-02-12 18:55 +0100 http://bitbucket.org/pypy/pypy/changeset/d417efd34983/ Log: close branch, which I think was abandoned From pypy.commits at gmail.com Fri Feb 15 06:48:55 2019 From: pypy.commits at gmail.com (arigo) Date: Fri, 15 Feb 2019 03:48:55 -0800 (PST) Subject: [pypy-commit] pypy default: These functions are elidable Message-ID: <5c66a727.1c69fb81.dc708.bdd9@mx.google.com> Author: Armin Rigo Branch: Changeset: r96017:f535756918e0 Date: 2019-02-12 11:39 +0100 http://bitbucket.org/pypy/pypy/changeset/f535756918e0/ Log: These functions are elidable diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -518,7 +518,7 @@ break return storage - at jit.dont_look_inside + at jit.elidable def codepoint_position_at_index(utf8, storage, index): """ Return byte index of a character inside utf8 encoded string, given storage of type UTF8_INDEX_STORAGE. The index must be smaller than @@ -546,7 +546,7 @@ pos = next_codepoint_pos(utf8, pos) return pos - at jit.dont_look_inside + at jit.elidable def codepoint_at_index(utf8, storage, index): """ Return codepoint of a character inside utf8 encoded string, given storage of type UTF8_INDEX_STORAGE @@ -564,7 +564,7 @@ bytepos = next_codepoint_pos(utf8, bytepos) return codepoint_at_pos(utf8, bytepos) - at jit.dont_look_inside + at jit.elidable def codepoint_index_at_byte_position(utf8, storage, bytepos): """ Return the character index for which codepoint_position_at_index(index) == bytepos. From pypy.commits at gmail.com Fri Feb 15 06:48:57 2019 From: pypy.commits at gmail.com (arigo) Date: Fri, 15 Feb 2019 03:48:57 -0800 (PST) Subject: [pypy-commit] pypy default: Optimized version of next_codepoint_pos() without the JIT Message-ID: <5c66a729.1c69fb81.5d321.9c3f@mx.google.com> Author: Armin Rigo Branch: Changeset: r96018:1ab6eb502ac8 Date: 2019-02-12 15:30 +0100 http://bitbucket.org/pypy/pypy/changeset/1ab6eb502ac8/ Log: Optimized version of next_codepoint_pos() without the JIT diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py --- a/rpython/rlib/rarithmetic.py +++ b/rpython/rlib/rarithmetic.py @@ -729,7 +729,9 @@ """ The JIT special-cases this too. """ from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.lltypesystem.lloperation import llop - return llop.int_force_ge_zero(lltype.Signed, n) + n = llop.int_force_ge_zero(lltype.Signed, n) + assert n >= 0 + return n def int_c_div(x, y): """Return the result of the C-style 'x / y'. This differs from the diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -19,7 +19,7 @@ from rpython.rlib.objectmodel import enforceargs, we_are_translated, specialize from rpython.rlib.objectmodel import always_inline, dont_inline, try_inline from rpython.rlib.rstring import StringBuilder -from rpython.rlib import jit, types +from rpython.rlib import jit, types, rarithmetic from rpython.rlib.signature import signature, finishsigs from rpython.rlib.types import char, none from rpython.rlib.rarithmetic import r_uint @@ -117,6 +117,12 @@ # chinese wikipedia, they're anywhere between 10% and 30% slower. # In extreme cases (small, only chinese text), they're 40% slower +# The following was found by hand to be more optimal than both, +# on x86-64... +_is_64bit = sys.maxint > 2**32 +_constant_ncp = rarithmetic.r_uint64(0xffff0000ffffffff) + + at always_inline def next_codepoint_pos(code, pos): """Gives the position of the next codepoint after pos. Assumes valid utf8. 'pos' must be before the end of the string. @@ -125,6 +131,11 @@ chr1 = ord(code[pos]) if chr1 <= 0x7F: return pos + 1 + if _is_64bit and not jit.we_are_jitted(): + # optimized for Intel x86-64 by hand + return pos + 1 + ( + ((chr1 > 0xDF) << 1) + + rarithmetic.intmask((_constant_ncp >> (chr1 & 0x3F)) & 1)) if chr1 <= 0xDF: return pos + 2 if chr1 <= 0xEF: @@ -162,7 +173,6 @@ ordch1 = ord(code[pos]) if ordch1 <= 0x7F or pos +1 >= lgt: return ordch1 - ordch2 = ord(code[pos+1]) if ordch1 <= 0xDF or pos +2 >= lgt: # 110yyyyy 10zzzzzz -> 00000000 00000yyy yyzzzzzz From pypy.commits at gmail.com Fri Feb 15 07:57:06 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 15 Feb 2019 04:57:06 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into py3.6 Message-ID: <5c66b722.1c69fb81.f03f.3d17@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96019:bf156a807410 Date: 2019-02-15 14:56 +0200 http://bitbucket.org/pypy/pypy/changeset/bf156a807410/ Log: merge default into py3.6 diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py --- a/rpython/rlib/rarithmetic.py +++ b/rpython/rlib/rarithmetic.py @@ -729,7 +729,9 @@ """ The JIT special-cases this too. """ from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.lltypesystem.lloperation import llop - return llop.int_force_ge_zero(lltype.Signed, n) + n = llop.int_force_ge_zero(lltype.Signed, n) + assert n >= 0 + return n def int_c_div(x, y): """Return the result of the C-style 'x / y'. This differs from the diff --git a/rpython/rlib/rsre/rsre_core.py b/rpython/rlib/rsre/rsre_core.py --- a/rpython/rlib/rsre/rsre_core.py +++ b/rpython/rlib/rsre/rsre_core.py @@ -151,7 +151,10 @@ # The following methods are provided to be overriden in # Utf8MatchContext. The non-utf8 implementation is provided # by the FixedMatchContext abstract subclass, in order to use - # the same @not_rpython safety trick as above. + # the same @not_rpython safety trick as above. If you get a + # "not_rpython" error during translation, either consider + # calling the methods xxx_indirect() instead of xxx(), or if + # applicable add the @specializectx decorator. ZERO = 0 @not_rpython def next(self, position): @@ -460,8 +463,7 @@ ptr = self.start_ptr if not self.next_char_ok(ctx, pattern, ptr, self.ppos3): return - assert not isinstance(ctx, AbstractMatchContext) - self.start_ptr = ctx.next(ptr) + self.start_ptr = ctx.next_indirect(ptr) return self.find_first_result(ctx, pattern) def next_char_ok(self, ctx, pattern, ptr, ppos): diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -19,7 +19,7 @@ from rpython.rlib.objectmodel import enforceargs, we_are_translated, specialize from rpython.rlib.objectmodel import always_inline, dont_inline, try_inline from rpython.rlib.rstring import StringBuilder -from rpython.rlib import jit, types +from rpython.rlib import jit, types, rarithmetic from rpython.rlib.signature import signature, finishsigs from rpython.rlib.types import char, none from rpython.rlib.rarithmetic import r_uint @@ -117,6 +117,12 @@ # chinese wikipedia, they're anywhere between 10% and 30% slower. # In extreme cases (small, only chinese text), they're 40% slower +# The following was found by hand to be more optimal than both, +# on x86-64... +_is_64bit = sys.maxint > 2**32 +_constant_ncp = rarithmetic.r_uint64(0xffff0000ffffffff) + + at always_inline def next_codepoint_pos(code, pos): """Gives the position of the next codepoint after pos. Assumes valid utf8. 'pos' must be before the end of the string. @@ -125,6 +131,11 @@ chr1 = ord(code[pos]) if chr1 <= 0x7F: return pos + 1 + if _is_64bit and not jit.we_are_jitted(): + # optimized for Intel x86-64 by hand + return pos + 1 + ( + ((chr1 > 0xDF) << 1) + + rarithmetic.intmask((_constant_ncp >> (chr1 & 0x3F)) & 1)) if chr1 <= 0xDF: return pos + 2 if chr1 <= 0xEF: @@ -162,7 +173,6 @@ ordch1 = ord(code[pos]) if ordch1 <= 0x7F or pos +1 >= lgt: return ordch1 - ordch2 = ord(code[pos+1]) if ordch1 <= 0xDF or pos +2 >= lgt: # 110yyyyy 10zzzzzz -> 00000000 00000yyy yyzzzzzz @@ -518,7 +528,7 @@ break return storage - at jit.dont_look_inside + at jit.elidable def codepoint_position_at_index(utf8, storage, index): """ Return byte index of a character inside utf8 encoded string, given storage of type UTF8_INDEX_STORAGE. The index must be smaller than @@ -546,7 +556,7 @@ pos = next_codepoint_pos(utf8, pos) return pos - at jit.dont_look_inside + at jit.elidable def codepoint_at_index(utf8, storage, index): """ Return codepoint of a character inside utf8 encoded string, given storage of type UTF8_INDEX_STORAGE @@ -564,7 +574,7 @@ bytepos = next_codepoint_pos(utf8, bytepos) return codepoint_at_pos(utf8, bytepos) - at jit.dont_look_inside + at jit.elidable def codepoint_index_at_byte_position(utf8, storage, bytepos): """ Return the character index for which codepoint_position_at_index(index) == bytepos. From pypy.commits at gmail.com Sat Feb 16 04:19:32 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sat, 16 Feb 2019 01:19:32 -0800 (PST) Subject: [pypy-commit] pypy default: maybe fix translation Message-ID: <5c67d5a4.1c69fb81.52090.e4ef@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96023:98772484a4cf Date: 2019-02-16 10:18 +0100 http://bitbucket.org/pypy/pypy/changeset/98772484a4cf/ Log: maybe fix translation diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -133,9 +133,11 @@ return pos + 1 if _is_64bit and not jit.we_are_jitted(): # optimized for Intel x86-64 by hand - return pos + 1 + ( + res = pos + 1 + ( ((chr1 > 0xDF) << 1) + rarithmetic.intmask((_constant_ncp >> (chr1 & 0x3F)) & 1)) + assert res >= 0 + return res if chr1 <= 0xDF: return pos + 2 if chr1 <= 0xEF: From pypy.commits at gmail.com Sat Feb 16 07:13:19 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 04:13:19 -0800 (PST) Subject: [pypy-commit] cffi default: On Windows, we still can't use Py_LIMITED_API for bad reasons. Message-ID: <5c67fe5f.1c69fb81.ab152.6f9e@mx.google.com> Author: Armin Rigo Branch: Changeset: r3219:739f2c59674d Date: 2019-02-16 13:13 +0100 http://bitbucket.org/cffi/cffi/changeset/739f2c59674d/ Log: On Windows, we still can't use Py_LIMITED_API for bad reasons. diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h --- a/cffi/_cffi_include.h +++ b/cffi/_cffi_include.h @@ -8,43 +8,20 @@ the same works for the other two macros. Py_DEBUG implies them, but not the other way around. - The implementation is messy (issue #350): on Windows, with _MSC_VER, - we have to define Py_LIMITED_API even before including pyconfig.h. - In that case, we guess what pyconfig.h will do to the macros above, - and check our guess after the #include. - - Note that on Windows, with CPython 3.x, you need virtualenv version - >= 16.0.0. Older versions don't copy PYTHON3.DLL. As a workaround - you can remove the definition of Py_LIMITED_API here. - - See also 'py_limited_api' in cffi/setuptools_ext.py. + Issue #350 is still open: on Windows, the code here causes it to link + with PYTHON36.DLL (for example) instead of PYTHON3.DLL. A fix was + attempted in 164e526a5515 and 14ce6985e1c3, but reverted: virtualenv + does not make PYTHON3.DLL available, and so the "correctly" compiled + version would not run inside a virtualenv. We will re-apply the fix + after virtualenv has been fixed for some time. For explanation, see + issue #355. For a workaround if you want PYTHON3.DLL and don't worry + about virtualenv, see issue #350. See also 'py_limited_api' in + setuptools_ext.py. */ #if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) -# ifdef _MSC_VER -# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif -# include - /* sanity-check: Py_LIMITED_API will cause crashes if any of these - are also defined. Normally, the Python file PC/pyconfig.h does not - cause any of these to be defined, with the exception that _DEBUG - causes Py_DEBUG. Double-check that. */ -# ifdef Py_LIMITED_API -# if defined(Py_DEBUG) -# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" -# endif -# if defined(Py_TRACE_REFS) -# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" -# endif -# if defined(Py_REF_DEBUG) -# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" -# endif -# endif -# else -# include -# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif +# include +# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) +# define Py_LIMITED_API # endif #endif diff --git a/cffi/setuptools_ext.py b/cffi/setuptools_ext.py --- a/cffi/setuptools_ext.py +++ b/cffi/setuptools_ext.py @@ -81,8 +81,14 @@ it doesn't so far, creating troubles. That's why we check for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) + + On Windows, with CPython <= 3.4, it's better not to use py_limited_api + because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. + For now we'll skip py_limited_api on all Windows versions to avoid an + inconsistent mess. """ - if 'py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount'): + if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') + and sys.platform != 'win32'): import setuptools try: setuptools_major_version = int(setuptools.__version__.partition('.')[0]) From pypy.commits at gmail.com Sat Feb 16 07:18:35 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 04:18:35 -0800 (PST) Subject: [pypy-commit] cffi default: whatsnew Message-ID: <5c67ff9b.1c69fb81.f68aa.baca@mx.google.com> Author: Armin Rigo Branch: Changeset: r3220:477090049c73 Date: 2019-02-16 13:19 +0100 http://bitbucket.org/cffi/cffi/changeset/477090049c73/ Log: whatsnew diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -3,6 +3,22 @@ ====================== +v1.12.1 +======= + +* CPython 3 on Windows: we again no longer compile with ``Py_LIMITED_API`` + by default because such modules *still* cannot be used with virtualenv. + The problem is that it doesn't work in CPython <= 3.4, and for + technical reason we can't enable this flag automatically based on the + version of Python. + + Like before, `Issue #350`_ mentions a workaround if you still want + the ``Py_LIMITED_API`` flag and *either* you are not concerned about + virtualenv *or* you are sure your module will not be used on CPython + <= 3.4: pass ``define_macros=[("Py_LIMITED_API", None)]`` to the + ``ffibuilder.set_source()`` call. + + v1.12 ===== From pypy.commits at gmail.com Sat Feb 16 08:19:15 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 05:19:15 -0800 (PST) Subject: [pypy-commit] cffi release-1.12: hg merge default Message-ID: <5c680dd3.1c69fb81.11f6.7dad@mx.google.com> Author: Armin Rigo Branch: release-1.12 Changeset: r3221:f70fc47c1871 Date: 2019-02-16 14:19 +0100 http://bitbucket.org/cffi/cffi/changeset/f70fc47c1871/ Log: hg merge default diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h --- a/cffi/_cffi_include.h +++ b/cffi/_cffi_include.h @@ -8,43 +8,20 @@ the same works for the other two macros. Py_DEBUG implies them, but not the other way around. - The implementation is messy (issue #350): on Windows, with _MSC_VER, - we have to define Py_LIMITED_API even before including pyconfig.h. - In that case, we guess what pyconfig.h will do to the macros above, - and check our guess after the #include. - - Note that on Windows, with CPython 3.x, you need virtualenv version - >= 16.0.0. Older versions don't copy PYTHON3.DLL. As a workaround - you can remove the definition of Py_LIMITED_API here. - - See also 'py_limited_api' in cffi/setuptools_ext.py. + Issue #350 is still open: on Windows, the code here causes it to link + with PYTHON36.DLL (for example) instead of PYTHON3.DLL. A fix was + attempted in 164e526a5515 and 14ce6985e1c3, but reverted: virtualenv + does not make PYTHON3.DLL available, and so the "correctly" compiled + version would not run inside a virtualenv. We will re-apply the fix + after virtualenv has been fixed for some time. For explanation, see + issue #355. For a workaround if you want PYTHON3.DLL and don't worry + about virtualenv, see issue #350. See also 'py_limited_api' in + setuptools_ext.py. */ #if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) -# ifdef _MSC_VER -# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif -# include - /* sanity-check: Py_LIMITED_API will cause crashes if any of these - are also defined. Normally, the Python file PC/pyconfig.h does not - cause any of these to be defined, with the exception that _DEBUG - causes Py_DEBUG. Double-check that. */ -# ifdef Py_LIMITED_API -# if defined(Py_DEBUG) -# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" -# endif -# if defined(Py_TRACE_REFS) -# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" -# endif -# if defined(Py_REF_DEBUG) -# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" -# endif -# endif -# else -# include -# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif +# include +# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) +# define Py_LIMITED_API # endif #endif diff --git a/cffi/setuptools_ext.py b/cffi/setuptools_ext.py --- a/cffi/setuptools_ext.py +++ b/cffi/setuptools_ext.py @@ -81,8 +81,14 @@ it doesn't so far, creating troubles. That's why we check for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) + + On Windows, with CPython <= 3.4, it's better not to use py_limited_api + because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. + For now we'll skip py_limited_api on all Windows versions to avoid an + inconsistent mess. """ - if 'py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount'): + if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') + and sys.platform != 'win32'): import setuptools try: setuptools_major_version = int(setuptools.__version__.partition('.')[0]) diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -3,6 +3,22 @@ ====================== +v1.12.1 +======= + +* CPython 3 on Windows: we again no longer compile with ``Py_LIMITED_API`` + by default because such modules *still* cannot be used with virtualenv. + The problem is that it doesn't work in CPython <= 3.4, and for + technical reason we can't enable this flag automatically based on the + version of Python. + + Like before, `Issue #350`_ mentions a workaround if you still want + the ``Py_LIMITED_API`` flag and *either* you are not concerned about + virtualenv *or* you are sure your module will not be used on CPython + <= 3.4: pass ``define_macros=[("Py_LIMITED_API", None)]`` to the + ``ffibuilder.set_source()`` call. + + v1.12 ===== diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py --- a/testing/cffi0/test_ownlib.py +++ b/testing/cffi0/test_ownlib.py @@ -351,6 +351,8 @@ def test_modify_struct_value(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") + if self.Backend is CTypesBackend: + py.test.skip("fails with the ctypes backend on some architectures") ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef struct { From pypy.commits at gmail.com Sat Feb 16 08:35:47 2019 From: pypy.commits at gmail.com (stevie_92) Date: Sat, 16 Feb 2019 05:35:47 -0800 (PST) Subject: [pypy-commit] pypy cpyext-gc-cycle: Added support for rawrefcount finalizers in incminimark Message-ID: <5c6811b3.1c69fb81.18cd3.5700@mx.google.com> Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r96024:09b2440acb51 Date: 2019-02-16 14:35 +0100 http://bitbucket.org/pypy/pypy/changeset/09b2440acb51/ Log: Added support for rawrefcount finalizers in incminimark Added call to tp_finalize in cpyext, if the gc found an unreachable object (still needs some testing) 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 @@ -1190,7 +1190,10 @@ state.C._PyPy_finalizer_type = rffi.llexternal( '_PyPy_finalizer_type', [PyGC_HeadPtr], lltype.Signed, compilation_info=eci, _nowrapper=True) - + state.C._Py_Finalize = rffi.llexternal('_Py_Finalize', + [PyObject], lltype.Void, + compilation_info=eci, + _nowrapper=True) def init_function(func): INIT_FUNCTIONS.append(func) 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 @@ -51,6 +51,7 @@ PyAPI_FUNC(void) Py_DecRef(PyObject *); extern Py_ssize_t _pypy_rawrefcount_w_marker_deallocating; PyAPI_FUNC(void) _Py_Dealloc(PyObject *); +PyAPI_FUNC(void) _Py_Finalize(PyObject *); #define Py_CLEAR(op) \ do { \ @@ -244,6 +245,8 @@ #define Py_TPFLAGS_DEFAULT Py_TPFLAGS_DEFAULT_EXTERNAL +#define Py_TPFLAGS_HAVE_FINALIZE (1UL << 0) + #define PyType_HasFeature(t,f) (((t)->tp_flags & (f)) != 0) #define PyType_FastSubclass(t,f) PyType_HasFeature(t,f) @@ -317,7 +320,7 @@ #define _PyGCHead_FINALIZED(g) (((g)->gc_refs & _PyGC_REFS_MASK_FINALIZED) != 0) #define _PyGCHead_SET_FINALIZED(g, v) do { \ - (g)->gc_refs = ((g)->gc_refs & ~_PyGC_REFS_MASK_FINALIZED) \ + (g)->gc_refs = ((g)->gc_refs & ~_PyGC_REFS_MASK_FINALIZED) \ | (v != 0); \ } while (0) diff --git a/pypy/module/cpyext/parse/cpyext_object.h b/pypy/module/cpyext/parse/cpyext_object.h --- a/pypy/module/cpyext/parse/cpyext_object.h +++ b/pypy/module/cpyext/parse/cpyext_object.h @@ -311,6 +311,8 @@ /* Type attribute cache version tag. Added in version 2.6 */ unsigned int tp_version_tag; + destructor tp_finalize; + } PyTypeObject; typedef struct _heaptypeobject { 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 @@ -406,6 +406,13 @@ # if w_obj is not None: # assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY + at specialize.ll() +def finalize(space, pyobj): + from pypy.module.cpyext.api import generic_cpy_call + assert is_pyobj(pyobj) + pyobj = rffi.cast(PyObject, pyobj) + state = space.fromcache(State) + generic_cpy_call(space, state.C._Py_Finalize, pyobj) @init_function def write_w_marker_deallocating(space): diff --git a/pypy/module/cpyext/src/object.c b/pypy/module/cpyext/src/object.c --- a/pypy/module/cpyext/src/object.c +++ b/pypy/module/cpyext/src/object.c @@ -62,9 +62,17 @@ } Py_ssize_t -_PyPy_finalizer_type(PyGC_Head *g) +_PyPy_finalizer_type(PyGC_Head *gc) { - return 0; + PyObject *op = FROM_GC(gc); + if (!_PyGCHead_FINALIZED(gc) && + PyType_HasFeature(Py_TYPE(op), Py_TPFLAGS_HAVE_FINALIZE) && + Py_TYPE(op)->tp_finalize != NULL) { + return 1; + } else { + return 0; + } + // TODO: legacy finalizer (tp_del) } void @@ -77,6 +85,23 @@ } void +_Py_Finalize(PyObject *op) +{ + PyGC_Head *gc = _Py_AS_GC(op); + destructor finalize; + + if (!_PyGCHead_FINALIZED(gc) && + PyType_HasFeature(Py_TYPE(op), Py_TPFLAGS_HAVE_FINALIZE) && + (finalize = Py_TYPE(op)->tp_finalize) != NULL) { + _PyGCHead_SET_FINALIZED(gc, 0); + Py_INCREF(op); + finalize(op); + assert(!PyErr_Occurred()); + Py_DECREF(op); + } +} + +void _PyPy_object_dealloc(PyObject *obj) { PyTypeObject *pto; diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py --- a/pypy/module/cpyext/state.py +++ b/pypy/module/cpyext/state.py @@ -61,7 +61,7 @@ if not self.space.config.translating: def dealloc_trigger(): from pypy.module.cpyext.pyobject import PyObject, decref, \ - incref, cts + incref, cts, finalize print 'dealloc_trigger...' while True: ob = rawrefcount.next_dead(PyObject) @@ -72,6 +72,11 @@ print 'deallocating PyObject', ob, 'of type', name decref(space, ob) while True: + py_obj = rawrefcount.next_cyclic_isolate(PyObject) + if not py_obj: + break + finalize(space, py_obj) + while True: ob = rawrefcount.cyclic_garbage_head(PyObject) if not ob: break @@ -252,7 +257,7 @@ def _rawrefcount_perform(space): - from pypy.module.cpyext.pyobject import PyObject, incref, decref + from pypy.module.cpyext.pyobject import PyObject, incref, decref, finalize while True: py_obj = rawrefcount.next_dead(PyObject) if not py_obj: @@ -260,6 +265,12 @@ decref(space, py_obj) while True: + py_obj = rawrefcount.next_cyclic_isolate(PyObject) + if not py_obj: + break + finalize(space, py_obj) + + while True: py_obj = rawrefcount.cyclic_garbage_head(PyObject) if not py_obj: break diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -1046,6 +1046,8 @@ Test if a simple collect is working TODO: make more precise """ + skip('does not work right now, because of how the test is set up, ' + 'see comment below') if self.runappdirect: skip('cannot import module with undefined functions') diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py --- a/rpython/memory/gc/incminimark.py +++ b/rpython/memory/gc/incminimark.py @@ -3013,6 +3013,11 @@ PYOBJ_GC_HDR_PTR)) RAWREFCOUNT_FINALIZER_TYPE = lltype.Ptr(lltype.FuncType([PYOBJ_GC_HDR_PTR], lltype.Signed)) + RAWREFCOUNT_FINALIZER_NONE = 0 + RAWREFCOUNT_FINALIZER_MODERN = 1 + RAWREFCOUNT_FINALIZER_LEGACY = 2 + RAWREFCOUNT_REFS_SHIFT = 1 + RAWREFCOUNT_REFS_MASK_FINALIZED = 1 def _pyobj(self, pyobjaddr): return llmemory.cast_adr_to_ptr(pyobjaddr, self.PYOBJ_HDR_PTR) @@ -3037,6 +3042,10 @@ lltype.malloc(self.PYOBJ_GC_HDR, flavor='raw', immortal=True) self.rrc_pyobj_old_list.c_gc_next = self.rrc_pyobj_old_list self.rrc_pyobj_old_list.c_gc_prev = self.rrc_pyobj_old_list + self.rrc_pyobj_isolate_list = \ + lltype.malloc(self.PYOBJ_GC_HDR, flavor='raw', immortal=True) + self.rrc_pyobj_isolate_list.c_gc_next = self.rrc_pyobj_isolate_list + self.rrc_pyobj_isolate_list.c_gc_prev = self.rrc_pyobj_isolate_list self.rrc_pyobj_garbage_list = \ lltype.malloc(self.PYOBJ_GC_HDR, flavor='raw', immortal=True) self.rrc_pyobj_garbage_list.c_gc_next = self.rrc_pyobj_garbage_list @@ -3107,9 +3116,15 @@ return self.rrc_dealloc_pending.pop() return llmemory.NULL + def rawrefcount_next_cyclic_isolate(self): + if not self._rrc_gc_list_is_empty(self.rrc_pyobj_isolate_list): + gchdr = self._rrc_gc_list_pop(self.rrc_pyobj_isolate_list) + self._rrc_gc_list_add(self.rrc_pyobj_old_list, gchdr) + return llmemory.cast_ptr_to_adr(self.rrc_gc_as_pyobj(gchdr)) + return llmemory.NULL + def rawrefcount_cyclic_garbage_head(self): - if self.rrc_pyobj_garbage_list.c_gc_next <> \ - self.rrc_pyobj_garbage_list: + if not self._rrc_gc_list_is_empty(self.rrc_pyobj_garbage_list): return llmemory.cast_ptr_to_adr( self.rrc_gc_as_pyobj(self.rrc_pyobj_garbage_list.c_gc_next)) else: @@ -3130,6 +3145,8 @@ def rrc_invoke_callback(self): if self.rrc_enabled and (self.rrc_dealloc_pending.non_empty() or + self.rrc_pyobj_isolate_list.c_gc_next <> + self.rrc_pyobj_isolate_list or self.rrc_pyobj_garbage_list.c_gc_next <> self.rrc_pyobj_garbage_list): self.rrc_dealloc_trigger_callback() @@ -3241,10 +3258,41 @@ _rrc_free._always_inline_ = True def rrc_major_collection_trace(self): - self._rrc_collect_rawrefcount_roots() + if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list): + # Check, if the cyclic isolate from the last collection cycle + # is reachable from outside, after the finalizers have been + # executed. + self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_old_list) + found_alive = False + gchdr = self.rrc_pyobj_old_list.c_gc_next + while gchdr <> self.rrc_pyobj_old_list: + if (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0: + found_alive = True + break + gchdr = gchdr.c_gc_next + if found_alive: + self._rrc_gc_list_merge(self.rrc_pyobj_old_list, + self.rrc_pyobj_list) + else: + self._rrc_gc_list_merge(self.rrc_pyobj_old_list, + self.rrc_pyobj_garbage_list) + + self._rrc_collect_rawrefcount_roots(self.rrc_pyobj_list) self._rrc_mark_rawrefcount() - self.rrc_p_list_old.foreach(self._rrc_major_trace, None) - self.rrc_o_list_old.foreach(self._rrc_major_trace, None) + + found_finalizer = False + gchdr = self.rrc_pyobj_old_list.c_gc_next + while gchdr <> self.rrc_pyobj_old_list: + if self.rrc_finalizer_type(gchdr) == \ + self.RAWREFCOUNT_FINALIZER_MODERN: + found_finalizer = True + gchdr = gchdr.c_gc_next + if found_finalizer: + self._rrc_gc_list_move(self.rrc_pyobj_old_list, + self.rrc_pyobj_isolate_list) + + self.rrc_p_list_old.foreach(self._rrc_major_trace, found_finalizer) + self.rrc_o_list_old.foreach(self._rrc_major_trace, found_finalizer) # TODO: for all unreachable objects, which are marked potentially # TODO: uncollectable, move them to the set of uncollectable objs @@ -3260,30 +3308,20 @@ # TODO: a list of callbacks, which has to be called after the # TODO: the GC runs - # TODO: call tp_finalize for unreachable objects - # TODO: (could resurrect objects, so we have to do it now) - # TODO: (set finalizer flag before calling and check if - # TODO: finalizer was not called before) - - # TODO: for all objects in unreachable, check if they - # TODO: are still unreachable. if not, abort and move all - # TODO: unreachable back to pyobj_list and mark all reachable - # TODO: pypy objects - - def _rrc_major_trace(self, pyobject, ignore): + def _rrc_major_trace(self, pyobject, found_finalizer): from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT # - # TODO: optimization: if no finalizers are found the cyclic rc - # TODO: can be used instead of the real rc, because the objects - # TODO: cannot be resurrected anyway - # pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject)) - # if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR): - # rc = pygchdr.c_gc_refs - # else: - rc = self._pyobj(pyobject).c_ob_refcnt - - if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT: # or rc == 0 + if not found_finalizer: + pygchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject)) + if pygchdr != lltype.nullptr(self.PYOBJ_GC_HDR): + rc = pygchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT + else: + rc = self._pyobj(pyobject).c_ob_refcnt + else: + rc = self._pyobj(pyobject).c_ob_refcnt + + if rc == REFCNT_FROM_PYPY or rc == REFCNT_FROM_PYPY_LIGHT or rc == 0: pass # the corresponding object may die else: # force the corresponding object to be alive @@ -3312,17 +3350,9 @@ self.rrc_o_list_old.delete() self.rrc_o_list_old = new_o_list - # merge old_list into garbage_list and clear old_list - if self.rrc_pyobj_old_list.c_gc_next <> self.rrc_pyobj_old_list: - next = self.rrc_pyobj_garbage_list.c_gc_next - next_old = self.rrc_pyobj_old_list.c_gc_next - prev_old = self.rrc_pyobj_old_list.c_gc_prev - self.rrc_pyobj_garbage_list.c_gc_next = next_old - next_old.c_gc_prev = self.rrc_pyobj_garbage_list - prev_old.c_gc_next = next - next.c_gc_prev = prev_old - self.rrc_pyobj_old_list.c_gc_next = self.rrc_pyobj_old_list - self.rrc_pyobj_old_list.c_gc_prev = self.rrc_pyobj_old_list + if not self._rrc_gc_list_is_empty(self.rrc_pyobj_old_list): + self._rrc_gc_list_merge(self.rrc_pyobj_old_list, + self.rrc_pyobj_garbage_list) def _rrc_major_free(self, pyobject, surviving_list, surviving_dict): # The pyobject survives if the corresponding obj survives. @@ -3338,18 +3368,21 @@ else: self._rrc_free(pyobject, True) - def _rrc_collect_rawrefcount_roots(self): + def _rrc_collect_rawrefcount_roots(self, pygclist): from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT # # Initialize the cyclic refcount with the real refcount. - pygchdr = self.rrc_pyobj_list.c_gc_next - while pygchdr <> self.rrc_pyobj_list: - pygchdr.c_gc_refs = self.rrc_gc_as_pyobj(pygchdr).c_ob_refcnt - if pygchdr.c_gc_refs >= REFCNT_FROM_PYPY_LIGHT: - pygchdr.c_gc_refs -= REFCNT_FROM_PYPY_LIGHT - elif pygchdr.c_gc_refs >= REFCNT_FROM_PYPY: - pygchdr.c_gc_refs -= REFCNT_FROM_PYPY + pygchdr = pygclist.c_gc_next + while pygchdr <> pygclist: + refcnt = self.rrc_gc_as_pyobj(pygchdr).c_ob_refcnt + if refcnt >= REFCNT_FROM_PYPY_LIGHT: + refcnt -= REFCNT_FROM_PYPY_LIGHT + elif refcnt >= REFCNT_FROM_PYPY: + refcnt -= REFCNT_FROM_PYPY + pygchdr.c_gc_refs = pygchdr.c_gc_refs & \ + self.RAWREFCOUNT_REFS_MASK_FINALIZED + pygchdr.c_gc_refs = refcnt << self.RAWREFCOUNT_REFS_SHIFT pygchdr = pygchdr.c_gc_next # For every object in this set, if it is marked, add 1 as a real @@ -3359,8 +3392,8 @@ # Subtract all internal refcounts from the cyclic refcount # of rawrefcounted objects - pygchdr = self.rrc_pyobj_list.c_gc_next - while pygchdr <> self.rrc_pyobj_list: + pygchdr = pygclist.c_gc_next + while pygchdr <> pygclist: pyobj = self.rrc_gc_as_pyobj(pygchdr) self._rrc_traverse(pyobj, -1) pygchdr = pygchdr.c_gc_next @@ -3374,22 +3407,16 @@ gchdr = self.rrc_pyobj_as_gc(self._pyobj(pyobject)) if gchdr <> lltype.nullptr(self.PYOBJ_GC_HDR): if self.header(obj).tid & (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS): - gchdr.c_gc_refs += 1 + gchdr.c_gc_refs += 1 << self.RAWREFCOUNT_REFS_SHIFT def _rrc_mark_rawrefcount(self): - if self.rrc_pyobj_list.c_gc_next == self.rrc_pyobj_list: - self.rrc_pyobj_old_list.c_gc_next = self.rrc_pyobj_old_list - self.rrc_pyobj_old_list.c_gc_prev = self.rrc_pyobj_old_list + if self._rrc_gc_list_is_empty(self.rrc_pyobj_list): + self._rrc_gc_list_init(self.rrc_pyobj_old_list) return # as long as new objects with cyclic a refcount > 0 or alive border # objects are found, increment the refcount of all referenced objects # of those newly found objects - self.rrc_pyobj_old_list.c_gc_next = self.rrc_pyobj_list.c_gc_next - self.rrc_pyobj_old_list.c_gc_prev = self.rrc_pyobj_list.c_gc_prev - self.rrc_pyobj_old_list.c_gc_next.c_gc_prev = self.rrc_pyobj_old_list - self.rrc_pyobj_old_list.c_gc_prev.c_gc_next = self.rrc_pyobj_old_list - self.rrc_pyobj_list.c_gc_next = self.rrc_pyobj_list - self.rrc_pyobj_list.c_gc_prev = self.rrc_pyobj_list + self._rrc_gc_list_move(self.rrc_pyobj_list, self.rrc_pyobj_old_list) found_alive = True # while found_alive: @@ -3397,7 +3424,7 @@ gchdr = self.rrc_pyobj_old_list.c_gc_next while gchdr <> self.rrc_pyobj_old_list: next_old = gchdr.c_gc_next - alive = gchdr.c_gc_refs > 0 + alive = (gchdr.c_gc_refs >> self.RAWREFCOUNT_REFS_SHIFT) > 0 pyobj = self.rrc_gc_as_pyobj(gchdr) if pyobj.c_ob_pypy_link <> 0: intobj = pyobj.c_ob_pypy_link @@ -3405,7 +3432,7 @@ if not alive and self.header(obj).tid & ( GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS): # add fake refcount, to mark it as live - gchdr.c_gc_refs += 1 + gchdr.c_gc_refs += 1 << self.RAWREFCOUNT_REFS_SHIFT alive = True if alive: # remove from old list @@ -3413,11 +3440,7 @@ next.c_gc_prev = gchdr.c_gc_prev gchdr.c_gc_prev.c_gc_next = next # add to new list - next = self.rrc_pyobj_list.c_gc_next - self.rrc_pyobj_list.c_gc_next = gchdr - gchdr.c_gc_prev = self.rrc_pyobj_list - gchdr.c_gc_next = next - next.c_gc_prev = gchdr + self._rrc_gc_list_add(self.rrc_pyobj_list, gchdr) # increment refcounts self._rrc_traverse(pyobj, 1) # mark recursively, if it is a pypyobj @@ -3443,7 +3466,8 @@ def _rrc_visit_action(self, pyobj, ignore): pygchdr = self.rrc_pyobj_as_gc(pyobj) if pygchdr <> lltype.nullptr(self.PYOBJ_GC_HDR): - pygchdr.c_gc_refs += self.rrc_refcnt_add + pygchdr.c_gc_refs += self.rrc_refcnt_add << \ + self.RAWREFCOUNT_REFS_SHIFT def _rrc_traverse(self, pyobj, refcnt_add): from rpython.rlib.objectmodel import we_are_translated @@ -3462,3 +3486,38 @@ def _rrc_gc_list_init(self, pygclist): pygclist.c_gc_next = pygclist pygclist.c_gc_prev = pygclist + + def _rrc_gc_list_add(self, pygclist, gchdr): + next = pygclist.c_gc_next + pygclist.c_gc_next = gchdr + gchdr.c_gc_prev = pygclist + gchdr.c_gc_next = next + next.c_gc_prev = gchdr + + def _rrc_gc_list_pop(self, pygclist): + ret = pygclist.c_gc_next + pygclist.c_gc_next = ret.c_gc_next + ret.c_gc_next.c_gc_prev = pygclist + return ret + + def _rrc_gc_list_move(self, pygclist_source, pygclist_dest): + pygclist_dest.c_gc_next = pygclist_source.c_gc_next + pygclist_dest.c_gc_prev = pygclist_source.c_gc_prev + pygclist_dest.c_gc_next.c_gc_prev = pygclist_dest + pygclist_dest.c_gc_prev.c_gc_next = pygclist_dest + pygclist_source.c_gc_next = pygclist_source + pygclist_source.c_gc_prev = pygclist_source + + def _rrc_gc_list_merge(self, pygclist_source, pygclist_dest): + next = pygclist_dest.c_gc_next + next_old = pygclist_source.c_gc_next + prev_old = pygclist_source.c_gc_prev + pygclist_dest.c_gc_next = next_old + next_old.c_gc_prev = pygclist_dest + prev_old.c_gc_next = next + next.c_gc_prev = prev_old + pygclist_source.c_gc_next = pygclist_source + pygclist_source.c_gc_prev = pygclist_source + + def _rrc_gc_list_is_empty(self, pygclist): + return pygclist.c_gc_next == pygclist diff --git a/rpython/memory/gc/test/dot/keep_finalizer_simple.dot b/rpython/memory/gc/test/dot/keep_finalizer_simple.dot --- a/rpython/memory/gc/test/dot/keep_finalizer_simple.dot +++ b/rpython/memory/gc/test/dot/keep_finalizer_simple.dot @@ -1,7 +1,7 @@ digraph G { "a" [type=P, alive=y]; "b" [type=B, alive=y]; - "c" [type=C, alive=y, finalizer=modern, resurrect=d]; + "c" [type=C, alive=y, finalizer=modern, resurrect="d"]; "d" [type=C, alive=y]; "e" [type=B, alive=y]; "f" [type=P, alive=y]; diff --git a/rpython/memory/gc/test/dot/partial_free_finalizer_nocycle.dot b/rpython/memory/gc/test/dot/partial_free_finalizer_nocycle.dot --- a/rpython/memory/gc/test/dot/partial_free_finalizer_nocycle.dot +++ b/rpython/memory/gc/test/dot/partial_free_finalizer_nocycle.dot @@ -1,7 +1,7 @@ digraph G { "a" [type=P, alive=n]; "b" [type=B, alive=n]; - "c" [type=C, alive=n, finalizer=modern, resurrect=d]; + "c" [type=C, alive=n, finalizer=modern, resurrect="d"]; "d" [type=C, alive=y]; "e" [type=B, alive=y]; "f" [type=P, alive=y]; diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py --- a/rpython/memory/gc/test/test_rawrefcount.py +++ b/rpython/memory/gc/test/test_rawrefcount.py @@ -9,6 +9,9 @@ RAWREFCOUNT_VISIT = IncrementalMiniMarkGC.RAWREFCOUNT_VISIT PYOBJ_GC_HDR = IncrementalMiniMarkGC.PYOBJ_GC_HDR PYOBJ_GC_HDR_PTR = IncrementalMiniMarkGC.PYOBJ_GC_HDR_PTR +RAWREFCOUNT_FINALIZER_MODERN = \ + IncrementalMiniMarkGC.RAWREFCOUNT_FINALIZER_MODERN +RAWREFCOUNT_FINALIZER_NONE = IncrementalMiniMarkGC.RAWREFCOUNT_FINALIZER_NONE S = lltype.GcForwardReference() S.become(lltype.GcStruct('S', @@ -27,6 +30,8 @@ self.gcobjs = [] self.pyobjs = [] self.pyobj_refs = [] + self.pyobj_finalizer = {} + self.pyobj_resurrect = {} def rawrefcount_tp_traverse(obj, callback, args): refs = self.pyobj_refs[self.pyobjs.index(obj)] @@ -40,7 +45,14 @@ return self.gcobjs[self.pyobjs.index(pyobj)] def rawrefcount_finalizer_type(gc): - return 0 + pyobj = self.pyobjs[self.gcobjs.index(gc)] + if pyobj in self.pyobjs and \ + self.pyobj_finalizer.has_key(self.pyobjs.index(pyobj)): + # TODO: improve test, so that NONE is returned, if finalizer + # has already been called + return self.pyobj_finalizer[self.pyobjs.index(pyobj)] + else: + return RAWREFCOUNT_FINALIZER_NONE self.pyobj_list = lltype.malloc(PYOBJ_GC_HDR_PTR.TO, flavor='raw', immortal=True) @@ -69,6 +81,10 @@ refs.append(pyobj_to) pyobj_to.c_ob_refcnt += 1 + def _rawrefcount_add_resurrect(self, pyobj_source, pyobj_target): + refs = self.pyobj_resurrect[self.pyobjs.index(pyobj_source)] = [] + refs.append(pyobj_target) + def _rawrefcount_pypyobj(self, intval, rooted=False, create_old=True): p1 = self.malloc(S) p1.x = intval @@ -439,6 +455,7 @@ nodes = {} # create objects from graph (always create old to prevent moving) + finalizers = False i = 0 for n in g.get_nodes(): name = n.get_name() @@ -448,6 +465,8 @@ rooted = attr['rooted'] == "y" if 'rooted' in attr else False ext_refcnt = int(attr['ext_refcnt']) if 'ext_refcnt' in attr else 0 finalizer = attr['finalizer'] if 'finalizer' in attr else None + if finalizer <> None: + finalizers = True resurrect = attr['resurrect'] if 'resurrect' in attr else None info = NodeInfo(type, alive, ext_refcnt, finalizer, resurrect) if type == "C": @@ -484,6 +503,19 @@ else: assert False # only 2 refs supported from pypy obj + # add finalizers + for name in nodes: + n = nodes[name] + if hasattr(n, "r"): + index = self.pyobjs.index(n.r) + resurrect = n.info.resurrect + if n.info.finalizer == "modern" and resurrect is not None: + self.pyobj_finalizer[index] = RAWREFCOUNT_FINALIZER_MODERN + self._rawrefcount_add_resurrect(n.r, nodes[resurrect].r) + nodes[resurrect].info.ext_refcnt += 1 + else: + self.pyobj_finalizer[index] = RAWREFCOUNT_FINALIZER_NONE + # quick self check, if traverse works properly dests_by_source = {} for e in g.get_edges(): @@ -508,6 +540,13 @@ def decref(pyobj, ignore): pyobj.c_ob_refcnt -= 1 if pyobj.c_ob_refcnt == 0: + if self.pyobj_resurrect.has_key(self.pyobjs.index(pyobj)): + resurrect = self.pyobj_resurrect[ + self.pyobjs.index(pyobj)] + for r in resurrect: + r.c_ob_refcnt += 1 + resurrect.remove(r) + if pyobj.c_ob_refcnt == 0: gchdr = self.gc.rrc_pyobj_as_gc(pyobj) next = gchdr.c_gc_next next.c_gc_prev = gchdr.c_gc_prev @@ -521,12 +560,20 @@ while next_dead <> llmemory.NULL: pyobj = llmemory.cast_adr_to_ptr(next_dead, self.gc.PYOBJ_HDR_PTR) - print "nextdead:", pyobj, "refcnt:", pyobj.c_ob_refcnt decref(pyobj, None) next_dead = self.gc.rawrefcount_next_dead() - # TODO: call finalizers here and during the next collection it - # will be checked if the CI is really trash + next = self.gc.rawrefcount_next_cyclic_isolate() + while next <> llmemory.NULL: + pyobj = llmemory.cast_adr_to_ptr(next, + self.gc.PYOBJ_HDR_PTR) + if self.pyobj_resurrect.has_key(self.pyobjs.index(pyobj)): + resurrect = self.pyobj_resurrect[self.pyobjs.index(pyobj)] + for r in resurrect: + r.c_ob_refcnt += 1 + # TODO: improve test, use flag in gc_refs instead + resurrect.remove(r) + next = self.gc.rawrefcount_next_cyclic_isolate() next_dead = self.gc.rawrefcount_cyclic_garbage_head() while next_dead <> llmemory.NULL: @@ -550,20 +597,20 @@ self.gc.rawrefcount_cyclic_garbage_remove() next_dead = self.gc.rawrefcount_cyclic_garbage_head() - # do a collection to find cyclic isolates + # do a collection to find cyclic isolates and clean them, if there are + # no finalizers self.gc.collect() - self.gc.rrc_invoke_callback() if self.trigger <> []: cleanup() - # now do another collection, to clean up cyclic trash - # TODO: maybe optimize, so that we don't need another major collection - self.gc.collect() - - self.gc.rrc_invoke_callback() - if self.trigger <> []: - cleanup() + if finalizers: + # now do another collection, to clean up cyclic trash, if there + # were finalizers involved + self.gc.collect() + self.gc.rrc_invoke_callback() + if self.trigger <> []: + cleanup() # check livelihood of objects, according to graph for name in nodes: diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py --- a/rpython/memory/gctransform/framework.py +++ b/rpython/memory/gctransform/framework.py @@ -502,6 +502,9 @@ self.rawrefcount_next_dead_ptr = getfn( GCClass.rawrefcount_next_dead, [s_gc], SomeAddress(), inline = True) + self.rawrefcount_next_cyclic_isolate_ptr = getfn( + GCClass.rawrefcount_next_cyclic_isolate, [s_gc], SomeAddress(), + inline = True) self.rawrefcount_cyclic_garbage_head_ptr = getfn( GCClass.rawrefcount_cyclic_garbage_head, [s_gc], SomeAddress(), inline = True) @@ -1377,6 +1380,12 @@ [self.rawrefcount_next_dead_ptr, self.c_const_gc], resultvar=hop.spaceop.result) + def gct_gc_rawrefcount_next_cyclic_isolate(self, hop): + assert hop.spaceop.result.concretetype == llmemory.Address + hop.genop("direct_call", + [self.rawrefcount_next_cyclic_isolate_ptr, self.c_const_gc], + resultvar=hop.spaceop.result) + def gct_gc_rawrefcount_cyclic_garbage_head(self, hop): assert hop.spaceop.result.concretetype == llmemory.Address hop.genop("direct_call", @@ -1385,7 +1394,8 @@ def gct_gc_rawrefcount_cyclic_garbage_remove(self, hop): hop.genop("direct_call", - [self.rawrefcount_cyclic_garbage_remove_ptr, self.c_const_gc]) + [self.rawrefcount_cyclic_garbage_remove_ptr, + self.c_const_gc]) def _set_into_gc_array_part(self, op): if op.opname == 'setarrayitem': diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py --- a/rpython/rlib/rawrefcount.py +++ b/rpython/rlib/rawrefcount.py @@ -142,6 +142,10 @@ old_pyobj_list.remove(old_pyobj_list[0]) @not_rpython +def next_cyclic_isolate(OB_PTR_TYPE): + return lltype.nullptr(OB_PTR_TYPE.TO) + + at not_rpython def _collect(track_allocation=True): """for tests only. Emulates a GC collection. Will invoke dealloc_trigger_callback() once if there are objects @@ -363,7 +367,7 @@ return _spec_p(hop, v_p) class Entry(ExtRegistryEntry): - _about_ = (next_dead, cyclic_garbage_head) + _about_ = (next_dead, cyclic_garbage_head, next_cyclic_isolate) def compute_result_annotation(self, s_OB_PTR_TYPE): from rpython.rtyper.llannotation import lltype_to_annotation @@ -375,6 +379,8 @@ name = 'gc_rawrefcount_next_dead' elif self.instance is cyclic_garbage_head: name = 'gc_rawrefcount_cyclic_garbage_head' + elif self.instance is next_cyclic_isolate: + name = 'gc_rawrefcount_next_cyclic_isolate' hop.exception_cannot_occur() v_ob = hop.genop(name, [], resulttype = llmemory.Address) return _spec_ob(hop, v_ob) diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py --- a/rpython/rtyper/llinterp.py +++ b/rpython/rtyper/llinterp.py @@ -972,6 +972,9 @@ def op_gc_rawrefcount_next_dead(self, *args): raise NotImplementedError("gc_rawrefcount_next_dead") + def op_gc_rawrefcount_next_cyclic_isolate(self, *args): + raise NotImplementedError("gc_rawrefcount_next_cyclic_isolate") + def op_gc_rawrefcount_cyclic_garbage_head(self, *args): raise NotImplementedError("gc_rawrefcount_cyclic_garbage_head") def op_gc_rawrefcount_cyclic_garbage_remove(self, *args): diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py --- a/rpython/rtyper/lltypesystem/lloperation.py +++ b/rpython/rtyper/lltypesystem/lloperation.py @@ -498,6 +498,7 @@ 'gc_rawrefcount_from_obj': LLOp(sideeffects=False), 'gc_rawrefcount_to_obj': LLOp(sideeffects=False), 'gc_rawrefcount_next_dead': LLOp(), + 'gc_rawrefcount_next_cyclic_isolate': LLOp(), 'gc_rawrefcount_cyclic_garbage_head': LLOp(sideeffects=False), 'gc_rawrefcount_cyclic_garbage_remove': LLOp(), From pypy.commits at gmail.com Sat Feb 16 09:05:23 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sat, 16 Feb 2019 06:05:23 -0800 (PST) Subject: [pypy-commit] pypy default: this should make unicode_eq actually use a direct_call! Message-ID: <5c6818a3.1c69fb81.dbe09.170e@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96025:37a9de9e88b0 Date: 2019-02-16 14:52 +0100 http://bitbucket.org/pypy/pypy/changeset/37a9de9e88b0/ Log: this should make unicode_eq actually use a direct_call! diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py --- a/pypy/objspace/std/dictmultiobject.py +++ b/pypy/objspace/std/dictmultiobject.py @@ -1219,6 +1219,7 @@ return unwrapped def unwrap(self, wrapped): + assert type(wrapped) is self.space.UnicodeObjectCls return wrapped def is_correct_type(self, w_obj): From pypy.commits at gmail.com Sat Feb 16 09:18:28 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 06:18:28 -0800 (PST) Subject: [pypy-commit] pypy default: Refactor PySequence_FAST_XXX to allow notably for specialized tuples (plus it's Message-ID: <5c681bb4.1c69fb81.cdf29.10b9@mx.google.com> Author: Armin Rigo Branch: Changeset: r96026:1d688af446c2 Date: 2019-02-16 13:53 +0100 http://bitbucket.org/pypy/pypy/changeset/1d688af446c2/ Log: Refactor PySequence_FAST_XXX to allow notably for specialized tuples (plus it's a clean-up, I think) diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py --- a/pypy/module/cpyext/listobject.py +++ b/pypy/module/cpyext/listobject.py @@ -104,7 +104,8 @@ def PyList_GET_SIZE(space, w_obj): """Macro form of PyList_Size() without error checking. """ - return space.len_w(w_obj) + assert isinstance(w_obj, W_ListObject) + return w_obj.length() @cpython_api([PyObject], Py_ssize_t, error=-1) diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py --- a/pypy/module/cpyext/sequence.py +++ b/pypy/module/cpyext/sequence.py @@ -50,50 +50,56 @@ converted to a sequence, and raises a TypeError, raise a new TypeError with m as the message text. If the conversion otherwise, fails, reraise the original exception""" - if isinstance(w_obj, tupleobject.W_TupleObject): + if isinstance(w_obj, tupleobject.W_AbstractTupleObject): return w_obj # CCC avoid the double conversion that occurs here if isinstance(w_obj, W_ListObject): - # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM - w_obj.convert_to_cpy_strategy(space) + # note: we used to call w_obj.convert_to_cpy_strategy() here, + # but we really have to call it from PySequence_Fast_GET_ITEM() + # because some people never call PySequence_Fast() if they know + # the object is a list. return w_obj try: - return W_ListObject.newlist_cpyext(space, space.listview(w_obj)) + return tupleobject.W_TupleObject(space.fixedview(w_obj)) except OperationError as e: if e.match(space, space.w_TypeError): raise OperationError(space.w_TypeError, space.newtext(rffi.charp2str(m))) raise e -# CCC this should be written as a C macro - at cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_borrowed=True) -def PySequence_Fast_GET_ITEM(space, w_obj, index): +# CCC this should be written as a C macro, at least for the tuple case + at cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_is_ll=True) +def PySequence_Fast_GET_ITEM(space, py_obj, index): """Return the ith element of o, assuming that o was returned by PySequence_Fast(), o is not NULL, and that i is within bounds. """ - if isinstance(w_obj, W_ListObject): - return w_obj.getitem(index) - elif isinstance(w_obj, tupleobject.W_TupleObject): - return w_obj.wrappeditems[index] - raise oefmt(space.w_TypeError, - "PySequence_Fast_GET_ITEM called but object is not a list or " - "sequence") + py_obj = rffi.cast(PyObject, py_obj) + if PyTuple_Check(space, py_obj): + from pypy.module.cpyext.tupleobject import PyTupleObject + py_tuple = rffi.cast(PyTupleObject, py_obj) + return py_tuple.c_ob_item[index] + else: + from pypy.module.cpyext.listobject import PyList_GET_ITEM + w_obj = from_ref(space, py_obj) + return PyList_GET_ITEM(space, w_obj, index) @cpython_api([rffi.VOIDP], Py_ssize_t, error=CANNOT_FAIL) -def PySequence_Fast_GET_SIZE(space, w_obj): +def PySequence_Fast_GET_SIZE(space, py_obj): """Returns the length of o, assuming that o was returned by PySequence_Fast() and that o is not NULL. The size can also be gotten by calling PySequence_Size() on o, but PySequence_Fast_GET_SIZE() is faster because it can assume o is a list or tuple.""" - if isinstance(w_obj, W_ListObject): - return w_obj.length() - elif isinstance(w_obj, tupleobject.W_TupleObject): - return len(w_obj.wrappeditems) - raise oefmt(space.w_TypeError, - "PySequence_Fast_GET_SIZE called but object is not a list or " - "sequence") + py_obj = rffi.cast(PyObject, py_obj) + if PyTuple_Check(space, py_obj): + from pypy.module.cpyext.tupleobject import PyTupleObject + py_tuple = rffi.cast(PyTupleObject, py_obj) + return py_tuple.c_ob_size + else: + from pypy.module.cpyext.listobject import PyList_GET_SIZE + w_obj = from_ref(space, py_obj) + return PyList_GET_SIZE(space, w_obj) @cpython_api([rffi.VOIDP], PyObjectP) -def PySequence_Fast_ITEMS(space, w_obj): +def PySequence_Fast_ITEMS(space, py_obj): """Return the underlying array of PyObject pointers. Assumes that o was returned by PySequence_Fast() and o is not NULL. @@ -101,18 +107,17 @@ So, only use the underlying array pointer in contexts where the sequence cannot change. """ - if isinstance(w_obj, W_ListObject): - cpy_strategy = space.fromcache(CPyListStrategy) - if w_obj.strategy is cpy_strategy: - return w_obj.get_raw_items() # asserts it's a cpyext strategy - elif isinstance(w_obj, tupleobject.W_TupleObject): + py_obj = rffi.cast(PyObject, py_obj) + if PyTuple_Check(space, py_obj): from pypy.module.cpyext.tupleobject import PyTupleObject - py_obj = as_pyobj(space, w_obj) py_tuple = rffi.cast(PyTupleObject, py_obj) return rffi.cast(PyObjectP, py_tuple.c_ob_item) - raise oefmt(space.w_TypeError, - "PySequence_Fast_ITEMS called but object is not the result of " - "PySequence_Fast") + else: + from pypy.module.cpyext.listobject import get_list_storage + w_obj = from_ref(space, py_obj) + assert isinstance(w_obj, W_ListObject) + storage = get_list_storage(space, w_obj) + return rffi.cast(PyObjectP, storage._elems) @cpython_api([PyObject, Py_ssize_t, Py_ssize_t], PyObject) def PySequence_GetSlice(space, w_obj, start, end): @@ -300,10 +305,6 @@ storage = self.unerase(w_list.lstorage) return storage._length - def get_raw_items(self, w_list): - storage = self.unerase(w_list.lstorage) - return storage._elems - def getslice(self, w_list, start, stop, step, length): w_list.switch_to_object_strategy() return w_list.strategy.getslice(w_list, start, stop, step, length) diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -138,6 +138,7 @@ 'itertools', 'time', 'binascii', 'mmap' ]) + spaceconfig["objspace.std.withspecialisedtuple"] = True @classmethod def preload_builtins(cls, space): diff --git a/pypy/module/cpyext/test/test_sequence.py b/pypy/module/cpyext/test/test_sequence.py --- a/pypy/module/cpyext/test/test_sequence.py +++ b/pypy/module/cpyext/test/test_sequence.py @@ -21,12 +21,14 @@ w_l = space.wrap([1, 2, 3, 4]) assert api.PySequence_Fast(w_l, "message") is w_l - assert space.int_w(api.PySequence_Fast_GET_ITEM(w_l, 1)) == 2 + py_result = api.PySequence_Fast_GET_ITEM(w_l, 1) + w_result = get_w_obj_and_decref(space, py_result) + assert space.int_w(w_result) == 2 assert api.PySequence_Fast_GET_SIZE(w_l) == 4 w_set = space.wrap(set((1, 2, 3, 4))) w_seq = api.PySequence_Fast(w_set, "message") - assert space.type(w_seq) is space.w_list + assert space.type(w_seq) is space.w_tuple assert space.len_w(w_seq) == 4 w_seq = api.PySequence_Tuple(w_set) @@ -83,7 +85,7 @@ def test_get_slice_fast(self, space, api): w_t = space.wrap([1, 2, 3, 4, 5]) - api.PySequence_Fast(w_t, "foo") # converts + api.PyList_GetItem(w_t, 0) # converts to cpy strategy assert space.unwrap(api.PySequence_GetSlice(w_t, 2, 4)) == [3, 4] assert space.unwrap(api.PySequence_GetSlice(w_t, 1, -1)) == [2, 3, 4] @@ -215,7 +217,7 @@ class TestCPyListStrategy(BaseApiTest): def test_getitem_setitem(self, space, api): w_l = space.wrap([1, 2, 3, 4]) - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy assert space.int_w(space.len(w_l)) == 4 assert space.int_w(space.getitem(w_l, space.wrap(1))) == 2 assert space.int_w(space.getitem(w_l, space.wrap(0))) == 1 @@ -229,17 +231,17 @@ w = space.wrap w_l = w([1, 2, 3, 4]) - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy space.call_method(w_l, 'insert', w(0), w(0)) assert space.int_w(space.len(w_l)) == 5 assert space.int_w(space.getitem(w_l, w(3))) == 3 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy space.call_method(w_l, 'sort') assert space.int_w(space.len(w_l)) == 5 assert space.int_w(space.getitem(w_l, w(0))) == 0 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_t = space.wrap(space.fixedview(w_l)) assert space.int_w(space.len(w_t)) == 5 assert space.int_w(space.getitem(w_t, w(0))) == 0 @@ -247,22 +249,22 @@ assert space.int_w(space.len(w_l2)) == 5 assert space.int_w(space.getitem(w_l2, w(0))) == 0 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_sum = space.add(w_l, w_l) assert space.int_w(space.len(w_sum)) == 10 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_prod = space.mul(w_l, space.wrap(2)) assert space.int_w(space.len(w_prod)) == 10 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_l.inplace_mul(2) assert space.int_w(space.len(w_l)) == 10 def test_getstorage_copy(self, space, api): w = space.wrap w_l = w([1, 2, 3, 4]) - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_l1 = w([]) space.setitem(w_l1, space.newslice(w(0), w(0), w(1)), w_l) @@ -285,6 +287,10 @@ if (objects == NULL) return NULL; size = PySequence_Fast_GET_SIZE(foo); + for (i = 0; i < size; ++i) { + if (objects[i] != PySequence_Fast_GET_ITEM(foo, i)) + return PyBool_FromLong(0); + } common_type = size > 0 ? Py_TYPE(objects[0]) : NULL; for (i = 1; i < size; ++i) { if (Py_TYPE(objects[i]) != common_type) { @@ -304,6 +310,9 @@ s = (1, 2, 3, 4) assert module.test_fast_sequence(s[0:-1]) assert module.test_fast_sequence(s[::-1]) + s = (1, 2) # specialized tuple + assert module.test_fast_sequence(s[0:-1]) + assert module.test_fast_sequence(s[::-1]) s = "1234" assert module.test_fast_sequence(s[0:-1]) assert module.test_fast_sequence(s[::-1]) diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py --- a/pypy/objspace/std/listobject.py +++ b/pypy/objspace/std/listobject.py @@ -215,13 +215,6 @@ storage = strategy.erase(list_f) return W_ListObject.from_storage_and_strategy(space, storage, strategy) - @staticmethod - def newlist_cpyext(space, list): - from pypy.module.cpyext.sequence import CPyListStrategy, CPyListStorage - strategy = space.fromcache(CPyListStrategy) - storage = strategy.erase(CPyListStorage(space, list)) - return W_ListObject.from_storage_and_strategy(space, storage, strategy) - def __repr__(self): """ representation for debugging purposes """ return "%s(%s, %s)" % (self.__class__.__name__, self.strategy, @@ -260,13 +253,6 @@ self.strategy = cpy_strategy self.lstorage = cpy_strategy.erase(CPyListStorage(space, lst)) - def get_raw_items(self): - from pypy.module.cpyext.sequence import CPyListStrategy - - cpy_strategy = self.space.fromcache(CPyListStrategy) - assert self.strategy is cpy_strategy # should we return an error? - return cpy_strategy.get_raw_items(self) - # ___________________________________________________ def init_from_list_w(self, list_w): From pypy.commits at gmail.com Sat Feb 16 09:18:30 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 06:18:30 -0800 (PST) Subject: [pypy-commit] pypy default: Fix test Message-ID: <5c681bb6.1c69fb81.cb376.651a@mx.google.com> Author: Armin Rigo Branch: Changeset: r96027:7fad2f4ca232 Date: 2019-02-16 14:30 +0100 http://bitbucket.org/pypy/pypy/changeset/7fad2f4ca232/ Log: Fix test diff --git a/pypy/module/cpyext/test/test_sequence.py b/pypy/module/cpyext/test/test_sequence.py --- a/pypy/module/cpyext/test/test_sequence.py +++ b/pypy/module/cpyext/test/test_sequence.py @@ -5,7 +5,7 @@ from pypy.module.cpyext.sequence import ( PySequence_Fast, PySequence_Contains, PySequence_Index, PySequence_GetItem, PySequence_SetItem, PySequence_DelItem) -from pypy.module.cpyext.pyobject import get_w_obj_and_decref +from pypy.module.cpyext.pyobject import get_w_obj_and_decref, from_ref from pypy.module.cpyext.state import State import pytest @@ -22,7 +22,7 @@ assert api.PySequence_Fast(w_l, "message") is w_l py_result = api.PySequence_Fast_GET_ITEM(w_l, 1) - w_result = get_w_obj_and_decref(space, py_result) + w_result = from_ref(space, py_result) assert space.int_w(w_result) == 2 assert api.PySequence_Fast_GET_SIZE(w_l) == 4 From pypy.commits at gmail.com Sat Feb 16 09:18:31 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 06:18:31 -0800 (PST) Subject: [pypy-commit] pypy default: Fix test Message-ID: <5c681bb7.1c69fb81.b3e3a.cd1b@mx.google.com> Author: Armin Rigo Branch: Changeset: r96028:63c291824396 Date: 2019-02-16 15:17 +0100 http://bitbucket.org/pypy/pypy/changeset/63c291824396/ Log: Fix test 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 @@ -528,7 +528,8 @@ py_type = rffi.cast(PyTypeObjectPtr, ref) assert py_type.c_tp_alloc - assert from_ref(space, py_type.c_tp_mro).wrappeditems is w_class.mro_w + w_tup = from_ref(space, py_type.c_tp_mro) + assert space.fixedview(w_tup) == w_class.mro_w decref(space, ref) From pypy.commits at gmail.com Sat Feb 16 09:41:43 2019 From: pypy.commits at gmail.com (fijal) Date: Sat, 16 Feb 2019 06:41:43 -0800 (PST) Subject: [pypy-commit] pypy arm64: add CMP_rr and push forward to next test Message-ID: <5c682127.1c69fb81.cf7e9.5349@mx.google.com> Author: Maciej Fijalkowski Branch: arm64 Changeset: r96029:5cb0444d17a2 Date: 2019-02-16 14:41 +0000 http://bitbucket.org/pypy/pypy/changeset/5cb0444d17a2/ Log: add CMP_rr and push forward to next test diff --git a/rpython/jit/backend/aarch64/TODO b/rpython/jit/backend/aarch64/TODO new file mode 100644 --- /dev/null +++ b/rpython/jit/backend/aarch64/TODO @@ -0,0 +1,2 @@ +* int_add - IMM +* int_cmp - IMM diff --git a/rpython/jit/backend/aarch64/assembler.py b/rpython/jit/backend/aarch64/assembler.py --- a/rpython/jit/backend/aarch64/assembler.py +++ b/rpython/jit/backend/aarch64/assembler.py @@ -5,8 +5,8 @@ #from rpython.jit.backend.arm.helper.regalloc import VMEM_imm_size from rpython.jit.backend.aarch64.opassembler import ResOpAssembler from rpython.jit.backend.aarch64.regalloc import (Regalloc, + operations as regalloc_operations, guard_operations, comp_operations) # CoreRegisterManager, check_imm_arg, VFPRegisterManager, - operations as regalloc_operations) #from rpython.jit.backend.arm import callbuilder from rpython.jit.backend.aarch64 import registers as r from rpython.jit.backend.llsupport import jitframe @@ -276,6 +276,12 @@ regalloc.possibly_free_vars_for_op(op) elif not we_are_translated() and op.getopnum() == rop.FORCE_SPILL: regalloc.prepare_force_spill(op) + elif i < len(operations) - 1 and regalloc.next_op_can_accept_cc(operations, i): + arglocs = guard_operations[operations[i + 1].getopnum()]( + regalloc, operations[i + 1], op) + if arglocs is not None: + xxx + regalloc.next_instruction() # advance one more else: arglocs = regalloc_operations[opnum](regalloc, op) if arglocs is not None: @@ -292,6 +298,13 @@ self.mc.mark_op(None) # end of the loop regalloc.operations = None + def dispatch_comparison(self, op): + opnum = op.getopnum() + arglocs = comp_operations[opnum](self._regalloc, op, True) + assert arglocs is not None + asm_comp_operations[opnum](self, op, arglocs) + return arglocs + # regalloc support def load(self, loc, value): """load an immediate value into a register""" @@ -415,12 +428,16 @@ raise NotImplementedError(msg) -def notimplemented_op(self, op, arglocs, regalloc): +def notimplemented_op(self, op, arglocs): print "[ARM/asm] %s not implemented" % op.getopname() raise NotImplementedError(op) +def notimplemented_comp_op(self, op, arglocs): + print "[ARM/asm] %s not implemented" % op.getopname() + raise NotImplementedError(op) asm_operations = [notimplemented_op] * (rop._LAST + 1) +asm_comp_operations = [notimplemented_comp_op] * (rop._LAST + 1) asm_extra_operations = {} for name, value in ResOpAssembler.__dict__.iteritems(): @@ -432,3 +449,7 @@ opname = name[len('emit_op_'):] num = getattr(rop, opname.upper()) asm_operations[num] = value + elif name.startswith('emit_comp_op_'): + opname = name[len('emit_comp_op_'):] + num = getattr(rop, opname.upper()) + asm_comp_operations[num] = value diff --git a/rpython/jit/backend/aarch64/codebuilder.py b/rpython/jit/backend/aarch64/codebuilder.py --- a/rpython/jit/backend/aarch64/codebuilder.py +++ b/rpython/jit/backend/aarch64/codebuilder.py @@ -104,6 +104,10 @@ base = 0b10001011000 self.write32((base << 21) | (rm << 16) | (rn << 5) | (rd)) + def CMP_rr(self, rn, rm): + base = 0b11101011000 + self.write32((base << 21) | (rm << 16) | (rn << 5) | 0b11111) + def BRK(self): self.write32(0b11010100001 << 21) diff --git a/rpython/jit/backend/aarch64/opassembler.py b/rpython/jit/backend/aarch64/opassembler.py --- a/rpython/jit/backend/aarch64/opassembler.py +++ b/rpython/jit/backend/aarch64/opassembler.py @@ -2,6 +2,7 @@ from rpython.jit.metainterp.history import (AbstractFailDescr, ConstInt, INT, FLOAT, REF) from rpython.jit.backend.aarch64 import registers as r +from rpython.jit.backend.arm import conditions as c # yes, arm, not aarch64 from rpython.jit.backend.llsupport.assembler import GuardToken, BaseAssembler class ResOpAssembler(BaseAssembler): @@ -24,6 +25,17 @@ else: self.mc.ADD_rr(res.value, l0.value, l1.value) + def emit_int_comp_op(self, op, arglocs): + l0, l1 = arglocs + + if l1.is_imm(): + xxx + self.mc.CMP_ri(l0.value, imm=l1.getint(), cond=fcond) + else: + self.mc.CMP_rr(l0.value, l1.value) + + emit_comp_op_int_le = emit_int_comp_op + def emit_op_increment_debug_counter(self, op, arglocs): return # XXXX base_loc, value_loc = arglocs @@ -31,6 +43,9 @@ self.mc.ADD_ri(value_loc.value, value_loc.value, 1) self.mc.STR_ri(value_loc.value, base_loc.value, 0) + def emit_op_label(self, op, arglocs): + pass + def emit_op_finish(self, op, arglocs): base_ofs = self.cpu.get_baseofs_of_frame_field() if len(arglocs) > 0: diff --git a/rpython/jit/backend/aarch64/regalloc.py b/rpython/jit/backend/aarch64/regalloc.py --- a/rpython/jit/backend/aarch64/regalloc.py +++ b/rpython/jit/backend/aarch64/regalloc.py @@ -177,6 +177,12 @@ self.possibly_free_vars(list(inputargs)) return operations + def loc(self, var): + if var.type == FLOAT: + return self.vfprm.loc(var) + else: + return self.rm.loc(var) + def possibly_free_var(self, var): if var.type == FLOAT: self.vfprm.possibly_free_var(var) @@ -245,7 +251,7 @@ # we would like the boxes to be after the jump. def _compute_hint_frame_locations_from_descr(self, descr): - arglocs = self.assembler.target_arglocs(descr) + arglocs = descr._arm_arglocs jump_op = self.final_jump_op assert len(arglocs) == jump_op.numargs() for i in range(jump_op.numargs()): @@ -271,23 +277,6 @@ self.free_temp_vars() return [base_loc, value_loc] - def _prepare_op_int_add(self, op, fcond): - XXX - boxes = op.getarglist() - a0, a1 = boxes - imm_a0 = check_imm_box(a0) - imm_a1 = check_imm_box(a1) - if not imm_a0 and imm_a1: - l0 = self.make_sure_var_in_reg(a0, boxes) - l1 = self.convert_to_imm(a1) - elif imm_a0 and not imm_a1: - l0 = self.convert_to_imm(a0) - l1 = self.make_sure_var_in_reg(a1, boxes) - else: - l0 = self.make_sure_var_in_reg(a0, boxes) - l1 = self.make_sure_var_in_reg(a1, boxes) - return [l0, l1] - def prepare_op_int_add(self, op): arg0 = op.getarg(0) arg1 = op.getarg(1) @@ -298,6 +287,71 @@ res = self.force_allocate_reg(op) return [l0, l1, res] + + def prepare_int_cmp(self, op, res_in_cc): + boxes = op.getarglist() + arg0, arg1 = boxes + imm_a1 = False # XXX check_imm_box(arg1) + + l0 = self.make_sure_var_in_reg(arg0, forbidden_vars=boxes) + if imm_a1: + l1 = self.convert_to_imm(arg1) + else: + l1 = self.make_sure_var_in_reg(arg1, forbidden_vars=boxes) + + self.possibly_free_vars_for_op(op) + self.free_temp_vars() + if not res_in_cc: + res = self.force_allocate_reg_or_cc(op) + return [l0, l1, res] + return [l0, l1] + + prepare_comp_op_int_lt = prepare_int_cmp + prepare_comp_op_int_le = prepare_int_cmp + + def prepare_op_int_le(self, op): + return self.prepare_int_cmp(op, False) + + def prepare_op_label(self, op): + descr = op.getdescr() + assert isinstance(descr, TargetToken) + inputargs = op.getarglist() + arglocs = [None] * len(inputargs) + # + # we use force_spill() on the boxes that are not going to be really + # used any more in the loop, but that are kept alive anyway + # by being in a next LABEL's or a JUMP's argument or fail_args + # of some guard + position = self.rm.position + for arg in inputargs: + assert not isinstance(arg, Const) + if self.last_real_usage.get(arg, -1) <= position: + self.force_spill_var(arg) + + # + for i in range(len(inputargs)): + arg = inputargs[i] + assert not isinstance(arg, Const) + loc = self.loc(arg) + arglocs[i] = loc + if loc.is_core_reg() or loc.is_vfp_reg(): + self.frame_manager.mark_as_free(arg) + # + descr._arm_arglocs = arglocs + descr._ll_loop_code = self.assembler.mc.currpos() + descr._arm_clt = self.assembler.current_clt + self.assembler.target_tokens_currently_compiling[descr] = None + self.possibly_free_vars_for_op(op) + # + # if the LABEL's descr is precisely the target of the JUMP at the + # end of the same loop, i.e. if what we are compiling is a single + # loop that ends up jumping to this LABEL, then we can now provide + # the hints about the expected position of the spilled variables. + jump_op = self.final_jump_op + if jump_op is not None and jump_op.getdescr() is descr: + self._compute_hint_frame_locations_from_descr(descr) + return [] + def prepare_op_finish(self, op): # the frame is in fp, but we have to point where in the frame is # the potential argument to FINISH @@ -308,6 +362,10 @@ locs = [] return locs + def prepare_guard_op_guard_true(self, op, prevop): + arglocs = self.assembler.dispatch_comparison(prevop) + xxx + prepare_op_nursery_ptr_increment = prepare_op_int_add def force_allocate_reg(self, var, forbidden_vars=[], selected_reg=None): @@ -330,8 +388,17 @@ print "[ARM64/regalloc] %s not implemented" % op.getopname() raise NotImplementedError(op) +def notimplemented_guard_op(self, op, prevop): + print "[ARM64/regalloc] %s not implemented" % op.getopname() + raise NotImplementedError(op) + +def notimplemented_comp_op(self, op, res_in_cc): + print "[ARM64/regalloc] %s not implemented" % op.getopname() + raise NotImplementedError(op) operations = [notimplemented] * (rop._LAST + 1) +guard_operations = [notimplemented_guard_op] * (rop._LAST + 1) +comp_operations = [notimplemented_comp_op] * (rop._LAST + 1) for key, value in rop.__dict__.items(): @@ -342,3 +409,12 @@ if hasattr(Regalloc, methname): func = getattr(Regalloc, methname).im_func operations[value] = func + methname = 'prepare_guard_op_%s' % key + if hasattr(Regalloc, methname): + func = getattr(Regalloc, methname).im_func + guard_operations[value] = func + methname = 'prepare_comp_op_%s' % key + if hasattr(Regalloc, methname): + func = getattr(Regalloc, methname).im_func + comp_operations[value] = func + \ No newline at end of file diff --git a/rpython/jit/backend/aarch64/test/test_instr_builder.py b/rpython/jit/backend/aarch64/test/test_instr_builder.py --- a/rpython/jit/backend/aarch64/test/test_instr_builder.py +++ b/rpython/jit/backend/aarch64/test/test_instr_builder.py @@ -124,6 +124,14 @@ cb.ADD_rr(rd.value, rn.value, rm.value) assert cb.hexdump() == assemble("ADD %r, %r, %r" % (rd, rn, rm)) + @settings(max_examples=20) + @given(rn=st.sampled_from(r.registers), + rm=st.sampled_from(r.registers)) + def test_CMP_rr(self, rn, rm): + cb = CodeBuilder() + cb.CMP_rr(rn.value, rm.value) + assert cb.hexdump() == assemble("CMP %r, %r" % (rn, rm)) + def test_BRK(self): cb = CodeBuilder() cb.BRK() From pypy.commits at gmail.com Sat Feb 16 10:25:36 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 07:25:36 -0800 (PST) Subject: [pypy-commit] cffi release-1.12: bump version number to 1.12.1 Message-ID: <5c682b70.1c69fb81.11cd6.5736@mx.google.com> Author: Armin Rigo Branch: release-1.12 Changeset: r3222:914388b2b8b8 Date: 2019-02-16 16:26 +0100 http://bitbucket.org/cffi/cffi/changeset/914388b2b8b8/ Log: bump version number to 1.12.1 diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -2,7 +2,7 @@ #include #include "structmember.h" -#define CFFI_VERSION "1.12.0" +#define CFFI_VERSION "1.12.1" #ifdef MS_WIN32 #include diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -12,7 +12,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.12.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.12.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,): diff --git a/cffi/__init__.py b/cffi/__init__.py --- a/cffi/__init__.py +++ b/cffi/__init__.py @@ -5,8 +5,8 @@ from .error import CDefError, FFIError, VerificationError, VerificationMissing from .error import PkgConfigError -__version__ = "1.12.0" -__version_info__ = (1, 12, 0) +__version__ = "1.12.1" +__version_info__ = (1, 12, 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/cffi/_embedding.h b/cffi/_embedding.h --- a/cffi/_embedding.h +++ b/cffi/_embedding.h @@ -221,7 +221,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.12.0" + "\ncompiled with cffi version: 1.12.1" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff --git a/doc/source/conf.py b/doc/source/conf.py --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -47,7 +47,7 @@ # The short X.Y version. version = '1.12' # The full version, including alpha/beta/rc tags. -release = '1.12.0' +release = '1.12.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -52,13 +52,13 @@ * https://pypi.python.org/pypi/cffi -* Checksums of the "source" package version 1.12.0: +* Checksums of the "source" package version 1.12.1: - - MD5: 8e3c92fb5a7b5e32390882dd8ed32275 + - MD5: ... - - SHA: 270cc6e3b4873c617647d05aaa6fd1302a1f0071 + - SHA: ... - - SHA256: 08090454ff236239e583a9119d0502a6b9817594c0a3714dd1d8593f2350ba11 + - SHA256: ... * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -198,7 +198,7 @@ `Mailing list `_ """, - version='1.12.0', + version='1.12.1', packages=['cffi'] if cpython else [], package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', '_embedding.h', '_cffi_errors.h']} From pypy.commits at gmail.com Sat Feb 16 10:26:58 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 07:26:58 -0800 (PST) Subject: [pypy-commit] pypy default: update to cffi/914388b2b8b8 Message-ID: <5c682bc2.1c69fb81.4218f.2410@mx.google.com> Author: Armin Rigo Branch: Changeset: r96030:f7ca84265e13 Date: 2019-02-16 16:26 +0100 http://bitbucket.org/pypy/pypy/changeset/f7ca84265e13/ Log: update to cffi/914388b2b8b8 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.12.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.12.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,): From pypy.commits at gmail.com Sat Feb 16 10:27:00 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 07:27:00 -0800 (PST) Subject: [pypy-commit] pypy default: update to cffi/914388b2b8b8 Message-ID: <5c682bc4.1c69fb81.c67bc.0518@mx.google.com> Author: Armin Rigo Branch: Changeset: r96031:e1ca8268d72b Date: 2019-02-16 16:27 +0100 http://bitbucket.org/pypy/pypy/changeset/e1ca8268d72b/ Log: update to cffi/914388b2b8b8 diff --git a/extra_tests/cffi_tests/cffi0/test_ownlib.py b/extra_tests/cffi_tests/cffi0/test_ownlib.py --- a/extra_tests/cffi_tests/cffi0/test_ownlib.py +++ b/extra_tests/cffi_tests/cffi0/test_ownlib.py @@ -352,6 +352,8 @@ def test_modify_struct_value(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") + if self.Backend is CTypesBackend: + py.test.skip("fails with the ctypes backend on some architectures") ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef struct { diff --git a/extra_tests/cffi_tests/embedding/thread3-test.c b/extra_tests/cffi_tests/embedding/thread3-test.c --- a/extra_tests/cffi_tests/embedding/thread3-test.c +++ b/extra_tests/cffi_tests/embedding/thread3-test.c @@ -52,5 +52,6 @@ assert(status == 0); } printf("done\n"); + fflush(stdout); /* this is occasionally needed on Windows */ return 0; } 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 @@ -5,8 +5,8 @@ from .error import CDefError, FFIError, VerificationError, VerificationMissing from .error import PkgConfigError -__version__ = "1.12.0" -__version_info__ = (1, 12, 0) +__version__ = "1.12.1" +__version_info__ = (1, 12, 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/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h --- a/lib_pypy/cffi/_cffi_include.h +++ b/lib_pypy/cffi/_cffi_include.h @@ -8,43 +8,20 @@ the same works for the other two macros. Py_DEBUG implies them, but not the other way around. - The implementation is messy (issue #350): on Windows, with _MSC_VER, - we have to define Py_LIMITED_API even before including pyconfig.h. - In that case, we guess what pyconfig.h will do to the macros above, - and check our guess after the #include. - - Note that on Windows, with CPython 3.x, you need virtualenv version - >= 16.0.0. Older versions don't copy PYTHON3.DLL. As a workaround - you can remove the definition of Py_LIMITED_API here. - - See also 'py_limited_api' in cffi/setuptools_ext.py. + Issue #350 is still open: on Windows, the code here causes it to link + with PYTHON36.DLL (for example) instead of PYTHON3.DLL. A fix was + attempted in 164e526a5515 and 14ce6985e1c3, but reverted: virtualenv + does not make PYTHON3.DLL available, and so the "correctly" compiled + version would not run inside a virtualenv. We will re-apply the fix + after virtualenv has been fixed for some time. For explanation, see + issue #355. For a workaround if you want PYTHON3.DLL and don't worry + about virtualenv, see issue #350. See also 'py_limited_api' in + setuptools_ext.py. */ #if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) -# ifdef _MSC_VER -# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif -# include - /* sanity-check: Py_LIMITED_API will cause crashes if any of these - are also defined. Normally, the Python file PC/pyconfig.h does not - cause any of these to be defined, with the exception that _DEBUG - causes Py_DEBUG. Double-check that. */ -# ifdef Py_LIMITED_API -# if defined(Py_DEBUG) -# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" -# endif -# if defined(Py_TRACE_REFS) -# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" -# endif -# if defined(Py_REF_DEBUG) -# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" -# endif -# endif -# else -# include -# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif +# include +# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) +# define Py_LIMITED_API # endif #endif 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 @@ -221,7 +221,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.12.0" + "\ncompiled with cffi version: 1.12.1" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); 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 @@ -81,8 +81,14 @@ it doesn't so far, creating troubles. That's why we check for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) + + On Windows, with CPython <= 3.4, it's better not to use py_limited_api + because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. + For now we'll skip py_limited_api on all Windows versions to avoid an + inconsistent mess. """ - if 'py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount'): + if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') + and sys.platform != 'win32'): import setuptools try: setuptools_major_version = int(setuptools.__version__.partition('.')[0]) From pypy.commits at gmail.com Sat Feb 16 10:27:04 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 07:27:04 -0800 (PST) Subject: [pypy-commit] cffi default: hg merge release-1.12 Message-ID: <5c682bc8.1c69fb81.772f4.98f0@mx.google.com> Author: Armin Rigo Branch: Changeset: r3223:1e4689c8fd83 Date: 2019-02-16 16:27 +0100 http://bitbucket.org/cffi/cffi/changeset/1e4689c8fd83/ Log: hg merge release-1.12 diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -2,7 +2,7 @@ #include #include "structmember.h" -#define CFFI_VERSION "1.12.0" +#define CFFI_VERSION "1.12.1" #ifdef MS_WIN32 #include diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -12,7 +12,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.12.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.12.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,): diff --git a/cffi/__init__.py b/cffi/__init__.py --- a/cffi/__init__.py +++ b/cffi/__init__.py @@ -5,8 +5,8 @@ from .error import CDefError, FFIError, VerificationError, VerificationMissing from .error import PkgConfigError -__version__ = "1.12.0" -__version_info__ = (1, 12, 0) +__version__ = "1.12.1" +__version_info__ = (1, 12, 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/cffi/_embedding.h b/cffi/_embedding.h --- a/cffi/_embedding.h +++ b/cffi/_embedding.h @@ -221,7 +221,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.12.0" + "\ncompiled with cffi version: 1.12.1" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff --git a/doc/source/conf.py b/doc/source/conf.py --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -47,7 +47,7 @@ # The short X.Y version. version = '1.12' # The full version, including alpha/beta/rc tags. -release = '1.12.0' +release = '1.12.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -52,13 +52,13 @@ * https://pypi.python.org/pypi/cffi -* Checksums of the "source" package version 1.12.0: +* Checksums of the "source" package version 1.12.1: - - MD5: 8e3c92fb5a7b5e32390882dd8ed32275 + - MD5: ... - - SHA: 270cc6e3b4873c617647d05aaa6fd1302a1f0071 + - SHA: ... - - SHA256: 08090454ff236239e583a9119d0502a6b9817594c0a3714dd1d8593f2350ba11 + - SHA256: ... * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -198,7 +198,7 @@ `Mailing list `_ """, - version='1.12.0', + version='1.12.1', packages=['cffi'] if cpython else [], package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', '_embedding.h', '_cffi_errors.h']} From pypy.commits at gmail.com Sat Feb 16 11:30:17 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 08:30:17 -0800 (PST) Subject: [pypy-commit] cffi release-1.12: md5/sha Message-ID: <5c683a99.1c69fb81.483c6.0453@mx.google.com> Author: Armin Rigo Branch: release-1.12 Changeset: r3224:ac7cb142e8f5 Date: 2019-02-16 17:29 +0100 http://bitbucket.org/cffi/cffi/changeset/ac7cb142e8f5/ Log: md5/sha diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -54,11 +54,11 @@ * Checksums of the "source" package version 1.12.1: - - MD5: ... + - MD5: d6d5c4805bbce844cf1368702b056e3c - - SHA: ... + - SHA: b4d8d74a22d0574cb056502daa2c377898f0a910 - - SHA256: ... + - SHA256: 9b6f7ba4e78c52c1a291d0c0c0bd745d19adde1a9e1c03cb899f0c6efd6f8033 * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` From pypy.commits at gmail.com Sat Feb 16 11:30:19 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 08:30:19 -0800 (PST) Subject: [pypy-commit] cffi release-1.12: Added tag v1.12.1 for changeset ac7cb142e8f5 Message-ID: <5c683a9b.1c69fb81.a40a7.5dbb@mx.google.com> Author: Armin Rigo Branch: release-1.12 Changeset: r3225:a8b27bd92c37 Date: 2019-02-16 17:30 +0100 http://bitbucket.org/cffi/cffi/changeset/a8b27bd92c37/ Log: Added tag v1.12.1 for changeset ac7cb142e8f5 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -18,3 +18,4 @@ e08abd4703fef26f036e82255f4070277a9e03bd v1.11.4 48416163071ed48300c3ae4358cc7fd841912413 v1.11.5 170fb3d1f0b30d32c9794ea40dbb51836dc7d947 v1.12.0 +ac7cb142e8f5dc73033d89c54c54e1b1a6413946 v1.12.1 From pypy.commits at gmail.com Sat Feb 16 11:30:21 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 08:30:21 -0800 (PST) Subject: [pypy-commit] cffi default: hg merge release-1.12 Message-ID: <5c683a9d.1c69fb81.ab1bd.dc85@mx.google.com> Author: Armin Rigo Branch: Changeset: r3226:fef771bfb514 Date: 2019-02-16 17:30 +0100 http://bitbucket.org/cffi/cffi/changeset/fef771bfb514/ Log: hg merge release-1.12 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -18,3 +18,4 @@ e08abd4703fef26f036e82255f4070277a9e03bd v1.11.4 48416163071ed48300c3ae4358cc7fd841912413 v1.11.5 170fb3d1f0b30d32c9794ea40dbb51836dc7d947 v1.12.0 +ac7cb142e8f5dc73033d89c54c54e1b1a6413946 v1.12.1 diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -54,11 +54,11 @@ * Checksums of the "source" package version 1.12.1: - - MD5: ... + - MD5: d6d5c4805bbce844cf1368702b056e3c - - SHA: ... + - SHA: b4d8d74a22d0574cb056502daa2c377898f0a910 - - SHA256: ... + - SHA256: 9b6f7ba4e78c52c1a291d0c0c0bd745d19adde1a9e1c03cb899f0c6efd6f8033 * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` From pypy.commits at gmail.com Sat Feb 16 13:16:11 2019 From: pypy.commits at gmail.com (mattip) Date: Sat, 16 Feb 2019 10:16:11 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch Message-ID: <5c68536b.1c69fb81.8c268.19f7@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96032:6e5e91318ea6 Date: 2019-02-16 20:13 +0200 http://bitbucket.org/pypy/pypy/changeset/6e5e91318ea6/ Log: merge default into branch diff --git a/extra_tests/cffi_tests/cffi0/test_ownlib.py b/extra_tests/cffi_tests/cffi0/test_ownlib.py --- a/extra_tests/cffi_tests/cffi0/test_ownlib.py +++ b/extra_tests/cffi_tests/cffi0/test_ownlib.py @@ -352,6 +352,8 @@ def test_modify_struct_value(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") + if self.Backend is CTypesBackend: + py.test.skip("fails with the ctypes backend on some architectures") ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef struct { diff --git a/extra_tests/cffi_tests/embedding/thread3-test.c b/extra_tests/cffi_tests/embedding/thread3-test.c --- a/extra_tests/cffi_tests/embedding/thread3-test.c +++ b/extra_tests/cffi_tests/embedding/thread3-test.c @@ -52,5 +52,6 @@ assert(status == 0); } printf("done\n"); + fflush(stdout); /* this is occasionally needed on Windows */ return 0; } 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 @@ -5,8 +5,8 @@ from .error import CDefError, FFIError, VerificationError, VerificationMissing from .error import PkgConfigError -__version__ = "1.12.0" -__version_info__ = (1, 12, 0) +__version__ = "1.12.1" +__version_info__ = (1, 12, 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/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h --- a/lib_pypy/cffi/_cffi_include.h +++ b/lib_pypy/cffi/_cffi_include.h @@ -8,43 +8,20 @@ the same works for the other two macros. Py_DEBUG implies them, but not the other way around. - The implementation is messy (issue #350): on Windows, with _MSC_VER, - we have to define Py_LIMITED_API even before including pyconfig.h. - In that case, we guess what pyconfig.h will do to the macros above, - and check our guess after the #include. - - Note that on Windows, with CPython 3.x, you need virtualenv version - >= 16.0.0. Older versions don't copy PYTHON3.DLL. As a workaround - you can remove the definition of Py_LIMITED_API here. - - See also 'py_limited_api' in cffi/setuptools_ext.py. + Issue #350 is still open: on Windows, the code here causes it to link + with PYTHON36.DLL (for example) instead of PYTHON3.DLL. A fix was + attempted in 164e526a5515 and 14ce6985e1c3, but reverted: virtualenv + does not make PYTHON3.DLL available, and so the "correctly" compiled + version would not run inside a virtualenv. We will re-apply the fix + after virtualenv has been fixed for some time. For explanation, see + issue #355. For a workaround if you want PYTHON3.DLL and don't worry + about virtualenv, see issue #350. See also 'py_limited_api' in + setuptools_ext.py. */ #if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) -# ifdef _MSC_VER -# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif -# include - /* sanity-check: Py_LIMITED_API will cause crashes if any of these - are also defined. Normally, the Python file PC/pyconfig.h does not - cause any of these to be defined, with the exception that _DEBUG - causes Py_DEBUG. Double-check that. */ -# ifdef Py_LIMITED_API -# if defined(Py_DEBUG) -# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" -# endif -# if defined(Py_TRACE_REFS) -# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" -# endif -# if defined(Py_REF_DEBUG) -# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" -# endif -# endif -# else -# include -# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif +# include +# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) +# define Py_LIMITED_API # endif #endif 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 @@ -221,7 +221,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.12.0" + "\ncompiled with cffi version: 1.12.1" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); 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 @@ -81,8 +81,14 @@ it doesn't so far, creating troubles. That's why we check for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) + + On Windows, with CPython <= 3.4, it's better not to use py_limited_api + because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. + For now we'll skip py_limited_api on all Windows versions to avoid an + inconsistent mess. """ - if 'py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount'): + if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') + and sys.platform != 'win32'): import setuptools try: setuptools_major_version = int(setuptools.__version__.partition('.')[0]) 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.12.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.12.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,): diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py --- a/pypy/module/cpyext/listobject.py +++ b/pypy/module/cpyext/listobject.py @@ -104,7 +104,8 @@ def PyList_GET_SIZE(space, w_obj): """Macro form of PyList_Size() without error checking. """ - return space.len_w(w_obj) + assert isinstance(w_obj, W_ListObject) + return w_obj.length() @cpython_api([PyObject], Py_ssize_t, error=-1) diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py --- a/pypy/module/cpyext/sequence.py +++ b/pypy/module/cpyext/sequence.py @@ -50,50 +50,56 @@ converted to a sequence, and raises a TypeError, raise a new TypeError with m as the message text. If the conversion otherwise, fails, reraise the original exception""" - if isinstance(w_obj, tupleobject.W_TupleObject): + if isinstance(w_obj, tupleobject.W_AbstractTupleObject): return w_obj # CCC avoid the double conversion that occurs here if isinstance(w_obj, W_ListObject): - # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM - w_obj.convert_to_cpy_strategy(space) + # note: we used to call w_obj.convert_to_cpy_strategy() here, + # but we really have to call it from PySequence_Fast_GET_ITEM() + # because some people never call PySequence_Fast() if they know + # the object is a list. return w_obj try: - return W_ListObject.newlist_cpyext(space, space.listview(w_obj)) + return tupleobject.W_TupleObject(space.fixedview(w_obj)) except OperationError as e: if e.match(space, space.w_TypeError): raise OperationError(space.w_TypeError, space.newtext(rffi.charp2str(m))) raise e -# CCC this should be written as a C macro - at cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_borrowed=True) -def PySequence_Fast_GET_ITEM(space, w_obj, index): +# CCC this should be written as a C macro, at least for the tuple case + at cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_is_ll=True) +def PySequence_Fast_GET_ITEM(space, py_obj, index): """Return the ith element of o, assuming that o was returned by PySequence_Fast(), o is not NULL, and that i is within bounds. """ - if isinstance(w_obj, W_ListObject): - return w_obj.getitem(index) - elif isinstance(w_obj, tupleobject.W_TupleObject): - return w_obj.wrappeditems[index] - raise oefmt(space.w_TypeError, - "PySequence_Fast_GET_ITEM called but object is not a list or " - "sequence") + py_obj = rffi.cast(PyObject, py_obj) + if PyTuple_Check(space, py_obj): + from pypy.module.cpyext.tupleobject import PyTupleObject + py_tuple = rffi.cast(PyTupleObject, py_obj) + return py_tuple.c_ob_item[index] + else: + from pypy.module.cpyext.listobject import PyList_GET_ITEM + w_obj = from_ref(space, py_obj) + return PyList_GET_ITEM(space, w_obj, index) @cpython_api([rffi.VOIDP], Py_ssize_t, error=CANNOT_FAIL) -def PySequence_Fast_GET_SIZE(space, w_obj): +def PySequence_Fast_GET_SIZE(space, py_obj): """Returns the length of o, assuming that o was returned by PySequence_Fast() and that o is not NULL. The size can also be gotten by calling PySequence_Size() on o, but PySequence_Fast_GET_SIZE() is faster because it can assume o is a list or tuple.""" - if isinstance(w_obj, W_ListObject): - return w_obj.length() - elif isinstance(w_obj, tupleobject.W_TupleObject): - return len(w_obj.wrappeditems) - raise oefmt(space.w_TypeError, - "PySequence_Fast_GET_SIZE called but object is not a list or " - "sequence") + py_obj = rffi.cast(PyObject, py_obj) + if PyTuple_Check(space, py_obj): + from pypy.module.cpyext.tupleobject import PyTupleObject + py_tuple = rffi.cast(PyTupleObject, py_obj) + return py_tuple.c_ob_size + else: + from pypy.module.cpyext.listobject import PyList_GET_SIZE + w_obj = from_ref(space, py_obj) + return PyList_GET_SIZE(space, w_obj) @cpython_api([rffi.VOIDP], PyObjectP) -def PySequence_Fast_ITEMS(space, w_obj): +def PySequence_Fast_ITEMS(space, py_obj): """Return the underlying array of PyObject pointers. Assumes that o was returned by PySequence_Fast() and o is not NULL. @@ -101,18 +107,17 @@ So, only use the underlying array pointer in contexts where the sequence cannot change. """ - if isinstance(w_obj, W_ListObject): - cpy_strategy = space.fromcache(CPyListStrategy) - if w_obj.strategy is cpy_strategy: - return w_obj.get_raw_items() # asserts it's a cpyext strategy - elif isinstance(w_obj, tupleobject.W_TupleObject): + py_obj = rffi.cast(PyObject, py_obj) + if PyTuple_Check(space, py_obj): from pypy.module.cpyext.tupleobject import PyTupleObject - py_obj = as_pyobj(space, w_obj) py_tuple = rffi.cast(PyTupleObject, py_obj) return rffi.cast(PyObjectP, py_tuple.c_ob_item) - raise oefmt(space.w_TypeError, - "PySequence_Fast_ITEMS called but object is not the result of " - "PySequence_Fast") + else: + from pypy.module.cpyext.listobject import get_list_storage + w_obj = from_ref(space, py_obj) + assert isinstance(w_obj, W_ListObject) + storage = get_list_storage(space, w_obj) + return rffi.cast(PyObjectP, storage._elems) @cpython_api([PyObject, Py_ssize_t, Py_ssize_t], PyObject) def PySequence_GetSlice(space, w_obj, start, end): @@ -300,10 +305,6 @@ storage = self.unerase(w_list.lstorage) return storage._length - def get_raw_items(self, w_list): - storage = self.unerase(w_list.lstorage) - return storage._elems - def getslice(self, w_list, start, stop, step, length): w_list.switch_to_object_strategy() return w_list.strategy.getslice(w_list, start, stop, step, length) diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -141,6 +141,7 @@ '_cffi_backend', ], "objspace.disable_entrypoints_in_cffi": True} + spaceconfig["objspace.std.withspecialisedtuple"] = True @classmethod def preload_builtins(cls, space): diff --git a/pypy/module/cpyext/test/test_sequence.py b/pypy/module/cpyext/test/test_sequence.py --- a/pypy/module/cpyext/test/test_sequence.py +++ b/pypy/module/cpyext/test/test_sequence.py @@ -5,7 +5,7 @@ from pypy.module.cpyext.sequence import ( PySequence_Fast, PySequence_Contains, PySequence_Index, PySequence_GetItem, PySequence_SetItem, PySequence_DelItem) -from pypy.module.cpyext.pyobject import get_w_obj_and_decref +from pypy.module.cpyext.pyobject import get_w_obj_and_decref, from_ref from pypy.module.cpyext.state import State import pytest @@ -21,12 +21,14 @@ w_l = space.wrap([1, 2, 3, 4]) assert api.PySequence_Fast(w_l, "message") is w_l - assert space.int_w(api.PySequence_Fast_GET_ITEM(w_l, 1)) == 2 + py_result = api.PySequence_Fast_GET_ITEM(w_l, 1) + w_result = from_ref(space, py_result) + assert space.int_w(w_result) == 2 assert api.PySequence_Fast_GET_SIZE(w_l) == 4 w_set = space.wrap(set((1, 2, 3, 4))) w_seq = api.PySequence_Fast(w_set, "message") - assert space.type(w_seq) is space.w_list + assert space.type(w_seq) is space.w_tuple assert space.len_w(w_seq) == 4 w_seq = api.PySequence_Tuple(w_set) @@ -83,7 +85,7 @@ def test_get_slice_fast(self, space, api): w_t = space.wrap([1, 2, 3, 4, 5]) - api.PySequence_Fast(w_t, "foo") # converts + api.PyList_GetItem(w_t, 0) # converts to cpy strategy assert space.unwrap(api.PySequence_GetSlice(w_t, 2, 4)) == [3, 4] assert space.unwrap(api.PySequence_GetSlice(w_t, 1, -1)) == [2, 3, 4] @@ -215,7 +217,7 @@ class TestCPyListStrategy(BaseApiTest): def test_getitem_setitem(self, space, api): w_l = space.wrap([1, 2, 3, 4]) - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy assert space.int_w(space.len(w_l)) == 4 assert space.int_w(space.getitem(w_l, space.wrap(1))) == 2 assert space.int_w(space.getitem(w_l, space.wrap(0))) == 1 @@ -229,17 +231,17 @@ w = space.wrap w_l = w([1, 2, 3, 4]) - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy space.call_method(w_l, 'insert', w(0), w(0)) assert space.int_w(space.len(w_l)) == 5 assert space.int_w(space.getitem(w_l, w(3))) == 3 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy space.call_method(w_l, 'sort') assert space.int_w(space.len(w_l)) == 5 assert space.int_w(space.getitem(w_l, w(0))) == 0 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_t = space.wrap(space.fixedview(w_l)) assert space.int_w(space.len(w_t)) == 5 assert space.int_w(space.getitem(w_t, w(0))) == 0 @@ -247,22 +249,22 @@ assert space.int_w(space.len(w_l2)) == 5 assert space.int_w(space.getitem(w_l2, w(0))) == 0 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_sum = space.add(w_l, w_l) assert space.int_w(space.len(w_sum)) == 10 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_prod = space.mul(w_l, space.wrap(2)) assert space.int_w(space.len(w_prod)) == 10 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_l.inplace_mul(2) assert space.int_w(space.len(w_l)) == 10 def test_getstorage_copy(self, space, api): w = space.wrap w_l = w([1, 2, 3, 4]) - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_l1 = w([]) space.setitem(w_l1, space.newslice(w(0), w(0), w(1)), w_l) @@ -285,6 +287,10 @@ if (objects == NULL) return NULL; size = PySequence_Fast_GET_SIZE(foo); + for (i = 0; i < size; ++i) { + if (objects[i] != PySequence_Fast_GET_ITEM(foo, i)) + return PyBool_FromLong(0); + } common_type = size > 0 ? Py_TYPE(objects[0]) : NULL; for (i = 1; i < size; ++i) { if (Py_TYPE(objects[i]) != common_type) { @@ -304,6 +310,9 @@ s = (1, 2, 3, 4) assert module.test_fast_sequence(s[0:-1]) assert module.test_fast_sequence(s[::-1]) + s = (1, 2) # specialized tuple + assert module.test_fast_sequence(s[0:-1]) + assert module.test_fast_sequence(s[::-1]) s = "1234" assert module.test_fast_sequence(s[0:-1]) assert module.test_fast_sequence(s[::-1]) 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 @@ -514,7 +514,8 @@ py_type = rffi.cast(PyTypeObjectPtr, ref) assert py_type.c_tp_alloc - assert from_ref(space, py_type.c_tp_mro).wrappeditems is w_class.mro_w + w_tup = from_ref(space, py_type.c_tp_mro) + assert space.fixedview(w_tup) == w_class.mro_w decref(space, ref) diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py --- a/pypy/objspace/std/dictmultiobject.py +++ b/pypy/objspace/std/dictmultiobject.py @@ -45,7 +45,8 @@ # for json decoder def create_empty_unicode_key_dict(space): return r_dict(unicode_eq, unicode_hash, - force_non_null=True) + force_non_null=True, + simple_hash_eq=True) def from_unicode_key_dict(space, d): strategy = space.fromcache(UnicodeDictStrategy) @@ -1176,6 +1177,7 @@ return unwrapped def unwrap(self, wrapped): + assert type(wrapped) is self.space.UnicodeObjectCls return wrapped def is_correct_type(self, w_obj): diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py --- a/pypy/objspace/std/listobject.py +++ b/pypy/objspace/std/listobject.py @@ -214,13 +214,6 @@ storage = strategy.erase(list_f) return W_ListObject.from_storage_and_strategy(space, storage, strategy) - @staticmethod - def newlist_cpyext(space, list): - from pypy.module.cpyext.sequence import CPyListStrategy, CPyListStorage - strategy = space.fromcache(CPyListStrategy) - storage = strategy.erase(CPyListStorage(space, list)) - return W_ListObject.from_storage_and_strategy(space, storage, strategy) - def __repr__(self): """ representation for debugging purposes """ return "%s(%s, %s)" % (self.__class__.__name__, self.strategy, @@ -259,13 +252,6 @@ self.strategy = cpy_strategy self.lstorage = cpy_strategy.erase(CPyListStorage(space, lst)) - def get_raw_items(self): - from pypy.module.cpyext.sequence import CPyListStrategy - - cpy_strategy = self.space.fromcache(CPyListStrategy) - assert self.strategy is cpy_strategy # should we return an error? - return cpy_strategy.get_raw_items(self) - # ___________________________________________________ def init_from_list_w(self, list_w): diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -133,9 +133,11 @@ return pos + 1 if _is_64bit and not jit.we_are_jitted(): # optimized for Intel x86-64 by hand - return pos + 1 + ( + res = pos + 1 + ( ((chr1 > 0xDF) << 1) + rarithmetic.intmask((_constant_ncp >> (chr1 & 0x3F)) & 1)) + assert res >= 0 + return res if chr1 <= 0xDF: return pos + 2 if chr1 <= 0xEF: From pypy.commits at gmail.com Sat Feb 16 15:28:02 2019 From: pypy.commits at gmail.com (arigo) Date: Sat, 16 Feb 2019 12:28:02 -0800 (PST) Subject: [pypy-commit] pypy default: Fix version number of _cffi_backend (thanks matti) Message-ID: <5c687252.1c69fb81.aea43.f24c@mx.google.com> Author: Armin Rigo Branch: Changeset: r96033:e29ffd88da0a Date: 2019-02-16 21:28 +0100 http://bitbucket.org/pypy/pypy/changeset/e29ffd88da0a/ Log: Fix version number of _cffi_backend (thanks matti) 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.12.0" +VERSION = "1.12.1" FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI try: From pypy.commits at gmail.com Sat Feb 16 15:29:46 2019 From: pypy.commits at gmail.com (mattip) Date: Sat, 16 Feb 2019 12:29:46 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch Message-ID: <5c6872ba.1c69fb81.8119a.86c4@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96034:3c6b4d7e1ce2 Date: 2019-02-16 22:29 +0200 http://bitbucket.org/pypy/pypy/changeset/3c6b4d7e1ce2/ Log: merge default into branch 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.12.0" +VERSION = "1.12.1" FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI try: From pypy.commits at gmail.com Sun Feb 17 07:40:37 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sun, 17 Feb 2019 04:40:37 -0800 (PST) Subject: [pypy-commit] pypy default: add shortcut to ensure that 'for c in uni' does not compute the index storage Message-ID: <5c695645.1c69fb81.9cd45.a733@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96035:637f18678c1c Date: 2019-02-17 12:32 +0100 http://bitbucket.org/pypy/pypy/changeset/637f18678c1c/ Log: add shortcut to ensure that 'for c in uni' does not compute the index storage diff --git a/pypy/objspace/std/iterobject.py b/pypy/objspace/std/iterobject.py --- a/pypy/objspace/std/iterobject.py +++ b/pypy/objspace/std/iterobject.py @@ -92,6 +92,33 @@ return w_item +class W_FastUnicodeIterObject(W_AbstractSeqIterObject): + """Sequence iterator specialized for unicode objects.""" + + def __init__(self, w_seq): + from pypy.objspace.std.unicodeobject import W_UnicodeObject + W_AbstractSeqIterObject.__init__(self, w_seq) + assert isinstance(w_seq, W_UnicodeObject) + self.byteindex = 0 + + def descr_next(self, space): + from pypy.objspace.std.unicodeobject import W_UnicodeObject + from rpython.rlib import rutf8 + w_seq = self.w_seq + if w_seq is None: + raise OperationError(space.w_StopIteration, space.w_None) + assert isinstance(w_seq, W_UnicodeObject) + index = self.index + if index == w_seq._length: + self.w_seq = None + raise OperationError(space.w_StopIteration, space.w_None) + start = self.byteindex + end = rutf8.next_codepoint_pos(w_seq._utf8, start) + w_res = W_UnicodeObject(w_seq._utf8[start:end], 1) + self.byteindex = end + return w_res + + class W_FastTupleIterObject(W_AbstractSeqIterObject): """Sequence iterator specialized for tuples, accessing directly their RPython-level list of wrapped objects. diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py --- a/pypy/objspace/std/objspace.py +++ b/pypy/objspace/std/objspace.py @@ -22,6 +22,7 @@ from pypy.objspace.std.floatobject import W_FloatObject from pypy.objspace.std.intobject import W_IntObject, setup_prebuilt, wrapint from pypy.objspace.std.iterobject import W_AbstractSeqIterObject, W_SeqIterObject +from pypy.objspace.std.iterobject import W_FastUnicodeIterObject from pypy.objspace.std.listobject import W_ListObject from pypy.objspace.std.longobject import W_LongObject, newlong from pypy.objspace.std.memoryobject import W_MemoryView @@ -339,6 +340,8 @@ return W_SliceObject(w_start, w_end, w_step) def newseqiter(self, w_obj): + if type(w_obj) is W_UnicodeObject: + return W_FastUnicodeIterObject(w_obj) return W_SeqIterObject(w_obj) def newbuffer(self, obj): diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -41,6 +41,18 @@ space.w_unicode, "__new__", space.w_unicode, w_uni) assert w_new is w_uni + def test_fast_iter(self): + space = self.space + w_uni = space.newutf8(u"aä".encode("utf-8"), 2) + old_index_storage = w_uni._index_storage + w_iter = space.iter(w_uni) + w_char1 = w_iter.descr_next(space) + w_char2 = w_iter.descr_next(space) + assert w_uni._index_storage is old_index_storage + assert space.eq_w(w_char1, w_uni._getitem_result(space, 0)) + assert space.eq_w(w_char2, w_uni._getitem_result(space, 1)) + + if HAS_HYPOTHESIS: @given(strategies.text(), strategies.integers(min_value=0, max_value=10), strategies.integers(min_value=-1, max_value=10)) From pypy.commits at gmail.com Sun Feb 17 07:40:40 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sun, 17 Feb 2019 04:40:40 -0800 (PST) Subject: [pypy-commit] pypy py3.5: merge default Message-ID: <5c695648.1c69fb81.11cd6.56c6@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.5 Changeset: r96036:c87433506699 Date: 2019-02-17 12:35 +0100 http://bitbucket.org/pypy/pypy/changeset/c87433506699/ Log: merge default diff --git a/extra_tests/cffi_tests/cffi0/test_ownlib.py b/extra_tests/cffi_tests/cffi0/test_ownlib.py --- a/extra_tests/cffi_tests/cffi0/test_ownlib.py +++ b/extra_tests/cffi_tests/cffi0/test_ownlib.py @@ -352,6 +352,8 @@ def test_modify_struct_value(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") + if self.Backend is CTypesBackend: + py.test.skip("fails with the ctypes backend on some architectures") ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef struct { diff --git a/extra_tests/cffi_tests/embedding/thread3-test.c b/extra_tests/cffi_tests/embedding/thread3-test.c --- a/extra_tests/cffi_tests/embedding/thread3-test.c +++ b/extra_tests/cffi_tests/embedding/thread3-test.c @@ -52,5 +52,6 @@ assert(status == 0); } printf("done\n"); + fflush(stdout); /* this is occasionally needed on Windows */ return 0; } 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 @@ -5,8 +5,8 @@ from .error import CDefError, FFIError, VerificationError, VerificationMissing from .error import PkgConfigError -__version__ = "1.12.0" -__version_info__ = (1, 12, 0) +__version__ = "1.12.1" +__version_info__ = (1, 12, 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/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h --- a/lib_pypy/cffi/_cffi_include.h +++ b/lib_pypy/cffi/_cffi_include.h @@ -8,43 +8,20 @@ the same works for the other two macros. Py_DEBUG implies them, but not the other way around. - The implementation is messy (issue #350): on Windows, with _MSC_VER, - we have to define Py_LIMITED_API even before including pyconfig.h. - In that case, we guess what pyconfig.h will do to the macros above, - and check our guess after the #include. - - Note that on Windows, with CPython 3.x, you need virtualenv version - >= 16.0.0. Older versions don't copy PYTHON3.DLL. As a workaround - you can remove the definition of Py_LIMITED_API here. - - See also 'py_limited_api' in cffi/setuptools_ext.py. + Issue #350 is still open: on Windows, the code here causes it to link + with PYTHON36.DLL (for example) instead of PYTHON3.DLL. A fix was + attempted in 164e526a5515 and 14ce6985e1c3, but reverted: virtualenv + does not make PYTHON3.DLL available, and so the "correctly" compiled + version would not run inside a virtualenv. We will re-apply the fix + after virtualenv has been fixed for some time. For explanation, see + issue #355. For a workaround if you want PYTHON3.DLL and don't worry + about virtualenv, see issue #350. See also 'py_limited_api' in + setuptools_ext.py. */ #if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) -# ifdef _MSC_VER -# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif -# include - /* sanity-check: Py_LIMITED_API will cause crashes if any of these - are also defined. Normally, the Python file PC/pyconfig.h does not - cause any of these to be defined, with the exception that _DEBUG - causes Py_DEBUG. Double-check that. */ -# ifdef Py_LIMITED_API -# if defined(Py_DEBUG) -# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" -# endif -# if defined(Py_TRACE_REFS) -# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" -# endif -# if defined(Py_REF_DEBUG) -# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" -# endif -# endif -# else -# include -# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif +# include +# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) +# define Py_LIMITED_API # endif #endif 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 @@ -221,7 +221,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.12.0" + "\ncompiled with cffi version: 1.12.1" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); 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 @@ -81,8 +81,14 @@ it doesn't so far, creating troubles. That's why we check for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) + + On Windows, with CPython <= 3.4, it's better not to use py_limited_api + because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. + For now we'll skip py_limited_api on all Windows versions to avoid an + inconsistent mess. """ - if 'py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount'): + if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') + and sys.platform != 'win32'): import setuptools try: setuptools_major_version = int(setuptools.__version__.partition('.')[0]) 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.12.0" +VERSION = "1.12.1" FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI try: 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.12.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.12.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,): diff --git a/pypy/module/cpyext/listobject.py b/pypy/module/cpyext/listobject.py --- a/pypy/module/cpyext/listobject.py +++ b/pypy/module/cpyext/listobject.py @@ -104,7 +104,8 @@ def PyList_GET_SIZE(space, w_obj): """Macro form of PyList_Size() without error checking. """ - return space.len_w(w_obj) + assert isinstance(w_obj, W_ListObject) + return w_obj.length() @cpython_api([PyObject], Py_ssize_t, error=-1) diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py --- a/pypy/module/cpyext/sequence.py +++ b/pypy/module/cpyext/sequence.py @@ -50,50 +50,56 @@ converted to a sequence, and raises a TypeError, raise a new TypeError with m as the message text. If the conversion otherwise, fails, reraise the original exception""" - if isinstance(w_obj, tupleobject.W_TupleObject): + if isinstance(w_obj, tupleobject.W_AbstractTupleObject): return w_obj # CCC avoid the double conversion that occurs here if isinstance(w_obj, W_ListObject): - # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM - w_obj.convert_to_cpy_strategy(space) + # note: we used to call w_obj.convert_to_cpy_strategy() here, + # but we really have to call it from PySequence_Fast_GET_ITEM() + # because some people never call PySequence_Fast() if they know + # the object is a list. return w_obj try: - return W_ListObject.newlist_cpyext(space, space.listview(w_obj)) + return tupleobject.W_TupleObject(space.fixedview(w_obj)) except OperationError as e: if e.match(space, space.w_TypeError): raise OperationError(space.w_TypeError, space.newtext(rffi.charp2str(m))) raise e -# CCC this should be written as a C macro - at cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_borrowed=True) -def PySequence_Fast_GET_ITEM(space, w_obj, index): +# CCC this should be written as a C macro, at least for the tuple case + at cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_is_ll=True) +def PySequence_Fast_GET_ITEM(space, py_obj, index): """Return the ith element of o, assuming that o was returned by PySequence_Fast(), o is not NULL, and that i is within bounds. """ - if isinstance(w_obj, W_ListObject): - return w_obj.getitem(index) - elif isinstance(w_obj, tupleobject.W_TupleObject): - return w_obj.wrappeditems[index] - raise oefmt(space.w_TypeError, - "PySequence_Fast_GET_ITEM called but object is not a list or " - "sequence") + py_obj = rffi.cast(PyObject, py_obj) + if PyTuple_Check(space, py_obj): + from pypy.module.cpyext.tupleobject import PyTupleObject + py_tuple = rffi.cast(PyTupleObject, py_obj) + return py_tuple.c_ob_item[index] + else: + from pypy.module.cpyext.listobject import PyList_GET_ITEM + w_obj = from_ref(space, py_obj) + return PyList_GET_ITEM(space, w_obj, index) @cpython_api([rffi.VOIDP], Py_ssize_t, error=CANNOT_FAIL) -def PySequence_Fast_GET_SIZE(space, w_obj): +def PySequence_Fast_GET_SIZE(space, py_obj): """Returns the length of o, assuming that o was returned by PySequence_Fast() and that o is not NULL. The size can also be gotten by calling PySequence_Size() on o, but PySequence_Fast_GET_SIZE() is faster because it can assume o is a list or tuple.""" - if isinstance(w_obj, W_ListObject): - return w_obj.length() - elif isinstance(w_obj, tupleobject.W_TupleObject): - return len(w_obj.wrappeditems) - raise oefmt(space.w_TypeError, - "PySequence_Fast_GET_SIZE called but object is not a list or " - "sequence") + py_obj = rffi.cast(PyObject, py_obj) + if PyTuple_Check(space, py_obj): + from pypy.module.cpyext.tupleobject import PyTupleObject + py_tuple = rffi.cast(PyTupleObject, py_obj) + return py_tuple.c_ob_size + else: + from pypy.module.cpyext.listobject import PyList_GET_SIZE + w_obj = from_ref(space, py_obj) + return PyList_GET_SIZE(space, w_obj) @cpython_api([rffi.VOIDP], PyObjectP) -def PySequence_Fast_ITEMS(space, w_obj): +def PySequence_Fast_ITEMS(space, py_obj): """Return the underlying array of PyObject pointers. Assumes that o was returned by PySequence_Fast() and o is not NULL. @@ -101,18 +107,17 @@ So, only use the underlying array pointer in contexts where the sequence cannot change. """ - if isinstance(w_obj, W_ListObject): - cpy_strategy = space.fromcache(CPyListStrategy) - if w_obj.strategy is cpy_strategy: - return w_obj.get_raw_items() # asserts it's a cpyext strategy - elif isinstance(w_obj, tupleobject.W_TupleObject): + py_obj = rffi.cast(PyObject, py_obj) + if PyTuple_Check(space, py_obj): from pypy.module.cpyext.tupleobject import PyTupleObject - py_obj = as_pyobj(space, w_obj) py_tuple = rffi.cast(PyTupleObject, py_obj) return rffi.cast(PyObjectP, py_tuple.c_ob_item) - raise oefmt(space.w_TypeError, - "PySequence_Fast_ITEMS called but object is not the result of " - "PySequence_Fast") + else: + from pypy.module.cpyext.listobject import get_list_storage + w_obj = from_ref(space, py_obj) + assert isinstance(w_obj, W_ListObject) + storage = get_list_storage(space, w_obj) + return rffi.cast(PyObjectP, storage._elems) @cpython_api([PyObject, Py_ssize_t, Py_ssize_t], PyObject) def PySequence_GetSlice(space, w_obj, start, end): @@ -300,10 +305,6 @@ storage = self.unerase(w_list.lstorage) return storage._length - def get_raw_items(self, w_list): - storage = self.unerase(w_list.lstorage) - return storage._elems - def getslice(self, w_list, start, stop, step, length): w_list.switch_to_object_strategy() return w_list.strategy.getslice(w_list, start, stop, step, length) diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -141,6 +141,7 @@ '_cffi_backend', ], "objspace.disable_entrypoints_in_cffi": True} + spaceconfig["objspace.std.withspecialisedtuple"] = True @classmethod def preload_builtins(cls, space): diff --git a/pypy/module/cpyext/test/test_sequence.py b/pypy/module/cpyext/test/test_sequence.py --- a/pypy/module/cpyext/test/test_sequence.py +++ b/pypy/module/cpyext/test/test_sequence.py @@ -5,7 +5,7 @@ from pypy.module.cpyext.sequence import ( PySequence_Fast, PySequence_Contains, PySequence_Index, PySequence_GetItem, PySequence_SetItem, PySequence_DelItem) -from pypy.module.cpyext.pyobject import get_w_obj_and_decref +from pypy.module.cpyext.pyobject import get_w_obj_and_decref, from_ref from pypy.module.cpyext.state import State import pytest @@ -21,12 +21,14 @@ w_l = space.wrap([1, 2, 3, 4]) assert api.PySequence_Fast(w_l, "message") is w_l - assert space.int_w(api.PySequence_Fast_GET_ITEM(w_l, 1)) == 2 + py_result = api.PySequence_Fast_GET_ITEM(w_l, 1) + w_result = from_ref(space, py_result) + assert space.int_w(w_result) == 2 assert api.PySequence_Fast_GET_SIZE(w_l) == 4 w_set = space.wrap(set((1, 2, 3, 4))) w_seq = api.PySequence_Fast(w_set, "message") - assert space.type(w_seq) is space.w_list + assert space.type(w_seq) is space.w_tuple assert space.len_w(w_seq) == 4 w_seq = api.PySequence_Tuple(w_set) @@ -83,7 +85,7 @@ def test_get_slice_fast(self, space, api): w_t = space.wrap([1, 2, 3, 4, 5]) - api.PySequence_Fast(w_t, "foo") # converts + api.PyList_GetItem(w_t, 0) # converts to cpy strategy assert space.unwrap(api.PySequence_GetSlice(w_t, 2, 4)) == [3, 4] assert space.unwrap(api.PySequence_GetSlice(w_t, 1, -1)) == [2, 3, 4] @@ -215,7 +217,7 @@ class TestCPyListStrategy(BaseApiTest): def test_getitem_setitem(self, space, api): w_l = space.wrap([1, 2, 3, 4]) - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy assert space.int_w(space.len(w_l)) == 4 assert space.int_w(space.getitem(w_l, space.wrap(1))) == 2 assert space.int_w(space.getitem(w_l, space.wrap(0))) == 1 @@ -229,17 +231,17 @@ w = space.wrap w_l = w([1, 2, 3, 4]) - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy space.call_method(w_l, 'insert', w(0), w(0)) assert space.int_w(space.len(w_l)) == 5 assert space.int_w(space.getitem(w_l, w(3))) == 3 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy space.call_method(w_l, 'sort') assert space.int_w(space.len(w_l)) == 5 assert space.int_w(space.getitem(w_l, w(0))) == 0 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_t = space.wrap(space.fixedview(w_l)) assert space.int_w(space.len(w_t)) == 5 assert space.int_w(space.getitem(w_t, w(0))) == 0 @@ -247,22 +249,22 @@ assert space.int_w(space.len(w_l2)) == 5 assert space.int_w(space.getitem(w_l2, w(0))) == 0 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_sum = space.add(w_l, w_l) assert space.int_w(space.len(w_sum)) == 10 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_prod = space.mul(w_l, space.wrap(2)) assert space.int_w(space.len(w_prod)) == 10 - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_l.inplace_mul(2) assert space.int_w(space.len(w_l)) == 10 def test_getstorage_copy(self, space, api): w = space.wrap w_l = w([1, 2, 3, 4]) - api.PySequence_Fast(w_l, "foo") # converts + api.PyList_GetItem(w_l, 0) # converts to cpy strategy w_l1 = w([]) space.setitem(w_l1, space.newslice(w(0), w(0), w(1)), w_l) @@ -285,6 +287,10 @@ if (objects == NULL) return NULL; size = PySequence_Fast_GET_SIZE(foo); + for (i = 0; i < size; ++i) { + if (objects[i] != PySequence_Fast_GET_ITEM(foo, i)) + return PyBool_FromLong(0); + } common_type = size > 0 ? Py_TYPE(objects[0]) : NULL; for (i = 1; i < size; ++i) { if (Py_TYPE(objects[i]) != common_type) { @@ -304,6 +310,9 @@ s = (1, 2, 3, 4) assert module.test_fast_sequence(s[0:-1]) assert module.test_fast_sequence(s[::-1]) + s = (1, 2) # specialized tuple + assert module.test_fast_sequence(s[0:-1]) + assert module.test_fast_sequence(s[::-1]) s = "1234" assert module.test_fast_sequence(s[0:-1]) assert module.test_fast_sequence(s[::-1]) 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 @@ -514,7 +514,8 @@ py_type = rffi.cast(PyTypeObjectPtr, ref) assert py_type.c_tp_alloc - assert from_ref(space, py_type.c_tp_mro).wrappeditems is w_class.mro_w + w_tup = from_ref(space, py_type.c_tp_mro) + assert space.fixedview(w_tup) == w_class.mro_w decref(space, ref) diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py --- a/pypy/objspace/std/dictmultiobject.py +++ b/pypy/objspace/std/dictmultiobject.py @@ -45,7 +45,8 @@ # for json decoder def create_empty_unicode_key_dict(space): return r_dict(unicode_eq, unicode_hash, - force_non_null=True) + force_non_null=True, + simple_hash_eq=True) def from_unicode_key_dict(space, d): strategy = space.fromcache(UnicodeDictStrategy) @@ -1176,6 +1177,7 @@ return unwrapped def unwrap(self, wrapped): + assert type(wrapped) is self.space.UnicodeObjectCls return wrapped def is_correct_type(self, w_obj): diff --git a/pypy/objspace/std/iterobject.py b/pypy/objspace/std/iterobject.py --- a/pypy/objspace/std/iterobject.py +++ b/pypy/objspace/std/iterobject.py @@ -100,6 +100,33 @@ return w_item +class W_FastUnicodeIterObject(W_AbstractSeqIterObject): + """Sequence iterator specialized for unicode objects.""" + + def __init__(self, w_seq): + from pypy.objspace.std.unicodeobject import W_UnicodeObject + W_AbstractSeqIterObject.__init__(self, w_seq) + assert isinstance(w_seq, W_UnicodeObject) + self.byteindex = 0 + + def descr_next(self, space): + from pypy.objspace.std.unicodeobject import W_UnicodeObject + from rpython.rlib import rutf8 + w_seq = self.w_seq + if w_seq is None: + raise OperationError(space.w_StopIteration, space.w_None) + assert isinstance(w_seq, W_UnicodeObject) + index = self.index + if index == w_seq._length: + self.w_seq = None + raise OperationError(space.w_StopIteration, space.w_None) + start = self.byteindex + end = rutf8.next_codepoint_pos(w_seq._utf8, start) + w_res = W_UnicodeObject(w_seq._utf8[start:end], 1) + self.byteindex = end + return w_res + + class W_FastTupleIterObject(W_AbstractSeqIterObject): """Sequence iterator specialized for tuples, accessing directly their RPython-level list of wrapped objects. diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py --- a/pypy/objspace/std/listobject.py +++ b/pypy/objspace/std/listobject.py @@ -214,13 +214,6 @@ storage = strategy.erase(list_f) return W_ListObject.from_storage_and_strategy(space, storage, strategy) - @staticmethod - def newlist_cpyext(space, list): - from pypy.module.cpyext.sequence import CPyListStrategy, CPyListStorage - strategy = space.fromcache(CPyListStrategy) - storage = strategy.erase(CPyListStorage(space, list)) - return W_ListObject.from_storage_and_strategy(space, storage, strategy) - def __repr__(self): """ representation for debugging purposes """ return "%s(%s, %s)" % (self.__class__.__name__, self.strategy, @@ -259,13 +252,6 @@ self.strategy = cpy_strategy self.lstorage = cpy_strategy.erase(CPyListStorage(space, lst)) - def get_raw_items(self): - from pypy.module.cpyext.sequence import CPyListStrategy - - cpy_strategy = self.space.fromcache(CPyListStrategy) - assert self.strategy is cpy_strategy # should we return an error? - return cpy_strategy.get_raw_items(self) - # ___________________________________________________ def init_from_list_w(self, list_w): diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py --- a/pypy/objspace/std/objspace.py +++ b/pypy/objspace/std/objspace.py @@ -26,6 +26,7 @@ from pypy.objspace.std.intobject import ( W_AbstractIntObject, W_IntObject, setup_prebuilt, wrapint) from pypy.objspace.std.iterobject import W_AbstractSeqIterObject, W_SeqIterObject +from pypy.objspace.std.iterobject import W_FastUnicodeIterObject from pypy.objspace.std.listobject import W_ListObject from pypy.objspace.std.longobject import W_LongObject, newlong from pypy.objspace.std.memoryobject import W_MemoryView @@ -365,6 +366,8 @@ return W_SliceObject(w_start, w_end, w_step) def newseqiter(self, w_obj): + if type(w_obj) is W_UnicodeObject: + return W_FastUnicodeIterObject(w_obj) return W_SeqIterObject(w_obj) def newmemoryview(self, w_obj): diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -38,6 +38,18 @@ space.w_unicode, "__new__", space.w_unicode, w_uni) assert w_new is w_uni + def test_fast_iter(self): + space = self.space + w_uni = space.newutf8(u"aä".encode("utf-8"), 2) + old_index_storage = w_uni._index_storage + w_iter = space.iter(w_uni) + w_char1 = w_iter.descr_next(space) + w_char2 = w_iter.descr_next(space) + assert w_uni._index_storage is old_index_storage + assert space.eq_w(w_char1, w_uni._getitem_result(space, 0)) + assert space.eq_w(w_char2, w_uni._getitem_result(space, 1)) + + if HAS_HYPOTHESIS: @given(strategies.text(), strategies.integers(min_value=0, max_value=10), strategies.integers(min_value=-1, max_value=10)) diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py --- a/rpython/rlib/rarithmetic.py +++ b/rpython/rlib/rarithmetic.py @@ -729,7 +729,9 @@ """ The JIT special-cases this too. """ from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.lltypesystem.lloperation import llop - return llop.int_force_ge_zero(lltype.Signed, n) + n = llop.int_force_ge_zero(lltype.Signed, n) + assert n >= 0 + return n def int_c_div(x, y): """Return the result of the C-style 'x / y'. This differs from the diff --git a/rpython/rlib/rsre/rsre_core.py b/rpython/rlib/rsre/rsre_core.py --- a/rpython/rlib/rsre/rsre_core.py +++ b/rpython/rlib/rsre/rsre_core.py @@ -151,7 +151,10 @@ # The following methods are provided to be overriden in # Utf8MatchContext. The non-utf8 implementation is provided # by the FixedMatchContext abstract subclass, in order to use - # the same @not_rpython safety trick as above. + # the same @not_rpython safety trick as above. If you get a + # "not_rpython" error during translation, either consider + # calling the methods xxx_indirect() instead of xxx(), or if + # applicable add the @specializectx decorator. ZERO = 0 @not_rpython def next(self, position): @@ -460,8 +463,7 @@ ptr = self.start_ptr if not self.next_char_ok(ctx, pattern, ptr, self.ppos3): return - assert not isinstance(ctx, AbstractMatchContext) - self.start_ptr = ctx.next(ptr) + self.start_ptr = ctx.next_indirect(ptr) return self.find_first_result(ctx, pattern) def next_char_ok(self, ctx, pattern, ptr, ppos): diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -19,7 +19,7 @@ from rpython.rlib.objectmodel import enforceargs, we_are_translated, specialize from rpython.rlib.objectmodel import always_inline, dont_inline, try_inline from rpython.rlib.rstring import StringBuilder -from rpython.rlib import jit, types +from rpython.rlib import jit, types, rarithmetic from rpython.rlib.signature import signature, finishsigs from rpython.rlib.types import char, none from rpython.rlib.rarithmetic import r_uint @@ -117,6 +117,12 @@ # chinese wikipedia, they're anywhere between 10% and 30% slower. # In extreme cases (small, only chinese text), they're 40% slower +# The following was found by hand to be more optimal than both, +# on x86-64... +_is_64bit = sys.maxint > 2**32 +_constant_ncp = rarithmetic.r_uint64(0xffff0000ffffffff) + + at always_inline def next_codepoint_pos(code, pos): """Gives the position of the next codepoint after pos. Assumes valid utf8. 'pos' must be before the end of the string. @@ -125,6 +131,13 @@ chr1 = ord(code[pos]) if chr1 <= 0x7F: return pos + 1 + if _is_64bit and not jit.we_are_jitted(): + # optimized for Intel x86-64 by hand + res = pos + 1 + ( + ((chr1 > 0xDF) << 1) + + rarithmetic.intmask((_constant_ncp >> (chr1 & 0x3F)) & 1)) + assert res >= 0 + return res if chr1 <= 0xDF: return pos + 2 if chr1 <= 0xEF: @@ -162,7 +175,6 @@ ordch1 = ord(code[pos]) if ordch1 <= 0x7F or pos +1 >= lgt: return ordch1 - ordch2 = ord(code[pos+1]) if ordch1 <= 0xDF or pos +2 >= lgt: # 110yyyyy 10zzzzzz -> 00000000 00000yyy yyzzzzzz @@ -518,7 +530,7 @@ break return storage - at jit.dont_look_inside + at jit.elidable def codepoint_position_at_index(utf8, storage, index): """ Return byte index of a character inside utf8 encoded string, given storage of type UTF8_INDEX_STORAGE. The index must be smaller than @@ -546,7 +558,7 @@ pos = next_codepoint_pos(utf8, pos) return pos - at jit.dont_look_inside + at jit.elidable def codepoint_at_index(utf8, storage, index): """ Return codepoint of a character inside utf8 encoded string, given storage of type UTF8_INDEX_STORAGE @@ -564,7 +576,7 @@ bytepos = next_codepoint_pos(utf8, bytepos) return codepoint_at_pos(utf8, bytepos) - at jit.dont_look_inside + at jit.elidable def codepoint_index_at_byte_position(utf8, storage, bytepos): """ Return the character index for which codepoint_position_at_index(index) == bytepos. From pypy.commits at gmail.com Sun Feb 17 07:40:45 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sun, 17 Feb 2019 04:40:45 -0800 (PST) Subject: [pypy-commit] pypy default: fix Message-ID: <5c69564d.1c69fb81.bdc43.2923@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96037:b42bf472ce7d Date: 2019-02-17 12:52 +0100 http://bitbucket.org/pypy/pypy/changeset/b42bf472ce7d/ Log: fix diff --git a/pypy/objspace/std/iterobject.py b/pypy/objspace/std/iterobject.py --- a/pypy/objspace/std/iterobject.py +++ b/pypy/objspace/std/iterobject.py @@ -116,6 +116,7 @@ end = rutf8.next_codepoint_pos(w_seq._utf8, start) w_res = W_UnicodeObject(w_seq._utf8[start:end], 1) self.byteindex = end + self.index += 1 return w_res diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -48,6 +48,7 @@ w_iter = space.iter(w_uni) w_char1 = w_iter.descr_next(space) w_char2 = w_iter.descr_next(space) + py.test.raises(OperationError, w_iter.descr_next, space) assert w_uni._index_storage is old_index_storage assert space.eq_w(w_char1, w_uni._getitem_result(space, 0)) assert space.eq_w(w_char2, w_uni._getitem_result(space, 1)) From pypy.commits at gmail.com Sun Feb 17 07:40:46 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sun, 17 Feb 2019 04:40:46 -0800 (PST) Subject: [pypy-commit] pypy py3.5: merge default Message-ID: <5c69564e.1c69fb81.ee26c.a0fe@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.5 Changeset: r96038:1c54055fec69 Date: 2019-02-17 12:53 +0100 http://bitbucket.org/pypy/pypy/changeset/1c54055fec69/ Log: merge default diff --git a/pypy/objspace/std/iterobject.py b/pypy/objspace/std/iterobject.py --- a/pypy/objspace/std/iterobject.py +++ b/pypy/objspace/std/iterobject.py @@ -124,6 +124,7 @@ end = rutf8.next_codepoint_pos(w_seq._utf8, start) w_res = W_UnicodeObject(w_seq._utf8[start:end], 1) self.byteindex = end + self.index += 1 return w_res diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -45,6 +45,7 @@ w_iter = space.iter(w_uni) w_char1 = w_iter.descr_next(space) w_char2 = w_iter.descr_next(space) + py.test.raises(OperationError, w_iter.descr_next, space) assert w_uni._index_storage is old_index_storage assert space.eq_w(w_char1, w_uni._getitem_result(space, 0)) assert space.eq_w(w_char2, w_uni._getitem_result(space, 1)) From pypy.commits at gmail.com Sun Feb 17 07:40:48 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sun, 17 Feb 2019 04:40:48 -0800 (PST) Subject: [pypy-commit] pypy py3.5: doesn't need the hack on py3.5 Message-ID: <5c695650.1c69fb81.816b2.247d@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.5 Changeset: r96039:227b54cdf79b Date: 2019-02-17 13:35 +0100 http://bitbucket.org/pypy/pypy/changeset/227b54cdf79b/ Log: doesn't need the hack on py3.5 diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py --- a/pypy/objspace/std/objspace.py +++ b/pypy/objspace/std/objspace.py @@ -366,8 +366,6 @@ return W_SliceObject(w_start, w_end, w_step) def newseqiter(self, w_obj): - if type(w_obj) is W_UnicodeObject: - return W_FastUnicodeIterObject(w_obj) return W_SeqIterObject(w_obj) def newmemoryview(self, w_obj): diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -93,6 +93,10 @@ def listview_utf8(self): return _create_list_from_unicode(self._utf8) + def descr_iter(self, space): + from pypy.objspace.std.iterobject import W_FastUnicodeIterObject + return W_FastUnicodeIterObject(self) + def ord(self, space): if self._len() != 1: raise oefmt(space.w_TypeError, From pypy.commits at gmail.com Sun Feb 17 07:40:50 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sun, 17 Feb 2019 04:40:50 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge py3.5 Message-ID: <5c695652.1c69fb81.57ebf.c86e@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96040:d6a918e4c217 Date: 2019-02-17 13:39 +0100 http://bitbucket.org/pypy/pypy/changeset/d6a918e4c217/ Log: merge py3.5 diff --git a/pypy/objspace/std/iterobject.py b/pypy/objspace/std/iterobject.py --- a/pypy/objspace/std/iterobject.py +++ b/pypy/objspace/std/iterobject.py @@ -100,6 +100,34 @@ return w_item +class W_FastUnicodeIterObject(W_AbstractSeqIterObject): + """Sequence iterator specialized for unicode objects.""" + + def __init__(self, w_seq): + from pypy.objspace.std.unicodeobject import W_UnicodeObject + W_AbstractSeqIterObject.__init__(self, w_seq) + assert isinstance(w_seq, W_UnicodeObject) + self.byteindex = 0 + + def descr_next(self, space): + from pypy.objspace.std.unicodeobject import W_UnicodeObject + from rpython.rlib import rutf8 + w_seq = self.w_seq + if w_seq is None: + raise OperationError(space.w_StopIteration, space.w_None) + assert isinstance(w_seq, W_UnicodeObject) + index = self.index + if index == w_seq._length: + self.w_seq = None + raise OperationError(space.w_StopIteration, space.w_None) + start = self.byteindex + end = rutf8.next_codepoint_pos(w_seq._utf8, start) + w_res = W_UnicodeObject(w_seq._utf8[start:end], 1) + self.byteindex = end + self.index += 1 + return w_res + + class W_FastTupleIterObject(W_AbstractSeqIterObject): """Sequence iterator specialized for tuples, accessing directly their RPython-level list of wrapped objects. diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py --- a/pypy/objspace/std/objspace.py +++ b/pypy/objspace/std/objspace.py @@ -26,6 +26,7 @@ from pypy.objspace.std.intobject import ( W_AbstractIntObject, W_IntObject, setup_prebuilt, wrapint) from pypy.objspace.std.iterobject import W_AbstractSeqIterObject, W_SeqIterObject +from pypy.objspace.std.iterobject import W_FastUnicodeIterObject from pypy.objspace.std.listobject import W_ListObject from pypy.objspace.std.longobject import W_LongObject, newlong from pypy.objspace.std.memoryobject import W_MemoryView diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -38,6 +38,19 @@ space.w_unicode, "__new__", space.w_unicode, w_uni) assert w_new is w_uni + def test_fast_iter(self): + space = self.space + w_uni = space.newutf8(u"aä".encode("utf-8"), 2) + old_index_storage = w_uni._index_storage + w_iter = space.iter(w_uni) + w_char1 = w_iter.descr_next(space) + w_char2 = w_iter.descr_next(space) + py.test.raises(OperationError, w_iter.descr_next, space) + assert w_uni._index_storage is old_index_storage + assert space.eq_w(w_char1, w_uni._getitem_result(space, 0)) + assert space.eq_w(w_char2, w_uni._getitem_result(space, 1)) + + if HAS_HYPOTHESIS: @given(strategies.text(), strategies.integers(min_value=0, max_value=10), strategies.integers(min_value=-1, max_value=10)) diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -93,6 +93,10 @@ def listview_utf8(self): return _create_list_from_unicode(self._utf8) + def descr_iter(self, space): + from pypy.objspace.std.iterobject import W_FastUnicodeIterObject + return W_FastUnicodeIterObject(self) + def ord(self, space): if self._len() != 1: raise oefmt(space.w_TypeError, From pypy.commits at gmail.com Sun Feb 17 09:43:48 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 17 Feb 2019 06:43:48 -0800 (PST) Subject: [pypy-commit] pypy default: remove dead code Message-ID: <5c697324.1c69fb81.3a5a8.c0ae@mx.google.com> Author: Matti Picus Branch: Changeset: r96041:4e661aec658f Date: 2019-02-17 16:41 +0200 http://bitbucket.org/pypy/pypy/changeset/4e661aec658f/ Log: remove dead code diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -918,55 +918,6 @@ return result.build() - at specialize.memo() -def _encode_unicode_error_handler(space): - # Fast version of the "strict" errors handler. - from rpython.rlib import runicode - def raise_unicode_exception_encode(errors, encoding, msg, uni, - startingpos, endingpos): - assert isinstance(uni, unicode) - u_len = len(uni) - utf8 = runicode.unicode_encode_utf8sp(uni, u_len) - raise OperationError(space.w_UnicodeEncodeError, - space.newtuple([space.newtext(encoding), - space.newtext(utf8, u_len), - space.newint(startingpos), - space.newint(endingpos), - space.newtext(msg)])) - return u'', None, 0 - return raise_unicode_exception_encode - - -def encode_utf8(space, uni, allow_surrogates=False): - # Note that Python3 tends to forbid *all* surrogates in utf-8. - # If allow_surrogates=True, then revert to the Python 2 behavior - # which never raises UnicodeEncodeError. Surrogate pairs are then - # allowed, either paired or lone. A paired surrogate is considered - # like the non-BMP character it stands for. See also *_utf8sp(). - from rpython.rlib import runicode - assert isinstance(uni, unicode) - return runicode.unicode_encode_utf_8( - uni, len(uni), "strict", - errorhandler=_encode_unicode_error_handler(space), - allow_surrogates=allow_surrogates) - -def encode_utf8sp(space, uni, allow_surrogates=True): - xxx - # Surrogate-preserving utf-8 encoding. Any surrogate character - # turns into its 3-bytes encoding, whether it is paired or not. - # This should always be reversible, and the reverse is - # decode_utf8sp(). - from rpython.rlib import runicode - return runicode.unicode_encode_utf8sp(uni, len(uni)) - -def decode_utf8sp(space, string): - # Surrogate-preserving utf-8 decoding. Assuming there is no - # encoding error, it should always be reversible, and the reverse is - # encode_utf8sp(). - return str_decode_utf8(string, "string", True, decode_never_raise, - allow_surrogates=True) - - # ____________________________________________________________ # utf-16 From pypy.commits at gmail.com Sun Feb 17 09:54:02 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 17 Feb 2019 06:54:02 -0800 (PST) Subject: [pypy-commit] pypy default: revert part of 4e661aec658f to keep synced with py3.6 Message-ID: <5c69758a.1c69fb81.68976.246e@mx.google.com> Author: Matti Picus Branch: Changeset: r96042:d13ae7d283ae Date: 2019-02-17 16:53 +0200 http://bitbucket.org/pypy/pypy/changeset/d13ae7d283ae/ Log: revert part of 4e661aec658f to keep synced with py3.6 diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -21,7 +21,7 @@ space.newtext(msg)])) return raise_unicode_exception_decode -def decode_never_raise(errors, encoding, msg, s, startingpos, endingpos): +def _decode_never_raise(errors, encoding, msg, s, startingpos, endingpos): assert startingpos >= 0 ux = ['\ux' + hex(ord(x))[2:].upper() for x in s[startingpos:endingpos]] return ''.join(ux), endingpos, 'b' @@ -918,6 +918,13 @@ return result.build() +def decode_utf8sp(space, string): + # Surrogate-preserving utf-8 decoding. Assuming there is no + # encoding error, it should always be reversible, and the reverse is + # encode_utf8sp(). + return str_decode_utf8(string, "string", True, _decode_never_raise, + allow_surrogates=True) + # ____________________________________________________________ # utf-16 From pypy.commits at gmail.com Mon Feb 18 03:35:41 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 18 Feb 2019 00:35:41 -0800 (PST) Subject: [pypy-commit] pypy default: fix test Message-ID: <5c6a6e5d.1c69fb81.dd427.3ff7@mx.google.com> Author: Armin Rigo Branch: Changeset: r96043:991f167e4663 Date: 2019-02-18 09:35 +0100 http://bitbucket.org/pypy/pypy/changeset/991f167e4663/ Log: fix test diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py --- a/pypy/module/pypyjit/test_pypy_c/test_string.py +++ b/pypy/module/pypyjit/test_pypy_c/test_string.py @@ -43,7 +43,7 @@ guard_no_exception(descr=...) i100 = int_lt(i98, 0) guard_true(i100, descr=...) - i102 = call_i(ConstClass(_ll_4_str_eq_slice_char__rpy_stringPtr_Signed_Signed_Char), p55, i83, 1, i87, descr=) + i102 = call_i(ConstClass(_ll_4_str_eq_slice_char__rpy_stringPtr_Signed_Signed_Char), p55, i83, 1, i89, descr=) guard_true(i102, descr=...) i104 = int_add(i74, 1) --TICK-- From pypy.commits at gmail.com Mon Feb 18 04:10:08 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 18 Feb 2019 01:10:08 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix setstate on unicode iters Message-ID: <5c6a7670.1c69fb81.43856.2081@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96044:e93780665de6 Date: 2019-02-18 10:09 +0100 http://bitbucket.org/pypy/pypy/changeset/e93780665de6/ Log: fix setstate on unicode iters diff --git a/pypy/objspace/std/iterobject.py b/pypy/objspace/std/iterobject.py --- a/pypy/objspace/std/iterobject.py +++ b/pypy/objspace/std/iterobject.py @@ -59,7 +59,7 @@ __next__ = interpindirect2app(W_AbstractSeqIterObject.descr_next), __reduce__ = interp2app(W_AbstractSeqIterObject.descr_reduce), __length_hint__ = interp2app(W_AbstractSeqIterObject.descr_length_hint), - __setstate__ = interp2app(W_AbstractSeqIterObject.descr_setstate), + __setstate__ = interpindirect2app(W_AbstractSeqIterObject.descr_setstate), ) W_AbstractSeqIterObject.typedef.acceptable_as_base_class = False @@ -127,6 +127,19 @@ self.index += 1 return w_res + def descr_setstate(self, space, w_state): + from pypy.objspace.std.unicodeobject import W_UnicodeObject + index = space.int_w(w_state) + w_seq = self.w_seq + if w_seq is not None: + assert isinstance(w_seq, W_UnicodeObject) + if index < 0: + index = 0 + if index >= w_seq._len(): + index = w_seq._len() + self.index = index + self.byteindex = w_seq._index_to_byte(index) + class W_FastTupleIterObject(W_AbstractSeqIterObject): """Sequence iterator specialized for tuples, accessing directly diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -1326,3 +1326,22 @@ assert str(e.value) == 'decoding str is not supported' e = raises(TypeError, str, z, 'supposedly_the_encoding') assert str(e.value) == 'decoding str is not supported' + + def test_reduce_iterator(self): + it = iter(u"abcdef") + assert next(it) == u"a" + assert next(it) == u"b" + assert next(it) == u"c" + assert next(it) == u"d" + args = it.__reduce__() + assert next(it) == u"e" + assert next(it) == u"f" + it2 = args[0](*args[1]) + it2.__setstate__(args[2]) + assert next(it2) == u"e" + assert next(it2) == u"f" + it3 = args[0](*args[1]) + it3.__setstate__(args[2]) + assert next(it3) == u"e" + assert next(it3) == u"f" + From pypy.commits at gmail.com Mon Feb 18 04:18:46 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 18 Feb 2019 01:18:46 -0800 (PST) Subject: [pypy-commit] pypy default: try to fix the test_pypy_c failures on 32bit Message-ID: <5c6a7876.1c69fb81.9c93d.9b0f@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96045:666d447ee17b Date: 2019-02-18 10:18 +0100 http://bitbucket.org/pypy/pypy/changeset/666d447ee17b/ Log: try to fix the test_pypy_c failures on 32bit diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py --- a/pypy/module/pypyjit/test_pypy_c/test_string.py +++ b/pypy/module/pypyjit/test_pypy_c/test_string.py @@ -39,11 +39,11 @@ guard_false(i94, descr=...) p96 = newstr(1) strsetitem(p96, 0, i89) - i98 = call_i(ConstClass(first_non_ascii_char), p96, descr=) + i98 = call_i(ConstClass(first_non_ascii_char), p96, descr=) guard_no_exception(descr=...) i100 = int_lt(i98, 0) guard_true(i100, descr=...) - i102 = call_i(ConstClass(_ll_4_str_eq_slice_char__rpy_stringPtr_Signed_Signed_Char), p55, i83, 1, i89, descr=) + i102 = call_i(ConstClass(_ll_4_str_eq_slice_char__rpy_stringPtr_Signed_Signed_Char), p55, i83, 1, i89, descr=) guard_true(i102, descr=...) i104 = int_add(i74, 1) --TICK-- @@ -216,7 +216,7 @@ guard_not_invalidated(descr=...) p80 = call_r(ConstClass(ll_str__IntegerR_SignedConst_Signed), i47, descr=) guard_no_exception(descr=...) - i51 = call_i(ConstClass(first_non_ascii_char), p80, descr=) + i51 = call_i(ConstClass(first_non_ascii_char), p80, descr=) guard_no_exception(descr=...) i52 = int_lt(i51, 0) guard_true(i52, descr=...) From pypy.commits at gmail.com Mon Feb 18 04:20:44 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 18 Feb 2019 01:20:44 -0800 (PST) Subject: [pypy-commit] pypy default: sorry, fix Message-ID: <5c6a78ec.1c69fb81.8027b.480a@mx.google.com> Author: Armin Rigo Branch: Changeset: r96046:ccd4a4363d59 Date: 2019-02-18 10:20 +0100 http://bitbucket.org/pypy/pypy/changeset/ccd4a4363d59/ Log: sorry, fix 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.12.0 +Version: 1.12.1 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski From pypy.commits at gmail.com Mon Feb 18 04:41:34 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 18 Feb 2019 01:41:34 -0800 (PST) Subject: [pypy-commit] pypy py3.6: We no longer get the dummy getfield_gc_r Message-ID: <5c6a7dce.1c69fb81.a6c23.4cdc@mx.google.com> Author: Armin Rigo Branch: py3.6 Changeset: r96047:1a39bb272421 Date: 2019-02-18 10:41 +0100 http://bitbucket.org/pypy/pypy/changeset/1a39bb272421/ Log: We no longer get the dummy getfield_gc_r 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 @@ -105,10 +105,7 @@ # ------------------------------- entry_bridge, = log.loops_by_filename(self.filepath, is_entry_bridge=True) ops = entry_bridge.ops_by_id('mutate', opcode='LOAD_ATTR') - # in PyPy3 we get a dummy getfield_gc_r (*) for - # W_UnicodeObject._utf8, which is usually removed by the backend assert log.opnames(ops) == ['guard_value', - 'getfield_gc_r', # <= (*) 'guard_not_invalidated', 'getfield_gc_i'] # the STORE_ATTR is folded away @@ -157,10 +154,7 @@ # ------------------------------- entry_bridge, = log.loops_by_filename(self.filepath, is_entry_bridge=True) ops = entry_bridge.ops_by_id('mutate', opcode='LOAD_ATTR') - # in PyPy3 we get a dummy getfield_gc_r (*) for - # W_UnicodeObject._utf8, which is usually removed by the backend assert log.opnames(ops) == ['guard_value', - 'getfield_gc_r', # <= (*) 'guard_not_invalidated', 'getfield_gc_r', 'guard_nonnull_class', 'getfield_gc_r', 'guard_value', # type check on the attribute From pypy.commits at gmail.com Mon Feb 18 04:45:00 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 18 Feb 2019 01:45:00 -0800 (PST) Subject: [pypy-commit] pypy py3.6: Fix for test_decode_ascii after the utf8 merge Message-ID: <5c6a7e9c.1c69fb81.9c93d.a452@mx.google.com> Author: Armin Rigo Branch: py3.6 Changeset: r96048:c2f51a1cf58c Date: 2019-02-18 10:45 +0100 http://bitbucket.org/pypy/pypy/changeset/c2f51a1cf58c/ Log: Fix for test_decode_ascii after the utf8 merge diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py --- a/pypy/module/pypyjit/test_pypy_c/test_string.py +++ b/pypy/module/pypyjit/test_pypy_c/test_string.py @@ -250,8 +250,11 @@ p80 = call_r(ConstClass(ll_char_mul__Char_Signed), 120, i53, descr=) guard_no_exception(descr=...) guard_not_invalidated(descr=...) - p53 = call_r(ConstClass(fast_str_decode_ascii), p80, descr=) + i59 = call_i(ConstClass(first_non_ascii_char), p80, descr=) guard_no_exception(descr=...) + i61 = int_lt(i59, 0) + guard_true(i61, descr=...) + i62 = strlen(p80) --TICK-- jump(..., descr=...) """) From pypy.commits at gmail.com Mon Feb 18 04:54:08 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 18 Feb 2019 01:54:08 -0800 (PST) Subject: [pypy-commit] pypy py3.6: The dummy_get_utf8? line is now not needed any more. Keep it around in Message-ID: <5c6a80c0.1c69fb81.62fc.4780@mx.google.com> Author: Armin Rigo Branch: py3.6 Changeset: r96049:53322e432860 Date: 2019-02-18 10:54 +0100 http://bitbucket.org/pypy/pypy/changeset/53322e432860/ Log: The dummy_get_utf8? line is now not needed any more. Keep it around in the tests for future reference just in case we need to reintroduce it, but it is parsed as a comment. diff --git a/pypy/module/pypyjit/test_pypy_c/model.py b/pypy/module/pypyjit/test_pypy_c/model.py --- a/pypy/module/pypyjit/test_pypy_c/model.py +++ b/pypy/module/pypyjit/test_pypy_c/model.py @@ -253,7 +253,6 @@ class OpMatcher(object): - DUMMY = 1000 def __init__(self, ops, id=None): self.ops = ops @@ -285,12 +284,11 @@ if line.strip() == 'guard_not_invalidated?': return 'guard_not_invalidated', None, [], '...', False if line.strip() == 'dummy_get_utf8?': - cls.DUMMY += 1 - return ('getfield_gc_r', - 'p%d' % cls.DUMMY, - ['ConstPtr(ptr%d)' % cls.DUMMY], - '', - False) + # this is because unicode used to generate dummy getfield_gc_r + # of the _utf8 field. Nowadays it no longer does. This line + # is equivalent to a comment now. + return None + # find the resvar, if any if ' = ' in line: resvar, _, line = line.partition(' = ') diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py --- a/pypy/module/pypyjit/test_pypy_c/test_call.py +++ b/pypy/module/pypyjit/test_pypy_c/test_call.py @@ -72,13 +72,12 @@ # LOAD_GLOBAL of OFFSET ops = entry_bridge.ops_by_id('cond', opcode='LOAD_GLOBAL') assert log.opnames(ops) == ["guard_value", - "getfield_gc_r", # dead load "guard_not_invalidated"] ops = entry_bridge.ops_by_id('add', opcode='LOAD_GLOBAL') assert log.opnames(ops) == [] # ops = entry_bridge.ops_by_id('call', opcode='LOAD_GLOBAL') - assert log.opnames(ops) == ["getfield_gc_r"] + assert log.opnames(ops) == [] # assert entry_bridge.match_by_id('call', """ dummy_get_utf8? From pypy.commits at gmail.com Mon Feb 18 04:59:59 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 18 Feb 2019 01:59:59 -0800 (PST) Subject: [pypy-commit] pypy py3.6: Goes with 53322e432860 Message-ID: <5c6a821f.1c69fb81.7da66.4e5a@mx.google.com> Author: Armin Rigo Branch: py3.6 Changeset: r96050:7c0f6e15febc Date: 2019-02-18 10:59 +0100 http://bitbucket.org/pypy/pypy/changeset/7c0f6e15febc/ Log: Goes with 53322e432860 diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py --- a/pypy/module/pypyjit/test_pypy_c/test_call.py +++ b/pypy/module/pypyjit/test_pypy_c/test_call.py @@ -132,8 +132,7 @@ ops = entry_bridge.ops_by_id('meth1', opcode='LOOKUP_METHOD') assert log.opnames(ops) == ['guard_value', 'getfield_gc_r', 'guard_value', - 'guard_not_invalidated', - 'getfield_gc_r'] + 'guard_not_invalidated'] # the second LOOKUP_METHOD is folded away assert list(entry_bridge.ops_by_id('meth2', opcode='LOOKUP_METHOD')) == [] # From pypy.commits at gmail.com Mon Feb 18 05:03:24 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 18 Feb 2019 02:03:24 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix raises Message-ID: <5c6a82ec.1c69fb81.da681.25ec@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96051:f648c40320e6 Date: 2019-02-18 11:02 +0100 http://bitbucket.org/pypy/pypy/changeset/f648c40320e6/ Log: fix raises diff --git a/pypy/module/pypyjit/test_pypy_c/test_00_model.py b/pypy/module/pypyjit/test_pypy_c/test_00_model.py --- a/pypy/module/pypyjit/test_pypy_c/test_00_model.py +++ b/pypy/module/pypyjit/test_pypy_c/test_00_model.py @@ -440,7 +440,7 @@ import sys sys.stderr.write('SKIP: foobar\n') # - raises(pytest.skip.Exception, "self.run(f, [])") + pytest.raises(pytest.skip.Exception, "self.run(f, [])") def test_parse_jitlog(self): def f(): From pypy.commits at gmail.com Mon Feb 18 05:07:43 2019 From: pypy.commits at gmail.com (arigo) Date: Mon, 18 Feb 2019 02:07:43 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix the first part of this test (only, for now) Message-ID: <5c6a83ef.1c69fb81.bffae.6870@mx.google.com> Author: Armin Rigo Branch: py3.6 Changeset: r96052:6ae608ba39ef Date: 2019-02-18 11:07 +0100 http://bitbucket.org/pypy/pypy/changeset/6ae608ba39ef/ Log: fix the first part of this test (only, for now) diff --git a/pypy/module/pypyjit/test_pypy_c/test_containers.py b/pypy/module/pypyjit/test_pypy_c/test_containers.py --- a/pypy/module/pypyjit/test_pypy_c/test_containers.py +++ b/pypy/module/pypyjit/test_pypy_c/test_containers.py @@ -64,19 +64,20 @@ i8 = int_lt(i5, i7) guard_true(i8, descr=...) guard_not_invalidated(descr=...) - p109 = call_r(ConstClass(ll_str__IntegerR_SignedConst_Signed), i5, descr=) + p10 = call_r(ConstClass(ll_str__IntegerR_SignedConst_Signed), i5, descr=) guard_no_exception(descr=...) - i80 = strlen(p109) - p86 = call_r(ConstClass(str_decode_utf_8), p109, i80, ConstPtr(ptr82), 1, ConstClass(raise_unicode_exception_decode), 1, descr=) + i80 = call_i(ConstClass(codepoints_in_utf8), p10, 0, _, descr=) guard_no_exception(descr=...) - p10 = getfield_gc_r(p86, descr=) guard_nonnull(p10, descr=...) + i99 = strhash(p10) - i99 = unicodehash(p10) # NOTE: with siphash24, notably on unicodes, computing the hash # may raise MemoryError - i12 = cond_call_value_i(i99, ConstClass(_ll_strhash__rpy_unicodePtr), p10, descr=) + i87 = cond_call_value_i(i99, ConstClass(_ll_strhash__rpy_stringPtr), p10, descr=) guard_no_exception(descr=...) + i89 = int_eq(i87, -1) + i12 = int_sub(i87, i89) + p13 = new(descr=...) p15 = new_array_clear(16, descr=) {{{ From pypy.commits at gmail.com Mon Feb 18 05:59:43 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 18 Feb 2019 02:59:43 -0800 (PST) Subject: [pypy-commit] pypy py3.6: make BUILD_CONST_KEY_MAP JIT-friendly Message-ID: <5c6a901f.1c69fb81.f03f.b0d3@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96053:2e21aa817a15 Date: 2019-02-18 11:58 +0100 http://bitbucket.org/pypy/pypy/changeset/2e21aa817a15/ Log: make BUILD_CONST_KEY_MAP JIT-friendly diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py --- a/pypy/interpreter/pyopcode.py +++ b/pypy/interpreter/pyopcode.py @@ -1442,11 +1442,15 @@ @jit.unroll_safe def BUILD_CONST_KEY_MAP(self, itemcount, next_instr): - keys_w = self.space.fixedview(self.popvalue()) + from pypy.objspace.std.tupleobject import W_AbstractTupleObject + # the reason why we don't use space.fixedview here is that then the + # immutability of the tuple would not propagate into the loop below in + # the JIT + w_keys = self.space.interp_w(W_AbstractTupleObject, self.popvalue()) w_dict = self.space.newdict() for i in range(itemcount): w_value = self.peekvalue(itemcount - 1 - i) - w_key = keys_w[i] + w_key = w_keys.getitem(self.space, i) self.space.setitem(w_dict, w_key, w_value) self.dropvalues(itemcount) self.pushvalue(w_dict) From pypy.commits at gmail.com Mon Feb 18 06:06:02 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 18 Feb 2019 03:06:02 -0800 (PST) Subject: [pypy-commit] pypy py3.6: this is an improvement Message-ID: <5c6a919a.1c69fb81.b3e3a.2aa5@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96054:9f1fd685935c Date: 2019-02-18 12:05 +0100 http://bitbucket.org/pypy/pypy/changeset/9f1fd685935c/ Log: this is an improvement diff --git a/pypy/module/pypyjit/test_pypy_c/test_misc.py b/pypy/module/pypyjit/test_pypy_c/test_misc.py --- a/pypy/module/pypyjit/test_pypy_c/test_misc.py +++ b/pypy/module/pypyjit/test_pypy_c/test_misc.py @@ -147,8 +147,6 @@ RANGE_ITER_STEP_1 = """ guard_not_invalidated? # W_IntRangeStepOneIterator.next() - i80 = int_lt(i11, 0) - guard_false(i80, descr=...) i16 = int_lt(i11, i12) guard_true(i16, descr=...) i20 = int_add(i11, 1) From pypy.commits at gmail.com Mon Feb 18 09:09:34 2019 From: pypy.commits at gmail.com (stian) Date: Mon, 18 Feb 2019 06:09:34 -0800 (PST) Subject: [pypy-commit] pypy py3-bigint-to-int: Branch to experiement with making bigints into ints whenever they fit Message-ID: <5c6abc9e.1c69fb81.ab1bd.f3ba@mx.google.com> Author: stian Branch: py3-bigint-to-int Changeset: r96055:d84813084f47 Date: 2019-02-18 14:35 +0100 http://bitbucket.org/pypy/pypy/changeset/d84813084f47/ Log: Branch to experiement with making bigints into ints whenever they fit From pypy.commits at gmail.com Mon Feb 18 09:09:37 2019 From: pypy.commits at gmail.com (stian) Date: Mon, 18 Feb 2019 06:09:37 -0800 (PST) Subject: [pypy-commit] pypy py3-bigint-to-int: Code and test for most ops Message-ID: <5c6abca1.1c69fb81.3e808.1245@mx.google.com> Author: stian Branch: py3-bigint-to-int Changeset: r96056:517a32bab8f7 Date: 2019-02-18 15:08 +0100 http://bitbucket.org/pypy/pypy/changeset/517a32bab8f7/ Log: Code and test for most ops diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py --- a/pypy/objspace/std/longobject.py +++ b/pypy/objspace/std/longobject.py @@ -236,11 +236,14 @@ def descr_sub(self, space, w_other): if isinstance(w_other, W_IntObject): - return W_LongObject(self.num.int_sub(w_other.int_w(space))) + res = self.num.int_sub(w_other.int_w(space)) elif not isinstance(w_other, W_AbstractLongObject): return space.w_NotImplemented - return W_LongObject(self.num.sub(w_other.asbigint())) - + res = self.num.sub(w_other.asbigint()) + try: + return W_IntObject(res.toint()) + except OverflowError: + return W_LongObject(res) @delegate_other def descr_rsub(self, space, w_other): return W_LongObject(w_other.asbigint().sub(self.num)) @@ -257,21 +260,29 @@ @func_renamer('descr_' + opname) def descr_binop(self, space, w_other): if isinstance(w_other, W_IntObject): - return W_LongObject(intop(self.num, w_other.int_w(space))) + res = intop(self.num, w_other.int_w(space)) elif not isinstance(w_other, W_AbstractLongObject): return space.w_NotImplemented - - return W_LongObject(op(self.num, w_other.asbigint())) + else: + res = op(self.num, w_other.asbigint()) + try: + return W_IntObject(res.toint()) + except OverflowError: + return W_LongObject(res) @func_renamer(descr_rname) def descr_rbinop(self, space, w_other): if isinstance(w_other, W_IntObject): - return W_LongObject(intop(self.num, w_other.int_w(space))) + res = intop(self.num, w_other.int_w(space)) elif not isinstance(w_other, W_AbstractLongObject): return space.w_NotImplemented - - return W_LongObject(op(w_other.asbigint(), self.num)) - + else: + res = op(w_other.asbigint(), self.num) + try: + return W_IntObject(res.toint()) + except OverflowError: + return W_LongObject(res) + return descr_binop, descr_rbinop descr_add, descr_radd = _make_generic_descr_binop('add') @@ -340,7 +351,11 @@ except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "long division or modulo by zero") - return newlong(space, z) + + try: + return space.newint(z.toint()) + except OverflowError: + return newlong(space, z) def _int_floordiv(self, space, other): try: @@ -357,7 +372,10 @@ except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "integer division or modulo by zero") - return newlong(space, z) + try: + return space.newint(z.toint()) + except OverflowError: + return newlong(space, z) def _int_mod(self, space, other): try: @@ -365,7 +383,10 @@ except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "long division or modulo by zero") - return newlong(space, z) + + # Int mod should always fit into an int. + return space.newint(z.toint()) + #return newlong(space, z) descr_mod, descr_rmod = _make_descr_binop(_mod, _int_mod) def _divmod(self, space, w_other): diff --git a/pypy/objspace/std/test/test_longobject.py b/pypy/objspace/std/test/test_longobject.py --- a/pypy/objspace/std/test/test_longobject.py +++ b/pypy/objspace/std/test/test_longobject.py @@ -1,6 +1,7 @@ # -*- encoding: utf-8 -*- import py from pypy.objspace.std import longobject as lobj +from pypy.objspace.std import intobject as iobj from rpython.rlib.rbigint import rbigint class TestW_LongObject: @@ -38,7 +39,21 @@ w_obj = space.newlong_from_rarith_int(r(x)) assert space.bigint_w(w_obj).eq(rbigint.fromlong(x)) - + def test_long_to_int(self): + a = lobj.W_LongObject.fromlong(8) + b = lobj.W_LongObject.fromlong(1) + + floordivres = a._floordiv(self.space, b) + assert type(floordivres) is iobj.W_IntObject + + modres = a._mod(self.space, b) + assert type(modres) is iobj.W_IntObject + + addres = a.descr_add(self.space, b) + assert type(addres) is iobj.W_IntObject + + subres = a.descr_sub(self.space, b) + assert type(subres) is iobj.W_IntObject class AppTestLong: def w__long(self, obj): From pypy.commits at gmail.com Mon Feb 18 09:17:32 2019 From: pypy.commits at gmail.com (stian) Date: Mon, 18 Feb 2019 06:17:32 -0800 (PST) Subject: [pypy-commit] pypy py3-bigint-to-int: More ops Message-ID: <5c6abe7c.1c69fb81.ac113.bf0f@mx.google.com> Author: stian Branch: py3-bigint-to-int Changeset: r96057:daa9f9b2439b Date: 2019-02-18 15:16 +0100 http://bitbucket.org/pypy/pypy/changeset/daa9f9b2439b/ Log: More ops diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py --- a/pypy/objspace/std/longobject.py +++ b/pypy/objspace/std/longobject.py @@ -246,7 +246,11 @@ return W_LongObject(res) @delegate_other def descr_rsub(self, space, w_other): - return W_LongObject(w_other.asbigint().sub(self.num)) + res = w_other.asbigint().sub(self.num) + try: + return W_IntObject(res.toint()) + except OverflowError: + return W_LongObject(res) def _make_generic_descr_binop(opname): if opname not in COMMUTATIVE_OPS: @@ -320,12 +324,20 @@ shift = w_other.asbigint().toint() except OverflowError: # b too big raise oefmt(space.w_OverflowError, "shift count too large") - return W_LongObject(self.num.lshift(shift)) - + res = self.num.lshift(shift) + try: + return W_IntObject(res.toint()) + except OverflowError: + return W_LongObject(res) + def _int_lshift(self, space, other): if other < 0: raise oefmt(space.w_ValueError, "negative shift count") - return W_LongObject(self.num.lshift(other)) + res = self.num.lshift(other) + try: + return W_IntObject(res.toint()) + except OverflowError: + return W_LongObject(res) descr_lshift, descr_rlshift = _make_descr_binop(_lshift, _int_lshift) @@ -336,13 +348,20 @@ shift = w_other.asbigint().toint() except OverflowError: # b too big # XXX maybe just return 0L instead? raise oefmt(space.w_OverflowError, "shift count too large") - return newlong(space, self.num.rshift(shift)) + res = self.num.rshift(shift) + try: + return space.newint(res.toint()) + except OverflowError: + return newlong(space, res) def _int_rshift(self, space, other): if other < 0: raise oefmt(space.w_ValueError, "negative shift count") - - return newlong(space, self.num.rshift(other)) + res = self.num.rshift(other) + try: + return space.newint(res.toint()) + except OverflowError: + return newlong(space, res) descr_rshift, descr_rrshift = _make_descr_binop(_rshift, _int_rshift) def _floordiv(self, space, w_other): From pypy.commits at gmail.com Mon Feb 18 09:39:22 2019 From: pypy.commits at gmail.com (stian) Date: Mon, 18 Feb 2019 06:39:22 -0800 (PST) Subject: [pypy-commit] pypy py3-bigint-to-int: Add tests for more ops Message-ID: <5c6ac39a.1c69fb81.62fc.9cc0@mx.google.com> Author: stian Branch: py3-bigint-to-int Changeset: r96058:521850d02e44 Date: 2019-02-18 15:38 +0100 http://bitbucket.org/pypy/pypy/changeset/521850d02e44/ Log: Add tests for more ops diff --git a/pypy/objspace/std/test/test_longobject.py b/pypy/objspace/std/test/test_longobject.py --- a/pypy/objspace/std/test/test_longobject.py +++ b/pypy/objspace/std/test/test_longobject.py @@ -49,11 +49,22 @@ modres = a._mod(self.space, b) assert type(modres) is iobj.W_IntObject + # Covers all of descr_binop? addres = a.descr_add(self.space, b) assert type(addres) is iobj.W_IntObject + # Covers all of descr_rbinop? + raddres = a.descr_radd(self.space, b) + assert type(raddres) is iobj.W_IntObject + subres = a.descr_sub(self.space, b) assert type(subres) is iobj.W_IntObject + + lshiftres = a._lshift(self.space, b) + assert type(lshiftres) is iobj.W_IntObject + + rshiftres = a._rshift(self.space, b) + assert type(rshiftres) is iobj.W_IntObject class AppTestLong: def w__long(self, obj): From pypy.commits at gmail.com Mon Feb 18 10:07:35 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:35 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch Message-ID: <5c6aca37.1c69fb81.c96d3.de9f@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96059:395bf13f5d16 Date: 2019-02-17 18:09 +0200 http://bitbucket.org/pypy/pypy/changeset/395bf13f5d16/ Log: merge default into branch diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -21,7 +21,7 @@ space.newtext(msg)])) return raise_unicode_exception_decode -def decode_never_raise(errors, encoding, msg, s, startingpos, endingpos): +def _decode_never_raise(errors, encoding, msg, s, startingpos, endingpos): assert startingpos >= 0 ux = ['\ux' + hex(ord(x))[2:].upper() for x in s[startingpos:endingpos]] return ''.join(ux), endingpos, 'b' @@ -1013,57 +1013,15 @@ return result.build() - at specialize.memo() -def _encode_unicode_error_handler(space): - # Fast version of the "strict" errors handler. # used only in (unused) encode_utf8 - from rpython.rlib import runicode - def raise_unicode_exception_encode(errors, encoding, msg, uni, - startingpos, endingpos): - assert isinstance(uni, unicode) - u_len = len(uni) - utf8 = runicode.unicode_encode_utf8sp(uni, u_len) - raise OperationError(space.w_UnicodeEncodeError, - space.newtuple([space.newtext(encoding), - space.newtext(utf8, u_len), - space.newint(startingpos), - space.newint(endingpos), - space.newtext(msg)])) - return u'', None, 0 - return raise_unicode_exception_encode - - -def encode_utf8(space, uni, allow_surrogates=False): - # Note that Python3 tends to forbid *all* surrogates in utf-8. - # If allow_surrogates=True, then revert to the Python 2 behavior - # which never raises UnicodeEncodeError. Surrogate pairs are then - # allowed, either paired or lone. A paired surrogate is considered - # like the non-BMP character it stands for. See also *_utf8sp(). xxx - from rpython.rlib import runicode - assert isinstance(uni, unicode) - return runicode.unicode_encode_utf_8( - uni, len(uni), "strict", - errorhandler=_encode_unicode_error_handler(space), - allow_surrogates=allow_surrogates) - -def encode_utf8sp(space, uni, allow_surrogates=True): - xxx - # Surrogate-preserving utf-8 encoding. Any surrogate character - # turns into its 3-bytes encoding, whether it is paired or not. - # This should always be reversible, and the reverse is - # decode_utf8sp(). - from rpython.rlib import runicode - return runicode.unicode_encode_utf8sp(uni, len(uni)) - def decode_utf8sp(space, string): # Surrogate-preserving utf-8 decoding. Assuming there is no # encoding error, it should always be reversible, and the reverse is # unused encode_utf8sp(). - return str_decode_utf8(string, "string", True, decode_never_raise, + return str_decode_utf8(string, "string", True, _decode_never_raise, allow_surrogates=True) - # ____________________________________________________________ # utf-16 From pypy.commits at gmail.com Mon Feb 18 10:07:38 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:38 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix merge, tests Message-ID: <5c6aca3a.1c69fb81.62fc.a504@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96060:6bfc3b49077d Date: 2019-02-17 18:17 +0200 http://bitbucket.org/pypy/pypy/changeset/6bfc3b49077d/ Log: fix merge, tests diff --git a/pypy/interpreter/test/test_unicodehelper.py b/pypy/interpreter/test/test_unicodehelper.py --- a/pypy/interpreter/test/test_unicodehelper.py +++ b/pypy/interpreter/test/test_unicodehelper.py @@ -1,13 +1,6 @@ -import py -import pytest -import struct -import sys from pypy.interpreter.unicodehelper import ( - encode_utf8, decode_utf8, - unicode_encode_utf_8, - unicode_encode_utf_32_be, str_decode_utf_32_be + utf8_encode_utf_8, decode_utf8sp, ) -from pypy.interpreter.unicodehelper import encode_utf8sp, decode_utf8sp class Hit(Exception): @@ -20,18 +13,6 @@ raise AttributeError(name) -def test_encode_utf8(): - space = FakeSpace() - assert encode_utf8(space, u"abc") == "abc" - assert encode_utf8(space, u"\u1234") == "\xe1\x88\xb4" - py.test.raises(Hit, encode_utf8, space, u"\ud800") - py.test.raises(Hit, encode_utf8, space, u"\udc00") - # for the following test, go to lengths to avoid CPython's optimizer - # and .pyc file storage, which collapse the two surrogates into one - c = u"\udc00" - py.test.raises(Hit, encode_utf8, space, u"\ud800" + c) - - def test_encode_utf_8_combine_surrogates(): """ In the case of a surrogate pair, the error handler should @@ -52,80 +33,20 @@ that is a valid surrogate pair. """ assert s[start:end] in [u'\udc80', u'\uD800\uDFFF'] - return [], None, end + return '', 0, end - unicode_encode_utf_8( - u, len(u), True, + utf8_encode_utf_8( + u, 'strict', errorhandler=errorhandler, allow_surrogates=False ) -def test_encode_utf8_allow_surrogates(): - sp = FakeSpace() - assert encode_utf8(sp, u"\ud800", allow_surrogates=True) == "\xed\xa0\x80" - assert encode_utf8(sp, u"\udc00", allow_surrogates=True) == "\xed\xb0\x80" - c = u"\udc00" - got = encode_utf8(sp, u"\ud800" + c, allow_surrogates=True) - assert got == "\xf0\x90\x80\x80" - -def test_encode_utf8sp(): - sp = FakeSpace() - assert encode_utf8sp(sp, u"\ud800") == "\xed\xa0\x80" - assert encode_utf8sp(sp, u"\udc00") == "\xed\xb0\x80" - c = u"\udc00" - got = encode_utf8sp(sp, u"\ud800" + c) - assert got == "\xed\xa0\x80\xed\xb0\x80" - -def test_decode_utf8(): - space = FakeSpace() - assert decode_utf8(space, "abc") == u"abc" - assert decode_utf8(space, "\xe1\x88\xb4") == u"\u1234" - py.test.raises(Hit, decode_utf8, space, "\xed\xa0\x80") - py.test.raises(Hit, decode_utf8, space, "\xed\xb0\x80") - py.test.raises(Hit, decode_utf8, space, "\xed\xa0\x80\xed\xb0\x80") - got = decode_utf8(space, "\xf0\x90\x80\x80") - if sys.maxunicode > 65535: - assert map(ord, got) == [0x10000] - else: - assert map(ord, got) == [55296, 56320] - -def test_decode_utf8_allow_surrogates(): - sp = FakeSpace() - assert decode_utf8(sp, "\xed\xa0\x80", allow_surrogates=True) == u"\ud800" - assert decode_utf8(sp, "\xed\xb0\x80", allow_surrogates=True) == u"\udc00" - got = decode_utf8(sp, "\xed\xa0\x80\xed\xb0\x80", allow_surrogates=True) - assert map(ord, got) == [0xd800, 0xdc00] - got = decode_utf8(sp, "\xf0\x90\x80\x80", allow_surrogates=True) - assert map(ord, got) == [0x10000] - def test_decode_utf8sp(): space = FakeSpace() - assert decode_utf8sp(space, "\xed\xa0\x80") == u"\ud800" - assert decode_utf8sp(space, "\xed\xb0\x80") == u"\udc00" + assert decode_utf8sp(space, "\xed\xa0\x80") == ("\xed\xa0\x80", 1, 3) + assert decode_utf8sp(space, "\xed\xb0\x80") == ("\xed\xb0\x80", 1, 3) got = decode_utf8sp(space, "\xed\xa0\x80\xed\xb0\x80") - assert map(ord, got) == [0xd800, 0xdc00] + assert map(ord, got[0].decode('utf8')) == [0xd800, 0xdc00] got = decode_utf8sp(space, "\xf0\x90\x80\x80") - assert map(ord, got) == [0x10000] + assert map(ord, got[0].decode('utf8')) == [0x10000] - at pytest.mark.parametrize('unich', [u"\ud800", u"\udc80"]) -def test_utf32_surrogates(unich): - assert (unicode_encode_utf_32_be(unich, 1, None) == - struct.pack('>i', ord(unich))) - with pytest.raises(UnicodeEncodeError): - unicode_encode_utf_32_be(unich, 1, None, allow_surrogates=False) - - def replace_with(ru, rs): - def errorhandler(errors, enc, msg, u, startingpos, endingpos): - if errors == 'strict': - raise UnicodeEncodeError(enc, u, startingpos, endingpos, msg) - return ru, rs, endingpos - return unicode_encode_utf_32_be( - u"<%s>" % unich, 3, None, - errorhandler, allow_surrogates=False) - - assert replace_with(u'rep', None) == u''.encode('utf-32-be') - assert (replace_with(None, '\xca\xfe\xca\xfe') == - '\x00\x00\x00<\xca\xfe\xca\xfe\x00\x00\x00>') - - with pytest.raises(UnicodeDecodeError): - str_decode_utf_32_be(b"\x00\x00\xdc\x80", 4, None) diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -1013,8 +1013,6 @@ return result.build() - # used only in (unused) encode_utf8 - xxx def decode_utf8sp(space, string): # Surrogate-preserving utf-8 decoding. Assuming there is no # encoding error, it should always be reversible, and the reverse is From pypy.commits at gmail.com Mon Feb 18 10:07:40 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:40 -0800 (PST) Subject: [pypy-commit] pypy py3.6: make utf8_encode_utf_8 non-recursive, and pass surrogate pairs to error handler Message-ID: <5c6aca3c.1c69fb81.18cd3.7427@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96061:74fc16b2e4b5 Date: 2019-02-17 20:08 +0200 http://bitbucket.org/pypy/pypy/changeset/74fc16b2e4b5/ Log: make utf8_encode_utf_8 non-recursive, and pass surrogate pairs to error handler diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -21,7 +21,7 @@ space.newtext(msg)])) return raise_unicode_exception_decode -def _decode_never_raise(errors, encoding, msg, s, startingpos, endingpos): +def decode_never_raise(errors, encoding, msg, s, startingpos, endingpos): assert startingpos >= 0 ux = ['\ux' + hex(ord(x))[2:].upper() for x in s[startingpos:endingpos]] return ''.join(ux), endingpos, 'b' @@ -218,20 +218,38 @@ return res.build(), len(s), len(s) def utf8_encode_utf_8(s, errors, errorhandler, allow_surrogates=False): - try: - lgt = rutf8.check_utf8(s, allow_surrogates=allow_surrogates) - except rutf8.CheckError as e: - # XXX change this to non-recursive - pos = e.pos - assert pos >= 0 - start = s[:pos] - upos = rutf8.codepoints_in_utf8(s, end=pos) - ru, lgt, rettype = errorhandler(errors, 'utf8', - 'surrogates not allowed', s, upos, upos + 1) - end = utf8_encode_utf_8(s[pos+3:], errors, errorhandler, - allow_surrogates=allow_surrogates) - s = start + ru + end - return s + size = len(s) + if size == 0: + return '' + pos = 0 + upos = 0 + result = StringBuilder(size) + while pos < size: + try: + lgt = rutf8.check_utf8(s, allow_surrogates=allow_surrogates, start=pos) + if pos == 0: + # fast path + return s + for ch in s[pos:]: + result.append(ch) + break + except rutf8.CheckError as e: + for ch in s[pos:e.pos]: + result.append(ch) + upos += rutf8.codepoints_in_utf8(s, start=pos, end=e.pos) + pos = e.pos + assert pos >= 0 + res, newindex, rettype = errorhandler(errors, 'utf8', + 'surrogates not allowed', s, upos, upos + 1) + if rettype == 'u': + for cp in rutf8.Utf8StringIterator(res): + result.append(chr(cp)) + else: + for ch in res: + result.append(ch) + upos = newindex + pos = rutf8._pos_at_index(s, upos) + return result.build() def utf8_encode_latin_1(s, errors, errorhandler, allow_surrogates=False): try: @@ -1017,7 +1035,7 @@ # Surrogate-preserving utf-8 decoding. Assuming there is no # encoding error, it should always be reversible, and the reverse is # unused encode_utf8sp(). - return str_decode_utf8(string, "string", True, _decode_never_raise, + return str_decode_utf8(string, "string", True, decode_never_raise, allow_surrogates=True) # ____________________________________________________________ diff --git a/pypy/module/_codecs/test/test_codecs.py b/pypy/module/_codecs/test/test_codecs.py --- a/pypy/module/_codecs/test/test_codecs.py +++ b/pypy/module/_codecs/test/test_codecs.py @@ -1149,7 +1149,6 @@ backslashreplace = ''.join('\\x%02x' % b for b in ill_surrogate) assert test_sequence.decode(encoding, "backslashreplace") == (before + backslashreplace + after) - def test_lone_surrogates_utf_8(self): """ @@ -1158,6 +1157,8 @@ """ e = raises(UnicodeEncodeError, u"\udc80\ud800\udfff".encode, "utf-8", "surrogateescape").value + assert e.start == 1 + assert e.end == 3 assert e.object[e.start:e.end] == u'\ud800\udfff' def test_charmap_encode(self): From pypy.commits at gmail.com Mon Feb 18 10:07:41 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:41 -0800 (PST) Subject: [pypy-commit] pypy default: partially restore test removed in baef7e3e3ac0 Message-ID: <5c6aca3d.1c69fb81.cf7e9.4977@mx.google.com> Author: Matti Picus Branch: Changeset: r96062:7693e4a720ad Date: 2019-02-18 16:05 +0200 http://bitbucket.org/pypy/pypy/changeset/7693e4a720ad/ Log: partially restore test removed in baef7e3e3ac0 diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -41,6 +41,15 @@ self._utf8 = utf8str self._length = length self._index_storage = rutf8.null_storage() + if not we_are_translated(): + try: + # best effort, too expensive to handle surrogates + ulength = len(utf8str.decode('utf8')) + except: + ulength = length + assert ulength == length + + @staticmethod def from_utf8builder(builder): From pypy.commits at gmail.com Mon Feb 18 10:07:43 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:43 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge heads Message-ID: <5c6aca3f.1c69fb81.97e84.5b9a@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96063:3f907d46d82c Date: 2019-02-18 16:12 +0200 http://bitbucket.org/pypy/pypy/changeset/3f907d46d82c/ Log: merge heads diff --git a/pypy/interpreter/test/test_unicodehelper.py b/pypy/interpreter/test/test_unicodehelper.py --- a/pypy/interpreter/test/test_unicodehelper.py +++ b/pypy/interpreter/test/test_unicodehelper.py @@ -1,13 +1,6 @@ -import py -import pytest -import struct -import sys from pypy.interpreter.unicodehelper import ( - encode_utf8, decode_utf8, - unicode_encode_utf_8, - unicode_encode_utf_32_be, str_decode_utf_32_be + utf8_encode_utf_8, decode_utf8sp, ) -from pypy.interpreter.unicodehelper import encode_utf8sp, decode_utf8sp class Hit(Exception): @@ -20,18 +13,6 @@ raise AttributeError(name) -def test_encode_utf8(): - space = FakeSpace() - assert encode_utf8(space, u"abc") == "abc" - assert encode_utf8(space, u"\u1234") == "\xe1\x88\xb4" - py.test.raises(Hit, encode_utf8, space, u"\ud800") - py.test.raises(Hit, encode_utf8, space, u"\udc00") - # for the following test, go to lengths to avoid CPython's optimizer - # and .pyc file storage, which collapse the two surrogates into one - c = u"\udc00" - py.test.raises(Hit, encode_utf8, space, u"\ud800" + c) - - def test_encode_utf_8_combine_surrogates(): """ In the case of a surrogate pair, the error handler should @@ -52,80 +33,20 @@ that is a valid surrogate pair. """ assert s[start:end] in [u'\udc80', u'\uD800\uDFFF'] - return [], None, end + return '', 0, end - unicode_encode_utf_8( - u, len(u), True, + utf8_encode_utf_8( + u, 'strict', errorhandler=errorhandler, allow_surrogates=False ) -def test_encode_utf8_allow_surrogates(): - sp = FakeSpace() - assert encode_utf8(sp, u"\ud800", allow_surrogates=True) == "\xed\xa0\x80" - assert encode_utf8(sp, u"\udc00", allow_surrogates=True) == "\xed\xb0\x80" - c = u"\udc00" - got = encode_utf8(sp, u"\ud800" + c, allow_surrogates=True) - assert got == "\xf0\x90\x80\x80" - -def test_encode_utf8sp(): - sp = FakeSpace() - assert encode_utf8sp(sp, u"\ud800") == "\xed\xa0\x80" - assert encode_utf8sp(sp, u"\udc00") == "\xed\xb0\x80" - c = u"\udc00" - got = encode_utf8sp(sp, u"\ud800" + c) - assert got == "\xed\xa0\x80\xed\xb0\x80" - -def test_decode_utf8(): - space = FakeSpace() - assert decode_utf8(space, "abc") == u"abc" - assert decode_utf8(space, "\xe1\x88\xb4") == u"\u1234" - py.test.raises(Hit, decode_utf8, space, "\xed\xa0\x80") - py.test.raises(Hit, decode_utf8, space, "\xed\xb0\x80") - py.test.raises(Hit, decode_utf8, space, "\xed\xa0\x80\xed\xb0\x80") - got = decode_utf8(space, "\xf0\x90\x80\x80") - if sys.maxunicode > 65535: - assert map(ord, got) == [0x10000] - else: - assert map(ord, got) == [55296, 56320] - -def test_decode_utf8_allow_surrogates(): - sp = FakeSpace() - assert decode_utf8(sp, "\xed\xa0\x80", allow_surrogates=True) == u"\ud800" - assert decode_utf8(sp, "\xed\xb0\x80", allow_surrogates=True) == u"\udc00" - got = decode_utf8(sp, "\xed\xa0\x80\xed\xb0\x80", allow_surrogates=True) - assert map(ord, got) == [0xd800, 0xdc00] - got = decode_utf8(sp, "\xf0\x90\x80\x80", allow_surrogates=True) - assert map(ord, got) == [0x10000] - def test_decode_utf8sp(): space = FakeSpace() - assert decode_utf8sp(space, "\xed\xa0\x80") == u"\ud800" - assert decode_utf8sp(space, "\xed\xb0\x80") == u"\udc00" + assert decode_utf8sp(space, "\xed\xa0\x80") == ("\xed\xa0\x80", 1, 3) + assert decode_utf8sp(space, "\xed\xb0\x80") == ("\xed\xb0\x80", 1, 3) got = decode_utf8sp(space, "\xed\xa0\x80\xed\xb0\x80") - assert map(ord, got) == [0xd800, 0xdc00] + assert map(ord, got[0].decode('utf8')) == [0xd800, 0xdc00] got = decode_utf8sp(space, "\xf0\x90\x80\x80") - assert map(ord, got) == [0x10000] + assert map(ord, got[0].decode('utf8')) == [0x10000] - at pytest.mark.parametrize('unich', [u"\ud800", u"\udc80"]) -def test_utf32_surrogates(unich): - assert (unicode_encode_utf_32_be(unich, 1, None) == - struct.pack('>i', ord(unich))) - with pytest.raises(UnicodeEncodeError): - unicode_encode_utf_32_be(unich, 1, None, allow_surrogates=False) - - def replace_with(ru, rs): - def errorhandler(errors, enc, msg, u, startingpos, endingpos): - if errors == 'strict': - raise UnicodeEncodeError(enc, u, startingpos, endingpos, msg) - return ru, rs, endingpos - return unicode_encode_utf_32_be( - u"<%s>" % unich, 3, None, - errorhandler, allow_surrogates=False) - - assert replace_with(u'rep', None) == u''.encode('utf-32-be') - assert (replace_with(None, '\xca\xfe\xca\xfe') == - '\x00\x00\x00<\xca\xfe\xca\xfe\x00\x00\x00>') - - with pytest.raises(UnicodeDecodeError): - str_decode_utf_32_be(b"\x00\x00\xdc\x80", 4, None) diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -218,20 +218,38 @@ return res.build(), len(s), len(s) def utf8_encode_utf_8(s, errors, errorhandler, allow_surrogates=False): - try: - lgt = rutf8.check_utf8(s, allow_surrogates=allow_surrogates) - except rutf8.CheckError as e: - # XXX change this to non-recursive - pos = e.pos - assert pos >= 0 - start = s[:pos] - upos = rutf8.codepoints_in_utf8(s, end=pos) - ru, lgt, rettype = errorhandler(errors, 'utf8', - 'surrogates not allowed', s, upos, upos + 1) - end = utf8_encode_utf_8(s[pos+3:], errors, errorhandler, - allow_surrogates=allow_surrogates) - s = start + ru + end - return s + size = len(s) + if size == 0: + return '' + pos = 0 + upos = 0 + result = StringBuilder(size) + while pos < size: + try: + lgt = rutf8.check_utf8(s, allow_surrogates=allow_surrogates, start=pos) + if pos == 0: + # fast path + return s + for ch in s[pos:]: + result.append(ch) + break + except rutf8.CheckError as e: + for ch in s[pos:e.pos]: + result.append(ch) + upos += rutf8.codepoints_in_utf8(s, start=pos, end=e.pos) + pos = e.pos + assert pos >= 0 + res, newindex, rettype = errorhandler(errors, 'utf8', + 'surrogates not allowed', s, upos, upos + 1) + if rettype == 'u': + for cp in rutf8.Utf8StringIterator(res): + result.append(chr(cp)) + else: + for ch in res: + result.append(ch) + upos = newindex + pos = rutf8._pos_at_index(s, upos) + return result.build() def utf8_encode_latin_1(s, errors, errorhandler, allow_surrogates=False): try: @@ -1013,49 +1031,6 @@ return result.build() - at specialize.memo() -def _encode_unicode_error_handler(space): - # Fast version of the "strict" errors handler. - # used only in (unused) encode_utf8 - from rpython.rlib import runicode - def raise_unicode_exception_encode(errors, encoding, msg, uni, - startingpos, endingpos): - assert isinstance(uni, unicode) - u_len = len(uni) - utf8 = runicode.unicode_encode_utf8sp(uni, u_len) - raise OperationError(space.w_UnicodeEncodeError, - space.newtuple([space.newtext(encoding), - space.newtext(utf8, u_len), - space.newint(startingpos), - space.newint(endingpos), - space.newtext(msg)])) - return u'', None, 0 - return raise_unicode_exception_encode - - -def encode_utf8(space, uni, allow_surrogates=False): - # Note that Python3 tends to forbid *all* surrogates in utf-8. - # If allow_surrogates=True, then revert to the Python 2 behavior - # which never raises UnicodeEncodeError. Surrogate pairs are then - # allowed, either paired or lone. A paired surrogate is considered - # like the non-BMP character it stands for. See also *_utf8sp(). - xxx - from rpython.rlib import runicode - assert isinstance(uni, unicode) - return runicode.unicode_encode_utf_8( - uni, len(uni), "strict", - errorhandler=_encode_unicode_error_handler(space), - allow_surrogates=allow_surrogates) - -def encode_utf8sp(space, uni, allow_surrogates=True): - xxx - # Surrogate-preserving utf-8 encoding. Any surrogate character - # turns into its 3-bytes encoding, whether it is paired or not. - # This should always be reversible, and the reverse is - # decode_utf8sp(). - from rpython.rlib import runicode - return runicode.unicode_encode_utf8sp(uni, len(uni)) - def decode_utf8sp(space, string): # Surrogate-preserving utf-8 decoding. Assuming there is no # encoding error, it should always be reversible, and the reverse is @@ -1063,7 +1038,6 @@ return str_decode_utf8(string, "string", True, decode_never_raise, allow_surrogates=True) - # ____________________________________________________________ # utf-16 diff --git a/pypy/module/_codecs/test/test_codecs.py b/pypy/module/_codecs/test/test_codecs.py --- a/pypy/module/_codecs/test/test_codecs.py +++ b/pypy/module/_codecs/test/test_codecs.py @@ -1149,7 +1149,6 @@ backslashreplace = ''.join('\\x%02x' % b for b in ill_surrogate) assert test_sequence.decode(encoding, "backslashreplace") == (before + backslashreplace + after) - def test_lone_surrogates_utf_8(self): """ @@ -1158,6 +1157,8 @@ """ e = raises(UnicodeEncodeError, u"\udc80\ud800\udfff".encode, "utf-8", "surrogateescape").value + assert e.start == 1 + assert e.end == 3 assert e.object[e.start:e.end] == u'\ud800\udfff' def test_charmap_encode(self): From pypy.commits at gmail.com Mon Feb 18 10:07:45 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:45 -0800 (PST) Subject: [pypy-commit] pypy py3.6: use lenght in codepoints not utf8 Message-ID: <5c6aca41.1c69fb81.51ed0.932a@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96064:a2c4c9f78c7a Date: 2019-02-18 15:05 +0200 http://bitbucket.org/pypy/pypy/changeset/a2c4c9f78c7a/ Log: use lenght in codepoints not utf8 diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -507,11 +507,13 @@ result = str(value) lgt += len(result) elif fmt == 'R': - result = space.utf8_w(space.repr(value)) - lgt += len(result) + s = space.repr(value) + result = space.utf8_w(s) + lgt += space.len_w(s) elif fmt == 'S': - result = space.utf8_w(space.str(value)) - lgt += len(result) + s = space.str(value) + result = space.utf8_w(s) + lgt += space.len_w(s) elif fmt == 'T': result = space.type(value).name lgt += len(result) From pypy.commits at gmail.com Mon Feb 18 10:07:47 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:47 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix test Message-ID: <5c6aca43.1c69fb81.dad7e.8395@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96065:393374a59e39 Date: 2019-02-18 15:05 +0200 http://bitbucket.org/pypy/pypy/changeset/393374a59e39/ Log: fix test diff --git a/pypy/objspace/std/test/test_intobject.py b/pypy/objspace/std/test/test_intobject.py --- a/pypy/objspace/std/test/test_intobject.py +++ b/pypy/objspace/std/test/test_intobject.py @@ -588,7 +588,7 @@ def test_int_error_msg_surrogate(self): value = u'123\ud800' e = raises(ValueError, int, value) - assert str(e.value) == "invalid literal for int() with base 10: %r" % value + assert str(e.value) == u"invalid literal for int() with base 10: %r" % value def test_non_numeric_input_types(self): # Test possible non-numeric types for the argument x, including From pypy.commits at gmail.com Mon Feb 18 10:07:49 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:49 -0800 (PST) Subject: [pypy-commit] pypy py3.6: turn UnicodeError while converting to decimal into ValueError, correct message Message-ID: <5c6aca45.1c69fb81.a49fa.4104@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96066:51e9bde8332f Date: 2019-02-18 15:06 +0200 http://bitbucket.org/pypy/pypy/changeset/51e9bde8332f/ Log: turn UnicodeError while converting to decimal into ValueError, correct message diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py --- a/pypy/objspace/std/intobject.py +++ b/pypy/objspace/std/intobject.py @@ -913,7 +913,12 @@ return _from_intlike(space, w_inttype, w_obj) elif space.isinstance_w(w_value, space.w_unicode): from pypy.objspace.std.unicodeobject import unicode_to_decimal_w - b = unicode_to_decimal_w(space, w_value, allow_surrogates=True) + try: + b = unicode_to_decimal_w(space, w_value, allow_surrogates=True) + except Exception: + raise oefmt(space.w_ValueError, + 'invalid literal for int() with base 10: %R', + w_value) return _string_to_int_or_long(space, w_inttype, w_value, b) elif (space.isinstance_w(w_value, space.w_bytearray) or space.isinstance_w(w_value, space.w_bytes)): @@ -941,7 +946,12 @@ if space.isinstance_w(w_value, space.w_unicode): from pypy.objspace.std.unicodeobject import unicode_to_decimal_w - s = unicode_to_decimal_w(space, w_value, allow_surrogates=True) + try: + s = unicode_to_decimal_w(space, w_value, allow_surrogates=True) + except Exception: + raise oefmt(space.w_ValueError, + 'invalid literal for int() with base %d: %S', + base, w_value) elif (space.isinstance_w(w_value, space.w_bytes) or space.isinstance_w(w_value, space.w_bytearray)): s = space.charbuf_w(w_value) From pypy.commits at gmail.com Mon Feb 18 10:07:50 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:50 -0800 (PST) Subject: [pypy-commit] pypy py3.6: collect surrogate pairs for error Message-ID: <5c6aca46.1c69fb81.43856.9727@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96067:813c99f810ac Date: 2019-02-18 15:07 +0200 http://bitbucket.org/pypy/pypy/changeset/813c99f810ac/ Log: collect surrogate pairs for error diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -239,8 +239,19 @@ upos += rutf8.codepoints_in_utf8(s, start=pos, end=e.pos) pos = e.pos assert pos >= 0 + # Try to get collect surrogates in one pass + # XXX do we care about performance in this case? + # XXX should this loop for more than one pair? + delta = 1 + uchr = rutf8.codepoint_at_pos(s, pos) + if 0xD800 <= uchr <= 0xDBFF: + pos = rutf8.next_codepoint_pos(s, pos) + if pos < size: + uchr = rutf8.codepoint_at_pos(s, pos) + if 0xDC00 <= uchr <= 0xDFFF: + delta += 1 res, newindex, rettype = errorhandler(errors, 'utf8', - 'surrogates not allowed', s, upos, upos + 1) + 'surrogates not allowed', s, upos, upos + delta) if rettype == 'u': for cp in rutf8.Utf8StringIterator(res): result.append(chr(cp)) From pypy.commits at gmail.com Mon Feb 18 10:07:52 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:52 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch Message-ID: <5c6aca48.1c69fb81.341ea.d17f@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96068:c3304e7116ad Date: 2019-02-18 16:06 +0200 http://bitbucket.org/pypy/pypy/changeset/c3304e7116ad/ Log: merge default into branch 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.12.0 +Version: 1.12.1 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -38,6 +38,15 @@ self._utf8 = utf8str self._length = length self._index_storage = rutf8.null_storage() + if not we_are_translated(): + try: + # best effort, too expensive to handle surrogates + ulength = len(utf8str.decode('utf8')) + except: + ulength = length + assert ulength == length + + @staticmethod def from_utf8builder(builder): From pypy.commits at gmail.com Mon Feb 18 10:07:54 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:54 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix error exposed by 7693e4a720ad Message-ID: <5c6aca4a.1c69fb81.85fcb.dbaa@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96069:8fb002913c55 Date: 2019-02-18 16:45 +0200 http://bitbucket.org/pypy/pypy/changeset/8fb002913c55/ Log: fix error exposed by 7693e4a720ad diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -516,7 +516,7 @@ lgt += space.len_w(s) elif fmt == 'T': result = space.type(value).name - lgt += len(result) + lgt += rutf8.codepoints_in_utf8(result) elif fmt == 'N': result = value.getname(space) lgt += len(result) From pypy.commits at gmail.com Mon Feb 18 10:07:57 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 07:07:57 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix translation Message-ID: <5c6aca4d.1c69fb81.a1b49.9460@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96070:21f7cebd010e Date: 2019-02-18 17:06 +0200 http://bitbucket.org/pypy/pypy/changeset/21f7cebd010e/ Log: fix translation diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -234,11 +234,12 @@ result.append(ch) break except rutf8.CheckError as e: - for ch in s[pos:e.pos]: + end = e.pos + assert end >= 0 + for ch in s[pos:end]: result.append(ch) - upos += rutf8.codepoints_in_utf8(s, start=pos, end=e.pos) - pos = e.pos - assert pos >= 0 + upos += rutf8.codepoints_in_utf8(s, start=pos, end=end) + pos = end # Try to get collect surrogates in one pass # XXX do we care about performance in this case? # XXX should this loop for more than one pair? From pypy.commits at gmail.com Mon Feb 18 10:49:40 2019 From: pypy.commits at gmail.com (stian) Date: Mon, 18 Feb 2019 07:49:40 -0800 (PST) Subject: [pypy-commit] pypy py3-bigint-to-int: Reduce this to int_mod and int_and ops for now. Message-ID: <5c6ad414.1c69fb81.ea95e.0ea3@mx.google.com> Author: stian Branch: py3-bigint-to-int Changeset: r96071:4e3d4c68bcde Date: 2019-02-18 16:49 +0100 http://bitbucket.org/pypy/pypy/changeset/4e3d4c68bcde/ Log: Reduce this to int_mod and int_and ops for now. diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py --- a/pypy/objspace/std/longobject.py +++ b/pypy/objspace/std/longobject.py @@ -234,23 +234,22 @@ descr_gt = _make_descr_cmp('gt') descr_ge = _make_descr_cmp('ge') - def descr_sub(self, space, w_other): - if isinstance(w_other, W_IntObject): - res = self.num.int_sub(w_other.int_w(space)) - elif not isinstance(w_other, W_AbstractLongObject): - return space.w_NotImplemented - res = self.num.sub(w_other.asbigint()) - try: - return W_IntObject(res.toint()) - except OverflowError: - return W_LongObject(res) - @delegate_other - def descr_rsub(self, space, w_other): - res = w_other.asbigint().sub(self.num) - try: - return W_IntObject(res.toint()) - except OverflowError: - return W_LongObject(res) + def _make_generic_descr_binop_noncommutative(opname): + methname = opname + '_' if opname in ('and', 'or') else opname + descr_rname = 'descr_r' + opname + op = getattr(rbigint, methname) + + @func_renamer('descr_' + opname) + @delegate_other + def descr_binop(self, space, w_other): + return W_LongObject(op(self.num, w_other.asbigint())) + + @func_renamer(descr_rname) + @delegate_other + def descr_rbinop(self, space, w_other): + return W_LongObject(op(w_other.asbigint(), self.num)) + + return descr_binop, descr_rbinop def _make_generic_descr_binop(opname): if opname not in COMMUTATIVE_OPS: @@ -264,49 +263,82 @@ @func_renamer('descr_' + opname) def descr_binop(self, space, w_other): if isinstance(w_other, W_IntObject): - res = intop(self.num, w_other.int_w(space)) + return W_LongObject(intop(self.num, w_other.int_w(space))) elif not isinstance(w_other, W_AbstractLongObject): return space.w_NotImplemented - else: - res = op(self.num, w_other.asbigint()) - try: - return W_IntObject(res.toint()) - except OverflowError: - return W_LongObject(res) + + return W_LongObject(op(self.num, w_other.asbigint())) + + @func_renamer(descr_rname) + def descr_rbinop(self, space, w_other): + if isinstance(w_other, W_IntObject): + return W_LongObject(intop(self.num, w_other.int_w(space))) + elif not isinstance(w_other, W_AbstractLongObject): + return space.w_NotImplemented + + return W_LongObject(op(w_other.asbigint(), self.num)) + + return descr_binop, descr_rbinop + + def _make_generic_descr_binop_maybeint(opname): + if opname not in COMMUTATIVE_OPS: + raise Exception("Not supported") + + methname = opname + '_' if opname in ('and', 'or') else opname + descr_rname = 'descr_r' + opname + op = getattr(rbigint, methname) + intop = getattr(rbigint, "int_" + methname) + + @func_renamer('descr_' + opname) + def descr_binop(self, space, w_other): + if isinstance(w_other, W_IntObject): + res = intop(self.num, w_other.int_w(space)) + try: + return W_IntObject(res.toint()) + except OverflowError: + return W_LongObject(res) + elif not isinstance(w_other, W_AbstractLongObject): + return space.w_NotImplemented + + return W_LongObject(op(self.num, w_other.asbigint())) @func_renamer(descr_rname) def descr_rbinop(self, space, w_other): if isinstance(w_other, W_IntObject): res = intop(self.num, w_other.int_w(space)) + try: + return W_IntObject(res.toint()) + except OverflowError: + return W_LongObject(res) elif not isinstance(w_other, W_AbstractLongObject): return space.w_NotImplemented - else: - res = op(w_other.asbigint(), self.num) - try: - return W_IntObject(res.toint()) - except OverflowError: - return W_LongObject(res) - + + return W_LongObject(op(w_other.asbigint(), self.num)) + return descr_binop, descr_rbinop - descr_add, descr_radd = _make_generic_descr_binop('add') - + descr_sub, descr_rsub = _make_generic_descr_binop_noncommutative('sub') descr_mul, descr_rmul = _make_generic_descr_binop('mul') - descr_and, descr_rand = _make_generic_descr_binop('and') + descr_and, descr_rand = _make_generic_descr_binop_maybeint('and') descr_or, descr_ror = _make_generic_descr_binop('or') descr_xor, descr_rxor = _make_generic_descr_binop('xor') - def _make_descr_binop(func, int_func): + def _make_descr_binop(func, int_func=None): opname = func.__name__[1:] - @func_renamer('descr_' + opname) - def descr_binop(self, space, w_other): - if isinstance(w_other, W_IntObject): - return int_func(self, space, w_other.int_w(space)) - elif not isinstance(w_other, W_AbstractLongObject): - return space.w_NotImplemented - return func(self, space, w_other) - + if int_func: + @func_renamer('descr_' + opname) + def descr_binop(self, space, w_other): + if isinstance(w_other, W_IntObject): + return int_func(self, space, w_other.int_w(space)) + elif not isinstance(w_other, W_AbstractLongObject): + return space.w_NotImplemented + return func(self, space, w_other) + else: + @delegate_other + @func_renamer('descr_' + opname) + def descr_binop(self, space, w_other): + return func(self, space, w_other) @delegate_other @func_renamer('descr_r' + opname) def descr_rbinop(self, space, w_other): @@ -324,20 +356,12 @@ shift = w_other.asbigint().toint() except OverflowError: # b too big raise oefmt(space.w_OverflowError, "shift count too large") - res = self.num.lshift(shift) - try: - return W_IntObject(res.toint()) - except OverflowError: - return W_LongObject(res) - - def _int_lshift(self, space, other): - if other < 0: + return W_LongObject(self.num.lshift(shift)) + + def _int_lshift(self, space, w_other): + if w_other < 0: raise oefmt(space.w_ValueError, "negative shift count") - res = self.num.lshift(other) - try: - return W_IntObject(res.toint()) - except OverflowError: - return W_LongObject(res) + return W_LongObject(self.num.lshift(w_other)) descr_lshift, descr_rlshift = _make_descr_binop(_lshift, _int_lshift) @@ -348,20 +372,13 @@ shift = w_other.asbigint().toint() except OverflowError: # b too big # XXX maybe just return 0L instead? raise oefmt(space.w_OverflowError, "shift count too large") - res = self.num.rshift(shift) - try: - return space.newint(res.toint()) - except OverflowError: - return newlong(space, res) + return newlong(space, self.num.rshift(shift)) - def _int_rshift(self, space, other): - if other < 0: + def _int_rshift(self, space, w_other): + if w_other < 0: raise oefmt(space.w_ValueError, "negative shift count") - res = self.num.rshift(other) - try: - return space.newint(res.toint()) - except OverflowError: - return newlong(space, res) + + return newlong(space, self.num.rshift(w_other)) descr_rshift, descr_rrshift = _make_descr_binop(_rshift, _int_rshift) def _floordiv(self, space, w_other): @@ -370,20 +387,16 @@ except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "long division or modulo by zero") - + return newlong(space, z) + + def _floordiv(self, space, w_other): try: - return space.newint(z.toint()) - except OverflowError: - return newlong(space, z) - - def _int_floordiv(self, space, other): - try: - z = self.num.int_floordiv(other) + z = self.num.floordiv(w_other.asbigint()) except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "integer division or modulo by zero") return newlong(space, z) - descr_floordiv, descr_rfloordiv = _make_descr_binop(_floordiv, _int_floordiv) + descr_floordiv, descr_rfloordiv = _make_descr_binop(_floordiv) def _mod(self, space, w_other): try: @@ -391,21 +404,17 @@ except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "integer division or modulo by zero") + return newlong(space, z) + + def _int_mod(self, space, w_other): try: - return space.newint(z.toint()) - except OverflowError: - return newlong(space, z) - - def _int_mod(self, space, other): - try: - z = self.num.int_mod(other) + z = self.num.int_mod(w_other) except ZeroDivisionError: raise oefmt(space.w_ZeroDivisionError, "long division or modulo by zero") - - # Int mod should always fit into an int. + #return newlong(space, z) + # Should always fit. return space.newint(z.toint()) - #return newlong(space, z) descr_mod, descr_rmod = _make_descr_binop(_mod, _int_mod) def _divmod(self, space, w_other): @@ -415,16 +424,7 @@ raise oefmt(space.w_ZeroDivisionError, "integer division or modulo by zero") return space.newtuple([newlong(space, div), newlong(space, mod)]) - - def _int_divmod(self, space, other): - try: - div, mod = self.num.int_divmod(other) - except ZeroDivisionError: - raise oefmt(space.w_ZeroDivisionError, - "long division or modulo by zero") - return space.newtuple([newlong(space, div), newlong(space, mod)]) - - descr_divmod, descr_rdivmod = _make_descr_binop(_divmod, _int_divmod) + descr_divmod, descr_rdivmod = _make_descr_binop(_divmod) def _hash_long(space, v): diff --git a/pypy/objspace/std/test/test_longobject.py b/pypy/objspace/std/test/test_longobject.py --- a/pypy/objspace/std/test/test_longobject.py +++ b/pypy/objspace/std/test/test_longobject.py @@ -42,29 +42,24 @@ def test_long_to_int(self): a = lobj.W_LongObject.fromlong(8) b = lobj.W_LongObject.fromlong(1) + c = iobj.W_IntObject(1) + + modres = a.descr_mod(self.space, b) + assert type(modres) is lobj.W_LongObject - floordivres = a._floordiv(self.space, b) - assert type(floordivres) is iobj.W_IntObject + intmodres = a.descr_mod(self.space, c) + assert type(intmodres) is iobj.W_IntObject - modres = a._mod(self.space, b) - assert type(modres) is iobj.W_IntObject + andres = a.descr_and(self.space, b) + assert type(andres) is lobj.W_LongObject - # Covers all of descr_binop? - addres = a.descr_add(self.space, b) - assert type(addres) is iobj.W_IntObject + intandres = a.descr_and(self.space, c) + assert type(intandres) is iobj.W_IntObject - # Covers all of descr_rbinop? - raddres = a.descr_radd(self.space, b) - assert type(raddres) is iobj.W_IntObject + # Maybe in next round. + #intdivlongres = a.descr_rfloordiv(self.space, c) + #assert type(intdivlongres) is iobj.W_IntObject - subres = a.descr_sub(self.space, b) - assert type(subres) is iobj.W_IntObject - - lshiftres = a._lshift(self.space, b) - assert type(lshiftres) is iobj.W_IntObject - - rshiftres = a._rshift(self.space, b) - assert type(rshiftres) is iobj.W_IntObject class AppTestLong: def w__long(self, obj): From pypy.commits at gmail.com Mon Feb 18 15:47:04 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 18 Feb 2019 12:47:04 -0800 (PST) Subject: [pypy-commit] pypy py3.6: another dead getfield_gc_r Message-ID: <5c6b19c8.1c69fb81.fb622.be2c@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96075:f87194e4cc02 Date: 2019-02-18 12:06 +0100 http://bitbucket.org/pypy/pypy/changeset/f87194e4cc02/ Log: another dead getfield_gc_r diff --git a/pypy/module/pypyjit/test_pypy_c/test_globals.py b/pypy/module/pypyjit/test_pypy_c/test_globals.py --- a/pypy/module/pypyjit/test_pypy_c/test_globals.py +++ b/pypy/module/pypyjit/test_pypy_c/test_globals.py @@ -16,6 +16,5 @@ assert log.result == 500 loop, = log.loops_by_filename(self.filepath) assert loop.match_by_id("loadglobal", """ - p1 = getfield_gc_r(..., descr=...) # dead guard_not_invalidated(descr=...) """) From pypy.commits at gmail.com Mon Feb 18 15:47:06 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 18 Feb 2019 12:47:06 -0800 (PST) Subject: [pypy-commit] pypy py3.6: make sure __import__ is not stored in a celldict cell, otherwise local imports are really slow Message-ID: <5c6b19ca.1c69fb81.6b762.2452@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96076:b7b3ef736a84 Date: 2019-02-18 21:45 +0100 http://bitbucket.org/pypy/pypy/changeset/b7b3ef736a84/ Log: make sure __import__ is not stored in a celldict cell, otherwise local imports are really slow diff --git a/pypy/interpreter/mixedmodule.py b/pypy/interpreter/mixedmodule.py --- a/pypy/interpreter/mixedmodule.py +++ b/pypy/interpreter/mixedmodule.py @@ -197,6 +197,17 @@ def get__doc__(cls, space): return space.newtext_or_none(cls.__doc__) + def setdictvalue_dont_introduce_cell(self, name, w_value): + """ inofficial interface on MixedModules to override an existing value + in the module but without introducing a cell (in the sense of + celldict.py) for it. Should be used sparingly, since it will trigger a + JIT recompile of all code that uses this module.""" + from pypy.objspace.std.celldict import setdictvalue_dont_introduce_cell + # first do a regular setdictvalue to force the lazy loading + self.setdictvalue(self.space, name, w_value) + # then ask cell dict to remove the cell, and store the value directly + setdictvalue_dont_introduce_cell(self, self.space, name, w_value) + @not_rpython def getinterpevalloader(pkgroot, spec): diff --git a/pypy/module/_frozen_importlib/__init__.py b/pypy/module/_frozen_importlib/__init__.py --- a/pypy/module/_frozen_importlib/__init__.py +++ b/pypy/module/_frozen_importlib/__init__.py @@ -80,7 +80,9 @@ """Copy our __import__ to builtins.""" if not we_are_translated(): self.startup_at_translation_time_only(space) - self.space.builtin.setdictvalue(space, '__import__', self.w_import) + # use special module api to prevent a cell from being introduced + self.space.builtin.setdictvalue_dont_introduce_cell( + '__import__', self.w_import) def startup_at_translation_time_only(self, space): # Issue #2834 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 @@ -186,5 +186,17 @@ def wrapvalue(space, value): return unwrap_cell(space, value) +def setdictvalue_dont_introduce_cell(w_obj, space, name, w_value): + from pypy.objspace.std.dictmultiobject import W_DictMultiObject + w_dict = w_obj.getdict(space) + if isinstance(w_dict, W_DictMultiObject): + strategy = w_dict.get_strategy() + if isinstance(strategy, ModuleDictStrategy): + dict_w = strategy.unerase(w_dict.dstorage) + strategy.mutated() + dict_w[name] = w_value + return + w_obj.setdictvalue(space, name, w_value) + create_iterator_classes(ModuleDictStrategy) diff --git a/pypy/objspace/std/test/test_celldict.py b/pypy/objspace/std/test/test_celldict.py --- a/pypy/objspace/std/test/test_celldict.py +++ b/pypy/objspace/std/test/test_celldict.py @@ -58,6 +58,31 @@ v3 = strategy.version assert v2 is v3 + def test_module_no_cell_interface(self): + from pypy.interpreter.module import Module + w_mod = Module(space, None) + assert isinstance(w_mod.w_dict, W_ModuleDictObject) + + key = "a" + value1 = object() + w_mod.setdictvalue(space, key, value1) + + strategy = w_mod.w_dict.get_strategy() + storage = strategy.unerase(w_mod.w_dict.dstorage) + assert storage["a"] is value1 + + value2 = object() + w_mod.setdictvalue_dont_introduce_cell(key, value2) + assert storage["a"] is value2 + + def test___import__not_a_cell(self): + # _frozen_importlib overrides __import__, which used to introduce a + # cell + w_dict = self.space.builtin.w_dict + storage = w_dict.get_strategy().unerase(w_dict.dstorage) + assert "Cell" not in repr(storage['__import__']) + + class AppTestModuleDict(object): def setup_class(cls): @@ -173,3 +198,4 @@ d[o] = 'baz' assert set(d) == set(['foo', 'λ', o]) assert "ObjectDictStrategy" in __pypy__.internal_repr(d) + From pypy.commits at gmail.com Tue Feb 19 02:45:15 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 23:45:15 -0800 (PST) Subject: [pypy-commit] pypy py3.6: redo 9387f96a5518 in two steps since file case changed (windows ...) Message-ID: <5c6bb40b.1c69fb81.33d77.ae05@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96082:2e2966805114 Date: 2019-02-19 09:40 +0200 http://bitbucket.org/pypy/pypy/changeset/2e2966805114/ Log: redo 9387f96a5518 in two steps since file case changed (windows ...) diff too long, truncating to 2000 out of 27878 lines diff --git a/lib-python/3/idlelib/AutoComplete.py b/lib-python/3/idlelib/AutoComplete.py deleted file mode 100644 --- a/lib-python/3/idlelib/AutoComplete.py +++ /dev/null @@ -1,232 +0,0 @@ -"""autocomplete.py - An IDLE extension for automatically completing names. - -This extension can complete either attribute names or file names. It can pop -a window with all available names, for the user to select from. -""" -import os -import string -import sys - -# These constants represent the two different types of completions. -# They must be defined here so autocomple_w can import them. -COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1) - -from idlelib import autocomplete_w -from idlelib.config import idleConf -from idlelib.hyperparser import HyperParser -import __main__ - -# This string includes all chars that may be in an identifier. -# TODO Update this here and elsewhere. -ID_CHARS = string.ascii_letters + string.digits + "_" - -SEPS = os.sep -if os.altsep: # e.g. '/' on Windows... - SEPS += os.altsep - - -class AutoComplete: - - menudefs = [ - ('edit', [ - ("Show Completions", "<>"), - ]) - ] - - popupwait = idleConf.GetOption("extensions", "AutoComplete", - "popupwait", type="int", default=0) - - def __init__(self, editwin=None): - self.editwin = editwin - if editwin is not None: # not in subprocess or test - self.text = editwin.text - self.autocompletewindow = None - # id of delayed call, and the index of the text insert when - # the delayed call was issued. If _delayed_completion_id is - # None, there is no delayed call. - self._delayed_completion_id = None - self._delayed_completion_index = None - - def _make_autocomplete_window(self): - return autocomplete_w.AutoCompleteWindow(self.text) - - def _remove_autocomplete_window(self, event=None): - if self.autocompletewindow: - self.autocompletewindow.hide_window() - self.autocompletewindow = None - - def force_open_completions_event(self, event): - """Happens when the user really wants to open a completion list, even - if a function call is needed. - """ - self.open_completions(True, False, True) - - def try_open_completions_event(self, event): - """Happens when it would be nice to open a completion list, but not - really necessary, for example after a dot, so function - calls won't be made. - """ - lastchar = self.text.get("insert-1c") - if lastchar == ".": - self._open_completions_later(False, False, False, - COMPLETE_ATTRIBUTES) - elif lastchar in SEPS: - self._open_completions_later(False, False, False, - COMPLETE_FILES) - - def autocomplete_event(self, event): - """Happens when the user wants to complete his word, and if necessary, - open a completion list after that (if there is more than one - completion) - """ - if hasattr(event, "mc_state") and event.mc_state or\ - not self.text.get("insert linestart", "insert").strip(): - # A modifier was pressed along with the tab or - # there is only previous whitespace on this line, so tab. - return None - if self.autocompletewindow and self.autocompletewindow.is_active(): - self.autocompletewindow.complete() - return "break" - else: - opened = self.open_completions(False, True, True) - return "break" if opened else None - - def _open_completions_later(self, *args): - self._delayed_completion_index = self.text.index("insert") - if self._delayed_completion_id is not None: - self.text.after_cancel(self._delayed_completion_id) - self._delayed_completion_id = \ - self.text.after(self.popupwait, self._delayed_open_completions, - *args) - - def _delayed_open_completions(self, *args): - self._delayed_completion_id = None - if self.text.index("insert") == self._delayed_completion_index: - self.open_completions(*args) - - def open_completions(self, evalfuncs, complete, userWantsWin, mode=None): - """Find the completions and create the AutoCompleteWindow. - Return True if successful (no syntax error or so found). - if complete is True, then if there's nothing to complete and no - start of completion, won't open completions and return False. - If mode is given, will open a completion list only in this mode. - """ - # Cancel another delayed call, if it exists. - if self._delayed_completion_id is not None: - self.text.after_cancel(self._delayed_completion_id) - self._delayed_completion_id = None - - hp = HyperParser(self.editwin, "insert") - curline = self.text.get("insert linestart", "insert") - i = j = len(curline) - if hp.is_in_string() and (not mode or mode==COMPLETE_FILES): - # Find the beginning of the string - # fetch_completions will look at the file system to determine whether the - # string value constitutes an actual file name - # XXX could consider raw strings here and unescape the string value if it's - # not raw. - self._remove_autocomplete_window() - mode = COMPLETE_FILES - # Find last separator or string start - while i and curline[i-1] not in "'\"" + SEPS: - i -= 1 - comp_start = curline[i:j] - j = i - # Find string start - while i and curline[i-1] not in "'\"": - i -= 1 - comp_what = curline[i:j] - elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES): - self._remove_autocomplete_window() - mode = COMPLETE_ATTRIBUTES - while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127): - i -= 1 - comp_start = curline[i:j] - if i and curline[i-1] == '.': - hp.set_index("insert-%dc" % (len(curline)-(i-1))) - comp_what = hp.get_expression() - if not comp_what or \ - (not evalfuncs and comp_what.find('(') != -1): - return None - else: - comp_what = "" - else: - return None - - if complete and not comp_what and not comp_start: - return None - comp_lists = self.fetch_completions(comp_what, mode) - if not comp_lists[0]: - return None - self.autocompletewindow = self._make_autocomplete_window() - return not self.autocompletewindow.show_window( - comp_lists, "insert-%dc" % len(comp_start), - complete, mode, userWantsWin) - - def fetch_completions(self, what, mode): - """Return a pair of lists of completions for something. The first list - is a sublist of the second. Both are sorted. - - If there is a Python subprocess, get the comp. list there. Otherwise, - either fetch_completions() is running in the subprocess itself or it - was called in an IDLE EditorWindow before any script had been run. - - The subprocess environment is that of the most recently run script. If - two unrelated modules are being edited some calltips in the current - module may be inoperative if the module was not the last to run. - """ - try: - rpcclt = self.editwin.flist.pyshell.interp.rpcclt - except: - rpcclt = None - if rpcclt: - return rpcclt.remotecall("exec", "get_the_completion_list", - (what, mode), {}) - else: - if mode == COMPLETE_ATTRIBUTES: - if what == "": - namespace = __main__.__dict__.copy() - namespace.update(__main__.__builtins__.__dict__) - bigl = eval("dir()", namespace) - bigl.sort() - if "__all__" in bigl: - smalll = sorted(eval("__all__", namespace)) - else: - smalll = [s for s in bigl if s[:1] != '_'] - else: - try: - entity = self.get_entity(what) - bigl = dir(entity) - bigl.sort() - if "__all__" in bigl: - smalll = sorted(entity.__all__) - else: - smalll = [s for s in bigl if s[:1] != '_'] - except: - return [], [] - - elif mode == COMPLETE_FILES: - if what == "": - what = "." - try: - expandedpath = os.path.expanduser(what) - bigl = os.listdir(expandedpath) - bigl.sort() - smalll = [s for s in bigl if s[:1] != '.'] - except OSError: - return [], [] - - if not smalll: - smalll = bigl - return smalll, bigl - - def get_entity(self, name): - """Lookup name in a namespace spanning sys.modules and __main.dict__""" - namespace = sys.modules.copy() - namespace.update(__main__.__dict__) - return eval(name, namespace) - - -if __name__ == '__main__': - from unittest import main - main('idlelib.idle_test.test_autocomplete', verbosity=2) diff --git a/lib-python/3/idlelib/AutoCompleteWindow.py b/lib-python/3/idlelib/AutoCompleteWindow.py deleted file mode 100644 --- a/lib-python/3/idlelib/AutoCompleteWindow.py +++ /dev/null @@ -1,416 +0,0 @@ -""" -An auto-completion window for IDLE, used by the AutoComplete extension -""" -from tkinter import * -from idlelib.MultiCall import MC_SHIFT -from idlelib.AutoComplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES - -HIDE_VIRTUAL_EVENT_NAME = "<>" -HIDE_SEQUENCES = ("", "") -KEYPRESS_VIRTUAL_EVENT_NAME = "<>" -# We need to bind event beyond so that the function will be called -# before the default specific IDLE function -KEYPRESS_SEQUENCES = ("", "", "", "", - "", "", "", "", - "", "") -KEYRELEASE_VIRTUAL_EVENT_NAME = "<>" -KEYRELEASE_SEQUENCE = "" -LISTUPDATE_SEQUENCE = "" -WINCONFIG_SEQUENCE = "" -DOUBLECLICK_SEQUENCE = "" - -class AutoCompleteWindow: - - def __init__(self, widget): - # The widget (Text) on which we place the AutoCompleteWindow - self.widget = widget - # The widgets we create - self.autocompletewindow = self.listbox = self.scrollbar = None - # The default foreground and background of a selection. Saved because - # they are changed to the regular colors of list items when the - # completion start is not a prefix of the selected completion - self.origselforeground = self.origselbackground = None - # The list of completions - self.completions = None - # A list with more completions, or None - self.morecompletions = None - # The completion mode. Either AutoComplete.COMPLETE_ATTRIBUTES or - # AutoComplete.COMPLETE_FILES - self.mode = None - # The current completion start, on the text box (a string) - self.start = None - # The index of the start of the completion - self.startindex = None - # The last typed start, used so that when the selection changes, - # the new start will be as close as possible to the last typed one. - self.lasttypedstart = None - # Do we have an indication that the user wants the completion window - # (for example, he clicked the list) - self.userwantswindow = None - # event ids - self.hideid = self.keypressid = self.listupdateid = self.winconfigid \ - = self.keyreleaseid = self.doubleclickid = None - # Flag set if last keypress was a tab - self.lastkey_was_tab = False - - def _change_start(self, newstart): - min_len = min(len(self.start), len(newstart)) - i = 0 - while i < min_len and self.start[i] == newstart[i]: - i += 1 - if i < len(self.start): - self.widget.delete("%s+%dc" % (self.startindex, i), - "%s+%dc" % (self.startindex, len(self.start))) - if i < len(newstart): - self.widget.insert("%s+%dc" % (self.startindex, i), - newstart[i:]) - self.start = newstart - - def _binary_search(self, s): - """Find the first index in self.completions where completions[i] is - greater or equal to s, or the last index if there is no such - one.""" - i = 0; j = len(self.completions) - while j > i: - m = (i + j) // 2 - if self.completions[m] >= s: - j = m - else: - i = m + 1 - return min(i, len(self.completions)-1) - - def _complete_string(self, s): - """Assuming that s is the prefix of a string in self.completions, - return the longest string which is a prefix of all the strings which - s is a prefix of them. If s is not a prefix of a string, return s.""" - first = self._binary_search(s) - if self.completions[first][:len(s)] != s: - # There is not even one completion which s is a prefix of. - return s - # Find the end of the range of completions where s is a prefix of. - i = first + 1 - j = len(self.completions) - while j > i: - m = (i + j) // 2 - if self.completions[m][:len(s)] != s: - j = m - else: - i = m + 1 - last = i-1 - - if first == last: # only one possible completion - return self.completions[first] - - # We should return the maximum prefix of first and last - first_comp = self.completions[first] - last_comp = self.completions[last] - min_len = min(len(first_comp), len(last_comp)) - i = len(s) - while i < min_len and first_comp[i] == last_comp[i]: - i += 1 - return first_comp[:i] - - def _selection_changed(self): - """Should be called when the selection of the Listbox has changed. - Updates the Listbox display and calls _change_start.""" - cursel = int(self.listbox.curselection()[0]) - - self.listbox.see(cursel) - - lts = self.lasttypedstart - selstart = self.completions[cursel] - if self._binary_search(lts) == cursel: - newstart = lts - else: - min_len = min(len(lts), len(selstart)) - i = 0 - while i < min_len and lts[i] == selstart[i]: - i += 1 - newstart = selstart[:i] - self._change_start(newstart) - - if self.completions[cursel][:len(self.start)] == self.start: - # start is a prefix of the selected completion - self.listbox.configure(selectbackground=self.origselbackground, - selectforeground=self.origselforeground) - else: - self.listbox.configure(selectbackground=self.listbox.cget("bg"), - selectforeground=self.listbox.cget("fg")) - # If there are more completions, show them, and call me again. - if self.morecompletions: - self.completions = self.morecompletions - self.morecompletions = None - self.listbox.delete(0, END) - for item in self.completions: - self.listbox.insert(END, item) - self.listbox.select_set(self._binary_search(self.start)) - self._selection_changed() - - def show_window(self, comp_lists, index, complete, mode, userWantsWin): - """Show the autocomplete list, bind events. - If complete is True, complete the text, and if there is exactly one - matching completion, don't open a list.""" - # Handle the start we already have - self.completions, self.morecompletions = comp_lists - self.mode = mode - self.startindex = self.widget.index(index) - self.start = self.widget.get(self.startindex, "insert") - if complete: - completed = self._complete_string(self.start) - start = self.start - self._change_start(completed) - i = self._binary_search(completed) - if self.completions[i] == completed and \ - (i == len(self.completions)-1 or - self.completions[i+1][:len(completed)] != completed): - # There is exactly one matching completion - return completed == start - self.userwantswindow = userWantsWin - self.lasttypedstart = self.start - - # Put widgets in place - self.autocompletewindow = acw = Toplevel(self.widget) - # Put it in a position so that it is not seen. - acw.wm_geometry("+10000+10000") - # Make it float - acw.wm_overrideredirect(1) - try: - # This command is only needed and available on Tk >= 8.4.0 for OSX - # Without it, call tips intrude on the typing process by grabbing - # the focus. - acw.tk.call("::tk::unsupported::MacWindowStyle", "style", acw._w, - "help", "noActivates") - except TclError: - pass - self.scrollbar = scrollbar = Scrollbar(acw, orient=VERTICAL) - self.listbox = listbox = Listbox(acw, yscrollcommand=scrollbar.set, - exportselection=False, bg="white") - for item in self.completions: - listbox.insert(END, item) - self.origselforeground = listbox.cget("selectforeground") - self.origselbackground = listbox.cget("selectbackground") - scrollbar.config(command=listbox.yview) - scrollbar.pack(side=RIGHT, fill=Y) - listbox.pack(side=LEFT, fill=BOTH, expand=True) - acw.lift() # work around bug in Tk 8.5.18+ (issue #24570) - - # Initialize the listbox selection - self.listbox.select_set(self._binary_search(self.start)) - self._selection_changed() - - # bind events - self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME, - self.hide_event) - for seq in HIDE_SEQUENCES: - self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq) - self.keypressid = self.widget.bind(KEYPRESS_VIRTUAL_EVENT_NAME, - self.keypress_event) - for seq in KEYPRESS_SEQUENCES: - self.widget.event_add(KEYPRESS_VIRTUAL_EVENT_NAME, seq) - self.keyreleaseid = self.widget.bind(KEYRELEASE_VIRTUAL_EVENT_NAME, - self.keyrelease_event) - self.widget.event_add(KEYRELEASE_VIRTUAL_EVENT_NAME,KEYRELEASE_SEQUENCE) - self.listupdateid = listbox.bind(LISTUPDATE_SEQUENCE, - self.listselect_event) - self.winconfigid = acw.bind(WINCONFIG_SEQUENCE, self.winconfig_event) - self.doubleclickid = listbox.bind(DOUBLECLICK_SEQUENCE, - self.doubleclick_event) - - def winconfig_event(self, event): - if not self.is_active(): - return - # Position the completion list window - text = self.widget - text.see(self.startindex) - x, y, cx, cy = text.bbox(self.startindex) - acw = self.autocompletewindow - acw_width, acw_height = acw.winfo_width(), acw.winfo_height() - text_width, text_height = text.winfo_width(), text.winfo_height() - new_x = text.winfo_rootx() + min(x, max(0, text_width - acw_width)) - new_y = text.winfo_rooty() + y - if (text_height - (y + cy) >= acw_height # enough height below - or y < acw_height): # not enough height above - # place acw below current line - new_y += cy - else: - # place acw above current line - new_y -= acw_height - acw.wm_geometry("+%d+%d" % (new_x, new_y)) - - def hide_event(self, event): - if not self.is_active(): - return - self.hide_window() - - def listselect_event(self, event): - if not self.is_active(): - return - self.userwantswindow = True - cursel = int(self.listbox.curselection()[0]) - self._change_start(self.completions[cursel]) - - def doubleclick_event(self, event): - # Put the selected completion in the text, and close the list - cursel = int(self.listbox.curselection()[0]) - self._change_start(self.completions[cursel]) - self.hide_window() - - def keypress_event(self, event): - if not self.is_active(): - return - keysym = event.keysym - if hasattr(event, "mc_state"): - state = event.mc_state - else: - state = 0 - if keysym != "Tab": - self.lastkey_was_tab = False - if (len(keysym) == 1 or keysym in ("underscore", "BackSpace") - or (self.mode == COMPLETE_FILES and keysym in - ("period", "minus"))) \ - and not (state & ~MC_SHIFT): - # Normal editing of text - if len(keysym) == 1: - self._change_start(self.start + keysym) - elif keysym == "underscore": - self._change_start(self.start + '_') - elif keysym == "period": - self._change_start(self.start + '.') - elif keysym == "minus": - self._change_start(self.start + '-') - else: - # keysym == "BackSpace" - if len(self.start) == 0: - self.hide_window() - return - self._change_start(self.start[:-1]) - self.lasttypedstart = self.start - self.listbox.select_clear(0, int(self.listbox.curselection()[0])) - self.listbox.select_set(self._binary_search(self.start)) - self._selection_changed() - return "break" - - elif keysym == "Return": - self.hide_window() - return - - elif (self.mode == COMPLETE_ATTRIBUTES and keysym in - ("period", "space", "parenleft", "parenright", "bracketleft", - "bracketright")) or \ - (self.mode == COMPLETE_FILES and keysym in - ("slash", "backslash", "quotedbl", "apostrophe")) \ - and not (state & ~MC_SHIFT): - # If start is a prefix of the selection, but is not '' when - # completing file names, put the whole - # selected completion. Anyway, close the list. - cursel = int(self.listbox.curselection()[0]) - if self.completions[cursel][:len(self.start)] == self.start \ - and (self.mode == COMPLETE_ATTRIBUTES or self.start): - self._change_start(self.completions[cursel]) - self.hide_window() - return - - elif keysym in ("Home", "End", "Prior", "Next", "Up", "Down") and \ - not state: - # Move the selection in the listbox - self.userwantswindow = True - cursel = int(self.listbox.curselection()[0]) - if keysym == "Home": - newsel = 0 - elif keysym == "End": - newsel = len(self.completions)-1 - elif keysym in ("Prior", "Next"): - jump = self.listbox.nearest(self.listbox.winfo_height()) - \ - self.listbox.nearest(0) - if keysym == "Prior": - newsel = max(0, cursel-jump) - else: - assert keysym == "Next" - newsel = min(len(self.completions)-1, cursel+jump) - elif keysym == "Up": - newsel = max(0, cursel-1) - else: - assert keysym == "Down" - newsel = min(len(self.completions)-1, cursel+1) - self.listbox.select_clear(cursel) - self.listbox.select_set(newsel) - self._selection_changed() - self._change_start(self.completions[newsel]) - return "break" - - elif (keysym == "Tab" and not state): - if self.lastkey_was_tab: - # two tabs in a row; insert current selection and close acw - cursel = int(self.listbox.curselection()[0]) - self._change_start(self.completions[cursel]) - self.hide_window() - return "break" - else: - # first tab; let AutoComplete handle the completion - self.userwantswindow = True - self.lastkey_was_tab = True - return - - elif any(s in keysym for s in ("Shift", "Control", "Alt", - "Meta", "Command", "Option")): - # A modifier key, so ignore - return - - elif event.char and event.char >= ' ': - # Regular character with a non-length-1 keycode - self._change_start(self.start + event.char) - self.lasttypedstart = self.start - self.listbox.select_clear(0, int(self.listbox.curselection()[0])) - self.listbox.select_set(self._binary_search(self.start)) - self._selection_changed() - return "break" - - else: - # Unknown event, close the window and let it through. - self.hide_window() - return - - def keyrelease_event(self, event): - if not self.is_active(): - return - if self.widget.index("insert") != \ - self.widget.index("%s+%dc" % (self.startindex, len(self.start))): - # If we didn't catch an event which moved the insert, close window - self.hide_window() - - def is_active(self): - return self.autocompletewindow is not None - - def complete(self): - self._change_start(self._complete_string(self.start)) - # The selection doesn't change. - - def hide_window(self): - if not self.is_active(): - return - - # unbind events - for seq in HIDE_SEQUENCES: - self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq) - self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid) - self.hideid = None - for seq in KEYPRESS_SEQUENCES: - self.widget.event_delete(KEYPRESS_VIRTUAL_EVENT_NAME, seq) - self.widget.unbind(KEYPRESS_VIRTUAL_EVENT_NAME, self.keypressid) - self.keypressid = None - self.widget.event_delete(KEYRELEASE_VIRTUAL_EVENT_NAME, - KEYRELEASE_SEQUENCE) - self.widget.unbind(KEYRELEASE_VIRTUAL_EVENT_NAME, self.keyreleaseid) - self.keyreleaseid = None - self.listbox.unbind(LISTUPDATE_SEQUENCE, self.listupdateid) - self.listupdateid = None - self.autocompletewindow.unbind(WINCONFIG_SEQUENCE, self.winconfigid) - self.winconfigid = None - - # destroy widgets - self.scrollbar.destroy() - self.scrollbar = None - self.listbox.destroy() - self.listbox = None - self.autocompletewindow.destroy() - self.autocompletewindow = None diff --git a/lib-python/3/idlelib/AutoExpand.py b/lib-python/3/idlelib/AutoExpand.py deleted file mode 100644 --- a/lib-python/3/idlelib/AutoExpand.py +++ /dev/null @@ -1,105 +0,0 @@ -'''Complete the current word before the cursor with words in the editor. - -Each menu selection or shortcut key selection replaces the word with a -different word with the same prefix. The search for matches begins -before the target and moves toward the top of the editor. It then starts -after the cursor and moves down. It then returns to the original word and -the cycle starts again. - -Changing the current text line or leaving the cursor in a different -place before requesting the next selection causes AutoExpand to reset -its state. - -This is an extension file and there is only one instance of AutoExpand. -''' -import re -import string - -###$ event <> -###$ win -###$ unix - -class AutoExpand: - - menudefs = [ - ('edit', [ - ('E_xpand Word', '<>'), - ]), - ] - - wordchars = string.ascii_letters + string.digits + "_" - - def __init__(self, editwin): - self.text = editwin.text - self.bell = self.text.bell - self.state = None - - def expand_word_event(self, event): - "Replace the current word with the next expansion." - curinsert = self.text.index("insert") - curline = self.text.get("insert linestart", "insert lineend") - if not self.state: - words = self.getwords() - index = 0 - else: - words, index, insert, line = self.state - if insert != curinsert or line != curline: - words = self.getwords() - index = 0 - if not words: - self.bell() - return "break" - word = self.getprevword() - self.text.delete("insert - %d chars" % len(word), "insert") - newword = words[index] - index = (index + 1) % len(words) - if index == 0: - self.bell() # Warn we cycled around - self.text.insert("insert", newword) - curinsert = self.text.index("insert") - curline = self.text.get("insert linestart", "insert lineend") - self.state = words, index, curinsert, curline - return "break" - - def getwords(self): - "Return a list of words that match the prefix before the cursor." - word = self.getprevword() - if not word: - return [] - before = self.text.get("1.0", "insert wordstart") - wbefore = re.findall(r"\b" + word + r"\w+\b", before) - del before - after = self.text.get("insert wordend", "end") - wafter = re.findall(r"\b" + word + r"\w+\b", after) - del after - if not wbefore and not wafter: - return [] - words = [] - dict = {} - # search backwards through words before - wbefore.reverse() - for w in wbefore: - if dict.get(w): - continue - words.append(w) - dict[w] = w - # search onwards through words after - for w in wafter: - if dict.get(w): - continue - words.append(w) - dict[w] = w - words.append(word) - return words - - def getprevword(self): - "Return the word prefix before the cursor." - line = self.text.get("insert linestart", "insert") - i = len(line) - while i > 0 and line[i-1] in self.wordchars: - i = i-1 - return line[i:] - -if __name__ == '__main__': - import unittest - unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2) diff --git a/lib-python/3/idlelib/Bindings.py b/lib-python/3/idlelib/Bindings.py deleted file mode 100644 --- a/lib-python/3/idlelib/Bindings.py +++ /dev/null @@ -1,96 +0,0 @@ -"""Define the menu contents, hotkeys, and event bindings. - -There is additional configuration information in the EditorWindow class (and -subclasses): the menus are created there based on the menu_specs (class) -variable, and menus not created are silently skipped in the code here. This -makes it possible, for example, to define a Debug menu which is only present in -the PythonShell window, and a Format menu which is only present in the Editor -windows. - -""" -from importlib.util import find_spec - -from idlelib.configHandler import idleConf - -# Warning: menudefs is altered in macosxSupport.overrideRootMenu() -# after it is determined that an OS X Aqua Tk is in use, -# which cannot be done until after Tk() is first called. -# Do not alter the 'file', 'options', or 'help' cascades here -# without altering overrideRootMenu() as well. -# TODO: Make this more robust - -menudefs = [ - # underscore prefixes character to underscore - ('file', [ - ('_New File', '<>'), - ('_Open...', '<>'), - ('Open _Module...', '<>'), - ('Class _Browser', '<>'), - ('_Path Browser', '<>'), - None, - ('_Save', '<>'), - ('Save _As...', '<>'), - ('Save Cop_y As...', '<>'), - None, - ('Prin_t Window', '<>'), - None, - ('_Close', '<>'), - ('E_xit', '<>'), - ]), - ('edit', [ - ('_Undo', '<>'), - ('_Redo', '<>'), - None, - ('Cu_t', '<>'), - ('_Copy', '<>'), - ('_Paste', '<>'), - ('Select _All', '<>'), - None, - ('_Find...', '<>'), - ('Find A_gain', '<>'), - ('Find _Selection', '<>'), - ('Find in Files...', '<>'), - ('R_eplace...', '<>'), - ('Go to _Line', '<>'), - ]), -('format', [ - ('_Indent Region', '<>'), - ('_Dedent Region', '<>'), - ('Comment _Out Region', '<>'), - ('U_ncomment Region', '<>'), - ('Tabify Region', '<>'), - ('Untabify Region', '<>'), - ('Toggle Tabs', '<>'), - ('New Indent Width', '<>'), - ]), - ('run', [ - ('Python Shell', '<>'), - ]), - ('shell', [ - ('_View Last Restart', '<>'), - ('_Restart Shell', '<>'), - None, - ('_Interrupt Execution', '<>'), - ]), - ('debug', [ - ('_Go to File/Line', '<>'), - ('!_Debugger', '<>'), - ('_Stack Viewer', '<>'), - ('!_Auto-open Stack Viewer', '<>'), - ]), - ('options', [ - ('Configure _IDLE', '<>'), - None, - ]), - ('help', [ - ('_About IDLE', '<>'), - None, - ('_IDLE Help', '<>'), - ('Python _Docs', '<>'), - ]), -] - -if find_spec('turtledemo'): - menudefs[-1][1].append(('Turtle Demo', '<>')) - -default_keydefs = idleConf.GetCurrentKeySet() diff --git a/lib-python/3/idlelib/CallTipWindow.py b/lib-python/3/idlelib/CallTipWindow.py deleted file mode 100644 --- a/lib-python/3/idlelib/CallTipWindow.py +++ /dev/null @@ -1,161 +0,0 @@ -"""A CallTip window class for Tkinter/IDLE. - -After ToolTip.py, which uses ideas gleaned from PySol -Used by the CallTips IDLE extension. -""" -from tkinter import Toplevel, Label, LEFT, SOLID, TclError - -HIDE_VIRTUAL_EVENT_NAME = "<>" -HIDE_SEQUENCES = ("", "") -CHECKHIDE_VIRTUAL_EVENT_NAME = "<>" -CHECKHIDE_SEQUENCES = ("", "") -CHECKHIDE_TIME = 100 # milliseconds - -MARK_RIGHT = "calltipwindowregion_right" - -class CallTip: - - def __init__(self, widget): - self.widget = widget - self.tipwindow = self.label = None - self.parenline = self.parencol = None - self.lastline = None - self.hideid = self.checkhideid = None - self.checkhide_after_id = None - - def position_window(self): - """Check if needs to reposition the window, and if so - do it.""" - curline = int(self.widget.index("insert").split('.')[0]) - if curline == self.lastline: - return - self.lastline = curline - self.widget.see("insert") - if curline == self.parenline: - box = self.widget.bbox("%d.%d" % (self.parenline, - self.parencol)) - else: - box = self.widget.bbox("%d.0" % curline) - if not box: - box = list(self.widget.bbox("insert")) - # align to left of window - box[0] = 0 - box[2] = 0 - x = box[0] + self.widget.winfo_rootx() + 2 - y = box[1] + box[3] + self.widget.winfo_rooty() - self.tipwindow.wm_geometry("+%d+%d" % (x, y)) - - def showtip(self, text, parenleft, parenright): - """Show the calltip, bind events which will close it and reposition it. - """ - # Only called in CallTips, where lines are truncated - self.text = text - if self.tipwindow or not self.text: - return - - self.widget.mark_set(MARK_RIGHT, parenright) - self.parenline, self.parencol = map( - int, self.widget.index(parenleft).split(".")) - - self.tipwindow = tw = Toplevel(self.widget) - self.position_window() - # remove border on calltip window - tw.wm_overrideredirect(1) - try: - # This command is only needed and available on Tk >= 8.4.0 for OSX - # Without it, call tips intrude on the typing process by grabbing - # the focus. - tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w, - "help", "noActivates") - except TclError: - pass - self.label = Label(tw, text=self.text, justify=LEFT, - background="#ffffe0", relief=SOLID, borderwidth=1, - font = self.widget['font']) - self.label.pack() - tw.lift() # work around bug in Tk 8.5.18+ (issue #24570) - - self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME, - self.checkhide_event) - for seq in CHECKHIDE_SEQUENCES: - self.widget.event_add(CHECKHIDE_VIRTUAL_EVENT_NAME, seq) - self.widget.after(CHECKHIDE_TIME, self.checkhide_event) - self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME, - self.hide_event) - for seq in HIDE_SEQUENCES: - self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq) - - def checkhide_event(self, event=None): - if not self.tipwindow: - # If the event was triggered by the same event that unbinded - # this function, the function will be called nevertheless, - # so do nothing in this case. - return - curline, curcol = map(int, self.widget.index("insert").split('.')) - if curline < self.parenline or \ - (curline == self.parenline and curcol <= self.parencol) or \ - self.widget.compare("insert", ">", MARK_RIGHT): - self.hidetip() - else: - self.position_window() - if self.checkhide_after_id is not None: - self.widget.after_cancel(self.checkhide_after_id) - self.checkhide_after_id = \ - self.widget.after(CHECKHIDE_TIME, self.checkhide_event) - - def hide_event(self, event): - if not self.tipwindow: - # See the explanation in checkhide_event. - return - self.hidetip() - - def hidetip(self): - if not self.tipwindow: - return - - for seq in CHECKHIDE_SEQUENCES: - self.widget.event_delete(CHECKHIDE_VIRTUAL_EVENT_NAME, seq) - self.widget.unbind(CHECKHIDE_VIRTUAL_EVENT_NAME, self.checkhideid) - self.checkhideid = None - for seq in HIDE_SEQUENCES: - self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq) - self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid) - self.hideid = None - - self.label.destroy() - self.label = None - self.tipwindow.destroy() - self.tipwindow = None - - self.widget.mark_unset(MARK_RIGHT) - self.parenline = self.parencol = self.lastline = None - - def is_active(self): - return bool(self.tipwindow) - - -def _calltip_window(parent): # htest # - from tkinter import Toplevel, Text, LEFT, BOTH - - top = Toplevel(parent) - top.title("Test calltips") - top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200, - parent.winfo_rooty() + 150)) - text = Text(top) - text.pack(side=LEFT, fill=BOTH, expand=1) - text.insert("insert", "string.split") - top.update() - calltip = CallTip(text) - - def calltip_show(event): - calltip.showtip("(s=Hello world)", "insert", "end") - def calltip_hide(event): - calltip.hidetip() - text.event_add("<>", "(") - text.event_add("<>", ")") - text.bind("<>", calltip_show) - text.bind("<>", calltip_hide) - text.focus_set() - -if __name__=='__main__': - from idlelib.idle_test.htest import run - run(_calltip_window) diff --git a/lib-python/3/idlelib/CallTips.py b/lib-python/3/idlelib/CallTips.py deleted file mode 100644 --- a/lib-python/3/idlelib/CallTips.py +++ /dev/null @@ -1,185 +0,0 @@ -"""calltips.py - An IDLE Extension to Jog Your Memory - -Call Tips are floating windows which display function, class, and method -parameter and docstring information when you type an opening parenthesis, and -which disappear when you type a closing parenthesis. - -""" -import inspect -import re -import sys -import textwrap -import types - -from idlelib import calltip_w -from idlelib.hyperparser import HyperParser -import __main__ - -class CallTips: - - menudefs = [ - ('edit', [ - ("Show call tip", "<>"), - ]) - ] - - def __init__(self, editwin=None): - if editwin is None: # subprocess and test - self.editwin = None - else: - self.editwin = editwin - self.text = editwin.text - self.active_calltip = None - self._calltip_window = self._make_tk_calltip_window - - def close(self): - self._calltip_window = None - - def _make_tk_calltip_window(self): - # See __init__ for usage - return calltip_w.CallTip(self.text) - - def _remove_calltip_window(self, event=None): - if self.active_calltip: - self.active_calltip.hidetip() - self.active_calltip = None - - def force_open_calltip_event(self, event): - "The user selected the menu entry or hotkey, open the tip." - self.open_calltip(True) - - def try_open_calltip_event(self, event): - """Happens when it would be nice to open a CallTip, but not really - necessary, for example after an opening bracket, so function calls - won't be made. - """ - self.open_calltip(False) - - def refresh_calltip_event(self, event): - if self.active_calltip and self.active_calltip.is_active(): - self.open_calltip(False) - - def open_calltip(self, evalfuncs): - self._remove_calltip_window() - - hp = HyperParser(self.editwin, "insert") - sur_paren = hp.get_surrounding_brackets('(') - if not sur_paren: - return - hp.set_index(sur_paren[0]) - expression = hp.get_expression() - if not expression: - return - if not evalfuncs and (expression.find('(') != -1): - return - argspec = self.fetch_tip(expression) - if not argspec: - return - self.active_calltip = self._calltip_window() - self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1]) - - def fetch_tip(self, expression): - """Return the argument list and docstring of a function or class. - - If there is a Python subprocess, get the calltip there. Otherwise, - either this fetch_tip() is running in the subprocess or it was - called in an IDLE running without the subprocess. - - The subprocess environment is that of the most recently run script. If - two unrelated modules are being edited some calltips in the current - module may be inoperative if the module was not the last to run. - - To find methods, fetch_tip must be fed a fully qualified name. - - """ - try: - rpcclt = self.editwin.flist.pyshell.interp.rpcclt - except AttributeError: - rpcclt = None - if rpcclt: - return rpcclt.remotecall("exec", "get_the_calltip", - (expression,), {}) - else: - return get_argspec(get_entity(expression)) - -def get_entity(expression): - """Return the object corresponding to expression evaluated - in a namespace spanning sys.modules and __main.dict__. - """ - if expression: - namespace = sys.modules.copy() - namespace.update(__main__.__dict__) - try: - return eval(expression, namespace) - except BaseException: - # An uncaught exception closes idle, and eval can raise any - # exception, especially if user classes are involved. - return None - -# The following are used in get_argspec and some in tests -_MAX_COLS = 85 -_MAX_LINES = 5 # enough for bytes -_INDENT = ' '*4 # for wrapped signatures -_first_param = re.compile(r'(?<=\()\w*\,?\s*') -_default_callable_argspec = "See source or doc" - -def _is_user_method(ob): - """Detect user methods on PyPy""" - return (isinstance(ob, types.MethodType) and - isinstance(ob.__code__, types.CodeType)) - -def _is_user_function(ob): - """Detect user methods on PyPy""" - return (isinstance(ob, types.FunctionType) and - isinstance(ob.__code__, types.CodeType)) - -def get_argspec(ob): - '''Return a string describing the signature of a callable object, or ''. - - For Python-coded functions and methods, the first line is introspected. - Delete 'self' parameter for classes (.__init__) and bound methods. - The next lines are the first lines of the doc string up to the first - empty line or _MAX_LINES. For builtins, this typically includes - the arguments in addition to the return value. - ''' - argspec = "" - try: - ob_call = ob.__call__ - except BaseException: - return argspec - if isinstance(ob, type): - fob = ob.__init__ - elif _is_user_method(ob_call): - fob = ob_call - else: - fob = ob - if (isinstance(fob, (types.FunctionType, types.MethodType)) and - hasattr(fob.__code__, 'co_code')): # PyPy: not on - argspec = inspect.formatargspec(*inspect.getfullargspec(fob)) - if (_is_user_method(ob) or _is_user_method(ob_call) or - (isinstance(ob, type) and _is_user_function(fob))): - argspec = _first_param.sub("", argspec) - - lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT) - if len(argspec) > _MAX_COLS else [argspec] if argspec else []) - - if _is_user_method(ob_call): - doc = ob_call.__doc__ - else: - doc = getattr(ob, "__doc__", "") - if doc: - for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]: - line = line.strip() - if not line: - break - if len(line) > _MAX_COLS: - line = line[: _MAX_COLS - 3] + '...' - lines.append(line) - argspec = '\n'.join(lines) - if not argspec: - argspec = _default_callable_argspec - return argspec - -if __name__ == '__main__': - from unittest import main - main('idlelib.idle_test.test_calltips', verbosity=2) diff --git a/lib-python/3/idlelib/ChangeLog b/lib-python/3/idlelib/ChangeLog --- a/lib-python/3/idlelib/ChangeLog +++ b/lib-python/3/idlelib/ChangeLog @@ -27,9 +27,9 @@ * INSTALLATION, setup.py: INSTALLATION: Remove the coexist.patch instructions - + **************** setup.py: - + Remove the idles script, add some words on IDLE Fork to the long_description, and clean up some line spacing. @@ -42,30 +42,30 @@ * PyShell.py, idle, idles: Implement idle command interface as suggested by GvR [idle-dev] 16 July **************** PyShell: Added functionality: - + usage: idle.py [-c command] [-d] [-i] [-r script] [-s] [-t title] [arg] ... - + idle file(s) (without options) edit the file(s) - + -c cmd run the command in a shell -d enable the debugger -i open an interactive shell -i file(s) open a shell and also an editor window for each file -r script run a file as a script in a shell -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else -t title set title of shell window - + Remaining arguments are applied to the command (-c) or script (-r). - + ****************** idles: Removed the idles script, not needed - + ****************** idle: Removed the IdleConf references, not required anymore 2001-07-16 17:08 kbk * INSTALLATION, coexist.patch: Added installation instructions. - + Added a patch which modifies idlefork so that it can co-exist with "official" IDLE in the site-packages directory. This patch is not necessary if only idlefork IDLE is installed. See INSTALLATION for @@ -74,7 +74,7 @@ 2001-07-16 15:50 kbk * idles: Add a script "idles" which opens a Python Shell window. - + The default behaviour of idlefork idle is to open an editor window instead of a shell. Complex expressions may be run in a fresh environment by selecting "run". There are times, however, when a @@ -90,7 +90,7 @@ * PyShell.py, setup.py: Add a script "idles" which opens a Python Shell window. - + The default behaviour of idlefork idle is to open an editor window instead of a shell. Complex expressions may be run in a fresh environment by selecting "run". There are times, however, when a @@ -110,13 +110,13 @@ * setup.py: Installing Idle to site-packages via Distutils does not copy the Idle help.txt file. - + Ref SF Python Patch 422471 2001-07-14 15:26 kbk * keydefs.py: py-cvs-2001_07_13 (Rev 1.3) merge - + "Make copy, cut and paste events case insensitive. Reported by Patrick K. O'Brien on idle-dev. (Should other bindings follow suit?)" --GvR @@ -124,7 +124,7 @@ 2001-07-14 15:21 kbk * idle.py: py-cvs-2001_07_13 (Rev 1.4) merge - + "Move the action of loading the configuration to the IdleConf module rather than the idle.py script. This has advantages and disadvantages; the biggest advantage being that we can more easily @@ -133,21 +133,21 @@ 2001-07-14 15:18 kbk * extend.txt: py-cvs-2001_07_13 (Rev 1.4) merge - + "Quick update to the extension mechanism (extend.py is gone, long live config.txt)" --GvR 2001-07-14 15:15 kbk * StackViewer.py: py-cvs-2001_07_13 (Rev 1.16) merge - + "Refactored, with some future plans in mind. This now uses the new gotofileline() method defined in FileList.py" --GvR 2001-07-14 15:10 kbk * PyShell.py: py-cvs-2001_07_13 (Rev 1.34) merge - + "Amazing. A very subtle change in policy in descr-branch actually found a bug here. Here's the deal: Class PyShell derives from class OutputWindow. Method PyShell.close() wants to invoke its @@ -166,19 +166,19 @@ 2001-07-14 14:59 kbk * PyParse.py: py-cvs-2001_07_13 (Rel 1.9) merge - + "Taught IDLE's autoident parser that "yield" is a keyword that begins a stmt. Along w/ the preceding change to keyword.py, making all this work w/ a future-stmt just looks harder and harder." --tim_one - + (From Rel 1.8: "Hack to make this still work with Python 1.5.2. ;-( " --fdrake) 2001-07-14 14:51 kbk * IdleConf.py: py-cvs-2001_07_13 (Rel 1.7) merge - + "Move the action of loading the configuration to the IdleConf module rather than the idle.py script. This has advantages and disadvantages; the biggest advantage being that we can more easily @@ -187,11 +187,11 @@ 2001-07-14 14:45 kbk * FileList.py: py-cvs-2000_07_13 (Rev 1.9) merge - + "Delete goodname() method, which is unused. Add gotofileline(), a convenience method which I intend to use in a variant. Rename test() to _test()." --GvR - + This was an interesting merge. The join completely missed removing goodname(), which was adjacent, but outside of, a small conflict. I only caught it by comparing the 1.1.3.2/1.1.3.3 diff. CVS ain't @@ -245,13 +245,13 @@ 2001-07-14 10:13 kbk * PyShell.py: cvs-py-rel2_1 (Rev 1.29 - 1.33) merge - + Merged the following py-cvs revs without conflict: 1.29 Reduce copyright text output at startup 1.30 Delay setting sys.args until Tkinter is fully initialized 1.31 Whitespace normalization 1.32 Turn syntax warning into error when interactive 1.33 Fix warning initialization bug - + Note that module is extensively modified wrt py-cvs 2001-07-14 06:33 kbk @@ -317,14 +317,14 @@ 2001-07-13 13:35 kbk * EditorWindow.py: py-cvs-rel2_1 (Rev 1.33 - 1.37) merge - + VP IDLE version depended on VP's ExecBinding.py and spawn.py to get the path to the Windows Doc directory (relative to python.exe). Removed this conflicting code in favor of py-cvs updates which on Windows use a hard coded path relative to the location of this module. py-cvs updates include support for webbrowser.py. Module still has BrowserControl.py for 1.5.2 support. - + At this point, the differences wrt py-cvs relate to menu functionality. @@ -1194,7 +1194,7 @@ ====================================================================== Python release 1.5.2b2, IDLE version 0.3 ====================================================================== - + Wed Feb 17 22:47:41 1999 Guido van Rossum * NEWS.txt: News in 0.3. @@ -1330,7 +1330,7 @@ ====================================================================== Python release 1.5.2b1, IDLE version 0.2 ====================================================================== - + Fri Jan 8 17:26:02 1999 Guido van Rossum * README.txt, NEWS.txt: What's new in this release. diff --git a/lib-python/3/idlelib/ClassBrowser.py b/lib-python/3/idlelib/ClassBrowser.py deleted file mode 100644 --- a/lib-python/3/idlelib/ClassBrowser.py +++ /dev/null @@ -1,236 +0,0 @@ -"""Class browser. - -XXX TO DO: - -- reparse when source changed (maybe just a button would be OK?) - (or recheck on window popup) -- add popup menu with more options (e.g. doc strings, base classes, imports) -- show function argument list? (have to do pattern matching on source) -- should the classes and methods lists also be in the module's menu bar? -- add base classes to class browser tree -""" - -import os -import sys -import pyclbr - -from idlelib import PyShell -from idlelib.WindowList import ListedToplevel -from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas -from idlelib.configHandler import idleConf - -file_open = None # Method...Item and Class...Item use this. -# Normally PyShell.flist.open, but there is no PyShell.flist for htest. - -class ClassBrowser: - - def __init__(self, flist, name, path, _htest=False): - # XXX This API should change, if the file doesn't end in ".py" - # XXX the code here is bogus! - """ - _htest - bool, change box when location running htest. - """ - global file_open - if not _htest: - file_open = PyShell.flist.open - self.name = name - self.file = os.path.join(path[0], self.name + ".py") - self._htest = _htest - self.init(flist) - - def close(self, event=None): - self.top.destroy() - self.node.destroy() - - def init(self, flist): - self.flist = flist - # reset pyclbr - pyclbr._modules.clear() - # create top - self.top = top = ListedToplevel(flist.root) - top.protocol("WM_DELETE_WINDOW", self.close) - top.bind("", self.close) - if self._htest: # place dialog below parent if running htest - top.geometry("+%d+%d" % - (flist.root.winfo_rootx(), flist.root.winfo_rooty() + 200)) - self.settitle() - top.focus_set() - # create scrolled canvas - theme = idleConf.CurrentTheme() - background = idleConf.GetHighlight(theme, 'normal')['background'] - sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1) - sc.frame.pack(expand=1, fill="both") - item = self.rootnode() - self.node = node = TreeNode(sc.canvas, None, item) - node.update() - node.expand() - - def settitle(self): - self.top.wm_title("Class Browser - " + self.name) - self.top.wm_iconname("Class Browser") - - def rootnode(self): - return ModuleBrowserTreeItem(self.file) - -class ModuleBrowserTreeItem(TreeItem): - - def __init__(self, file): - self.file = file - - def GetText(self): - return os.path.basename(self.file) - - def GetIconName(self): - return "python" - - def GetSubList(self): - sublist = [] - for name in self.listclasses(): - item = ClassBrowserTreeItem(name, self.classes, self.file) - sublist.append(item) - return sublist - - def OnDoubleClick(self): - if os.path.normcase(self.file[-3:]) != ".py": - return - if not os.path.exists(self.file): - return - PyShell.flist.open(self.file) - - def IsExpandable(self): - return os.path.normcase(self.file[-3:]) == ".py" - - def listclasses(self): - dir, file = os.path.split(self.file) - name, ext = os.path.splitext(file) - if os.path.normcase(ext) != ".py": - return [] - try: - dict = pyclbr.readmodule_ex(name, [dir] + sys.path) - except ImportError: - return [] - items = [] - self.classes = {} - for key, cl in dict.items(): - if cl.module == name: - s = key - if hasattr(cl, 'super') and cl.super: - supers = [] - for sup in cl.super: - if type(sup) is type(''): - sname = sup - else: - sname = sup.name - if sup.module != cl.module: - sname = "%s.%s" % (sup.module, sname) - supers.append(sname) - s = s + "(%s)" % ", ".join(supers) - items.append((cl.lineno, s)) - self.classes[s] = cl - items.sort() - list = [] - for item, s in items: - list.append(s) - return list - -class ClassBrowserTreeItem(TreeItem): - - def __init__(self, name, classes, file): - self.name = name - self.classes = classes - self.file = file - try: - self.cl = self.classes[self.name] - except (IndexError, KeyError): - self.cl = None - self.isfunction = isinstance(self.cl, pyclbr.Function) - - def GetText(self): - if self.isfunction: - return "def " + self.name + "(...)" - else: - return "class " + self.name - - def GetIconName(self): - if self.isfunction: - return "python" - else: - return "folder" - - def IsExpandable(self): - if self.cl: - try: - return not not self.cl.methods - except AttributeError: - return False - - def GetSubList(self): - if not self.cl: - return [] - sublist = [] - for name in self.listmethods(): - item = MethodBrowserTreeItem(name, self.cl, self.file) - sublist.append(item) - return sublist - - def OnDoubleClick(self): - if not os.path.exists(self.file): - return - edit = file_open(self.file) - if hasattr(self.cl, 'lineno'): - lineno = self.cl.lineno - edit.gotoline(lineno) - - def listmethods(self): - if not self.cl: - return [] - items = [] - for name, lineno in self.cl.methods.items(): - items.append((lineno, name)) - items.sort() - list = [] - for item, name in items: - list.append(name) - return list - -class MethodBrowserTreeItem(TreeItem): - - def __init__(self, name, cl, file): - self.name = name - self.cl = cl - self.file = file - - def GetText(self): - return "def " + self.name + "(...)" - - def GetIconName(self): - return "python" # XXX - - def IsExpandable(self): - return 0 - - def OnDoubleClick(self): - if not os.path.exists(self.file): - return - edit = file_open(self.file) - edit.gotoline(self.cl.methods[self.name]) - -def _class_browser(parent): #Wrapper for htest - try: - file = __file__ - except NameError: - file = sys.argv[0] - if sys.argv[1:]: - file = sys.argv[1] - else: - file = sys.argv[0] - dir, file = os.path.split(file) - name = os.path.splitext(file)[0] - flist = PyShell.PyShellFileList(parent) - global file_open - file_open = flist.open - ClassBrowser(flist, name, [dir], _htest=True) - -if __name__ == "__main__": - from idlelib.idle_test.htest import run - run(_class_browser) diff --git a/lib-python/3/idlelib/CodeContext.py b/lib-python/3/idlelib/CodeContext.py deleted file mode 100644 --- a/lib-python/3/idlelib/CodeContext.py +++ /dev/null @@ -1,178 +0,0 @@ -"""codecontext - Extension to display the block context above the edit window - -Once code has scrolled off the top of a window, it can be difficult to -determine which block you are in. This extension implements a pane at the top -of each IDLE edit window which provides block structure hints. These hints are -the lines which contain the block opening keywords, e.g. 'if', for the -enclosing block. The number of hint lines is determined by the numlines -variable in the codecontext section of config-extensions.def. Lines which do -not open blocks are not shown in the context hints pane. - -""" -import re -from sys import maxsize as INFINITY - -import tkinter -from tkinter.constants import TOP, LEFT, X, W, SUNKEN - -from idlelib.config import idleConf - -BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for", - "if", "try", "while", "with"} -UPDATEINTERVAL = 100 # millisec -FONTUPDATEINTERVAL = 1000 # millisec - -getspacesfirstword =\ - lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups() - -class CodeContext: - menudefs = [('options', [('!Code Conte_xt', '<>')])] - context_depth = idleConf.GetOption("extensions", "CodeContext", - "numlines", type="int", default=3) - bgcolor = idleConf.GetOption("extensions", "CodeContext", - "bgcolor", type="str", default="LightGray") - fgcolor = idleConf.GetOption("extensions", "CodeContext", - "fgcolor", type="str", default="Black") - def __init__(self, editwin): - self.editwin = editwin - self.text = editwin.text - self.textfont = self.text["font"] - self.label = None - # self.info is a list of (line number, indent level, line text, block - # keyword) tuples providing the block structure associated with - # self.topvisible (the linenumber of the line displayed at the top of - # the edit window). self.info[0] is initialized as a 'dummy' line which - # starts the toplevel 'block' of the module. - self.info = [(0, -1, "", False)] - self.topvisible = 1 - visible = idleConf.GetOption("extensions", "CodeContext", - "visible", type="bool", default=False) - if visible: - self.toggle_code_context_event() - self.editwin.setvar('<>', True) - # Start two update cycles, one for context lines, one for font changes. - self.text.after(UPDATEINTERVAL, self.timer_event) - self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) - - def toggle_code_context_event(self, event=None): - if not self.label: - # Calculate the border width and horizontal padding required to - # align the context with the text in the main Text widget. - # - # All values are passed through getint(), since some - # values may be pixel objects, which can't simply be added to ints. - widgets = self.editwin.text, self.editwin.text_frame - # Calculate the required vertical padding - padx = 0 - for widget in widgets: - padx += widget.tk.getint(widget.pack_info()['padx']) - padx += widget.tk.getint(widget.cget('padx')) - # Calculate the required border width - border = 0 - for widget in widgets: - border += widget.tk.getint(widget.cget('border')) - self.label = tkinter.Label(self.editwin.top, - text="\n" * (self.context_depth - 1), - anchor=W, justify=LEFT, - font=self.textfont, - bg=self.bgcolor, fg=self.fgcolor, - width=1, #don't request more than we get - padx=padx, border=border, - relief=SUNKEN) - # Pack the label widget before and above the text_frame widget, - # thus ensuring that it will appear directly above text_frame - self.label.pack(side=TOP, fill=X, expand=False, - before=self.editwin.text_frame) - else: - self.label.destroy() - self.label = None - idleConf.SetOption("extensions", "CodeContext", "visible", - str(self.label is not None)) - idleConf.SaveUserCfgFiles() - - def get_line_info(self, linenum): - """Get the line indent value, text, and any block start keyword - - If the line does not start a block, the keyword value is False. - The indentation of empty lines (or comment lines) is INFINITY. - - """ - text = self.text.get("%d.0" % linenum, "%d.end" % linenum) - spaces, firstword = getspacesfirstword(text) - opener = firstword in BLOCKOPENERS and firstword - if len(text) == len(spaces) or text[len(spaces)] == '#': - indent = INFINITY - else: - indent = len(spaces) - return indent, text, opener - - def get_context(self, new_topvisible, stopline=1, stopindent=0): - """Get context lines, starting at new_topvisible and working backwards. - - Stop when stopline or stopindent is reached. Return a tuple of context - data and the indent level at the top of the region inspected. - - """ - assert stopline > 0 - lines = [] - # The indentation level we are currently in: - lastindent = INFINITY - # For a line to be interesting, it must begin with a block opening - # keyword, and have less indentation than lastindent. - for linenum in range(new_topvisible, stopline-1, -1): - indent, text, opener = self.get_line_info(linenum) - if indent < lastindent: - lastindent = indent - if opener in ("else", "elif"): - # We also show the if statement - lastindent += 1 - if opener and linenum < new_topvisible and indent >= stopindent: - lines.append((linenum, indent, text, opener)) - if lastindent <= stopindent: - break - lines.reverse() - return lines, lastindent - - def update_code_context(self): - """Update context information and lines visible in the context pane. - - """ - new_topvisible = int(self.text.index("@0,0").split('.')[0]) - if self.topvisible == new_topvisible: # haven't scrolled - return - if self.topvisible < new_topvisible: # scroll down - lines, lastindent = self.get_context(new_topvisible, - self.topvisible) - # retain only context info applicable to the region - # between topvisible and new_topvisible: - while self.info[-1][1] >= lastindent: - del self.info[-1] - elif self.topvisible > new_topvisible: # scroll up - stopindent = self.info[-1][1] + 1 - # retain only context info associated - # with lines above new_topvisible: - while self.info[-1][0] >= new_topvisible: - stopindent = self.info[-1][1] - del self.info[-1] - lines, lastindent = self.get_context(new_topvisible, - self.info[-1][0]+1, - stopindent) - self.info.extend(lines) - self.topvisible = new_topvisible - # empty lines in context pane: - context_strings = [""] * max(0, self.context_depth - len(self.info)) - # followed by the context hint lines: - context_strings += [x[2] for x in self.info[-self.context_depth:]] - self.label["text"] = '\n'.join(context_strings) - - def timer_event(self): - if self.label: - self.update_code_context() - self.text.after(UPDATEINTERVAL, self.timer_event) - - def font_timer_event(self): - newtextfont = self.text["font"] - if self.label and newtextfont != self.textfont: - self.textfont = newtextfont - self.label["font"] = self.textfont - self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) diff --git a/lib-python/3/idlelib/ColorDelegator.py b/lib-python/3/idlelib/ColorDelegator.py deleted file mode 100644 --- a/lib-python/3/idlelib/ColorDelegator.py +++ /dev/null @@ -1,281 +0,0 @@ -import time -import re -import keyword -import builtins -from tkinter import TkVersion -from idlelib.Delegator import Delegator -from idlelib.configHandler import idleConf - -DEBUG = False - -def any(name, alternates): - "Return a named group pattern matching list of alternates." - return "(?P<%s>" % name + "|".join(alternates) + ")" - -def make_pat(): - kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b" - builtinlist = [str(name) for name in dir(builtins) - if not name.startswith('_') and \ - name not in keyword.kwlist] - # self.file = open("file") : - # 1st 'file' colorized normal, 2nd as builtin, 3rd as string - builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b" - comment = any("COMMENT", [r"#[^\n]*"]) - stringprefix = r"(\br|u|ur|R|U|UR|Ur|uR|b|B|br|Br|bR|BR|rb|rB|Rb|RB)?" - sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?" - dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?' - sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" - dq3string = stringprefix + r'"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' - string = any("STRING", [sq3string, dq3string, sqstring, dqstring]) - return kw + "|" + builtin + "|" + comment + "|" + string +\ - "|" + any("SYNC", [r"\n"]) - -prog = re.compile(make_pat(), re.S) -idprog = re.compile(r"\s+(\w+)", re.S) - -def color_config(text): # Called from htest, Editor, and Turtle Demo. - '''Set color opitons of Text widget. - - Should be called whenever ColorDelegator is called. - ''' - # Not automatic because ColorDelegator does not know 'text'. - theme = idleConf.CurrentTheme() - normal_colors = idleConf.GetHighlight(theme, 'normal') - cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg') - select_colors = idleConf.GetHighlight(theme, 'hilite') - text.config( - foreground=normal_colors['foreground'], - background=normal_colors['background'], - insertbackground=cursor_color, - selectforeground=select_colors['foreground'], - selectbackground=select_colors['background'], - ) - if TkVersion >= 8.5: - text.config( - inactiveselectbackground=select_colors['background']) - - -class ColorDelegator(Delegator): - - def __init__(self): - Delegator.__init__(self) - self.prog = prog - self.idprog = idprog - self.LoadTagDefs() - - def setdelegate(self, delegate): - if self.delegate is not None: - self.unbind("<>") - Delegator.setdelegate(self, delegate) - if delegate is not None: - self.config_colors() - self.bind("<>", self.toggle_colorize_event) - self.notify_range("1.0", "end") - else: - # No delegate - stop any colorizing - self.stop_colorizing = True - self.allow_colorizing = False - - def config_colors(self): - for tag, cnf in self.tagdefs.items(): - if cnf: - self.tag_configure(tag, **cnf) - self.tag_raise('sel') - - def LoadTagDefs(self): - theme = idleConf.CurrentTheme() - self.tagdefs = { - "COMMENT": idleConf.GetHighlight(theme, "comment"), - "KEYWORD": idleConf.GetHighlight(theme, "keyword"), - "BUILTIN": idleConf.GetHighlight(theme, "builtin"), - "STRING": idleConf.GetHighlight(theme, "string"), - "DEFINITION": idleConf.GetHighlight(theme, "definition"), - "SYNC": {'background':None,'foreground':None}, - "TODO": {'background':None,'foreground':None}, - "ERROR": idleConf.GetHighlight(theme, "error"), - # The following is used by ReplaceDialog: - "hit": idleConf.GetHighlight(theme, "hit"), - } - - if DEBUG: print('tagdefs',self.tagdefs) - - def insert(self, index, chars, tags=None): - index = self.index(index) - self.delegate.insert(index, chars, tags) - self.notify_range(index, index + "+%dc" % len(chars)) - - def delete(self, index1, index2=None): - index1 = self.index(index1) - self.delegate.delete(index1, index2) - self.notify_range(index1) - - after_id = None - allow_colorizing = True - colorizing = False - - def notify_range(self, index1, index2=None): - self.tag_add("TODO", index1, index2) - if self.after_id: - if DEBUG: print("colorizing already scheduled") - return - if self.colorizing: - self.stop_colorizing = True - if DEBUG: print("stop colorizing") - if self.allow_colorizing: - if DEBUG: print("schedule colorizing") - self.after_id = self.after(1, self.recolorize) - - close_when_done = None # Window to be closed when done colorizing - - def close(self, close_when_done=None): - if self.after_id: - after_id = self.after_id - self.after_id = None - if DEBUG: print("cancel scheduled recolorizer") - self.after_cancel(after_id) - self.allow_colorizing = False - self.stop_colorizing = True From pypy.commits at gmail.com Tue Feb 19 02:45:18 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 23:45:18 -0800 (PST) Subject: [pypy-commit] pypy py3.6: redo 9387f96a5518 in two steps since file case changed (windows ...) Message-ID: <5c6bb40e.1c69fb81.f3eb2.0d1b@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96083:c51e9fb1f2f9 Date: 2019-02-19 09:41 +0200 http://bitbucket.org/pypy/pypy/changeset/c51e9fb1f2f9/ Log: redo 9387f96a5518 in two steps since file case changed (windows ...) diff too long, truncating to 2000 out of 19492 lines diff --git a/lib-python/3/idlelib/__init__.py b/lib-python/3/idlelib/__init__.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/__init__.py @@ -0,0 +1,10 @@ +"""The idlelib package implements the Idle application. + +Idle includes an interactive shell and editor. +Starting with Python 3.6, IDLE requires tcl/tk 8.5 or later. +Use the files named idle.* to start Idle. + +The other files are private implementations. Their details are subject to +change. See PEP 434 for more. Import them at your own risk. +""" +testing = False # Set True by test.test_idle. diff --git a/lib-python/3/idlelib/__main__.py b/lib-python/3/idlelib/__main__.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/__main__.py @@ -0,0 +1,8 @@ +""" +IDLE main entry point + +Run IDLE as python -m idlelib +""" +import idlelib.pyshell +idlelib.pyshell.main() +# This file does not work for 2.7; See issue 24212. diff --git a/lib-python/3/idlelib/_pyclbr.py b/lib-python/3/idlelib/_pyclbr.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/_pyclbr.py @@ -0,0 +1,402 @@ +# A private copy of 3.7.0a1 pyclbr for use by idlelib.browser +"""Parse a Python module and describe its classes and functions. + +Parse enough of a Python file to recognize imports and class and +function definitions, and to find out the superclasses of a class. + +The interface consists of a single function: + readmodule_ex(module, path=None) +where module is the name of a Python module, and path is an optional +list of directories where the module is to be searched. If present, +path is prepended to the system search path sys.path. The return value +is a dictionary. The keys of the dictionary are the names of the +classes and functions defined in the module (including classes that are +defined via the from XXX import YYY construct). The values are +instances of classes Class and Function. One special key/value pair is +present for packages: the key '__path__' has a list as its value which +contains the package search path. + +Classes and Functions have a common superclass: _Object. Every instance +has the following attributes: + module -- name of the module; + name -- name of the object; + file -- file in which the object is defined; + lineno -- line in the file where the object's definition starts; + parent -- parent of this object, if any; + children -- nested objects contained in this object. +The 'children' attribute is a dictionary mapping names to objects. + +Instances of Function describe functions with the attributes from _Object. + +Instances of Class describe classes with the attributes from _Object, +plus the following: + super -- list of super classes (Class instances if possible); + methods -- mapping of method names to beginning line numbers. +If the name of a super class is not recognized, the corresponding +entry in the list of super classes is not a class instance but a +string giving the name of the super class. Since import statements +are recognized and imported modules are scanned as well, this +shouldn't happen often. +""" + +import io +import sys +import importlib.util +import tokenize +from token import NAME, DEDENT, OP + +__all__ = ["readmodule", "readmodule_ex", "Class", "Function"] + +_modules = {} # Initialize cache of modules we've seen. + + +class _Object: + "Informaton about Python class or function." + def __init__(self, module, name, file, lineno, parent): + self.module = module + self.name = name + self.file = file + self.lineno = lineno + self.parent = parent + self.children = {} + + def _addchild(self, name, obj): + self.children[name] = obj + + +class Function(_Object): + "Information about a Python function, including methods." + def __init__(self, module, name, file, lineno, parent=None): + _Object.__init__(self, module, name, file, lineno, parent) + + +class Class(_Object): + "Information about a Python class." + def __init__(self, module, name, super, file, lineno, parent=None): + _Object.__init__(self, module, name, file, lineno, parent) + self.super = [] if super is None else super + self.methods = {} + + def _addmethod(self, name, lineno): + self.methods[name] = lineno + + +def _nest_function(ob, func_name, lineno): + "Return a Function after nesting within ob." + newfunc = Function(ob.module, func_name, ob.file, lineno, ob) + ob._addchild(func_name, newfunc) + if isinstance(ob, Class): + ob._addmethod(func_name, lineno) + return newfunc + +def _nest_class(ob, class_name, lineno, super=None): + "Return a Class after nesting within ob." + newclass = Class(ob.module, class_name, super, ob.file, lineno, ob) + ob._addchild(class_name, newclass) + return newclass + +def readmodule(module, path=None): + """Return Class objects for the top-level classes in module. + + This is the original interface, before Functions were added. + """ + + res = {} + for key, value in _readmodule(module, path or []).items(): + if isinstance(value, Class): + res[key] = value + return res + +def readmodule_ex(module, path=None): + """Return a dictionary with all functions and classes in module. + + Search for module in PATH + sys.path. + If possible, include imported superclasses. + Do this by reading source, without importing (and executing) it. + """ + return _readmodule(module, path or []) + +def _readmodule(module, path, inpackage=None): + """Do the hard work for readmodule[_ex]. + + If inpackage is given, it must be the dotted name of the package in + which we are searching for a submodule, and then PATH must be the + package search path; otherwise, we are searching for a top-level + module, and path is combined with sys.path. + """ + # Compute the full module name (prepending inpackage if set). + if inpackage is not None: + fullmodule = "%s.%s" % (inpackage, module) + else: + fullmodule = module + + # Check in the cache. + if fullmodule in _modules: + return _modules[fullmodule] + + # Initialize the dict for this module's contents. + tree = {} + + # Check if it is a built-in module; we don't do much for these. + if module in sys.builtin_module_names and inpackage is None: + _modules[module] = tree + return tree + + # Check for a dotted module name. + i = module.rfind('.') + if i >= 0: + package = module[:i] + submodule = module[i+1:] + parent = _readmodule(package, path, inpackage) + if inpackage is not None: + package = "%s.%s" % (inpackage, package) + if not '__path__' in parent: + raise ImportError('No package named {}'.format(package)) + return _readmodule(submodule, parent['__path__'], package) + + # Search the path for the module. + f = None + if inpackage is not None: + search_path = path + else: + search_path = path + sys.path + spec = importlib.util._find_spec_from_path(fullmodule, search_path) + _modules[fullmodule] = tree + # Is module a package? + if spec.submodule_search_locations is not None: + tree['__path__'] = spec.submodule_search_locations + try: + source = spec.loader.get_source(fullmodule) + if source is None: + return tree + except (AttributeError, ImportError): + # If module is not Python source, we cannot do anything. + return tree + + fname = spec.loader.get_filename(fullmodule) + return _create_tree(fullmodule, path, fname, source, tree, inpackage) + + +def _create_tree(fullmodule, path, fname, source, tree, inpackage): + """Return the tree for a particular module. + + fullmodule (full module name), inpackage+module, becomes o.module. + path is passed to recursive calls of _readmodule. + fname becomes o.file. + source is tokenized. Imports cause recursive calls to _readmodule. + tree is {} or {'__path__': }. + inpackage, None or string, is passed to recursive calls of _readmodule. + + The effect of recursive calls is mutation of global _modules. + """ + f = io.StringIO(source) + + stack = [] # Initialize stack of (class, indent) pairs. + + g = tokenize.generate_tokens(f.readline) + try: + for tokentype, token, start, _end, _line in g: + if tokentype == DEDENT: + lineno, thisindent = start + # Close previous nested classes and defs. + while stack and stack[-1][1] >= thisindent: + del stack[-1] + elif token == 'def': + lineno, thisindent = start + # Close previous nested classes and defs. + while stack and stack[-1][1] >= thisindent: + del stack[-1] + tokentype, func_name, start = next(g)[0:3] + if tokentype != NAME: + continue # Skip def with syntax error. + cur_func = None + if stack: + cur_obj = stack[-1][0] + cur_func = _nest_function(cur_obj, func_name, lineno) + else: + # It is just a function. + cur_func = Function(fullmodule, func_name, fname, lineno) + tree[func_name] = cur_func + stack.append((cur_func, thisindent)) + elif token == 'class': + lineno, thisindent = start + # Close previous nested classes and defs. + while stack and stack[-1][1] >= thisindent: + del stack[-1] + tokentype, class_name, start = next(g)[0:3] + if tokentype != NAME: + continue # Skip class with syntax error. + # Parse what follows the class name. + tokentype, token, start = next(g)[0:3] + inherit = None + if token == '(': + names = [] # Initialize list of superclasses. + level = 1 + super = [] # Tokens making up current superclass. + while True: + tokentype, token, start = next(g)[0:3] + if token in (')', ',') and level == 1: + n = "".join(super) + if n in tree: + # We know this super class. + n = tree[n] + else: + c = n.split('.') + if len(c) > 1: + # Super class form is module.class: + # look in module for class. + m = c[-2] + c = c[-1] + if m in _modules: + d = _modules[m] + if c in d: + n = d[c] + names.append(n) + super = [] + if token == '(': + level += 1 + elif token == ')': + level -= 1 + if level == 0: + break + elif token == ',' and level == 1: + pass + # Only use NAME and OP (== dot) tokens for type name. + elif tokentype in (NAME, OP) and level == 1: + super.append(token) + # Expressions in the base list are not supported. + inherit = names + if stack: + cur_obj = stack[-1][0] + cur_class = _nest_class( + cur_obj, class_name, lineno, inherit) + else: + cur_class = Class(fullmodule, class_name, inherit, + fname, lineno) + tree[class_name] = cur_class + stack.append((cur_class, thisindent)) + elif token == 'import' and start[1] == 0: + modules = _getnamelist(g) + for mod, _mod2 in modules: + try: + # Recursively read the imported module. + if inpackage is None: + _readmodule(mod, path) + else: + try: + _readmodule(mod, path, inpackage) + except ImportError: + _readmodule(mod, []) + except: + # If we can't find or parse the imported module, + # too bad -- don't die here. + pass + elif token == 'from' and start[1] == 0: + mod, token = _getname(g) + if not mod or token != "import": + continue + names = _getnamelist(g) + try: + # Recursively read the imported module. + d = _readmodule(mod, path, inpackage) + except: + # If we can't find or parse the imported module, + # too bad -- don't die here. + continue + # Add any classes that were defined in the imported module + # to our name space if they were mentioned in the list. + for n, n2 in names: + if n in d: + tree[n2 or n] = d[n] + elif n == '*': + # Don't add names that start with _. + for n in d: + if n[0] != '_': + tree[n] = d[n] + except StopIteration: + pass + + f.close() + return tree + + +def _getnamelist(g): + """Return list of (dotted-name, as-name or None) tuples for token source g. + + An as-name is the name that follows 'as' in an as clause. + """ + names = [] + while True: + name, token = _getname(g) + if not name: + break + if token == 'as': + name2, token = _getname(g) + else: + name2 = None + names.append((name, name2)) + while token != "," and "\n" not in token: + token = next(g)[1] + if token != ",": + break + return names + + +def _getname(g): + "Return (dotted-name or None, next-token) tuple for token source g." + parts = [] + tokentype, token = next(g)[0:2] + if tokentype != NAME and token != '*': + return (None, token) + parts.append(token) + while True: + tokentype, token = next(g)[0:2] + if token != '.': + break + tokentype, token = next(g)[0:2] + if tokentype != NAME: + break + parts.append(token) + return (".".join(parts), token) + + +def _main(): + "Print module output (default this file) for quick visual check." + import os + try: + mod = sys.argv[1] + except: + mod = __file__ + if os.path.exists(mod): + path = [os.path.dirname(mod)] + mod = os.path.basename(mod) + if mod.lower().endswith(".py"): + mod = mod[:-3] + else: + path = [] + tree = readmodule_ex(mod, path) + lineno_key = lambda a: getattr(a, 'lineno', 0) + objs = sorted(tree.values(), key=lineno_key, reverse=True) + indent_level = 2 + while objs: + obj = objs.pop() + if isinstance(obj, list): + # Value is a __path__ key. + continue + if not hasattr(obj, 'indent'): + obj.indent = 0 + + if isinstance(obj, _Object): + new_objs = sorted(obj.children.values(), + key=lineno_key, reverse=True) + for ob in new_objs: + ob.indent = obj.indent + indent_level + objs.extend(new_objs) + if isinstance(obj, Class): + print("{}class {} {} {}" + .format(' ' * obj.indent, obj.name, obj.super, obj.lineno)) + elif isinstance(obj, Function): + print("{}def {} {}".format(' ' * obj.indent, obj.name, obj.lineno)) + +if __name__ == "__main__": + _main() diff --git a/lib-python/3/idlelib/autocomplete.py b/lib-python/3/idlelib/autocomplete.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/autocomplete.py @@ -0,0 +1,231 @@ +"""Complete either attribute names or file names. + +Either on demand or after a user-selected delay after a key character, +pop up a list of candidates. +""" +import os +import string +import sys + +# These constants represent the two different types of completions. +# They must be defined here so autocomple_w can import them. +COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1) + +from idlelib import autocomplete_w +from idlelib.config import idleConf +from idlelib.hyperparser import HyperParser +import __main__ + +# This string includes all chars that may be in an identifier. +# TODO Update this here and elsewhere. +ID_CHARS = string.ascii_letters + string.digits + "_" + +SEPS = os.sep +if os.altsep: # e.g. '/' on Windows... + SEPS += os.altsep + + +class AutoComplete: + + def __init__(self, editwin=None): + self.editwin = editwin + if editwin is not None: # not in subprocess or test + self.text = editwin.text + self.autocompletewindow = None + # id of delayed call, and the index of the text insert when + # the delayed call was issued. If _delayed_completion_id is + # None, there is no delayed call. + self._delayed_completion_id = None + self._delayed_completion_index = None + + @classmethod + def reload(cls): + cls.popupwait = idleConf.GetOption( + "extensions", "AutoComplete", "popupwait", type="int", default=0) + + def _make_autocomplete_window(self): + return autocomplete_w.AutoCompleteWindow(self.text) + + def _remove_autocomplete_window(self, event=None): + if self.autocompletewindow: + self.autocompletewindow.hide_window() + self.autocompletewindow = None + + def force_open_completions_event(self, event): + """Happens when the user really wants to open a completion list, even + if a function call is needed. + """ + self.open_completions(True, False, True) + return "break" + + def try_open_completions_event(self, event): + """Happens when it would be nice to open a completion list, but not + really necessary, for example after a dot, so function + calls won't be made. + """ + lastchar = self.text.get("insert-1c") + if lastchar == ".": + self._open_completions_later(False, False, False, + COMPLETE_ATTRIBUTES) + elif lastchar in SEPS: + self._open_completions_later(False, False, False, + COMPLETE_FILES) + + def autocomplete_event(self, event): + """Happens when the user wants to complete his word, and if necessary, + open a completion list after that (if there is more than one + completion) + """ + if hasattr(event, "mc_state") and event.mc_state or\ + not self.text.get("insert linestart", "insert").strip(): + # A modifier was pressed along with the tab or + # there is only previous whitespace on this line, so tab. + return None + if self.autocompletewindow and self.autocompletewindow.is_active(): + self.autocompletewindow.complete() + return "break" + else: + opened = self.open_completions(False, True, True) + return "break" if opened else None + + def _open_completions_later(self, *args): + self._delayed_completion_index = self.text.index("insert") + if self._delayed_completion_id is not None: + self.text.after_cancel(self._delayed_completion_id) + self._delayed_completion_id = \ + self.text.after(self.popupwait, self._delayed_open_completions, + *args) + + def _delayed_open_completions(self, *args): + self._delayed_completion_id = None + if self.text.index("insert") == self._delayed_completion_index: + self.open_completions(*args) + + def open_completions(self, evalfuncs, complete, userWantsWin, mode=None): + """Find the completions and create the AutoCompleteWindow. + Return True if successful (no syntax error or so found). + if complete is True, then if there's nothing to complete and no + start of completion, won't open completions and return False. + If mode is given, will open a completion list only in this mode. + """ + # Cancel another delayed call, if it exists. + if self._delayed_completion_id is not None: + self.text.after_cancel(self._delayed_completion_id) + self._delayed_completion_id = None + + hp = HyperParser(self.editwin, "insert") + curline = self.text.get("insert linestart", "insert") + i = j = len(curline) + if hp.is_in_string() and (not mode or mode==COMPLETE_FILES): + # Find the beginning of the string + # fetch_completions will look at the file system to determine whether the + # string value constitutes an actual file name + # XXX could consider raw strings here and unescape the string value if it's + # not raw. + self._remove_autocomplete_window() + mode = COMPLETE_FILES + # Find last separator or string start + while i and curline[i-1] not in "'\"" + SEPS: + i -= 1 + comp_start = curline[i:j] + j = i + # Find string start + while i and curline[i-1] not in "'\"": + i -= 1 + comp_what = curline[i:j] + elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES): + self._remove_autocomplete_window() + mode = COMPLETE_ATTRIBUTES + while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127): + i -= 1 + comp_start = curline[i:j] + if i and curline[i-1] == '.': + hp.set_index("insert-%dc" % (len(curline)-(i-1))) + comp_what = hp.get_expression() + if not comp_what or \ + (not evalfuncs and comp_what.find('(') != -1): + return None + else: + comp_what = "" + else: + return None + + if complete and not comp_what and not comp_start: + return None + comp_lists = self.fetch_completions(comp_what, mode) + if not comp_lists[0]: + return None + self.autocompletewindow = self._make_autocomplete_window() + return not self.autocompletewindow.show_window( + comp_lists, "insert-%dc" % len(comp_start), + complete, mode, userWantsWin) + + def fetch_completions(self, what, mode): + """Return a pair of lists of completions for something. The first list + is a sublist of the second. Both are sorted. + + If there is a Python subprocess, get the comp. list there. Otherwise, + either fetch_completions() is running in the subprocess itself or it + was called in an IDLE EditorWindow before any script had been run. + + The subprocess environment is that of the most recently run script. If + two unrelated modules are being edited some calltips in the current + module may be inoperative if the module was not the last to run. + """ + try: + rpcclt = self.editwin.flist.pyshell.interp.rpcclt + except: + rpcclt = None + if rpcclt: + return rpcclt.remotecall("exec", "get_the_completion_list", + (what, mode), {}) + else: + if mode == COMPLETE_ATTRIBUTES: + if what == "": + namespace = __main__.__dict__.copy() + namespace.update(__main__.__builtins__.__dict__) + bigl = eval("dir()", namespace) + bigl.sort() + if "__all__" in bigl: + smalll = sorted(eval("__all__", namespace)) + else: + smalll = [s for s in bigl if s[:1] != '_'] + else: + try: + entity = self.get_entity(what) + bigl = dir(entity) + bigl.sort() + if "__all__" in bigl: + smalll = sorted(entity.__all__) + else: + smalll = [s for s in bigl if s[:1] != '_'] + except: + return [], [] + + elif mode == COMPLETE_FILES: + if what == "": + what = "." + try: + expandedpath = os.path.expanduser(what) + bigl = os.listdir(expandedpath) + bigl.sort() + smalll = [s for s in bigl if s[:1] != '.'] + except OSError: + return [], [] + + if not smalll: + smalll = bigl + return smalll, bigl + + def get_entity(self, name): + """Lookup name in a namespace spanning sys.modules and __main.dict__""" + namespace = sys.modules.copy() + namespace.update(__main__.__dict__) + return eval(name, namespace) + + +AutoComplete.reload() + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_autocomplete', verbosity=2) diff --git a/lib-python/3/idlelib/autocomplete_w.py b/lib-python/3/idlelib/autocomplete_w.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/autocomplete_w.py @@ -0,0 +1,467 @@ +""" +An auto-completion window for IDLE, used by the autocomplete extension +""" +import platform + +from tkinter import * +from tkinter.ttk import Scrollbar + +from idlelib.autocomplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES +from idlelib.multicall import MC_SHIFT + +HIDE_VIRTUAL_EVENT_NAME = "<>" +HIDE_FOCUS_OUT_SEQUENCE = "" +HIDE_SEQUENCES = (HIDE_FOCUS_OUT_SEQUENCE, "") +KEYPRESS_VIRTUAL_EVENT_NAME = "<>" +# We need to bind event beyond so that the function will be called +# before the default specific IDLE function +KEYPRESS_SEQUENCES = ("", "", "", "", + "", "", "", "", + "", "") +KEYRELEASE_VIRTUAL_EVENT_NAME = "<>" +KEYRELEASE_SEQUENCE = "" +LISTUPDATE_SEQUENCE = "" +WINCONFIG_SEQUENCE = "" +DOUBLECLICK_SEQUENCE = "" + +class AutoCompleteWindow: + + def __init__(self, widget): + # The widget (Text) on which we place the AutoCompleteWindow + self.widget = widget + # The widgets we create + self.autocompletewindow = self.listbox = self.scrollbar = None + # The default foreground and background of a selection. Saved because + # they are changed to the regular colors of list items when the + # completion start is not a prefix of the selected completion + self.origselforeground = self.origselbackground = None + # The list of completions + self.completions = None + # A list with more completions, or None + self.morecompletions = None + # The completion mode. Either autocomplete.COMPLETE_ATTRIBUTES or + # autocomplete.COMPLETE_FILES + self.mode = None + # The current completion start, on the text box (a string) + self.start = None + # The index of the start of the completion + self.startindex = None + # The last typed start, used so that when the selection changes, + # the new start will be as close as possible to the last typed one. + self.lasttypedstart = None + # Do we have an indication that the user wants the completion window + # (for example, he clicked the list) + self.userwantswindow = None + # event ids + self.hideid = self.keypressid = self.listupdateid = self.winconfigid \ + = self.keyreleaseid = self.doubleclickid = None + # Flag set if last keypress was a tab + self.lastkey_was_tab = False + + def _change_start(self, newstart): + min_len = min(len(self.start), len(newstart)) + i = 0 + while i < min_len and self.start[i] == newstart[i]: + i += 1 + if i < len(self.start): + self.widget.delete("%s+%dc" % (self.startindex, i), + "%s+%dc" % (self.startindex, len(self.start))) + if i < len(newstart): + self.widget.insert("%s+%dc" % (self.startindex, i), + newstart[i:]) + self.start = newstart + + def _binary_search(self, s): + """Find the first index in self.completions where completions[i] is + greater or equal to s, or the last index if there is no such + one.""" + i = 0; j = len(self.completions) + while j > i: + m = (i + j) // 2 + if self.completions[m] >= s: + j = m + else: + i = m + 1 + return min(i, len(self.completions)-1) + + def _complete_string(self, s): + """Assuming that s is the prefix of a string in self.completions, + return the longest string which is a prefix of all the strings which + s is a prefix of them. If s is not a prefix of a string, return s.""" + first = self._binary_search(s) + if self.completions[first][:len(s)] != s: + # There is not even one completion which s is a prefix of. + return s + # Find the end of the range of completions where s is a prefix of. + i = first + 1 + j = len(self.completions) + while j > i: + m = (i + j) // 2 + if self.completions[m][:len(s)] != s: + j = m + else: + i = m + 1 + last = i-1 + + if first == last: # only one possible completion + return self.completions[first] + + # We should return the maximum prefix of first and last + first_comp = self.completions[first] + last_comp = self.completions[last] + min_len = min(len(first_comp), len(last_comp)) + i = len(s) + while i < min_len and first_comp[i] == last_comp[i]: + i += 1 + return first_comp[:i] + + def _selection_changed(self): + """Should be called when the selection of the Listbox has changed. + Updates the Listbox display and calls _change_start.""" + cursel = int(self.listbox.curselection()[0]) + + self.listbox.see(cursel) + + lts = self.lasttypedstart + selstart = self.completions[cursel] + if self._binary_search(lts) == cursel: + newstart = lts + else: + min_len = min(len(lts), len(selstart)) + i = 0 + while i < min_len and lts[i] == selstart[i]: + i += 1 + newstart = selstart[:i] + self._change_start(newstart) + + if self.completions[cursel][:len(self.start)] == self.start: + # start is a prefix of the selected completion + self.listbox.configure(selectbackground=self.origselbackground, + selectforeground=self.origselforeground) + else: + self.listbox.configure(selectbackground=self.listbox.cget("bg"), + selectforeground=self.listbox.cget("fg")) + # If there are more completions, show them, and call me again. + if self.morecompletions: + self.completions = self.morecompletions + self.morecompletions = None + self.listbox.delete(0, END) + for item in self.completions: + self.listbox.insert(END, item) + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + + def show_window(self, comp_lists, index, complete, mode, userWantsWin): + """Show the autocomplete list, bind events. + If complete is True, complete the text, and if there is exactly one + matching completion, don't open a list.""" + # Handle the start we already have + self.completions, self.morecompletions = comp_lists + self.mode = mode + self.startindex = self.widget.index(index) + self.start = self.widget.get(self.startindex, "insert") + if complete: + completed = self._complete_string(self.start) + start = self.start + self._change_start(completed) + i = self._binary_search(completed) + if self.completions[i] == completed and \ + (i == len(self.completions)-1 or + self.completions[i+1][:len(completed)] != completed): + # There is exactly one matching completion + return completed == start + self.userwantswindow = userWantsWin + self.lasttypedstart = self.start + + # Put widgets in place + self.autocompletewindow = acw = Toplevel(self.widget) + # Put it in a position so that it is not seen. + acw.wm_geometry("+10000+10000") + # Make it float + acw.wm_overrideredirect(1) + try: + # This command is only needed and available on Tk >= 8.4.0 for OSX + # Without it, call tips intrude on the typing process by grabbing + # the focus. + acw.tk.call("::tk::unsupported::MacWindowStyle", "style", acw._w, + "help", "noActivates") + except TclError: + pass + self.scrollbar = scrollbar = Scrollbar(acw, orient=VERTICAL) + self.listbox = listbox = Listbox(acw, yscrollcommand=scrollbar.set, + exportselection=False, bg="white") + for item in self.completions: + listbox.insert(END, item) + self.origselforeground = listbox.cget("selectforeground") + self.origselbackground = listbox.cget("selectbackground") + scrollbar.config(command=listbox.yview) + scrollbar.pack(side=RIGHT, fill=Y) + listbox.pack(side=LEFT, fill=BOTH, expand=True) + acw.lift() # work around bug in Tk 8.5.18+ (issue #24570) + + # Initialize the listbox selection + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + + # bind events + self.hideaid = acw.bind(HIDE_VIRTUAL_EVENT_NAME, self.hide_event) + self.hidewid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME, self.hide_event) + acw.event_add(HIDE_VIRTUAL_EVENT_NAME, HIDE_FOCUS_OUT_SEQUENCE) + for seq in HIDE_SEQUENCES: + self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq) + + self.keypressid = self.widget.bind(KEYPRESS_VIRTUAL_EVENT_NAME, + self.keypress_event) + for seq in KEYPRESS_SEQUENCES: + self.widget.event_add(KEYPRESS_VIRTUAL_EVENT_NAME, seq) + self.keyreleaseid = self.widget.bind(KEYRELEASE_VIRTUAL_EVENT_NAME, + self.keyrelease_event) + self.widget.event_add(KEYRELEASE_VIRTUAL_EVENT_NAME,KEYRELEASE_SEQUENCE) + self.listupdateid = listbox.bind(LISTUPDATE_SEQUENCE, + self.listselect_event) + self.winconfigid = acw.bind(WINCONFIG_SEQUENCE, self.winconfig_event) + self.doubleclickid = listbox.bind(DOUBLECLICK_SEQUENCE, + self.doubleclick_event) + return None + + def winconfig_event(self, event): + if not self.is_active(): + return + # Position the completion list window + text = self.widget + text.see(self.startindex) + x, y, cx, cy = text.bbox(self.startindex) + acw = self.autocompletewindow + acw_width, acw_height = acw.winfo_width(), acw.winfo_height() + text_width, text_height = text.winfo_width(), text.winfo_height() + new_x = text.winfo_rootx() + min(x, max(0, text_width - acw_width)) + new_y = text.winfo_rooty() + y + if (text_height - (y + cy) >= acw_height # enough height below + or y < acw_height): # not enough height above + # place acw below current line + new_y += cy + else: + # place acw above current line + new_y -= acw_height + acw.wm_geometry("+%d+%d" % (new_x, new_y)) + + if platform.system().startswith('Windows'): + # See issue 15786. When on Windows platform, Tk will misbehave + # to call winconfig_event multiple times, we need to prevent this, + # otherwise mouse button double click will not be able to used. + acw.unbind(WINCONFIG_SEQUENCE, self.winconfigid) + self.winconfigid = None + + def _hide_event_check(self): + if not self.autocompletewindow: + return + + try: + if not self.autocompletewindow.focus_get(): + self.hide_window() + except KeyError: + # See issue 734176, when user click on menu, acw.focus_get() + # will get KeyError. + self.hide_window() + + def hide_event(self, event): + # Hide autocomplete list if it exists and does not have focus or + # mouse click on widget / text area. + if self.is_active(): + if event.type == EventType.FocusOut: + # On Windows platform, it will need to delay the check for + # acw.focus_get() when click on acw, otherwise it will return + # None and close the window + self.widget.after(1, self._hide_event_check) + elif event.type == EventType.ButtonPress: + # ButtonPress event only bind to self.widget + self.hide_window() + + def listselect_event(self, event): + if self.is_active(): + self.userwantswindow = True + cursel = int(self.listbox.curselection()[0]) + self._change_start(self.completions[cursel]) + + def doubleclick_event(self, event): + # Put the selected completion in the text, and close the list + cursel = int(self.listbox.curselection()[0]) + self._change_start(self.completions[cursel]) + self.hide_window() + + def keypress_event(self, event): + if not self.is_active(): + return None + keysym = event.keysym + if hasattr(event, "mc_state"): + state = event.mc_state + else: + state = 0 + if keysym != "Tab": + self.lastkey_was_tab = False + if (len(keysym) == 1 or keysym in ("underscore", "BackSpace") + or (self.mode == COMPLETE_FILES and keysym in + ("period", "minus"))) \ + and not (state & ~MC_SHIFT): + # Normal editing of text + if len(keysym) == 1: + self._change_start(self.start + keysym) + elif keysym == "underscore": + self._change_start(self.start + '_') + elif keysym == "period": + self._change_start(self.start + '.') + elif keysym == "minus": + self._change_start(self.start + '-') + else: + # keysym == "BackSpace" + if len(self.start) == 0: + self.hide_window() + return None + self._change_start(self.start[:-1]) + self.lasttypedstart = self.start + self.listbox.select_clear(0, int(self.listbox.curselection()[0])) + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + return "break" + + elif keysym == "Return": + self.complete() + self.hide_window() + return 'break' + + elif (self.mode == COMPLETE_ATTRIBUTES and keysym in + ("period", "space", "parenleft", "parenright", "bracketleft", + "bracketright")) or \ + (self.mode == COMPLETE_FILES and keysym in + ("slash", "backslash", "quotedbl", "apostrophe")) \ + and not (state & ~MC_SHIFT): + # If start is a prefix of the selection, but is not '' when + # completing file names, put the whole + # selected completion. Anyway, close the list. + cursel = int(self.listbox.curselection()[0]) + if self.completions[cursel][:len(self.start)] == self.start \ + and (self.mode == COMPLETE_ATTRIBUTES or self.start): + self._change_start(self.completions[cursel]) + self.hide_window() + return None + + elif keysym in ("Home", "End", "Prior", "Next", "Up", "Down") and \ + not state: + # Move the selection in the listbox + self.userwantswindow = True + cursel = int(self.listbox.curselection()[0]) + if keysym == "Home": + newsel = 0 + elif keysym == "End": + newsel = len(self.completions)-1 + elif keysym in ("Prior", "Next"): + jump = self.listbox.nearest(self.listbox.winfo_height()) - \ + self.listbox.nearest(0) + if keysym == "Prior": + newsel = max(0, cursel-jump) + else: + assert keysym == "Next" + newsel = min(len(self.completions)-1, cursel+jump) + elif keysym == "Up": + newsel = max(0, cursel-1) + else: + assert keysym == "Down" + newsel = min(len(self.completions)-1, cursel+1) + self.listbox.select_clear(cursel) + self.listbox.select_set(newsel) + self._selection_changed() + self._change_start(self.completions[newsel]) + return "break" + + elif (keysym == "Tab" and not state): + if self.lastkey_was_tab: + # two tabs in a row; insert current selection and close acw + cursel = int(self.listbox.curselection()[0]) + self._change_start(self.completions[cursel]) + self.hide_window() + return "break" + else: + # first tab; let AutoComplete handle the completion + self.userwantswindow = True + self.lastkey_was_tab = True + return None + + elif any(s in keysym for s in ("Shift", "Control", "Alt", + "Meta", "Command", "Option")): + # A modifier key, so ignore + return None + + elif event.char and event.char >= ' ': + # Regular character with a non-length-1 keycode + self._change_start(self.start + event.char) + self.lasttypedstart = self.start + self.listbox.select_clear(0, int(self.listbox.curselection()[0])) + self.listbox.select_set(self._binary_search(self.start)) + self._selection_changed() + return "break" + + else: + # Unknown event, close the window and let it through. + self.hide_window() + return None + + def keyrelease_event(self, event): + if not self.is_active(): + return + if self.widget.index("insert") != \ + self.widget.index("%s+%dc" % (self.startindex, len(self.start))): + # If we didn't catch an event which moved the insert, close window + self.hide_window() + + def is_active(self): + return self.autocompletewindow is not None + + def complete(self): + self._change_start(self._complete_string(self.start)) + # The selection doesn't change. + + def hide_window(self): + if not self.is_active(): + return + + # unbind events + self.autocompletewindow.event_delete(HIDE_VIRTUAL_EVENT_NAME, + HIDE_FOCUS_OUT_SEQUENCE) + for seq in HIDE_SEQUENCES: + self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq) + + self.autocompletewindow.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideaid) + self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hidewid) + self.hideaid = None + self.hidewid = None + for seq in KEYPRESS_SEQUENCES: + self.widget.event_delete(KEYPRESS_VIRTUAL_EVENT_NAME, seq) + self.widget.unbind(KEYPRESS_VIRTUAL_EVENT_NAME, self.keypressid) + self.keypressid = None + self.widget.event_delete(KEYRELEASE_VIRTUAL_EVENT_NAME, + KEYRELEASE_SEQUENCE) + self.widget.unbind(KEYRELEASE_VIRTUAL_EVENT_NAME, self.keyreleaseid) + self.keyreleaseid = None + self.listbox.unbind(LISTUPDATE_SEQUENCE, self.listupdateid) + self.listupdateid = None + if self.winconfigid: + self.autocompletewindow.unbind(WINCONFIG_SEQUENCE, self.winconfigid) + self.winconfigid = None + + # Re-focusOn frame.text (See issue #15786) + self.widget.focus_set() + + # destroy widgets + self.scrollbar.destroy() + self.scrollbar = None + self.listbox.destroy() + self.listbox = None + self.autocompletewindow.destroy() + self.autocompletewindow = None + + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_autocomplete_w', verbosity=2, exit=False) + +# TODO: autocomplete/w htest here diff --git a/lib-python/3/idlelib/autoexpand.py b/lib-python/3/idlelib/autoexpand.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/autoexpand.py @@ -0,0 +1,96 @@ +'''Complete the current word before the cursor with words in the editor. + +Each menu selection or shortcut key selection replaces the word with a +different word with the same prefix. The search for matches begins +before the target and moves toward the top of the editor. It then starts +after the cursor and moves down. It then returns to the original word and +the cycle starts again. + +Changing the current text line or leaving the cursor in a different +place before requesting the next selection causes AutoExpand to reset +its state. + +There is only one instance of Autoexpand. +''' +import re +import string + + +class AutoExpand: + wordchars = string.ascii_letters + string.digits + "_" + + def __init__(self, editwin): + self.text = editwin.text + self.bell = self.text.bell + self.state = None + + def expand_word_event(self, event): + "Replace the current word with the next expansion." + curinsert = self.text.index("insert") + curline = self.text.get("insert linestart", "insert lineend") + if not self.state: + words = self.getwords() + index = 0 + else: + words, index, insert, line = self.state + if insert != curinsert or line != curline: + words = self.getwords() + index = 0 + if not words: + self.bell() + return "break" + word = self.getprevword() + self.text.delete("insert - %d chars" % len(word), "insert") + newword = words[index] + index = (index + 1) % len(words) + if index == 0: + self.bell() # Warn we cycled around + self.text.insert("insert", newword) + curinsert = self.text.index("insert") + curline = self.text.get("insert linestart", "insert lineend") + self.state = words, index, curinsert, curline + return "break" + + def getwords(self): + "Return a list of words that match the prefix before the cursor." + word = self.getprevword() + if not word: + return [] + before = self.text.get("1.0", "insert wordstart") + wbefore = re.findall(r"\b" + word + r"\w+\b", before) + del before + after = self.text.get("insert wordend", "end") + wafter = re.findall(r"\b" + word + r"\w+\b", after) + del after + if not wbefore and not wafter: + return [] + words = [] + dict = {} + # search backwards through words before + wbefore.reverse() + for w in wbefore: + if dict.get(w): + continue + words.append(w) + dict[w] = w + # search onwards through words after + for w in wafter: + if dict.get(w): + continue + words.append(w) + dict[w] = w + words.append(word) + return words + + def getprevword(self): + "Return the word prefix before the cursor." + line = self.text.get("insert linestart", "insert") + i = len(line) + while i > 0 and line[i-1] in self.wordchars: + i = i-1 + return line[i:] + + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_autoexpand', verbosity=2) diff --git a/lib-python/3/idlelib/browser.py b/lib-python/3/idlelib/browser.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/browser.py @@ -0,0 +1,248 @@ +"""Module browser. + +XXX TO DO: + +- reparse when source changed (maybe just a button would be OK?) + (or recheck on window popup) +- add popup menu with more options (e.g. doc strings, base classes, imports) +- add base classes to class browser tree +- finish removing limitation to x.py files (ModuleBrowserTreeItem) +""" + +import os +from idlelib import _pyclbr as pyclbr +import sys + +from idlelib.config import idleConf +from idlelib import pyshell +from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas +from idlelib.window import ListedToplevel + + +file_open = None # Method...Item and Class...Item use this. +# Normally pyshell.flist.open, but there is no pyshell.flist for htest. + + +def transform_children(child_dict, modname=None): + """Transform a child dictionary to an ordered sequence of objects. + + The dictionary maps names to pyclbr information objects. + Filter out imported objects. + Augment class names with bases. + Sort objects by line number. + + The current tree only calls this once per child_dic as it saves + TreeItems once created. A future tree and tests might violate this, + so a check prevents multiple in-place augmentations. + """ + obs = [] # Use list since values should already be sorted. + for key, obj in child_dict.items(): + if modname is None or obj.module == modname: + if hasattr(obj, 'super') and obj.super and obj.name == key: + # If obj.name != key, it has already been suffixed. + supers = [] + for sup in obj.super: + if type(sup) is type(''): + sname = sup + else: + sname = sup.name + if sup.module != obj.module: + sname = f'{sup.module}.{sname}' + supers.append(sname) + obj.name += '({})'.format(', '.join(supers)) + obs.append(obj) + return sorted(obs, key=lambda o: o.lineno) + + +class ModuleBrowser: + """Browse module classes and functions in IDLE. + """ + # This class is also the base class for pathbrowser.PathBrowser. + # Init and close are inherited, other methods are overridden. + # PathBrowser.__init__ does not call __init__ below. + + def __init__(self, master, path, *, _htest=False, _utest=False): + """Create a window for browsing a module's structure. + + Args: + master: parent for widgets. + path: full path of file to browse. + _htest - bool; change box location when running htest. + -utest - bool; suppress contents when running unittest. + + Global variables: + file_open: Function used for opening a file. + + Instance variables: + name: Module name. + file: Full path and module with .py extension. Used in + creating ModuleBrowserTreeItem as the rootnode for + the tree and subsequently in the children. + """ + self.master = master + self.path = path + self._htest = _htest + self._utest = _utest + self.init() + + def close(self, event=None): + "Dismiss the window and the tree nodes." + self.top.destroy() + self.node.destroy() + + def init(self): + "Create browser tkinter widgets, including the tree." + global file_open + root = self.master + flist = (pyshell.flist if not (self._htest or self._utest) + else pyshell.PyShellFileList(root)) + file_open = flist.open + pyclbr._modules.clear() + + # create top + self.top = top = ListedToplevel(root) + top.protocol("WM_DELETE_WINDOW", self.close) + top.bind("", self.close) + if self._htest: # place dialog below parent if running htest + top.geometry("+%d+%d" % + (root.winfo_rootx(), root.winfo_rooty() + 200)) + self.settitle() + top.focus_set() + + # create scrolled canvas + theme = idleConf.CurrentTheme() + background = idleConf.GetHighlight(theme, 'normal')['background'] + sc = ScrolledCanvas(top, bg=background, highlightthickness=0, + takefocus=1) + sc.frame.pack(expand=1, fill="both") + item = self.rootnode() + self.node = node = TreeNode(sc.canvas, None, item) + if not self._utest: + node.update() + node.expand() + + def settitle(self): + "Set the window title." + self.top.wm_title("Module Browser - " + os.path.basename(self.path)) + self.top.wm_iconname("Module Browser") + + def rootnode(self): + "Return a ModuleBrowserTreeItem as the root of the tree." + return ModuleBrowserTreeItem(self.path) + + +class ModuleBrowserTreeItem(TreeItem): + """Browser tree for Python module. + + Uses TreeItem as the basis for the structure of the tree. + Used by both browsers. + """ + + def __init__(self, file): + """Create a TreeItem for the file. + + Args: + file: Full path and module name. + """ + self.file = file + + def GetText(self): + "Return the module name as the text string to display." + return os.path.basename(self.file) + + def GetIconName(self): + "Return the name of the icon to display." + return "python" + + def GetSubList(self): + "Return ChildBrowserTreeItems for children." + return [ChildBrowserTreeItem(obj) for obj in self.listchildren()] + + def OnDoubleClick(self): + "Open a module in an editor window when double clicked." + if os.path.normcase(self.file[-3:]) != ".py": + return + if not os.path.exists(self.file): + return + file_open(self.file) + + def IsExpandable(self): + "Return True if Python (.py) file." + return os.path.normcase(self.file[-3:]) == ".py" + + def listchildren(self): + "Return sequenced classes and functions in the module." + dir, base = os.path.split(self.file) + name, ext = os.path.splitext(base) + if os.path.normcase(ext) != ".py": + return [] + try: + tree = pyclbr.readmodule_ex(name, [dir] + sys.path) + except ImportError: + return [] + return transform_children(tree, name) + + +class ChildBrowserTreeItem(TreeItem): + """Browser tree for child nodes within the module. + + Uses TreeItem as the basis for the structure of the tree. + """ + + def __init__(self, obj): + "Create a TreeItem for a pyclbr class/function object." + self.obj = obj + self.name = obj.name + self.isfunction = isinstance(obj, pyclbr.Function) + + def GetText(self): + "Return the name of the function/class to display." + name = self.name + if self.isfunction: + return "def " + name + "(...)" + else: + return "class " + name + + def GetIconName(self): + "Return the name of the icon to display." + if self.isfunction: + return "python" + else: + return "folder" + + def IsExpandable(self): + "Return True if self.obj has nested objects." + return self.obj.children != {} + + def GetSubList(self): + "Return ChildBrowserTreeItems for children." + return [ChildBrowserTreeItem(obj) + for obj in transform_children(self.obj.children)] + + def OnDoubleClick(self): + "Open module with file_open and position to lineno." + try: + edit = file_open(self.obj.file) + edit.gotoline(self.obj.lineno) + except (OSError, AttributeError): + pass + + +def _module_browser(parent): # htest # + if len(sys.argv) > 1: # If pass file on command line. + file = sys.argv[1] + else: + file = __file__ + # Add nested objects for htest. + class Nested_in_func(TreeNode): + def nested_in_class(): pass + def closure(): + class Nested_in_closure: pass + ModuleBrowser(parent, file, _htest=True) + +if __name__ == "__main__": + if len(sys.argv) == 1: # If pass file on command line, unittest fails. + from unittest import main + main('idlelib.idle_test.test_browser', verbosity=2, exit=False) + from idlelib.idle_test.htest import run + run(_module_browser) diff --git a/lib-python/3/idlelib/calltip.py b/lib-python/3/idlelib/calltip.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/calltip.py @@ -0,0 +1,178 @@ +"""Pop up a reminder of how to call a function. + +Call Tips are floating windows which display function, class, and method +parameter and docstring information when you type an opening parenthesis, and +which disappear when you type a closing parenthesis. +""" +import inspect +import re +import sys +import textwrap +import types + +from idlelib import calltip_w +from idlelib.hyperparser import HyperParser +import __main__ + + +class Calltip: + + def __init__(self, editwin=None): + if editwin is None: # subprocess and test + self.editwin = None + else: + self.editwin = editwin + self.text = editwin.text + self.active_calltip = None + self._calltip_window = self._make_tk_calltip_window + + def close(self): + self._calltip_window = None + + def _make_tk_calltip_window(self): + # See __init__ for usage + return calltip_w.CalltipWindow(self.text) + + def _remove_calltip_window(self, event=None): + if self.active_calltip: + self.active_calltip.hidetip() + self.active_calltip = None + + def force_open_calltip_event(self, event): + "The user selected the menu entry or hotkey, open the tip." + self.open_calltip(True) + return "break" + + def try_open_calltip_event(self, event): + """Happens when it would be nice to open a calltip, but not really + necessary, for example after an opening bracket, so function calls + won't be made. + """ + self.open_calltip(False) + + def refresh_calltip_event(self, event): + if self.active_calltip and self.active_calltip.tipwindow: + self.open_calltip(False) + + def open_calltip(self, evalfuncs): + self._remove_calltip_window() + + hp = HyperParser(self.editwin, "insert") + sur_paren = hp.get_surrounding_brackets('(') + if not sur_paren: + return + hp.set_index(sur_paren[0]) + expression = hp.get_expression() + if not expression: + return + if not evalfuncs and (expression.find('(') != -1): + return + argspec = self.fetch_tip(expression) + if not argspec: + return + self.active_calltip = self._calltip_window() + self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1]) + + def fetch_tip(self, expression): + """Return the argument list and docstring of a function or class. + + If there is a Python subprocess, get the calltip there. Otherwise, + either this fetch_tip() is running in the subprocess or it was + called in an IDLE running without the subprocess. + + The subprocess environment is that of the most recently run script. If + two unrelated modules are being edited some calltips in the current + module may be inoperative if the module was not the last to run. + + To find methods, fetch_tip must be fed a fully qualified name. + + """ + try: + rpcclt = self.editwin.flist.pyshell.interp.rpcclt + except AttributeError: + rpcclt = None + if rpcclt: + return rpcclt.remotecall("exec", "get_the_calltip", + (expression,), {}) + else: + return get_argspec(get_entity(expression)) + + +def get_entity(expression): + """Return the object corresponding to expression evaluated + in a namespace spanning sys.modules and __main.dict__. + """ + if expression: + namespace = sys.modules.copy() + namespace.update(__main__.__dict__) + try: + return eval(expression, namespace) + except BaseException: + # An uncaught exception closes idle, and eval can raise any + # exception, especially if user classes are involved. + return None + +# The following are used in get_argspec and some in tests +_MAX_COLS = 85 +_MAX_LINES = 5 # enough for bytes +_INDENT = ' '*4 # for wrapped signatures +_first_param = re.compile(r'(?<=\()\w*\,?\s*') +_default_callable_argspec = "See source or doc" +_invalid_method = "invalid method signature" +_argument_positional = "\n['/' marks preceding arguments as positional-only]\n" + +def get_argspec(ob): + '''Return a string describing the signature of a callable object, or ''. + + For Python-coded functions and methods, the first line is introspected. + Delete 'self' parameter for classes (.__init__) and bound methods. + The next lines are the first lines of the doc string up to the first + empty line or _MAX_LINES. For builtins, this typically includes + the arguments in addition to the return value. + ''' + argspec = default = "" + try: + ob_call = ob.__call__ + except BaseException: + return default + + fob = ob_call if isinstance(ob_call, types.MethodType) else ob + + try: + argspec = str(inspect.signature(fob)) + except ValueError as err: + msg = str(err) + if msg.startswith(_invalid_method): + return _invalid_method + + if '/' in argspec: + """Using AC's positional argument should add the explain""" + argspec += _argument_positional + if isinstance(fob, type) and argspec == '()': + """fob with no argument, use default callable argspec""" + argspec = _default_callable_argspec + + lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT) + if len(argspec) > _MAX_COLS else [argspec] if argspec else []) + + if isinstance(ob_call, types.MethodType): + doc = ob_call.__doc__ + else: + doc = getattr(ob, "__doc__", "") + if doc: + for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]: + line = line.strip() + if not line: + break + if len(line) > _MAX_COLS: + line = line[: _MAX_COLS - 3] + '...' + lines.append(line) + argspec = '\n'.join(lines) + if not argspec: + argspec = _default_callable_argspec + return argspec + + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_calltip', verbosity=2) diff --git a/lib-python/3/idlelib/calltip_w.py b/lib-python/3/idlelib/calltip_w.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/calltip_w.py @@ -0,0 +1,200 @@ +"""A call-tip window class for Tkinter/IDLE. + +After tooltip.py, which uses ideas gleaned from PySol. +Used by calltip.py. +""" +from tkinter import Label, LEFT, SOLID, TclError + +from idlelib.tooltip import TooltipBase + +HIDE_EVENT = "<>" +HIDE_SEQUENCES = ("", "") +CHECKHIDE_EVENT = "<>" +CHECKHIDE_SEQUENCES = ("", "") +CHECKHIDE_TIME = 100 # milliseconds + +MARK_RIGHT = "calltipwindowregion_right" + + +class CalltipWindow(TooltipBase): + """A call-tip widget for tkinter text widgets.""" + + def __init__(self, text_widget): + """Create a call-tip; shown by showtip(). + + text_widget: a Text widget with code for which call-tips are desired + """ + # Note: The Text widget will be accessible as self.anchor_widget + super(CalltipWindow, self).__init__(text_widget) + + self.label = self.text = None + self.parenline = self.parencol = self.lastline = None + self.hideid = self.checkhideid = None + self.checkhide_after_id = None + + def get_position(self): + """Choose the position of the call-tip.""" + curline = int(self.anchor_widget.index("insert").split('.')[0]) + if curline == self.parenline: + anchor_index = (self.parenline, self.parencol) + else: + anchor_index = (curline, 0) + box = self.anchor_widget.bbox("%d.%d" % anchor_index) + if not box: + box = list(self.anchor_widget.bbox("insert")) + # align to left of window + box[0] = 0 + box[2] = 0 + return box[0] + 2, box[1] + box[3] + + def position_window(self): + "Reposition the window if needed." + curline = int(self.anchor_widget.index("insert").split('.')[0]) + if curline == self.lastline: + return + self.lastline = curline + self.anchor_widget.see("insert") + super(CalltipWindow, self).position_window() + + def showtip(self, text, parenleft, parenright): + """Show the call-tip, bind events which will close it and reposition it. + + text: the text to display in the call-tip + parenleft: index of the opening parenthesis in the text widget + parenright: index of the closing parenthesis in the text widget, + or the end of the line if there is no closing parenthesis + """ + # Only called in calltip.Calltip, where lines are truncated + self.text = text + if self.tipwindow or not self.text: + return + + self.anchor_widget.mark_set(MARK_RIGHT, parenright) + self.parenline, self.parencol = map( + int, self.anchor_widget.index(parenleft).split(".")) + + super(CalltipWindow, self).showtip() + + self._bind_events() + + def showcontents(self): + """Create the call-tip widget.""" + self.label = Label(self.tipwindow, text=self.text, justify=LEFT, + background="#ffffe0", relief=SOLID, borderwidth=1, + font=self.anchor_widget['font']) + self.label.pack() + + def checkhide_event(self, event=None): + """Handle CHECK_HIDE_EVENT: call hidetip or reschedule.""" + if not self.tipwindow: + # If the event was triggered by the same event that unbound + # this function, the function will be called nevertheless, + # so do nothing in this case. + return None + + # Hide the call-tip if the insertion cursor moves outside of the + # parenthesis. + curline, curcol = map(int, self.anchor_widget.index("insert").split('.')) + if curline < self.parenline or \ + (curline == self.parenline and curcol <= self.parencol) or \ + self.anchor_widget.compare("insert", ">", MARK_RIGHT): + self.hidetip() + return "break" + + # Not hiding the call-tip. + + self.position_window() + # Re-schedule this function to be called again in a short while. + if self.checkhide_after_id is not None: + self.anchor_widget.after_cancel(self.checkhide_after_id) + self.checkhide_after_id = \ + self.anchor_widget.after(CHECKHIDE_TIME, self.checkhide_event) + return None + + def hide_event(self, event): + """Handle HIDE_EVENT by calling hidetip.""" + if not self.tipwindow: + # See the explanation in checkhide_event. + return None + self.hidetip() + return "break" + + def hidetip(self): + """Hide the call-tip.""" + if not self.tipwindow: + return + + try: + self.label.destroy() + except TclError: + pass + self.label = None + + self.parenline = self.parencol = self.lastline = None + try: + self.anchor_widget.mark_unset(MARK_RIGHT) + except TclError: + pass + + try: + self._unbind_events() + except (TclError, ValueError): + # ValueError may be raised by MultiCall + pass + + super(CalltipWindow, self).hidetip() + + def _bind_events(self): + """Bind event handlers.""" + self.checkhideid = self.anchor_widget.bind(CHECKHIDE_EVENT, + self.checkhide_event) + for seq in CHECKHIDE_SEQUENCES: + self.anchor_widget.event_add(CHECKHIDE_EVENT, seq) + self.anchor_widget.after(CHECKHIDE_TIME, self.checkhide_event) + self.hideid = self.anchor_widget.bind(HIDE_EVENT, + self.hide_event) + for seq in HIDE_SEQUENCES: + self.anchor_widget.event_add(HIDE_EVENT, seq) + + def _unbind_events(self): + """Unbind event handlers.""" + for seq in CHECKHIDE_SEQUENCES: + self.anchor_widget.event_delete(CHECKHIDE_EVENT, seq) + self.anchor_widget.unbind(CHECKHIDE_EVENT, self.checkhideid) + self.checkhideid = None + for seq in HIDE_SEQUENCES: + self.anchor_widget.event_delete(HIDE_EVENT, seq) + self.anchor_widget.unbind(HIDE_EVENT, self.hideid) + self.hideid = None + + +def _calltip_window(parent): # htest # + from tkinter import Toplevel, Text, LEFT, BOTH + + top = Toplevel(parent) + top.title("Test call-tips") + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("250x100+%d+%d" % (x + 175, y + 150)) + text = Text(top) + text.pack(side=LEFT, fill=BOTH, expand=1) + text.insert("insert", "string.split") + top.update() + + calltip = CalltipWindow(text) + def calltip_show(event): + calltip.showtip("(s='Hello world')", "insert", "end") + def calltip_hide(event): + calltip.hidetip() + text.event_add("<>", "(") + text.event_add("<>", ")") + text.bind("<>", calltip_show) + text.bind("<>", calltip_hide) + + text.focus_set() + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_calltip_w', verbosity=2, exit=False) + + from idlelib.idle_test.htest import run + run(_calltip_window) diff --git a/lib-python/3/idlelib/codecontext.py b/lib-python/3/idlelib/codecontext.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/codecontext.py @@ -0,0 +1,240 @@ +"""codecontext - display the block context above the edit window + +Once code has scrolled off the top of a window, it can be difficult to +determine which block you are in. This extension implements a pane at the top +of each IDLE edit window which provides block structure hints. These hints are +the lines which contain the block opening keywords, e.g. 'if', for the +enclosing block. The number of hint lines is determined by the maxlines +variable in the codecontext section of config-extensions.def. Lines which do +not open blocks are not shown in the context hints pane. + +""" +import re +from sys import maxsize as INFINITY + +import tkinter +from tkinter.constants import TOP, LEFT, X, W, SUNKEN + +from idlelib.config import idleConf + +BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for", + "if", "try", "while", "with", "async"} +UPDATEINTERVAL = 100 # millisec +CONFIGUPDATEINTERVAL = 1000 # millisec + + +def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")): + "Extract the beginning whitespace and first word from codeline." + return c.match(codeline).groups() + + +def get_line_info(codeline): + """Return tuple of (line indent value, codeline, block start keyword). + + The indentation of empty lines (or comment lines) is INFINITY. + If the line does not start a block, the keyword value is False. + """ + spaces, firstword = get_spaces_firstword(codeline) + indent = len(spaces) + if len(codeline) == indent or codeline[indent] == '#': + indent = INFINITY + opener = firstword in BLOCKOPENERS and firstword + return indent, codeline, opener + + +class CodeContext: + "Display block context above the edit window." + + def __init__(self, editwin): + """Initialize settings for context block. + + editwin is the Editor window for the context block. + self.text is the editor window text widget. + self.textfont is the editor window font. + + self.context displays the code context text above the editor text. + Initially None, it is toggled via <>. + self.topvisible is the number of the top text line displayed. + self.info is a list of (line number, indent level, line text, + block keyword) tuples for the block structure above topvisible. + self.info[0] is initialized with a 'dummy' line which + starts the toplevel 'block' of the module. + + self.t1 and self.t2 are two timer events on the editor text widget to + monitor for changes to the context text or editor font. + """ + self.editwin = editwin + self.text = editwin.text + self.textfont = self.text["font"] + self.contextcolors = CodeContext.colors + self.context = None + self.topvisible = 1 + self.info = [(0, -1, "", False)] + # Start two update cycles, one for context lines, one for font changes. + self.t1 = self.text.after(UPDATEINTERVAL, self.timer_event) + self.t2 = self.text.after(CONFIGUPDATEINTERVAL, self.config_timer_event) + + @classmethod + def reload(cls): + "Load class variables from config." + cls.context_depth = idleConf.GetOption("extensions", "CodeContext", + "maxlines", type="int", default=15) + cls.colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context') + + def __del__(self): + "Cancel scheduled events." + try: + self.text.after_cancel(self.t1) + self.text.after_cancel(self.t2) + except: + pass + + def toggle_code_context_event(self, event=None): + """Toggle code context display. + + If self.context doesn't exist, create it to match the size of the editor + window text (toggle on). If it does exist, destroy it (toggle off). + Return 'break' to complete the processing of the binding. + """ + if not self.context: + # Calculate the border width and horizontal padding required to + # align the context with the text in the main Text widget. + # + # All values are passed through getint(), since some + # values may be pixel objects, which can't simply be added to ints. + widgets = self.editwin.text, self.editwin.text_frame + # Calculate the required horizontal padding and border width. + padx = 0 + border = 0 + for widget in widgets: + padx += widget.tk.getint(widget.pack_info()['padx']) From pypy.commits at gmail.com Tue Feb 19 02:45:32 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 18 Feb 2019 23:45:32 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge heads Message-ID: <5c6bb41c.1c69fb81.b6750.9170@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96084:56db4a646df6 Date: 2019-02-19 09:43 +0200 http://bitbucket.org/pypy/pypy/changeset/56db4a646df6/ Log: merge heads diff too long, truncating to 2000 out of 29000 lines diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -58,3 +58,12 @@ 3f6eaa010fce78cc7973bdc1dfdb95970f08fed2 release-pypy3.5-v5.10.1 ab0b9caf307db6592905a80b8faffd69b39005b8 release-pypy2.7-v6.0.0 fdd60ed87e941677e8ea11acf9f1819466521bf2 release-pypy3.5-v6.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +9112c8071614108b1042bfef0713915107004d62 release-pypy2.7-v7.0.0 +c8805ee6d7846ca2722b106eeaa2f128c699aba3 release-pypy2.7-v7.0.0 +1f86f25937b6ae6c8b25236c35228fac587678bf release-pypy3.5-v7.0.0 +928a4f70d3de7d17449456946154c5da6e600162 release-pypy3.5-v7.0.0 +dab365a465140aa79a5f3ba4db784c4af4d5c195 release-pypy3.6-v7.0.0 +fb40f7a5524c77b80e6c468e087d621610137261 release-pypy3.6-v7.0.0 diff --git a/TODO b/TODO new file mode 100644 --- /dev/null +++ b/TODO @@ -0,0 +1,20 @@ +* find a better way to run "find" without creating the index storage, if one + if one is not already readily available (understand cost now, improve after merge) +* improve performance of splitlines (CF) +* think about cost of utf8 list strategy (CF) +* revisit why runicode import str_decode_utf_8_impl needed instead of runicode + import str_decode_utf_8 +* revisit remaining places in win32 where we do utf8.decode('utf-8'), they should work + directly with utf8 (can be converted via runicode.str_decode_utf_8 as well) + - rutf8.utf8_encode_mbcs + - unicodehelper.fsencode + - _winreg.interp_winreg +* remove 'assert not isinstance(*, unicode) +* add a flag that prevents support for unicode in rpython and enable it in PyPy (CF, Armin) +* convert all realunicode_w to unicode_w after we flush out all old uses of + unicode_w +* review all uses of W_Unicode.text_w, right now it is exactly W_Unicode.utf8_w. + It shoud only return valid utf8 (see 0be26dc39a59 which broke translation on + win32 and failed tests on linux64). Then we can use it in places like + _socket.interp_func.getaddrinfo instead of space.encode_unicode_object(w_port, + 'utf-8', 'strict') diff --git a/extra_tests/cffi_tests/cffi0/test_ownlib.py b/extra_tests/cffi_tests/cffi0/test_ownlib.py --- a/extra_tests/cffi_tests/cffi0/test_ownlib.py +++ b/extra_tests/cffi_tests/cffi0/test_ownlib.py @@ -352,6 +352,8 @@ def test_modify_struct_value(self): if self.module is None: py.test.skip("fix the auto-generation of the tiny test lib") + if self.Backend is CTypesBackend: + py.test.skip("fails with the ctypes backend on some architectures") ffi = FFI(backend=self.Backend()) ffi.cdef(""" typedef struct { diff --git a/extra_tests/cffi_tests/embedding/thread3-test.c b/extra_tests/cffi_tests/embedding/thread3-test.c --- a/extra_tests/cffi_tests/embedding/thread3-test.c +++ b/extra_tests/cffi_tests/embedding/thread3-test.c @@ -52,5 +52,6 @@ assert(status == 0); } printf("done\n"); + fflush(stdout); /* this is occasionally needed on Windows */ return 0; } diff --git a/lib-python/3/idlelib/calltips.py b/lib-python/3/idlelib/calltips.py new file mode 100644 --- /dev/null +++ b/lib-python/3/idlelib/calltips.py @@ -0,0 +1,175 @@ +"""calltips.py - An IDLE Extension to Jog Your Memory + +Call Tips are floating windows which display function, class, and method +parameter and docstring information when you type an opening parenthesis, and +which disappear when you type a closing parenthesis. + +""" +import inspect +import re +import sys +import textwrap +import types + +from idlelib import calltip_w +from idlelib.hyperparser import HyperParser +import __main__ + +class CallTips: + + menudefs = [ + ('edit', [ + ("Show call tip", "<>"), + ]) + ] + + def __init__(self, editwin=None): + if editwin is None: # subprocess and test + self.editwin = None + else: + self.editwin = editwin + self.text = editwin.text + self.active_calltip = None + self._calltip_window = self._make_tk_calltip_window + + def close(self): + self._calltip_window = None + + def _make_tk_calltip_window(self): + # See __init__ for usage + return calltip_w.CallTip(self.text) + + def _remove_calltip_window(self, event=None): + if self.active_calltip: + self.active_calltip.hidetip() + self.active_calltip = None + + def force_open_calltip_event(self, event): + "The user selected the menu entry or hotkey, open the tip." + self.open_calltip(True) + + def try_open_calltip_event(self, event): + """Happens when it would be nice to open a CallTip, but not really + necessary, for example after an opening bracket, so function calls + won't be made. + """ + self.open_calltip(False) + + def refresh_calltip_event(self, event): + if self.active_calltip and self.active_calltip.is_active(): + self.open_calltip(False) + + def open_calltip(self, evalfuncs): + self._remove_calltip_window() + + hp = HyperParser(self.editwin, "insert") + sur_paren = hp.get_surrounding_brackets('(') + if not sur_paren: + return + hp.set_index(sur_paren[0]) + expression = hp.get_expression() + if not expression: + return + if not evalfuncs and (expression.find('(') != -1): + return + argspec = self.fetch_tip(expression) + if not argspec: + return + self.active_calltip = self._calltip_window() + self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1]) + + def fetch_tip(self, expression): + """Return the argument list and docstring of a function or class. + + If there is a Python subprocess, get the calltip there. Otherwise, + either this fetch_tip() is running in the subprocess or it was + called in an IDLE running without the subprocess. + + The subprocess environment is that of the most recently run script. If + two unrelated modules are being edited some calltips in the current + module may be inoperative if the module was not the last to run. + + To find methods, fetch_tip must be fed a fully qualified name. + + """ + try: + rpcclt = self.editwin.flist.pyshell.interp.rpcclt + except AttributeError: + rpcclt = None + if rpcclt: + return rpcclt.remotecall("exec", "get_the_calltip", + (expression,), {}) + else: + return get_argspec(get_entity(expression)) + +def get_entity(expression): + """Return the object corresponding to expression evaluated + in a namespace spanning sys.modules and __main.dict__. + """ + if expression: + namespace = sys.modules.copy() + namespace.update(__main__.__dict__) + try: + return eval(expression, namespace) + except BaseException: + # An uncaught exception closes idle, and eval can raise any + # exception, especially if user classes are involved. + return None + +# The following are used in get_argspec and some in tests +_MAX_COLS = 85 +_MAX_LINES = 5 # enough for bytes +_INDENT = ' '*4 # for wrapped signatures +_first_param = re.compile(r'(?<=\()\w*\,?\s*') +_default_callable_argspec = "See source or doc" + + +def get_argspec(ob): + '''Return a string describing the signature of a callable object, or ''. + + For Python-coded functions and methods, the first line is introspected. + Delete 'self' parameter for classes (.__init__) and bound methods. + The next lines are the first lines of the doc string up to the first + empty line or _MAX_LINES. For builtins, this typically includes + the arguments in addition to the return value. + ''' + argspec = "" + try: + ob_call = ob.__call__ + except BaseException: + return argspec + if isinstance(ob, type): + fob = ob.__init__ + elif isinstance(ob_call, types.MethodType): + fob = ob_call + else: + fob = ob + if isinstance(fob, (types.FunctionType, types.MethodType)): + argspec = inspect.formatargspec(*inspect.getfullargspec(fob)) + if (isinstance(ob, (type, types.MethodType)) or + isinstance(ob_call, types.MethodType)): + argspec = _first_param.sub("", argspec) + + lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT) + if len(argspec) > _MAX_COLS else [argspec] if argspec else []) + + if isinstance(ob_call, types.MethodType): + doc = ob_call.__doc__ + else: + doc = getattr(ob, "__doc__", "") + if doc: + for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]: + line = line.strip() + if not line: + break + if len(line) > _MAX_COLS: + line = line[: _MAX_COLS - 3] + '...' + lines.append(line) + argspec = '\n'.join(lines) + if not argspec: + argspec = _default_callable_argspec + return argspec + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_calltips', verbosity=2) 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.12.0 +Version: 1.12.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 @@ -5,8 +5,8 @@ from .error import CDefError, FFIError, VerificationError, VerificationMissing from .error import PkgConfigError -__version__ = "1.12.0" -__version_info__ = (1, 12, 0) +__version__ = "1.12.1" +__version_info__ = (1, 12, 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/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h --- a/lib_pypy/cffi/_cffi_include.h +++ b/lib_pypy/cffi/_cffi_include.h @@ -8,43 +8,20 @@ the same works for the other two macros. Py_DEBUG implies them, but not the other way around. - The implementation is messy (issue #350): on Windows, with _MSC_VER, - we have to define Py_LIMITED_API even before including pyconfig.h. - In that case, we guess what pyconfig.h will do to the macros above, - and check our guess after the #include. - - Note that on Windows, with CPython 3.x, you need virtualenv version - >= 16.0.0. Older versions don't copy PYTHON3.DLL. As a workaround - you can remove the definition of Py_LIMITED_API here. - - See also 'py_limited_api' in cffi/setuptools_ext.py. + Issue #350 is still open: on Windows, the code here causes it to link + with PYTHON36.DLL (for example) instead of PYTHON3.DLL. A fix was + attempted in 164e526a5515 and 14ce6985e1c3, but reverted: virtualenv + does not make PYTHON3.DLL available, and so the "correctly" compiled + version would not run inside a virtualenv. We will re-apply the fix + after virtualenv has been fixed for some time. For explanation, see + issue #355. For a workaround if you want PYTHON3.DLL and don't worry + about virtualenv, see issue #350. See also 'py_limited_api' in + setuptools_ext.py. */ #if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) -# ifdef _MSC_VER -# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif -# include - /* sanity-check: Py_LIMITED_API will cause crashes if any of these - are also defined. Normally, the Python file PC/pyconfig.h does not - cause any of these to be defined, with the exception that _DEBUG - causes Py_DEBUG. Double-check that. */ -# ifdef Py_LIMITED_API -# if defined(Py_DEBUG) -# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" -# endif -# if defined(Py_TRACE_REFS) -# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" -# endif -# if defined(Py_REF_DEBUG) -# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" -# endif -# endif -# else -# include -# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) -# define Py_LIMITED_API -# endif +# include +# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) +# define Py_LIMITED_API # endif #endif 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 @@ -221,7 +221,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.12.0" + "\ncompiled with cffi version: 1.12.1" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); 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 @@ -81,8 +81,14 @@ it doesn't so far, creating troubles. That's why we check for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) + + On Windows, with CPython <= 3.4, it's better not to use py_limited_api + because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. + For now we'll skip py_limited_api on all Windows versions to avoid an + inconsistent mess. """ - if 'py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount'): + if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') + and sys.platform != 'win32'): import setuptools try: setuptools_major_version = int(setuptools.__version__.partition('.')[0]) diff --git a/pypy/TODO b/pypy/TODO --- a/pypy/TODO +++ b/pypy/TODO @@ -1,6 +1,3 @@ -... - - antocuni's older TODO: * run coverage against the parser/astbuilder/astcompiler: it's probably full of @@ -11,3 +8,5 @@ * re-enable BUILD_LIST_FROM_ARG: see the comment in astcompiler/codegen.py in ast.ListComp.build_container + +* review use of std_decode_utf8, we probably do not want to be using it diff --git a/pypy/doc/extending.rst b/pypy/doc/extending.rst --- a/pypy/doc/extending.rst +++ b/pypy/doc/extending.rst @@ -45,16 +45,13 @@ with the `CPython ctypes`_ version. It works for large examples, such as pyglet. PyPy's implementation is not strictly 100% compatible with CPython, but close enough for most cases. - -We also used to provide ``ctypes-configure`` for some API-level access. -This is now viewed as a precursor of CFFI, which you should use instead. More (but older) information is available :doc:`here `. Also, ctypes' performance is not as good as CFFI's. .. _CPython ctypes: http://docs.python.org/library/ctypes.html PyPy implements ctypes as pure Python code around two built-in modules -called ``_ffi`` and ``_rawffi``, which give a very low-level binding to +called ``_rawffi`` and ``_rawffi.alt``, which give a very low-level binding to the C library libffi_. Nowadays it is not recommended to use directly these two modules. diff --git a/pypy/doc/index.rst b/pypy/doc/index.rst --- a/pypy/doc/index.rst +++ b/pypy/doc/index.rst @@ -103,7 +103,7 @@ the `development mailing list`_. .. _#pypy on irc.freenode.net: irc://irc.freenode.net/pypy -.. _here: https://botbot.me/freenode/pypy/ +.. _here: https://quodlibet.duckdns.org/irc/pypy/latest.log.html#irc-end .. _Development mailing list: http://mail.python.org/mailman/listinfo/pypy-dev .. _Commit mailing list: http://mail.python.org/mailman/listinfo/pypy-commit .. _Development bug/feature tracker: https://bitbucket.org/pypy/pypy/issues diff --git a/pypy/doc/release-v7.0.0.rst b/pypy/doc/release-v7.0.0.rst --- a/pypy/doc/release-v7.0.0.rst +++ b/pypy/doc/release-v7.0.0.rst @@ -19,11 +19,12 @@ Until we can work with downstream providers to distribute builds with PyPy, we have made packages for some common packages `available as wheels`_. -The GC `hooks`_ , which can be used to gain more insights into its +The `GC hooks`_ , which can be used to gain more insights into its performance, has been improved and it is now possible to manually manage the GC by using a combination of ``gc.disable`` and ``gc.collect_step``. See the `GC blog post`_. +.. _`GC hooks`: http://doc.pypy.org/en/latest/gc_info.html#semi-manual-gc-management We updated the `cffi`_ module included in PyPy to version 1.12, and the `cppyy`_ backend to 1.4. Please use these to wrap your C and C++ code, @@ -39,7 +40,7 @@ The utf8 branch that changes internal representation of unicode to utf8 did not make it into the release, so there is still more goodness coming. -You can download the v6.0 releases here: +You can download the v7.0 releases here: http://pypy.org/download.html @@ -49,7 +50,7 @@ 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 +and `RPython`_ documentation improvements, tweaking popular modules to run on pypy, or general `help`_ with making RPython's JIT even better. .. _`PyPy`: index.html 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 @@ -4,3 +4,31 @@ .. this is a revision shortly after release-pypy-7.0.0 .. startrev: 481c69f7d81f + +.. branch: zlib-copying-third-time-a-charm + +Make sure zlib decompressobjs have their streams deallocated immediately +on flush. + +.. branch: zlib-copying-redux + +Fix calling copy on already-flushed compressobjs. + + + +.. branch: math-improvements + +Improve performance of long operations where one of the operands fits into +an int. + +.. branch: regalloc-playground + +Improve register allocation in the JIT. + +.. branch: promote-unicode + +Implement rlib.jit.promote_unicode to complement promote_string + +.. branch: unicode-utf8 + +Use utf8 internally to represent unicode, with the goal of never using rpython-level unicode diff --git a/pypy/doc/whatsnew-pypy2-5.10.0.rst b/pypy/doc/whatsnew-pypy2-5.10.0.rst --- a/pypy/doc/whatsnew-pypy2-5.10.0.rst +++ b/pypy/doc/whatsnew-pypy2-5.10.0.rst @@ -1,42 +1,42 @@ -========================== -What's new in PyPy2.7 5.10 -========================== - -.. this is a revision shortly after release-pypy2.7-v5.9.0 -.. startrev:d56dadcef996 - - -.. branch: cppyy-packaging - -Cleanup and improve cppyy packaging - -.. branch: docs-osx-brew-openssl - -.. branch: keep-debug-symbols - -Add a smartstrip tool, which can optionally keep the debug symbols in a -separate file, instead of just stripping them away. Use it in packaging - -.. branch: bsd-patches - -Fix failures on FreeBSD, contributed by David Naylor as patches on the issue -tracker (issues 2694, 2695, 2696, 2697) - -.. branch: run-extra-tests - -Run extra_tests/ in buildbot - -.. branch: vmprof-0.4.10 - -Upgrade the _vmprof backend to vmprof 0.4.10 - -.. branch: fix-vmprof-stacklet-switch -.. branch: fix-vmprof-stacklet-switch-2 - -Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) - -.. branch: win32-vcvars - -.. branch: rdict-fast-hash - -Make it possible to declare that the hash function of an r_dict is fast in RPython. +========================== +What's new in PyPy2.7 5.10 +========================== + +.. this is a revision shortly after release-pypy2.7-v5.9.0 +.. startrev:d56dadcef996 + + +.. branch: cppyy-packaging + +Cleanup and improve cppyy packaging + +.. branch: docs-osx-brew-openssl + +.. branch: keep-debug-symbols + +Add a smartstrip tool, which can optionally keep the debug symbols in a +separate file, instead of just stripping them away. Use it in packaging + +.. branch: bsd-patches + +Fix failures on FreeBSD, contributed by David Naylor as patches on the issue +tracker (issues 2694, 2695, 2696, 2697) + +.. branch: run-extra-tests + +Run extra_tests/ in buildbot + +.. branch: vmprof-0.4.10 + +Upgrade the _vmprof backend to vmprof 0.4.10 + +.. branch: fix-vmprof-stacklet-switch +.. branch: fix-vmprof-stacklet-switch-2 + +Fix a vmprof+continulets (i.e. greenelts, eventlet, gevent, ...) + +.. branch: win32-vcvars + +.. branch: rdict-fast-hash + +Make it possible to declare that the hash function of an r_dict is fast in RPython. diff --git a/pypy/doc/whatsnew-pypy2-6.0.0.rst b/pypy/doc/whatsnew-pypy2-6.0.0.rst --- a/pypy/doc/whatsnew-pypy2-6.0.0.rst +++ b/pypy/doc/whatsnew-pypy2-6.0.0.rst @@ -1,132 +1,128 @@ -=========================== -What's new in PyPy2.7 5.10+ -=========================== - -.. this is a revision shortly after release-pypy2.7-v5.10.0 -.. startrev: 6b024edd9d12 - -.. branch: cpyext-avoid-roundtrip - -Big refactoring of some cpyext code, which avoids a lot of nonsense when -calling C from Python and vice-versa: the result is a big speedup in -function/method calls, up to 6 times faster. - -.. branch: cpyext-datetime2 - -Support ``tzinfo`` field on C-API datetime objects, fixes latest pandas HEAD - - -.. branch: mapdict-size-limit - -Fix a corner case of mapdict: When an instance is used like a dict (using -``setattr`` and ``getattr``, or ``.__dict__``) and a lot of attributes are -added, then the performance using mapdict is linear in the number of -attributes. This is now fixed (by switching to a regular dict after 80 -attributes). - - -.. branch: cpyext-faster-arg-passing - -When using cpyext, improve the speed of passing certain objects from PyPy to C -code, most notably None, True, False, types, all instances of C-defined types. -Before, a dict lookup was needed every time such an object crossed over, now it -is just a field read. - - -.. branch: 2634_datetime_timedelta_performance - -Improve datetime + timedelta performance. - -.. branch: memory-accounting - -Improve way to describe memory - -.. branch: msvc14 - -Allow compilaiton with Visual Studio 2017 compiler suite on windows - -.. branch: winapi - -Update _winapi and internal _winbase_cffi (via _winbase_build) for python 3 - -.. branch: refactor-slots - -Refactor cpyext slots. - - -.. branch: call-loopinvariant-into-bridges - -Speed up branchy code that does a lot of function inlining by saving one call -to read the TLS in most bridges. - -.. branch: rpython-sprint - -Refactor in rpython signatures - -.. branch: cpyext-tls-operror2 - -Store error state thread-locally in executioncontext, fixes issue #2764 - -.. branch: cpyext-fast-typecheck - -Optimize `Py*_Check` for `Bool`, `Float`, `Set`. Also refactor and simplify -`W_PyCWrapperObject` which is used to call slots from the C-API, greatly -improving microbenchmarks in https://github.com/antocuni/cpyext-benchmarks - - -.. branch: fix-sre-problems - -Fix two (unrelated) JIT bugs manifesting in the re module: - -- green fields are broken and were thus disabled, plus their usage removed from - the _sre implementation - -- in rare "trace is too long" situations, the JIT could break behaviour - arbitrarily. - -.. branch: jit-hooks-can-be-disabled - -Be more efficient about JIT hooks. Make it possible for the frontend to declare -that jit hooks are currently not enabled at all. in that case, the list of ops -does not have to be created in the case of the on_abort hook (which is -expensive). - - -.. branch: pyparser-improvements - -Improve speed of Python parser, improve ParseError messages slightly. - -.. branch: ioctl-arg-size - -Work around possible bugs in upstream ioctl users, like CPython allocate at -least 1024 bytes for the arg in calls to ``ioctl(fd, request, arg)``. Fixes -issue #2776 - -.. branch: cpyext-subclass-setattr - -Fix for python-level classes that inherit from C-API types, previously the -`w_obj` was not necessarily preserved throughout the lifetime of the `pyobj` -which led to cases where instance attributes were lost. Fixes issue #2793 - - -.. branch: pyparser-improvements-2 - -Improve line offsets that are reported by SyntaxError. Improve error messages -for a few situations, including mismatched parenthesis. - -.. branch: issue2752 - -Fix a rare GC bug that was introduced more than one year ago, but was -not diagnosed before issue #2752. - -.. branch: gc-hooks - -Introduce GC hooks, as documented in doc/gc_info.rst - -.. branch: gc-hook-better-timestamp - -Improve GC hooks - -.. branch: cppyy-packaging - -Update backend to 0.6.0 and support exceptions through wrappers +=========================== +What's new in PyPy2.7 5.10+ +=========================== + +.. this is a revision shortly after release-pypy2.7-v5.10.0 +.. startrev: 6b024edd9d12 + +.. branch: cpyext-avoid-roundtrip + +Big refactoring of some cpyext code, which avoids a lot of nonsense when +calling C from Python and vice-versa: the result is a big speedup in +function/method calls, up to 6 times faster. + +.. branch: cpyext-datetime2 + +Support ``tzinfo`` field on C-API datetime objects, fixes latest pandas HEAD + + +.. branch: mapdict-size-limit + +Fix a corner case of mapdict: When an instance is used like a dict (using +``setattr`` and ``getattr``, or ``.__dict__``) and a lot of attributes are +added, then the performance using mapdict is linear in the number of +attributes. This is now fixed (by switching to a regular dict after 80 +attributes). + + +.. branch: cpyext-faster-arg-passing + +When using cpyext, improve the speed of passing certain objects from PyPy to C +code, most notably None, True, False, types, all instances of C-defined types. +Before, a dict lookup was needed every time such an object crossed over, now it +is just a field read. + + +.. branch: 2634_datetime_timedelta_performance + +Improve datetime + timedelta performance. + +.. branch: memory-accounting + +Improve way to describe memory + +.. branch: msvc14 + +Allow compilaiton with Visual Studio 2017 compiler suite on windows + +.. branch: refactor-slots + +Refactor cpyext slots. + + +.. branch: call-loopinvariant-into-bridges + +Speed up branchy code that does a lot of function inlining by saving one call +to read the TLS in most bridges. + +.. branch: rpython-sprint + +Refactor in rpython signatures + +.. branch: cpyext-tls-operror2 + +Store error state thread-locally in executioncontext, fixes issue #2764 + +.. branch: cpyext-fast-typecheck + +Optimize `Py*_Check` for `Bool`, `Float`, `Set`. Also refactor and simplify +`W_PyCWrapperObject` which is used to call slots from the C-API, greatly +improving microbenchmarks in https://github.com/antocuni/cpyext-benchmarks + + +.. branch: fix-sre-problems + +Fix two (unrelated) JIT bugs manifesting in the re module: + +- green fields are broken and were thus disabled, plus their usage removed from + the _sre implementation + +- in rare "trace is too long" situations, the JIT could break behaviour + arbitrarily. + +.. branch: jit-hooks-can-be-disabled + +Be more efficient about JIT hooks. Make it possible for the frontend to declare +that jit hooks are currently not enabled at all. in that case, the list of ops +does not have to be created in the case of the on_abort hook (which is +expensive). + + +.. branch: pyparser-improvements + +Improve speed of Python parser, improve ParseError messages slightly. + +.. branch: ioctl-arg-size + +Work around possible bugs in upstream ioctl users, like CPython allocate at +least 1024 bytes for the arg in calls to ``ioctl(fd, request, arg)``. Fixes +issue #2776 + +.. branch: cpyext-subclass-setattr + +Fix for python-level classes that inherit from C-API types, previously the +`w_obj` was not necessarily preserved throughout the lifetime of the `pyobj` +which led to cases where instance attributes were lost. Fixes issue #2793 + + +.. branch: pyparser-improvements-2 + +Improve line offsets that are reported by SyntaxError. Improve error messages +for a few situations, including mismatched parenthesis. + +.. branch: issue2752 + +Fix a rare GC bug that was introduced more than one year ago, but was +not diagnosed before issue #2752. + +.. branch: gc-hooks + +Introduce GC hooks, as documented in doc/gc_info.rst + +.. branch: gc-hook-better-timestamp + +Improve GC hooks + +.. branch: cppyy-packaging + +Update backend to 0.6.0 and support exceptions through wrappers diff --git a/pypy/doc/whatsnew-pypy2-7.0.0.rst b/pypy/doc/whatsnew-pypy2-7.0.0.rst --- a/pypy/doc/whatsnew-pypy2-7.0.0.rst +++ b/pypy/doc/whatsnew-pypy2-7.0.0.rst @@ -1,73 +1,73 @@ -========================== -What's new in PyPy2.7 6.0+ -========================== - -.. this is a revision shortly after release-pypy-6.0.0 -.. startrev: e50e11af23f1 - -.. branch: cppyy-packaging - -Main items: vastly better template resolution and improved performance. In -detail: upgrade to backend 1.4, improved handling of templated methods and -functions (in particular automatic deduction of types), improved pythonization -interface, range of compatibility fixes for Python3, free functions now take -fast libffi path when possible, moves for strings (incl. from Python str), -easier/faster handling of std::vector by numpy, improved and faster object -identity preservation - -.. branch: socket_default_timeout_blockingness - -Make sure 'blocking-ness' of socket is set along with default timeout - -.. branch: crypt_h - -Include crypt.h for crypt() on Linux - -.. branch: gc-more-logging - -Log additional gc-minor and gc-collect-step info in the PYPYLOG - -.. branch: reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb - - -.. branch: pyparser-improvements-3 - -Small refactorings in the Python parser. - -.. branch: fix-readme-typo - -.. branch: py3.6-wordcode - -implement new wordcode instruction encoding on the 3.6 branch - -.. branch: socket_default_timeout_blockingness - -Backport CPython fix for possible shell injection issue in `distutils.spawn`, -https://bugs.python.org/issue34540 - -.. branch: cffi_dlopen_unicode - -Enable use of unicode file names in `dlopen` - -.. branch: rlock-in-rpython - -Backport CPython fix for `thread.RLock` - - -.. branch: expose-gc-time - -Make GC hooks measure time in seconds (as opposed to an opaque unit). - -.. branch: cleanup-test_lib_pypy - -Update most test_lib_pypy/ tests and move them to extra_tests/. - -.. branch: gc-disable - -Make it possible to manually manage the GC by using a combination of -gc.disable() and gc.collect_step(). Make sure to write a proper release -announcement in which we explain that existing programs could leak memory if -they run for too much time between a gc.disable()/gc.enable() +========================== +What's new in PyPy2.7 6.0+ +========================== + +.. this is a revision shortly after release-pypy-6.0.0 +.. startrev: e50e11af23f1 + +.. branch: cppyy-packaging + +Main items: vastly better template resolution and improved performance. In +detail: upgrade to backend 1.4, improved handling of templated methods and +functions (in particular automatic deduction of types), improved pythonization +interface, range of compatibility fixes for Python3, free functions now take +fast libffi path when possible, moves for strings (incl. from Python str), +easier/faster handling of std::vector by numpy, improved and faster object +identity preservation + +.. branch: socket_default_timeout_blockingness + +Make sure 'blocking-ness' of socket is set along with default timeout + +.. branch: crypt_h + +Include crypt.h for crypt() on Linux + +.. branch: gc-more-logging + +Log additional gc-minor and gc-collect-step info in the PYPYLOG + +.. branch: reverse-debugger + +The reverse-debugger branch has been merged. For more information, see +https://bitbucket.org/pypy/revdb + + +.. branch: pyparser-improvements-3 + +Small refactorings in the Python parser. + +.. branch: fix-readme-typo + +.. branch: py3.6-wordcode + +implement new wordcode instruction encoding on the 3.6 branch + +.. branch: socket_default_timeout_blockingness + +Backport CPython fix for possible shell injection issue in `distutils.spawn`, +https://bugs.python.org/issue34540 + +.. branch: cffi_dlopen_unicode + +Enable use of unicode file names in `dlopen` + +.. branch: rlock-in-rpython + +Backport CPython fix for `thread.RLock` + + +.. branch: expose-gc-time + +Make GC hooks measure time in seconds (as opposed to an opaque unit). + +.. branch: cleanup-test_lib_pypy + +Update most test_lib_pypy/ tests and move them to extra_tests/. + +.. branch: gc-disable + +Make it possible to manually manage the GC by using a combination of +gc.disable() and gc.collect_step(). Make sure to write a proper release +announcement in which we explain that existing programs could leak memory if +they run for too much time between a gc.disable()/gc.enable() diff --git a/pypy/doc/whatsnew-pypy3-5.10.0.rst b/pypy/doc/whatsnew-pypy3-5.10.0.rst --- a/pypy/doc/whatsnew-pypy3-5.10.0.rst +++ b/pypy/doc/whatsnew-pypy3-5.10.0.rst @@ -1,21 +1,7 @@ -========================= -What's new in PyPy3 5.9+ -========================= - -.. this is the revision after release-pypy3.5-5.9 -.. startrev: be41e3ac0a29 - -.. branch: sched_yield -Add sched_yield posix attribute - -.. branch: py3.5-appexec -Raise if space.is_true(space.appexec()) used in app level tests, fix tests -that did this - -.. branch: py3.5-mac-embedding -Download and patch dependencies when building cffi-based stdlib modules - -.. branch: os_lockf - -.. branch: py3.5-xattr -Add posix.*attr() functions +======================== +What's new in PyPy3 7.0+ +======================== + +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c + diff --git a/pypy/doc/whatsnew-pypy3-6.0.0.rst b/pypy/doc/whatsnew-pypy3-6.0.0.rst --- a/pypy/doc/whatsnew-pypy3-6.0.0.rst +++ b/pypy/doc/whatsnew-pypy3-6.0.0.rst @@ -1,28 +1,7 @@ -========================= -What's new in PyPy3 5.10+ -========================= +======================== +What's new in PyPy3 7.0+ +======================== -.. this is the revision after release-pypy3.5-v5.10 -.. startrev: 34c63fba0bba +.. this is the revision after release-pypy3.5-v7.0 +.. startrev: 9d2fa7c63b7c -.. branch: hroncok/fix-typeerror-str-does-not-support-the-b-1514414905375 - -Fix for bytestrings in console repl - -.. branch: py3-winreg - -Update winreg module to use unicode, wide-strings - -.. branch: cpyext-py3-instancemethod-attributes - -Add missing ``__doc__``, ``__module__``, ``__name__`` attributes to -``instancemethod`` - -.. branch: winapi - -Update support for _winapi cffi module for python3 - -.. branch: py3.5-refactor-slots - -Refactor cpyext slots. - diff --git a/pypy/doc/whatsnew-pypy3-7.0.0.rst b/pypy/doc/whatsnew-pypy3-7.0.0.rst --- a/pypy/doc/whatsnew-pypy3-7.0.0.rst +++ b/pypy/doc/whatsnew-pypy3-7.0.0.rst @@ -5,21 +5,16 @@ .. this is the revision after release-pypy3.5-v6.0 .. startrev: 580e3e26cd32 -.. branch: hroncok/fix-multiprocessing-regression-on-newer--1524656522151 +.. branch: unicode-utf8 -Fix multiprocessing regression on newer glibcs +Use utf-8 internally to represent unicode strings -.. branch: py3.5-user-site-impl +.. branch: unicode-utf8-py3 -Use implementation-specific site directories in sysconfig like in Python2 +Use utf-8 internally to represent unicode strings .. branch: alex_gaynor/remove-an-unneeded-call-into-openssl-th-1526429141011 Remove an unneeded call into OpenSSL, from cpython https://github.com/python/cpython/pull/6887 - -.. branch: py3.5-reverse-debugger - -The reverse-debugger branch has been merged. For more information, see -https://bitbucket.org/pypy/revdb 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 @@ -4,3 +4,7 @@ .. this is the revision after release-pypy3.5-v7.0 .. startrev: 9d2fa7c63b7c + +.. branch: unicode-utf8-py3 + +Use utf8 instead of rpython-level unicode \ No newline at end of file diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -83,7 +83,7 @@ ## con.interact() except OperationError as e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return 1 finally: @@ -91,7 +91,7 @@ space.finish() except OperationError as e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return 1 return exitcode @@ -148,7 +148,7 @@ except OperationError as e: if verbose: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return rffi.cast(rffi.INT, -1) finally: @@ -202,7 +202,7 @@ """) except OperationError as e: debug("OperationError:") - debug(" operror-type: " + e.w_type.getname(space).encode('utf-8')) + debug(" operror-type: " + e.w_type.getname(space)) debug(" operror-value: " + space.text_w(space.str(e.get_w_value(space)))) return -1 return 0 diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py --- a/pypy/interpreter/argument.py +++ b/pypy/interpreter/argument.py @@ -596,6 +596,10 @@ except IndexError: name = '?' else: + w_enc = space.newtext(space.sys.defaultencoding) + w_err = space.newtext("replace") + w_name = space.call_method(w_name, "encode", w_enc, + w_err) name = space.text_w(w_name) break self.kwd_name = name diff --git a/pypy/interpreter/astcompiler/astbuilder.py b/pypy/interpreter/astcompiler/astbuilder.py --- a/pypy/interpreter/astcompiler/astbuilder.py +++ b/pypy/interpreter/astcompiler/astbuilder.py @@ -58,6 +58,7 @@ self.space = space self.compile_info = compile_info self.root_node = n + # used in f-strings self.recursive_parser = recursive_parser def build_ast(self): diff --git a/pypy/interpreter/astcompiler/fstring.py b/pypy/interpreter/astcompiler/fstring.py --- a/pypy/interpreter/astcompiler/fstring.py +++ b/pypy/interpreter/astcompiler/fstring.py @@ -3,6 +3,7 @@ from pypy.interpreter import error from pypy.interpreter import unicodehelper from rpython.rlib.rstring import StringBuilder +from rpython.rlib.rutf8 import codepoints_in_utf8 def add_constant_string(astbuilder, joined_pieces, w_string, atom_node): @@ -21,10 +22,8 @@ joined_pieces.append(node(w_string, atom_node.get_lineno(), atom_node.get_column())) -def f_constant_string(astbuilder, joined_pieces, u, atom_node): - space = astbuilder.space - add_constant_string(astbuilder, joined_pieces, space.newunicode(u), - atom_node) +def f_constant_string(astbuilder, joined_pieces, w_u, atom_node): + add_constant_string(astbuilder, joined_pieces, w_u, atom_node) def f_string_compile(astbuilder, source, atom_node): # Note: a f-string is kept as a single literal up to here. @@ -259,20 +258,20 @@ i += 1 fstr.current_index = i + space = astbuilder.space literal = builder.build() + lgt = codepoints_in_utf8(literal) if not fstr.raw_mode and '\\' in literal: - space = astbuilder.space literal = parsestring.decode_unicode_utf8(space, literal, 0, len(literal)) - return unicodehelper.decode_unicode_escape(space, literal) - else: - return literal.decode('utf-8') + literal, lgt, pos = unicodehelper.decode_unicode_escape(space, literal) + return space.newtext(literal, lgt) def fstring_find_literal_and_expr(astbuilder, fstr, atom_node, rec): - # Return a tuple with the next literal part, and optionally the + # Return a tuple with the next literal part as a W_Unicode, and optionally the # following expression node. Updates the current index inside 'fstr'. - literal = fstring_find_literal(astbuilder, fstr, atom_node, rec) + w_u = fstring_find_literal(astbuilder, fstr, atom_node, rec) s = fstr.unparsed i = fstr.current_index @@ -284,7 +283,7 @@ # We must now be the start of an expression, on a '{'. assert s[i] == '{' expr = fstring_find_expr(astbuilder, fstr, atom_node, rec) - return literal, expr + return w_u, expr def parse_f_string(astbuilder, joined_pieces, fstr, atom_node, rec=0): @@ -303,11 +302,11 @@ "really the case", atom_node) while True: - literal, expr = fstring_find_literal_and_expr(astbuilder, fstr, + w_u, expr = fstring_find_literal_and_expr(astbuilder, fstr, atom_node, rec) # add the literal part - f_constant_string(astbuilder, joined_pieces, literal, atom_node) + f_constant_string(astbuilder, joined_pieces, w_u, atom_node) if expr is None: break # We're done with this f-string. diff --git a/pypy/interpreter/astcompiler/misc.py b/pypy/interpreter/astcompiler/misc.py --- a/pypy/interpreter/astcompiler/misc.py +++ b/pypy/interpreter/astcompiler/misc.py @@ -116,7 +116,7 @@ # only intern identifier-like strings from pypy.objspace.std.unicodeobject import _isidentifier if (space.is_w(space.type(w_const), space.w_unicode) and - _isidentifier(space.unicode_w(w_const))): + _isidentifier(space.utf8_w(w_const))): return space.new_interned_w_str(w_const) return w_const diff --git a/pypy/interpreter/astcompiler/optimize.py b/pypy/interpreter/astcompiler/optimize.py --- a/pypy/interpreter/astcompiler/optimize.py +++ b/pypy/interpreter/astcompiler/optimize.py @@ -5,7 +5,7 @@ from pypy.tool import stdlib_opcode as ops from pypy.interpreter.error import OperationError from rpython.rlib.unroll import unrolling_iterable -from rpython.rlib.runicode import MAXUNICODE +from rpython.rlib.rutf8 import MAXUNICODE from rpython.rlib.objectmodel import specialize @@ -329,7 +329,7 @@ # produce compatible pycs. if (self.space.isinstance_w(w_obj, self.space.w_unicode) and self.space.isinstance_w(w_const, self.space.w_unicode)): - #unistr = self.space.unicode_w(w_const) + #unistr = self.space.utf8_w(w_const) #if len(unistr) == 1: # ch = ord(unistr[0]) #else: diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py --- a/pypy/interpreter/astcompiler/test/test_compiler.py +++ b/pypy/interpreter/astcompiler/test/test_compiler.py @@ -1,5 +1,6 @@ from __future__ import division import py, sys +from pytest import raises from pypy.interpreter.astcompiler import codegen, astbuilder, symtable, optimize from pypy.interpreter.pyparser import pyparse from pypy.interpreter.pyparser.test import expressions @@ -75,7 +76,7 @@ space = self.space pyco_expr = space.createcompiler().compile(evalexpr, '', 'eval', 0) w_res = space.exec_(pyco_expr, w_dict, w_dict) - res = space.str_w(space.repr(w_res)) + res = space.text_w(space.repr(w_res)) expected_repr = self.get_py3_repr(expected) if isinstance(expected, float): # Float representation can vary a bit between interpreter @@ -1255,7 +1256,6 @@ def test_revdb_metavar(self): from pypy.interpreter.reverse_debugging import dbstate, setup_revdb - self.space.config.translation.reverse_debugger = True self.space.reverse_debugging = True try: setup_revdb(self.space) @@ -1270,9 +1270,6 @@ class AppTestCompiler: - def setup_class(cls): - cls.w_maxunicode = cls.space.wrap(sys.maxunicode) - def test_docstring_not_loaded(self): import io, dis, sys ns = {} @@ -1442,7 +1439,7 @@ ''', d) return d['f'](5) """) - assert 'generator' in space.str_w(space.repr(w_generator)) + assert 'generator' in space.text_w(space.repr(w_generator)) def test_folding_of_list_constants(self): for source in ( diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -3,7 +3,7 @@ from rpython.rlib.cache import Cache from rpython.tool.uid import HUGEVAL_BYTES -from rpython.rlib import jit, types +from rpython.rlib import jit, types, rutf8 from rpython.rlib.debug import make_sure_not_resized from rpython.rlib.objectmodel import (we_are_translated, newlist_hint, compute_unique_id, specialize, not_rpython) @@ -80,10 +80,10 @@ def getname(self, space): try: - return space.unicode_w(space.getattr(self, space.newtext('__name__'))) + return space.utf8_w(space.getattr(self, space.newtext('__name__'))) except OperationError as e: if e.match(space, space.w_TypeError) or e.match(space, space.w_AttributeError): - return u'?' + return '?' raise def getaddrstring(self, space): @@ -105,9 +105,9 @@ w_id = space.rshift(w_id, w_4) return ''.join(addrstring) - def getrepr(self, space, info, moreinfo=u''): - addrstring = unicode(self.getaddrstring(space)) - return space.newunicode(u"<%s at 0x%s%s>" % (info, addrstring, moreinfo)) + def getrepr(self, space, info, moreinfo=''): + addrstring = self.getaddrstring(space) + return space.newtext("<%s at 0x%s%s>" % (info, addrstring, moreinfo)) def getslotvalue(self, index): raise NotImplementedError @@ -245,11 +245,14 @@ def bytes_w(self, space): self._typed_unwrap_error(space, "bytes") - def unicode_w(self, space): - self._typed_unwrap_error(space, "string") + def text_w(self, space): + self._typed_unwrap_error(space, "unicode") - def text_w(self, space): - self._typed_unwrap_error(space, "string") + def utf8_w(self, space): + self._typed_unwrap_error(space, "unicode") + + def convert_to_w_unicode(self, space): + self._typed_unwrap_error(space, "unicode") def bytearray_list_of_chars_w(self, space): self._typed_unwrap_error(space, "bytearray") @@ -423,7 +426,7 @@ self.builtin_modules = {} self.reloading_modules = {} - self.interned_strings = make_weak_value_dictionary(self, unicode, W_Root) + self.interned_strings = make_weak_value_dictionary(self, str, W_Root) self.actionflag = ActionFlag() # changed by the signal module self.check_signal_action = None # changed by the signal module make_finalizer_queue(W_Root, self) @@ -784,12 +787,12 @@ def setitem_str(self, w_obj, key, w_value): # key is a "text", i.e. a byte string (in python3 it - # represents a utf-8-encoded unicode) + # represents a valid utf-8-encoded unicode) return self.setitem(w_obj, self.newtext(key), w_value) def finditem_str(self, w_obj, key): # key is a "text", i.e. a byte string (in python3 it - # represents a utf-8-encoded unicode) + # represents a valid utf-8-encoded unicode) return self.finditem(w_obj, self.newtext(key)) def finditem(self, w_obj, w_key): @@ -823,9 +826,9 @@ def new_interned_w_str(self, w_u): assert isinstance(w_u, W_Root) # and is not None - u = self.unicode_w(w_u) + u = self.utf8_w(w_u) if not we_are_translated(): - assert type(u) is unicode + assert type(u) is str w_u1 = self.interned_strings.get(u) if w_u1 is None: w_u1 = w_u @@ -838,12 +841,11 @@ # returns a "text" object (ie str in python2 and unicode in python3) if not we_are_translated(): assert type(s) is str - u = s.decode('utf-8') - w_s1 = self.interned_strings.get(u) + w_s1 = self.interned_strings.get(s) if w_s1 is None: - w_s1 = self.newunicode(u) + w_s1 = self.newtext(s) if self._side_effects_ok(): - self.interned_strings.set(u, w_s1) + self.interned_strings.set(s, w_s1) return w_s1 def _revdb_startup(self): @@ -882,11 +884,7 @@ # interface for marshal_impl if not we_are_translated(): assert type(s) is str - try: - u = s.decode('utf-8') - except UnicodeDecodeError: - return None - return self.interned_strings.get(u) # may be None + return self.interned_strings.get(s) # may be None @specialize.arg(1) def descr_self_interp_w(self, RequiredClass, w_obj): @@ -1069,7 +1067,7 @@ """ return None - def listview_unicode(self, w_list): + def listview_utf8(self, w_list): """ Return a list of unwrapped unicode out of a list of unicode. If the argument is not a list or does not contain only unicode, return None. May return None anyway. @@ -1099,8 +1097,15 @@ def newlist_bytes(self, list_s): return self.newlist([self.newbytes(s) for s in list_s]) - def newlist_unicode(self, list_u): - return self.newlist([self.newunicode(u) for u in list_u]) + def newlist_utf8(self, list_u, is_ascii): + l_w = [None] * len(list_u) + for i, item in enumerate(list_u): + if not is_ascii: + length = rutf8.check_utf8(item, True) + else: + length = len(item) + l_w[i] = self.newutf8(item, length) + return self.newlist(l_w) def newlist_int(self, list_i): return self.newlist([self.newint(i) for i in list_i]) @@ -1598,6 +1603,8 @@ else: assert False + if self.isinstance_w(w_obj, self.w_unicode): + return w_obj.charbuf_w(self) def text_or_none_w(self, w_obj): return None if self.is_none(w_obj) else self.text_w(w_obj) @@ -1620,18 +1627,22 @@ an utf-8 encoded rpython string. """ assert w_obj is not None + if not self.isinstance_w(w_obj, self.w_unicode): + w_obj._typed_unwrap_error(self, "unicode") return w_obj.text_w(self) @not_rpython # tests only; should be replaced with bytes_w or text_w def str_w(self, w_obj): """ - if w_obj is unicode, call text_w() (i.e., return the UTF-8-nosg + if w_obj is unicode, call utf8_w() (i.e., return the UTF-8-nosg encoded string). Else, call bytes_w(). We should kill str_w completely and manually substitute it with text_w/bytes_w at all call sites. It remains for now for tests only. """ + XXX # deprecated, leaving in place for clear errors if self.isinstance_w(w_obj, self.w_unicode): + # XXX lo text_w, but better to deprecate str_w than to fix this return w_obj.text_w(self) else: return w_obj.bytes_w(self) @@ -1714,23 +1725,38 @@ assert w_obj is not None return w_obj.float_w(self, allow_conversion) - @specialize.argtype(1) - def unicode_w(self, w_obj): - assert w_obj is not None - return w_obj.unicode_w(self) + def utf8_w(self, w_obj): + return w_obj.utf8_w(self) - def unicode0_w(self, w_obj): - "Like unicode_w, but rejects strings with NUL bytes." + def utf8_0_w(self, w_obj): + "Like utf_w, but rejects strings with NUL bytes." from rpython.rlib import rstring - result = w_obj.unicode_w(self) - if u'\x00' in result: + result = w_obj.utf8_w(self) + if '\x00' in result: + raise oefmt(self.w_TypeError, + "argument must be a string without NUL " + "characters") + return rstring.assert_str0(result) + + def convert_to_w_unicode(self, w_obj): + return w_obj.convert_to_w_unicode(self) + + def realunicode_w(self, w_obj): + from pypy.interpreter.unicodehelper import decode_utf8sp + utf8 = self.utf8_w(w_obj) + return decode_utf8sp(self, utf8)[0].decode('utf8') + + def utf8_0_w(self, w_obj): + "Like utf8_w, but rejects strings with NUL bytes." + from rpython.rlib import rstring + result = w_obj.utf8_w(self) + if '\x00' in result: raise oefmt(self.w_ValueError, - "argument must be a unicode string without NUL " + "argument must be a utf8 string without NUL " "characters") return rstring.assert_str0(result) realtext_w = text_w # Python 2 compatibility - realunicode_w = unicode_w def fsencode(space, w_obj): from pypy.interpreter.unicodehelper import fsencode @@ -1755,6 +1781,27 @@ w_obj = self.fsencode(w_obj) return self.bytesbuf0_w(w_obj) + def convert_arg_to_w_unicode(self, w_obj, strict=None): + # XXX why convert_to_w_unicode does something slightly different? + from pypy.objspace.std.unicodeobject import W_UnicodeObject + # for z_translation tests + if hasattr(self, 'is_fake_objspace'): return self.newtext("foobar") + return W_UnicodeObject.convert_arg_to_w_unicode(self, w_obj, strict) + + def utf8_len_w(self, w_obj): + w_obj = self.convert_arg_to_w_unicode(w_obj) + return w_obj._utf8, w_obj._len() + + def realutf8_w(self, w_obj): + # Like utf8_w(), but only works if w_obj is really of type + # 'unicode'. On Python 3 this is the same as utf8_w(). + from pypy.objspace.std.unicodeobject import W_UnicodeObject + # for z_translation tests + if hasattr(self, 'is_fake_objspace'): return self.newtext("foobar") + if not isinstance(w_obj, W_UnicodeObject): + raise oefmt(self.w_TypeError, "argument must be a unicode") + return self.utf8_w(w_obj) + def bytesbuf0_w(self, w_obj): # Like bytes0_w(), but also accept a read-only buffer. from rpython.rlib import rstring @@ -1777,7 +1824,7 @@ w_obj = fspath(self, w_obj) else: w_obj = self.fsdecode(w_obj) - return self.unicode0_w(w_obj) + return self.utf8_w(w_obj) def bool_w(self, w_obj): # Unwraps a bool, also accepting an int for compatibility. @@ -2105,7 +2152,7 @@ 'float_w', 'uint_w', 'bigint_w', - 'unicode_w', + 'utf8_w', 'unwrap', 'is_true', 'is_w', diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -9,8 +9,7 @@ from rpython.rlib.objectmodel import we_are_translated, specialize from rpython.rlib.objectmodel import dont_inline, not_rpython from rpython.rlib import rstack, rstackovf -from rpython.rlib import rwin32 -from rpython.rlib import runicode +from rpython.rlib import rwin32, rutf8 from pypy.interpreter import debug @@ -21,7 +20,8 @@ def strerror(errno): """Translate an error code to a unicode message string.""" from pypy.module._codecs.locale import str_decode_locale_surrogateescape - return str_decode_locale_surrogateescape(os.strerror(errno)) + utf8, lgt = str_decode_locale_surrogateescape(os.strerror(errno)) + return utf8, lgt class OperationError(Exception): """Interpreter-level exception that signals an exception that should be @@ -72,7 +72,7 @@ space = getattr(self.w_type, 'space', None) if space is not None: if self.__class__ is not OperationError and s is None: - s = self._compute_value(space) + s, lgt = self._compute_value(space) try: s = space.text_w(s) except Exception: @@ -306,8 +306,8 @@ def get_w_value(self, space): w_value = self._w_value if w_value is None: - value = self._compute_value(space) - self._w_value = w_value = space.newunicode(value) + value, lgt = self._compute_value(space) + self._w_value = w_value = space.newtext(value, lgt) return w_value def _compute_value(self, space): @@ -478,16 +478,7 @@ assert len(formats) > 0, "unsupported: no % command found" return tuple(parts), tuple(formats) -def _decode_utf8(string): - # when building the error message, don't crash if the byte string - # provided is not valid UTF-8 - assert isinstance(string, str) - result, consumed = runicode.str_decode_utf_8( - string, len(string), "replace", final=True) - return result - def get_operrcls2(valuefmt): - valuefmt = valuefmt.decode('ascii') strings, formats = decompose_valuefmt(valuefmt) assert len(strings) == len(formats) + 1 try: @@ -507,30 +498,51 @@ def _compute_value(self, space): lst = [None] * (len(formats) + len(formats) + 1) + lgt = 0 for i, fmt, attr in entries: lst[i + i] = self.xstrings[i] + lgt += len(self.xstrings[i]) value = getattr(self, attr) if fmt == 'd': - result = str(value).decode('ascii') + result = str(value) + lgt += len(result) elif fmt == 'R': - result = space.unicode_w(space.repr(value)) + s = space.repr(value) + result = space.utf8_w(s) + lgt += space.len_w(s) elif fmt == 'S': - result = space.unicode_w(space.str(value)) + s = space.str(value) + result = space.utf8_w(s) + lgt += space.len_w(s) elif fmt == 'T': - result = _decode_utf8(space.type(value).name) + result = space.type(value).name + lgt += rutf8.codepoints_in_utf8(result) elif fmt == 'N': result = value.getname(space) + lgt += len(result) elif fmt == '8': - result = _decode_utf8(value) + # u'str\uxxxx' -> 'str\xXX\xXX' -> u"'str\xXX\xXX'" + from pypy.interpreter import unicodehelper + result, _lgt, pos = unicodehelper.str_decode_utf8( + value, 'replace', True, + unicodehelper.decode_never_raise, True) + lgt += _lgt + elif isinstance(value, unicode): + # 's' + result = str(value.encode('utf-8')) + lgt += len(value) else: - if isinstance(value, unicode): - result = value - else: - result = _decode_utf8(str(value)) + result = str(value) + try: + lgt += rutf8.check_utf8(result, True) + except rutf8.CheckError as e: + lgt -= e.pos lst[i + i + 1] = result lst[-1] = self.xstrings[-1] - return u''.join(lst) - # + lgt += len(self.xstrings[-1]) + retval = ''.join(lst) + return retval, lgt + _fmtcache2[formats] = OpErrFmt return OpErrFmt, strings @@ -540,7 +552,7 @@ self.setup(w_type) def _compute_value(self, space): - return self._value.decode('utf-8') + return self._value, len(self._value) def async(self, space): # also matches a RuntimeError("maximum rec.") if the stack is @@ -571,8 +583,8 @@ %8 - The result of arg.decode('utf-8') %N - The result of w_arg.getname(space) - %R - The result of space.unicode_w(space.repr(w_arg)) - %S - The result of space.unicode_w(space.str(w_arg)) + %R - The result of space.utf8_w(space.repr(w_arg)) + %S - The result of space.utf8_w(space.str(w_arg)) %T - The result of space.type(w_arg).name """ @@ -627,12 +639,13 @@ if rwin32.WIN32 and isinstance(e, WindowsError): winerror = e.winerror try: - msg = rwin32.FormatErrorW(winerror) + msg, lgt = rwin32.FormatErrorW(winerror) except ValueError: - msg = u'Windows Error %d' % winerror + msg = 'Windows Error %d' % winerror + lgt = len(msg) w_errno = space.w_None w_winerror = space.newint(winerror) - w_msg = space.newunicode(msg) + w_msg = space.newtext(msg, lgt) else: errno = e.errno if errno == EINTR: @@ -641,12 +654,13 @@ return None try: - msg = strerror(errno) + msg, lgt = strerror(errno) except ValueError: - msg = u'error %d' % errno + msg = 'error %d' % errno + lgt = len(msg) w_errno = space.newint(errno) w_winerror = space.w_None - w_msg = space.newunicode(msg) + w_msg = space.newtext(msg, lgt) if w_filename is None: w_filename = space.w_None @@ -676,9 +690,9 @@ eintr_retry=eintr_retry) def exception_from_errno(space, w_type, errno): - msg = strerror(errno) + msg, lgt = strerror(errno) w_error = space.call_function(w_type, space.newint(errno), - space.newunicode(msg)) + space.newtext(msg, lgt)) return OperationError(w_type, w_error) def exception_from_saved_errno(space, w_type): diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py --- a/pypy/interpreter/function.py +++ b/pypy/interpreter/function.py @@ -45,7 +45,8 @@ closure=None, w_ann=None, forcename=None, qualname=None): self.space = space self.name = forcename or code.co_name - self.qualname = qualname or self.name.decode('utf-8') + self.qualname = qualname or self.name + assert isinstance(self.qualname, str) self.w_doc = None # lazily read from code.getdocstring() self.code = code # Code instance self.w_func_globals = w_globals # the globals dictionary @@ -255,7 +256,7 @@ return self.call_args(__args__) def descr_function_repr(self): - return self.getrepr(self.space, u'function %s' % self.qualname) + return self.getrepr(self.space, 'function %s' % self.qualname) def _cleanup_(self): @@ -313,7 +314,7 @@ tup_base = [] tup_state = [ space.newtext(self.name), - space.newunicode(self.qualname), + space.newtext(self.qualname), w_doc, self.code, w_func_globals, @@ -337,7 +338,7 @@ self.space = space self.name = space.text_w(w_name) - self.qualname = space.unicode_w(w_qualname) + self.qualname = space.utf8_w(w_qualname) self.code = space.interp_w(Code, w_code) if not space.is_w(w_closure, space.w_None): from pypy.interpreter.nestedscope import Cell @@ -430,11 +431,11 @@ "__name__ must be set to a string object") def fget_func_qualname(self, space): - return space.newunicode(self.qualname) + return space.newtext(self.qualname) def fset_func_qualname(self, space, w_name): try: - self.qualname = space.unicode_w(w_name) + self.qualname = space.realutf8_w(w_name) except OperationError as e: if e.match(space, space.w_TypeError): raise oefmt(space.w_TypeError, @@ -549,14 +550,14 @@ name = self.w_function.getname(self.space) else: try: - name = space.unicode_w(w_name) + name = space.utf8_w(w_name) except OperationError as e: if not e.match(space, space.w_TypeError): raise - name = u'?' - objrepr = space.unicode_w(space.repr(self.w_instance)) - s = u'' % (name, objrepr) - return space.newunicode(s) + name = '?' + objrepr = space.utf8_w(space.repr(self.w_instance)) + s = b'' % (name, objrepr) + return space.newtext(s) def descr_method_getattribute(self, w_attr): space = self.space @@ -598,7 +599,7 @@ else: w_builtins = space.getbuiltinmodule('builtins') new_inst = space.getattr(w_builtins, space.newtext('getattr')) - tup = [w_instance, space.newunicode(w_function.getname(space))] + tup = [w_instance, space.newtext(w_function.getname(space))] return space.newtuple([new_inst, space.newtuple(tup)]) @@ -699,7 +700,7 @@ return self.space.newtext('' % (self.name,)) def descr__reduce__(self, space): - return space.newunicode(self.qualname) + return space.newtext(self.qualname) def is_builtin_code(w_func): from pypy.interpreter.gateway import BuiltinCode diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py --- a/pypy/interpreter/gateway.py +++ b/pypy/interpreter/gateway.py @@ -174,6 +174,9 @@ def visit_unicode(self, el, app_sig): self.checked_space_method(el, app_sig) + def visit_utf8(self, el, app_sig): + self.checked_space_method(el, app_sig) + def visit_fsencode(self, el, app_sig): self.checked_space_method(el, app_sig) @@ -324,7 +327,10 @@ self.run_args.append("space.text0_w(%s)" % (self.scopenext(),)) def visit_unicode(self, typ): - self.run_args.append("space.unicode_w(%s)" % (self.scopenext(),)) + self.run_args.append("space.realunicode_w(%s)" % (self.scopenext(),)) + + def visit_utf8(self, typ): + self.run_args.append("space.utf8_w(%s)" % (self.scopenext(),)) def visit_fsencode(self, typ): self.run_args.append("space.fsencode_w(%s)" % (self.scopenext(),)) @@ -492,11 +498,14 @@ self.unwrap.append("space.text_w(%s)" % (self.nextarg(),)) def visit_unicode(self, typ): - self.unwrap.append("space.unicode_w(%s)" % (self.nextarg(),)) + self.unwrap.append("space.realunicode_w(%s)" % (self.nextarg(),)) def visit_text0(self, typ): self.unwrap.append("space.text0_w(%s)" % (self.nextarg(),)) + def visit_utf8(self, typ): + self.unwrap.append("space.utf8_w(%s)" % (self.nextarg(),)) + def visit_fsencode(self, typ): self.unwrap.append("space.fsencode_w(%s)" % (self.nextarg(),)) @@ -567,8 +576,10 @@ assert typ in (int, str, float, unicode, r_longlong, r_uint, r_ulonglong, bool) if typ is r_int is r_longlong: return 'gateway_r_longlong_w' - elif typ in (str, unicode): - return typ.__name__ + '_w' + elif typ is str: + return 'utf8_w' + elif typ is unicode: + return 'realunicode_w' elif typ is bool: # For argument clinic's "bool" specifier: accept any object, and # convert it to a boolean value. If you don't want this @@ -1113,7 +1124,7 @@ kw_defs_w = [] for name, w_def in sorted(alldefs_w.items()): assert name in sig.kwonlyargnames - w_name = space.newunicode(name.decode('utf-8')) + w_name = space.newtext(name) kw_defs_w.append((w_name, w_def)) return defs_w, kw_defs_w diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py --- a/pypy/interpreter/generator.py +++ b/pypy/interpreter/generator.py @@ -38,14 +38,12 @@ # 'qualname' is a unicode string if self._qualname is not None: return self._qualname - return self.get_name().decode('utf-8') + return self.get_name() def descr__repr__(self, space): addrstring = self.getaddrstring(space) - return space.newunicode(u"<%s object %s at 0x%s>" % - (self.KIND_U, - self.get_qualname(), - unicode(addrstring))) + return space.newtext("<%s object %s at 0x%s>" % + (self.KIND, self.get_qualname(), addrstring)) def descr_send(self, w_arg): """send(arg) -> send 'arg' into generator/coroutine, @@ -229,7 +227,7 @@ e2.chain_exceptions_from_cause(space, e) raise e2 else: - space.warn(space.newunicode(u"generator '%s' raised StopIteration" + space.warn(space.newtext("generator '%s' raised StopIteration" % self.get_qualname()), space.w_DeprecationWarning) @@ -329,11 +327,11 @@ "__name__ must be set to a string object") def descr__qualname__(self, space): - return space.newunicode(self.get_qualname()) + return space.newtext(self.get_qualname()) def descr_set__qualname__(self, space, w_name): try: - self._qualname = space.unicode_w(w_name) + self._qualname = space.utf8_w(w_name) except OperationError as e: if e.match(space, space.w_TypeError): raise oefmt(space.w_TypeError, @@ -422,8 +420,8 @@ self.frame is not None and \ self.frame.last_instr == -1: space = self.space - msg = u"coroutine '%s' was never awaited" % self.get_qualname() - space.warn(space.newunicode(msg), space.w_RuntimeWarning) + msg = "coroutine '%s' was never awaited" % self.get_qualname() + space.warn(space.newtext(msg), space.w_RuntimeWarning) GeneratorOrCoroutine._finalize_(self) diff --git a/pypy/interpreter/mixedmodule.py b/pypy/interpreter/mixedmodule.py --- a/pypy/interpreter/mixedmodule.py +++ b/pypy/interpreter/mixedmodule.py @@ -130,7 +130,7 @@ bltin.w_module = self.w_name func._builtinversion_ = bltin bltin.name = name - bltin.qualname = bltin.name.decode('utf-8') + bltin.qualname = bltin.name w_value = bltin space.setitem(self.w_dict, w_name, w_value) return w_value @@ -197,6 +197,17 @@ def get__doc__(cls, space): return space.newtext_or_none(cls.__doc__) + def setdictvalue_dont_introduce_cell(self, name, w_value): + """ inofficial interface on MixedModules to override an existing value From pypy.commits at gmail.com Tue Feb 19 03:50:48 2019 From: pypy.commits at gmail.com (arigo) Date: Tue, 19 Feb 2019 00:50:48 -0800 (PST) Subject: [pypy-commit] pypy default: Fix for pyarrow calling invalid macros Message-ID: <5c6bc368.1c69fb81.bdc43.e12a@mx.google.com> Author: Armin Rigo Branch: Changeset: r96085:4d4e7569ddd5 Date: 2019-02-19 09:50 +0100 http://bitbucket.org/pypy/pypy/changeset/4d4e7569ddd5/ Log: Fix for pyarrow calling invalid macros diff --git a/pypy/module/cpyext/cdatetime.py b/pypy/module/cpyext/cdatetime.py --- a/pypy/module/cpyext/cdatetime.py +++ b/pypy/module/cpyext/cdatetime.py @@ -296,25 +296,41 @@ def PyDateTime_DATE_GET_HOUR(space, w_obj): """Return the hour, as an int from 0 through 23. """ - return space.int_w(space.getattr(w_obj, space.newtext("hour"))) + # w_obj must be a datetime.timedate object. However, I've seen libraries + # call this macro with a datetime.date object. I think it returns + # nonsense in CPython, but it doesn't crash. We'll just return zero + # in case there is no field 'hour'. + try: + return space.int_w(space.getattr(w_obj, space.newtext("hour"))) + except OperationError: + return 0 @cpython_api([rffi.VOIDP], rffi.INT_real, error=CANNOT_FAIL) def PyDateTime_DATE_GET_MINUTE(space, w_obj): """Return the minute, as an int from 0 through 59. """ - return space.int_w(space.getattr(w_obj, space.newtext("minute"))) + try: + return space.int_w(space.getattr(w_obj, space.newtext("minute"))) + except OperationError: + return 0 # see comments in PyDateTime_DATE_GET_HOUR @cpython_api([rffi.VOIDP], rffi.INT_real, error=CANNOT_FAIL) def PyDateTime_DATE_GET_SECOND(space, w_obj): """Return the second, as an int from 0 through 59. """ - return space.int_w(space.getattr(w_obj, space.newtext("second"))) + try: + return space.int_w(space.getattr(w_obj, space.newtext("second"))) + except OperationError: + return 0 # see comments in PyDateTime_DATE_GET_HOUR @cpython_api([rffi.VOIDP], rffi.INT_real, error=CANNOT_FAIL) def PyDateTime_DATE_GET_MICROSECOND(space, w_obj): """Return the microsecond, as an int from 0 through 999999. """ - return space.int_w(space.getattr(w_obj, space.newtext("microsecond"))) + try: + return space.int_w(space.getattr(w_obj, space.newtext("microsecond"))) + except OperationError: + return 0 # see comments in PyDateTime_DATE_GET_HOUR @cpython_api([rffi.VOIDP], rffi.INT_real, error=CANNOT_FAIL) def PyDateTime_TIME_GET_HOUR(space, w_obj): From pypy.commits at gmail.com Tue Feb 19 05:00:30 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 02:00:30 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix marginally useful whatsnew for branch Message-ID: <5c6bd3be.1c69fb81.c37d.cf60@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96086:c8a4e88385a8 Date: 2019-02-19 09:57 +0200 http://bitbucket.org/pypy/pypy/changeset/c8a4e88385a8/ Log: fix marginally useful whatsnew for branch diff --git a/pypy/doc/test/test_whatsnew.py b/pypy/doc/test/test_whatsnew.py --- a/pypy/doc/test/test_whatsnew.py +++ b/pypy/doc/test/test_whatsnew.py @@ -89,7 +89,7 @@ startrev, documented = parse_doc(last_whatsnew) merged, branch = get_merged_branches(ROOT, startrev, '') merged.discard('default') - merged.discard('py3.5') + merged.discard('py3.6') merged.discard('') not_documented = merged.difference(documented) not_merged = documented.difference(merged) @@ -100,7 +100,7 @@ print '\n'.join(not_merged) print assert not not_documented - if branch == 'py3.5': + if branch == 'py3.6': assert not not_merged else: assert branch in documented, 'Please document this branch before merging: %s' % branch 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 @@ -2,9 +2,10 @@ What's new in PyPy3 7.0+ ======================== -.. this is the revision after release-pypy3.5-v7.0 -.. startrev: 9d2fa7c63b7c +.. this is the revision after release-pypy3.6-v7.0 +.. startrev: 33fe3b2cf186 -.. branch: unicode-utf8-py3 +.. branch: py3.5 -Use utf8 instead of rpython-level unicode \ No newline at end of file +Merge in py.35 and use this branch as the primary pypy3 one + From pypy.commits at gmail.com Tue Feb 19 05:00:32 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 02:00:32 -0800 (PST) Subject: [pypy-commit] pypy default: add an error message for out-of-range Message-ID: <5c6bd3c0.1c69fb81.bdc43.f918@mx.google.com> Author: Matti Picus Branch: Changeset: r96087:5802580694f9 Date: 2019-02-19 11:33 +0200 http://bitbucket.org/pypy/pypy/changeset/5802580694f9/ Log: add an error message for out-of-range diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -89,7 +89,7 @@ builder.append(chr((0x80 | ((code >> 6) & 0x3f)))) builder.append(chr((0x80 | (code & 0x3f)))) return - raise ValueError + raise ValueError('character U+%x is not in range [U+0000; U+10ffff]' % code) @dont_inline def _nonascii_unichr_as_utf8_append_nosurrogates(builder, code): From pypy.commits at gmail.com Tue Feb 19 05:00:34 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 02:00:34 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into py3.6 Message-ID: <5c6bd3c2.1c69fb81.214bc.06b7@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96088:1c7aea3d0597 Date: 2019-02-19 11:35 +0200 http://bitbucket.org/pypy/pypy/changeset/1c7aea3d0597/ Log: merge default into py3.6 diff --git a/pypy/module/cpyext/cdatetime.py b/pypy/module/cpyext/cdatetime.py --- a/pypy/module/cpyext/cdatetime.py +++ b/pypy/module/cpyext/cdatetime.py @@ -296,25 +296,41 @@ def PyDateTime_DATE_GET_HOUR(space, w_obj): """Return the hour, as an int from 0 through 23. """ - return space.int_w(space.getattr(w_obj, space.newtext("hour"))) + # w_obj must be a datetime.timedate object. However, I've seen libraries + # call this macro with a datetime.date object. I think it returns + # nonsense in CPython, but it doesn't crash. We'll just return zero + # in case there is no field 'hour'. + try: + return space.int_w(space.getattr(w_obj, space.newtext("hour"))) + except OperationError: + return 0 @cpython_api([rffi.VOIDP], rffi.INT_real, error=CANNOT_FAIL) def PyDateTime_DATE_GET_MINUTE(space, w_obj): """Return the minute, as an int from 0 through 59. """ - return space.int_w(space.getattr(w_obj, space.newtext("minute"))) + try: + return space.int_w(space.getattr(w_obj, space.newtext("minute"))) + except OperationError: + return 0 # see comments in PyDateTime_DATE_GET_HOUR @cpython_api([rffi.VOIDP], rffi.INT_real, error=CANNOT_FAIL) def PyDateTime_DATE_GET_SECOND(space, w_obj): """Return the second, as an int from 0 through 59. """ - return space.int_w(space.getattr(w_obj, space.newtext("second"))) + try: + return space.int_w(space.getattr(w_obj, space.newtext("second"))) + except OperationError: + return 0 # see comments in PyDateTime_DATE_GET_HOUR @cpython_api([rffi.VOIDP], rffi.INT_real, error=CANNOT_FAIL) def PyDateTime_DATE_GET_MICROSECOND(space, w_obj): """Return the microsecond, as an int from 0 through 999999. """ - return space.int_w(space.getattr(w_obj, space.newtext("microsecond"))) + try: + return space.int_w(space.getattr(w_obj, space.newtext("microsecond"))) + except OperationError: + return 0 # see comments in PyDateTime_DATE_GET_HOUR @cpython_api([rffi.VOIDP], rffi.INT_real, error=CANNOT_FAIL) def PyDateTime_TIME_GET_HOUR(space, w_obj): diff --git a/pypy/module/test_lib_pypy/README.txt b/pypy/module/test_lib_pypy/README.txt --- a/pypy/module/test_lib_pypy/README.txt +++ b/pypy/module/test_lib_pypy/README.txt @@ -5,3 +5,5 @@ Note that if you run it with a PyPy from elsewhere, it will not pick up the changes to lib-python and lib_pypy. + +DEPRECATED: put tests in ./extra_tests instead! diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -89,7 +89,7 @@ builder.append(chr((0x80 | ((code >> 6) & 0x3f)))) builder.append(chr((0x80 | (code & 0x3f)))) return - raise ValueError + raise ValueError('character U+%x is not in range [U+0000; U+10ffff]' % code) @dont_inline def _nonascii_unichr_as_utf8_append_nosurrogates(builder, code): From pypy.commits at gmail.com Tue Feb 19 05:00:36 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 02:00:36 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix test - conversion of py_obj to w_obj can fail Message-ID: <5c6bd3c4.1c69fb81.43856.452a@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96089:0f9a6721ad53 Date: 2019-02-19 11:40 +0200 http://bitbucket.org/pypy/pypy/changeset/0f9a6721ad53/ Log: fix test - conversion of py_obj to w_obj can fail diff --git a/pypy/module/cpyext/test/test_unicodeobject.py b/pypy/module/cpyext/test/test_unicodeobject.py --- a/pypy/module/cpyext/test/test_unicodeobject.py +++ b/pypy/module/cpyext/test/test_unicodeobject.py @@ -373,7 +373,9 @@ m = self.import_module('_widechar') if m.get_sizeof_wchar() != 4: pytest.skip('only for sizeof(wchar)==4') - raises(ValueError, m.test_widechar) + exc = raises(ValueError, m.test_widechar) + assert (str(exc.value) == 'character U+110000 is not in range ' + '[U+0000; U+10ffff]'), str(exc.value) def test_AsUTFNString(self): module = self.import_extension('foo', [ diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -52,6 +52,13 @@ _2BYTE_KIND = 2 _4BYTE_KIND = 4 +kind_to_name = { + 0: 'WCHAR_KIND', + 1: '_1BYTE_KIND', + 2: '_2BYTE_KIND', + 4: '_4BYTE_KIND', + } + def new_empty_unicode(space, length): """ @@ -84,7 +91,10 @@ be modified after this call. """ lgt = get_wsize(py_obj) - s_utf8 = rffi.wcharpsize2utf8(get_wbuffer(py_obj), lgt) + try: + s_utf8 = rffi.wcharpsize2utf8(get_wbuffer(py_obj), lgt) + except ValueError as e: + raise oefmt(space.w_ValueError, e.args[0]) w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type)) w_obj = space.allocate_instance(unicodeobject.W_UnicodeObject, w_type) w_obj.__init__(s_utf8, lgt) @@ -270,10 +280,16 @@ return runicode.UNICHR(rutf8.MAXUNICODE) @cts.decl("int _PyUnicode_Ready(PyObject *unicode)", error=-1) -def _PyUnicode_Ready(space, w_obj): - assert isinstance(w_obj, unicodeobject.W_UnicodeObject) - py_obj = as_pyobj(space, w_obj) - assert get_kind(py_obj) == WCHAR_KIND +def _PyUnicode_Ready(space, py_obj): + # conversion from pyobj to space.w_unicode can fail, + # so create the rpython object here and not in the api wrapper + kind = get_kind(py_obj) + if kind == WCHAR_KIND: + w_obj = from_ref(space, rffi.cast(PyObject, py_obj)) + else: + s = kind_to_name.get(kind, "INVALID") + raise oefmt(ValueError, + "converting %s PyUnicodeObject not supported yet", s) return _readify(space, py_obj, space.utf8_w(w_obj)) def _readify(space, py_obj, value): From pypy.commits at gmail.com Tue Feb 19 05:00:38 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 02:00:38 -0800 (PST) Subject: [pypy-commit] pypy default: backport rpython changes Message-ID: <5c6bd3c6.1c69fb81.62aed.7b32@mx.google.com> Author: Matti Picus Branch: Changeset: r96090:2ea6562691b9 Date: 2019-02-19 11:48 +0200 http://bitbucket.org/pypy/pypy/changeset/2ea6562691b9/ Log: backport rpython changes diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py --- a/rpython/rlib/_rsocket_rffi.py +++ b/rpython/rlib/_rsocket_rffi.py @@ -1270,7 +1270,8 @@ getprotobyname = external('getprotobyname', [rffi.CCHARP], lltype.Ptr(cConfig.protoent)) if _POSIX: - fcntl = external('fcntl', [socketfd_type, rffi.INT, rffi.INT], rffi.INT) + fcntl = external('fcntl', [socketfd_type, rffi.INT, rffi.INT], rffi.INT, + save_err=SAVE_ERR) socketpair_t = rffi.CArray(socketfd_type) socketpair = external('socketpair', [rffi.INT, rffi.INT, rffi.INT, lltype.Ptr(socketpair_t)], rffi.INT, @@ -1282,7 +1283,7 @@ if _WIN32: ioctlsocket = external('ioctlsocket', [socketfd_type, rffi.LONG, rffi.ULONGP], - rffi.INT) + rffi.INT, save_err=SAVE_ERR) select = external('select', [rffi.INT, fd_set, fd_set, diff --git a/rpython/rlib/rposix_stat.py b/rpython/rlib/rposix_stat.py --- a/rpython/rlib/rposix_stat.py +++ b/rpython/rlib/rposix_stat.py @@ -686,7 +686,7 @@ # the "reparse points" is missing win32traits = make_win32_traits(traits) - hFile = win32traits.CreateFile(path, + hFile = win32traits.CreateFile(traits.as_str0(path), win32traits.FILE_READ_ATTRIBUTES, 0, lltype.nullptr(rwin32.LPSECURITY_ATTRIBUTES.TO), diff --git a/rpython/rlib/rsocket.py b/rpython/rlib/rsocket.py --- a/rpython/rlib/rsocket.py +++ b/rpython/rlib/rsocket.py @@ -567,17 +567,21 @@ if hasattr(_c, 'fcntl'): def _setblocking(self, block): orig_delay_flag = intmask(_c.fcntl(self.fd, _c.F_GETFL, 0)) + if orig_delay_flag == -1: + raise self.error_handler() if block: delay_flag = orig_delay_flag & ~_c.O_NONBLOCK else: delay_flag = orig_delay_flag | _c.O_NONBLOCK if orig_delay_flag != delay_flag: - _c.fcntl(self.fd, _c.F_SETFL, delay_flag) + if _c.fcntl(self.fd, _c.F_SETFL, delay_flag) == -1: + raise self.error_handler() elif hasattr(_c, 'ioctlsocket'): def _setblocking(self, block): flag = lltype.malloc(rffi.ULONGP.TO, 1, flavor='raw') flag[0] = rffi.cast(rffi.ULONG, not block) - _c.ioctlsocket(self.fd, _c.FIONBIO, flag) + if _c.ioctlsocket(self.fd, _c.FIONBIO, flag) != 0: + raise self.error_handler() lltype.free(flag, flavor='raw') if hasattr(_c, 'poll') and not _c.poll_may_be_broken: diff --git a/rpython/rlib/signature.py b/rpython/rlib/signature.py --- a/rpython/rlib/signature.py +++ b/rpython/rlib/signature.py @@ -9,7 +9,7 @@ def foo(...) The arguments paramNtype and returntype should be instances - of the classes in rpython.annotator.types. + of the classes in rpython.rlib.types. """ returntype = kwargs.pop('returns', None) if returntype is None: From pypy.commits at gmail.com Tue Feb 19 05:00:40 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 02:00:40 -0800 (PST) Subject: [pypy-commit] pypy py3.6: minimize diff to default (formatting) Message-ID: <5c6bd3c8.1c69fb81.60e11.e8b1@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96091:f3df6cd7daaf Date: 2019-02-19 11:50 +0200 http://bitbucket.org/pypy/pypy/changeset/f3df6cd7daaf/ Log: minimize diff to default (formatting) diff --git a/rpython/rlib/rposix_stat.py b/rpython/rlib/rposix_stat.py --- a/rpython/rlib/rposix_stat.py +++ b/rpython/rlib/rposix_stat.py @@ -685,6 +685,7 @@ # XXX 'traverse' is ignored, and everything related to # the "reparse points" is missing win32traits = make_win32_traits(traits) + hFile = win32traits.CreateFile(traits.as_str0(path), win32traits.FILE_READ_ATTRIBUTES, 0, From pypy.commits at gmail.com Tue Feb 19 07:18:56 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 04:18:56 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix formatting, merge repititions Message-ID: <5c6bf430.1c69fb81.66add.638c@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96092:43e56c60e223 Date: 2019-02-19 13:18 +0200 http://bitbucket.org/pypy/pypy/changeset/43e56c60e223/ Log: fix formatting, merge repititions diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -64,11 +64,6 @@ slen = len(bytes) uni, lgt = runicode.str_decode_mbcs(bytes, slen, 'strict', final=True, errorhandler=errorhandler, force_ignore=False) - - utf8 = uni.encode('utf-8') - - utf8 = uni.encode('utf-8') - utf8 = uni.encode('utf-8') elif 0 and _MACOSX: bytes = space.bytes_w(w_string) @@ -242,7 +237,7 @@ pos = end # Try to get collect surrogates in one pass # XXX do we care about performance in this case? - # XXX should this loop for more than one pair? + # XXX should this loop for more than one pair? delta = 1 uchr = rutf8.codepoint_at_pos(s, pos) if 0xD800 <= uchr <= 0xDBFF: @@ -250,7 +245,7 @@ if pos < size: uchr = rutf8.codepoint_at_pos(s, pos) if 0xDC00 <= uchr <= 0xDFFF: - delta += 1 + delta += 1 res, newindex, rettype = errorhandler(errors, 'utf8', 'surrogates not allowed', s, upos, upos + delta) if rettype == 'u': From pypy.commits at gmail.com Tue Feb 19 07:18:58 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 04:18:58 -0800 (PST) Subject: [pypy-commit] pypy py3.6: test, fix for infinite encoding due to bad error handler Message-ID: <5c6bf432.1c69fb81.54295.2eeb@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96093:6090343709fa Date: 2019-02-19 13:47 +0200 http://bitbucket.org/pypy/pypy/changeset/6090343709fa/ Log: test, fix for infinite encoding due to bad error handler diff --git a/pypy/interpreter/test/test_unicodehelper.py b/pypy/interpreter/test/test_unicodehelper.py --- a/pypy/interpreter/test/test_unicodehelper.py +++ b/pypy/interpreter/test/test_unicodehelper.py @@ -1,3 +1,5 @@ +import pytest + from pypy.interpreter.unicodehelper import ( utf8_encode_utf_8, decode_utf8sp, ) @@ -16,13 +18,11 @@ def test_encode_utf_8_combine_surrogates(): """ In the case of a surrogate pair, the error handler should - return back a start and stop position of the full surrogate - pair (new behavior inherited from python3.6) + called with a start and stop position of the full surrogate + pair (new behavior in python3.6) """ u = u"\udc80\ud800\udfff" - handler_num = 0 - def errorhandler(errors, encoding, msg, s, start, end): """ This handler will be called twice, so asserting both times: @@ -33,7 +33,7 @@ that is a valid surrogate pair. """ assert s[start:end] in [u'\udc80', u'\uD800\uDFFF'] - return '', 0, end + return '', end, 'b' utf8_encode_utf_8( u, 'strict', @@ -41,6 +41,24 @@ allow_surrogates=False ) +def test_bad_error_handler(): + u = u"\udc80\ud800\udfff" + + def errorhandler(errors, encoding, msg, s, start, end): + """ + This handler will be called twice, so asserting both times: + + 1. the first time, 0xDC80 will be handled as a single surrogate, + since it is a standalone character and an invalid surrogate. + 2. the second time, the characters will be 0xD800 and 0xDFFF, since + that is a valid surrogate pair. + """ + assert s[start:end] in [u'\udc80', u'\uD800\uDFFF'] + return '', start, 'b' + + assert pytest.raises(IndexError, utf8_encode_utf_8, u, 'strict', + errorhandler=errorhandler, allow_surrogates=False) + def test_decode_utf8sp(): space = FakeSpace() assert decode_utf8sp(space, "\xed\xa0\x80") == ("\xed\xa0\x80", 1, 3) diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -254,6 +254,10 @@ else: for ch in res: result.append(ch) + if newindex <= upos: + raise IndexError( + "position %d from error handler invalid, already encoded %d", + newindex, upos) upos = newindex pos = rutf8._pos_at_index(s, upos) return result.build() diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -732,8 +732,11 @@ if errors is None: errors = 'strict' state = space.fromcache(CodecState) - result = unicodehelper.utf8_encode_utf_8(utf8, errors, + try: + result = unicodehelper.utf8_encode_utf_8(utf8, errors, state.encode_error_handler, allow_surrogates=False) + except IndexError as e: + raise oefmt(space.w_IndexError, e.args[0]) return space.newtuple([space.newbytes(result), space.newint(lgt)]) @unwrap_spec(string='bufferstr', errors='text_or_none', From pypy.commits at gmail.com Tue Feb 19 07:19:00 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 04:19:00 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix Message-ID: <5c6bf434.1c69fb81.14fd2.5293@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96094:2e27a5ddeaed Date: 2019-02-19 14:18 +0200 http://bitbucket.org/pypy/pypy/changeset/2e27a5ddeaed/ Log: fix diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -736,7 +736,7 @@ result = unicodehelper.utf8_encode_utf_8(utf8, errors, state.encode_error_handler, allow_surrogates=False) except IndexError as e: - raise oefmt(space.w_IndexError, e.args[0]) + raise oefmt(space.w_IndexError, '%s' % e.args[0]) return space.newtuple([space.newbytes(result), space.newint(lgt)]) @unwrap_spec(string='bufferstr', errors='text_or_none', diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -94,7 +94,7 @@ try: s_utf8 = rffi.wcharpsize2utf8(get_wbuffer(py_obj), lgt) except ValueError as e: - raise oefmt(space.w_ValueError, e.args[0]) + raise oefmt(space.w_ValueError, '%s' % e.args[0]) w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type)) w_obj = space.allocate_instance(unicodeobject.W_UnicodeObject, w_type) w_obj.__init__(s_utf8, lgt) @@ -288,7 +288,7 @@ w_obj = from_ref(space, rffi.cast(PyObject, py_obj)) else: s = kind_to_name.get(kind, "INVALID") - raise oefmt(ValueError, + raise oefmt(space.w_ValueError, "converting %s PyUnicodeObject not supported yet", s) return _readify(space, py_obj, space.utf8_w(w_obj)) From pypy.commits at gmail.com Tue Feb 19 11:07:28 2019 From: pypy.commits at gmail.com (rlamy) Date: Tue, 19 Feb 2019 08:07:28 -0800 (PST) Subject: [pypy-commit] pypy default: Call internal methods from PyDict_XXXItem() instead of going through dunder Message-ID: <5c6c29c0.1c69fb81.afeb2.0455@mx.google.com> Author: Ronan Lamy Branch: Changeset: r96095:5062f3687585 Date: 2019-02-19 16:04 +0000 http://bitbucket.org/pypy/pypy/changeset/5062f3687585/ Log: Call internal methods from PyDict_XXXItem() instead of going through dunder methods (CPython compatibility) Fixes issue #2954 diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -2,6 +2,7 @@ from rpython.rlib.objectmodel import specialize from pypy.interpreter.error import OperationError from pypy.objspace.std.classdict import ClassDictStrategy +from pypy.objspace.std.dictmultiobject import W_DictMultiObject from pypy.interpreter.typedef import GetSetProperty from pypy.module.cpyext.api import ( cpython_api, CANNOT_FAIL, build_type_checkers_flags, Py_ssize_t, @@ -71,68 +72,59 @@ @cpython_api([PyObject, PyObject], PyObject, error=CANNOT_FAIL, result_borrowed=True) def PyDict_GetItem(space, w_dict, w_key): - try: - w_res = space.getitem(w_dict, w_key) - except: - return None + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) # NOTE: this works so far because all our dict strategies store # *values* as full objects, which stay alive as long as the dict is # alive and not modified. So we can return a borrowed ref. # XXX this is wrong with IntMutableCell. Hope it works... - return w_res + return w_dict.getitem(w_key) @cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_SetItem(space, w_dict, w_key, w_obj): - if PyDict_Check(space, w_dict): - space.setitem(w_dict, w_key, w_obj) - return 0 - else: - PyErr_BadInternalCall(space) + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) + w_dict.setitem(w_key, w_obj) + return 0 @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_DelItem(space, w_dict, w_key): - if PyDict_Check(space, w_dict): - space.delitem(w_dict, w_key) - return 0 - else: - PyErr_BadInternalCall(space) + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) + w_dict.descr_delitem(space, w_key) + return 0 @cpython_api([PyObject, CONST_STRING, PyObject], rffi.INT_real, error=-1) def PyDict_SetItemString(space, w_dict, key_ptr, w_obj): - if PyDict_Check(space, w_dict): - key = rffi.charp2str(key_ptr) - space.setitem_str(w_dict, key, w_obj) - return 0 - else: - PyErr_BadInternalCall(space) + w_key = space.newtext(rffi.charp2str(key_ptr)) + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) + w_dict.setitem(w_key, w_obj) + return 0 @cpython_api([PyObject, CONST_STRING], PyObject, error=CANNOT_FAIL, result_borrowed=True) def PyDict_GetItemString(space, w_dict, key): """This is the same as PyDict_GetItem(), but key is specified as a char*, rather than a PyObject*.""" - try: - w_res = space.finditem_str(w_dict, rffi.charp2str(key)) - except: - w_res = None + w_key = space.newtext(rffi.charp2str(key)) + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) # NOTE: this works so far because all our dict strategies store # *values* as full objects, which stay alive as long as the dict is # alive and not modified. So we can return a borrowed ref. # XXX this is wrong with IntMutableCell. Hope it works... - return w_res + return w_dict.getitem(w_key) @cpython_api([PyObject, CONST_STRING], rffi.INT_real, error=-1) def PyDict_DelItemString(space, w_dict, key_ptr): """Remove the entry in dictionary p which has a key specified by the string key. Return 0 on success or -1 on failure.""" - if PyDict_Check(space, w_dict): - key = rffi.charp2str(key_ptr) - # our dicts dont have a standardized interface, so we need - # to go through the space - space.delitem(w_dict, space.newtext(key)) - return 0 - else: - PyErr_BadInternalCall(space) + w_key = space.newtext(rffi.charp2str(key_ptr)) + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) + w_dict.descr_delitem(space, w_key) + return 0 @cpython_api([PyObject], Py_ssize_t, error=-1) def PyDict_Size(space, w_obj): @@ -182,7 +174,7 @@ """ override = rffi.cast(lltype.Signed, override) w_keys = space.call_method(w_b, "keys") - for w_key in space.iteriterable(w_keys): + for w_key in space.iteriterable(w_keys): if not _has_val(space, w_a, w_key) or override != 0: space.setitem(w_a, w_key, space.getitem(w_b, w_key)) return 0 diff --git a/pypy/module/cpyext/test/test_dictobject.py b/pypy/module/cpyext/test/test_dictobject.py --- a/pypy/module/cpyext/test/test_dictobject.py +++ b/pypy/module/cpyext/test/test_dictobject.py @@ -310,3 +310,67 @@ assert module.dict_delitem(d, 'a') == 0 r = module.dict_next({'a': 1, 'b': 2}) assert r == 2 + + def test_subclassing(self): + module = self.import_extension('foo', [ + ("dict_setitem", "METH_VARARGS", + """ + PyObject *d, *key, *value; + if (!PyArg_ParseTuple(args, "OOO", &d, &key, &value)) { + return NULL; + } + if (PyDict_SetItem(d, key, value) < 0) { + return NULL; + } + Py_RETURN_NONE; + """), + ("dict_delitem", "METH_VARARGS", + """ + PyObject *d, *key; + if (!PyArg_ParseTuple(args, "OO", &d, &key)) { + return NULL; + } + if (PyDict_DelItem(d, key) < 0) { + return NULL; + } + Py_RETURN_NONE; + """), + ("dict_getitem", "METH_VARARGS", + """ + PyObject *d, *key, *result; + if (!PyArg_ParseTuple(args, "OO", &d, &key)) { + return NULL; + } + result = PyDict_GetItem(d, key); + Py_XINCREF(result); + return result; + """), + ]) + + class mydict(dict): + def __setitem__(self, key, value): + dict.__setitem__(self, key, 42) + + def __delitem__(self, key): + dict.__setitem__(self, key, None) + d = {} + module.dict_setitem(d, 1, 2) + assert d[1] == 2 + d = mydict() + d[1] = 2 + assert d[1] == 42 + module.dict_setitem(d, 2, 3) + assert d[2] == 3 + del d[2] + assert d[2] is None + module.dict_delitem(d, 2) + assert 2 not in d + + class mydict2(dict): + def __getitem__(self, key): + return 42 + + d = mydict2() + d[1] = 2 + assert d[1] == 42 + assert module.dict_getitem(d, 1) == 2 From pypy.commits at gmail.com Tue Feb 19 14:23:42 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 11:23:42 -0800 (PST) Subject: [pypy-commit] pypy default: use a utf8 specific error for OutOfRange Message-ID: <5c6c57be.1c69fb81.2f2ff.49c8@mx.google.com> Author: Matti Picus Branch: Changeset: r96096:213fc2573b4d Date: 2019-02-19 21:05 +0200 http://bitbucket.org/pypy/pypy/changeset/213fc2573b4d/ Log: use a utf8 specific error for OutOfRange diff --git a/pypy/module/_cffi_backend/ctypeprim.py b/pypy/module/_cffi_backend/ctypeprim.py --- a/pypy/module/_cffi_backend/ctypeprim.py +++ b/pypy/module/_cffi_backend/ctypeprim.py @@ -175,7 +175,7 @@ value = misc.read_raw_ulong_data(cdata, self.size) # r_uint try: utf8 = rutf8.unichr_as_utf8(value, allow_surrogates=True) - except ValueError: + except rutf8.OutOfRange: if self.is_signed_wchar: s = hex(intmask(value)) else: diff --git a/pypy/module/_cffi_backend/wchar_helper.py b/pypy/module/_cffi_backend/wchar_helper.py --- a/pypy/module/_cffi_backend/wchar_helper.py +++ b/pypy/module/_cffi_backend/wchar_helper.py @@ -24,7 +24,7 @@ j += 1 try: rutf8.unichr_as_utf8_append(u, ch, allow_surrogates=True) - except ValueError: + except rutf8.OutOfRange: raise OutOfRange(ch) return u.build(), length diff --git a/pypy/objspace/std/formatting.py b/pypy/objspace/std/formatting.py --- a/pypy/objspace/std/formatting.py +++ b/pypy/objspace/std/formatting.py @@ -473,7 +473,7 @@ if do_unicode: try: c = rutf8.unichr_as_utf8(r_uint(n)) - except ValueError: + except rutf8.OutOfRange: raise oefmt(space.w_OverflowError, "unicode character code out of range") self.std_wp(c, False) diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -30,6 +30,11 @@ MAXUNICODE = 0x10ffff allow_surrogate_by_default = False + +class OutOfRange(Exception): + def __init__(self, code): + self.code = code + # we need a way to accept both r_uint and int(nonneg=True) #@signature(types.int_nonneg(), types.bool(), returns=types.str()) def unichr_as_utf8(code, allow_surrogates=False): @@ -44,7 +49,7 @@ return chr((0xc0 | (code >> 6))) + chr((0x80 | (code & 0x3f))) if code <= r_uint(0xFFFF): if not allow_surrogates and 0xD800 <= code <= 0xDfff: - raise ValueError + raise OutOfRange(code) return (chr((0xe0 | (code >> 12))) + chr((0x80 | ((code >> 6) & 0x3f))) + chr((0x80 | (code & 0x3f)))) @@ -53,7 +58,7 @@ chr((0x80 | ((code >> 12) & 0x3f))) + chr((0x80 | ((code >> 6) & 0x3f))) + chr((0x80 | (code & 0x3f)))) - raise ValueError + raise OutOfRange(code) @try_inline def unichr_as_utf8_append(builder, code, allow_surrogates=False): @@ -89,7 +94,7 @@ builder.append(chr((0x80 | ((code >> 6) & 0x3f)))) builder.append(chr((0x80 | (code & 0x3f)))) return - raise ValueError('character U+%x is not in range [U+0000; U+10ffff]' % code) + raise OutOfRange(code) @dont_inline def _nonascii_unichr_as_utf8_append_nosurrogates(builder, code): @@ -110,7 +115,7 @@ builder.append(chr((0x80 | ((code >> 6) & 0x3f)))) builder.append(chr((0x80 | (code & 0x3f)))) return - raise ValueError + raise OutOfRange(code) # note - table lookups are really slow. Measured on various elements of obama diff --git a/rpython/rlib/test/test_rutf8.py b/rpython/rlib/test/test_rutf8.py --- a/rpython/rlib/test/test_rutf8.py +++ b/rpython/rlib/test/test_rutf8.py @@ -9,7 +9,7 @@ def test_unichr_as_utf8(c, allow_surrogates): i = ord(c) if not allow_surrogates and 0xD800 <= i <= 0xDFFF: - with pytest.raises(ValueError): + with pytest.raises(rutf8.OutOfRange): rutf8.unichr_as_utf8(i, allow_surrogates) else: u = rutf8.unichr_as_utf8(i, allow_surrogates) From pypy.commits at gmail.com Tue Feb 19 14:23:47 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 11:23:47 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch Message-ID: <5c6c57c3.1c69fb81.3e4a.1560@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96097:805c1a75f5f0 Date: 2019-02-19 21:06 +0200 http://bitbucket.org/pypy/pypy/changeset/805c1a75f5f0/ Log: merge default into branch diff --git a/pypy/module/_cffi_backend/ctypeprim.py b/pypy/module/_cffi_backend/ctypeprim.py --- a/pypy/module/_cffi_backend/ctypeprim.py +++ b/pypy/module/_cffi_backend/ctypeprim.py @@ -175,7 +175,7 @@ value = misc.read_raw_ulong_data(cdata, self.size) # r_uint try: utf8 = rutf8.unichr_as_utf8(value, allow_surrogates=True) - except ValueError: + except rutf8.OutOfRange: if self.is_signed_wchar: s = hex(intmask(value)) else: diff --git a/pypy/module/_cffi_backend/wchar_helper.py b/pypy/module/_cffi_backend/wchar_helper.py --- a/pypy/module/_cffi_backend/wchar_helper.py +++ b/pypy/module/_cffi_backend/wchar_helper.py @@ -24,7 +24,7 @@ j += 1 try: rutf8.unichr_as_utf8_append(u, ch, allow_surrogates=True) - except ValueError: + except rutf8.OutOfRange: raise OutOfRange(ch) return u.build(), length diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -2,6 +2,7 @@ from rpython.rlib.objectmodel import specialize from pypy.interpreter.error import OperationError from pypy.objspace.std.classdict import ClassDictStrategy +from pypy.objspace.std.dictmultiobject import W_DictMultiObject from pypy.interpreter.typedef import GetSetProperty from pypy.module.cpyext.api import ( cpython_api, CANNOT_FAIL, build_type_checkers_flags, Py_ssize_t, cts, @@ -71,68 +72,59 @@ @cpython_api([PyObject, PyObject], PyObject, error=CANNOT_FAIL, result_borrowed=True) def PyDict_GetItem(space, w_dict, w_key): - try: - w_res = space.getitem(w_dict, w_key) - except: - return None + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) # NOTE: this works so far because all our dict strategies store # *values* as full objects, which stay alive as long as the dict is # alive and not modified. So we can return a borrowed ref. # XXX this is wrong with IntMutableCell. Hope it works... - return w_res + return w_dict.getitem(w_key) @cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_SetItem(space, w_dict, w_key, w_obj): - if PyDict_Check(space, w_dict): - space.setitem(w_dict, w_key, w_obj) - return 0 - else: - PyErr_BadInternalCall(space) + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) + w_dict.setitem(w_key, w_obj) + return 0 @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_DelItem(space, w_dict, w_key): - if PyDict_Check(space, w_dict): - space.delitem(w_dict, w_key) - return 0 - else: - PyErr_BadInternalCall(space) + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) + w_dict.descr_delitem(space, w_key) + return 0 @cpython_api([PyObject, CONST_STRING, PyObject], rffi.INT_real, error=-1) def PyDict_SetItemString(space, w_dict, key_ptr, w_obj): - if PyDict_Check(space, w_dict): - key = rffi.charp2str(key_ptr) - space.setitem_str(w_dict, key, w_obj) - return 0 - else: - PyErr_BadInternalCall(space) + w_key = space.newtext(rffi.charp2str(key_ptr)) + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) + w_dict.setitem(w_key, w_obj) + return 0 @cpython_api([PyObject, CONST_STRING], PyObject, error=CANNOT_FAIL, result_borrowed=True) def PyDict_GetItemString(space, w_dict, key): """This is the same as PyDict_GetItem(), but key is specified as a char*, rather than a PyObject*.""" - try: - w_res = space.finditem_str(w_dict, rffi.charp2str(key)) - except: - w_res = None + w_key = space.newtext(rffi.charp2str(key)) + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) # NOTE: this works so far because all our dict strategies store # *values* as full objects, which stay alive as long as the dict is # alive and not modified. So we can return a borrowed ref. # XXX this is wrong with IntMutableCell. Hope it works... - return w_res + return w_dict.getitem(w_key) @cpython_api([PyObject, CONST_STRING], rffi.INT_real, error=-1) def PyDict_DelItemString(space, w_dict, key_ptr): """Remove the entry in dictionary p which has a key specified by the string key. Return 0 on success or -1 on failure.""" - if PyDict_Check(space, w_dict): - key = rffi.charp2str(key_ptr) - # our dicts dont have a standardized interface, so we need - # to go through the space - space.delitem(w_dict, space.newtext(key)) - return 0 - else: - PyErr_BadInternalCall(space) + w_key = space.newtext(rffi.charp2str(key_ptr)) + if not isinstance(w_dict, W_DictMultiObject): + raise PyErr_BadInternalCall(space) + w_dict.descr_delitem(space, w_key) + return 0 @cpython_api([PyObject], Py_ssize_t, error=-1) def PyDict_Size(space, w_obj): @@ -191,7 +183,7 @@ """ override = rffi.cast(lltype.Signed, override) w_keys = space.call_method(w_b, "keys") - for w_key in space.iteriterable(w_keys): + for w_key in space.iteriterable(w_keys): if not _has_val(space, w_a, w_key) or override != 0: space.setitem(w_a, w_key, space.getitem(w_b, w_key)) return 0 diff --git a/pypy/module/cpyext/test/test_dictobject.py b/pypy/module/cpyext/test/test_dictobject.py --- a/pypy/module/cpyext/test/test_dictobject.py +++ b/pypy/module/cpyext/test/test_dictobject.py @@ -332,3 +332,67 @@ assert module.dict_delitem(d, 'a') == 0 r = module.dict_next({'a': 1, 'b': 2}) assert r == 2 + + def test_subclassing(self): + module = self.import_extension('foo', [ + ("dict_setitem", "METH_VARARGS", + """ + PyObject *d, *key, *value; + if (!PyArg_ParseTuple(args, "OOO", &d, &key, &value)) { + return NULL; + } + if (PyDict_SetItem(d, key, value) < 0) { + return NULL; + } + Py_RETURN_NONE; + """), + ("dict_delitem", "METH_VARARGS", + """ + PyObject *d, *key; + if (!PyArg_ParseTuple(args, "OO", &d, &key)) { + return NULL; + } + if (PyDict_DelItem(d, key) < 0) { + return NULL; + } + Py_RETURN_NONE; + """), + ("dict_getitem", "METH_VARARGS", + """ + PyObject *d, *key, *result; + if (!PyArg_ParseTuple(args, "OO", &d, &key)) { + return NULL; + } + result = PyDict_GetItem(d, key); + Py_XINCREF(result); + return result; + """), + ]) + + class mydict(dict): + def __setitem__(self, key, value): + dict.__setitem__(self, key, 42) + + def __delitem__(self, key): + dict.__setitem__(self, key, None) + d = {} + module.dict_setitem(d, 1, 2) + assert d[1] == 2 + d = mydict() + d[1] = 2 + assert d[1] == 42 + module.dict_setitem(d, 2, 3) + assert d[2] == 3 + del d[2] + assert d[2] is None + module.dict_delitem(d, 2) + assert 2 not in d + + class mydict2(dict): + def __getitem__(self, key): + return 42 + + d = mydict2() + d[1] = 2 + assert d[1] == 42 + assert module.dict_getitem(d, 1) == 2 diff --git a/pypy/objspace/std/formatting.py b/pypy/objspace/std/formatting.py --- a/pypy/objspace/std/formatting.py +++ b/pypy/objspace/std/formatting.py @@ -479,7 +479,7 @@ if do_unicode: try: c = rutf8.unichr_as_utf8(r_uint(n)) - except ValueError: + except rutf8.OutOfRange: raise oefmt(space.w_OverflowError, "unicode character code out of range") self.std_wp(c, False) diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -30,6 +30,11 @@ MAXUNICODE = 0x10ffff allow_surrogate_by_default = False + +class OutOfRange(Exception): + def __init__(self, code): + self.code = code + # we need a way to accept both r_uint and int(nonneg=True) #@signature(types.int_nonneg(), types.bool(), returns=types.str()) def unichr_as_utf8(code, allow_surrogates=False): @@ -44,7 +49,7 @@ return chr((0xc0 | (code >> 6))) + chr((0x80 | (code & 0x3f))) if code <= r_uint(0xFFFF): if not allow_surrogates and 0xD800 <= code <= 0xDfff: - raise ValueError + raise OutOfRange(code) return (chr((0xe0 | (code >> 12))) + chr((0x80 | ((code >> 6) & 0x3f))) + chr((0x80 | (code & 0x3f)))) @@ -53,7 +58,7 @@ chr((0x80 | ((code >> 12) & 0x3f))) + chr((0x80 | ((code >> 6) & 0x3f))) + chr((0x80 | (code & 0x3f)))) - raise ValueError + raise OutOfRange(code) @try_inline def unichr_as_utf8_append(builder, code, allow_surrogates=False): @@ -89,7 +94,7 @@ builder.append(chr((0x80 | ((code >> 6) & 0x3f)))) builder.append(chr((0x80 | (code & 0x3f)))) return - raise ValueError('character U+%x is not in range [U+0000; U+10ffff]' % code) + raise OutOfRange(code) @dont_inline def _nonascii_unichr_as_utf8_append_nosurrogates(builder, code): @@ -110,7 +115,7 @@ builder.append(chr((0x80 | ((code >> 6) & 0x3f)))) builder.append(chr((0x80 | (code & 0x3f)))) return - raise ValueError + raise OutOfRange(code) # note - table lookups are really slow. Measured on various elements of obama diff --git a/rpython/rlib/test/test_rutf8.py b/rpython/rlib/test/test_rutf8.py --- a/rpython/rlib/test/test_rutf8.py +++ b/rpython/rlib/test/test_rutf8.py @@ -9,7 +9,7 @@ def test_unichr_as_utf8(c, allow_surrogates): i = ord(c) if not allow_surrogates and 0xD800 <= i <= 0xDFFF: - with pytest.raises(ValueError): + with pytest.raises(rutf8.OutOfRange): rutf8.unichr_as_utf8(i, allow_surrogates) else: u = rutf8.unichr_as_utf8(i, allow_surrogates) From pypy.commits at gmail.com Tue Feb 19 14:23:49 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 11:23:49 -0800 (PST) Subject: [pypy-commit] pypy py3.6: use rutf8.OutOfRange error Message-ID: <5c6c57c5.1c69fb81.c5055.bd6a@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96098:6890d4e0c302 Date: 2019-02-19 21:21 +0200 http://bitbucket.org/pypy/pypy/changeset/6890d4e0c302/ Log: use rutf8.OutOfRange error diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -212,6 +212,11 @@ i = end return res.build(), len(s), len(s) +class ErrorHandlerError(Exception): + def __init__(self, new, old): + self.new = new + self.old = old + def utf8_encode_utf_8(s, errors, errorhandler, allow_surrogates=False): size = len(s) if size == 0: @@ -255,9 +260,7 @@ for ch in res: result.append(ch) if newindex <= upos: - raise IndexError( - "position %d from error handler invalid, already encoded %d", - newindex, upos) + raise ErrorHandlerError(newindex, upos) upos = newindex pos = rutf8._pos_at_index(s, upos) return result.build() @@ -521,7 +524,7 @@ try: builder.append_code(chr) pos += digits - except ValueError: + except rutf8.OutOfRange: message = "illegal Unicode character" r, pos, rettype = errorhandler( errors, encoding, message, s, pos - 2, pos + digits) diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py --- a/pypy/module/_codecs/interp_codecs.py +++ b/pypy/module/_codecs/interp_codecs.py @@ -735,8 +735,11 @@ try: result = unicodehelper.utf8_encode_utf_8(utf8, errors, state.encode_error_handler, allow_surrogates=False) - except IndexError as e: - raise oefmt(space.w_IndexError, '%s' % e.args[0]) + except unicodehelper.ErrorHandlerError as e: + raise oefmt(space.w_IndexError, + "position %d from error handler invalid, already encoded %d", + e.new,e.old) + return space.newtuple([space.newbytes(result), space.newint(lgt)]) @unwrap_spec(string='bufferstr', errors='text_or_none', diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -93,8 +93,9 @@ lgt = get_wsize(py_obj) try: s_utf8 = rffi.wcharpsize2utf8(get_wbuffer(py_obj), lgt) - except ValueError as e: - raise oefmt(space.w_ValueError, '%s' % e.args[0]) + except rutf8.OutOfRange as e: + raise oefmt(space.w_ValueError, + 'character U+%x is not in range [U+0000; U+10ffff]' % e.code) w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type)) w_obj = space.allocate_instance(unicodeobject.W_UnicodeObject, w_type) w_obj.__init__(s_utf8, lgt) From pypy.commits at gmail.com Tue Feb 19 14:23:52 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 11:23:52 -0800 (PST) Subject: [pypy-commit] pypy default: use rutf8.OutOfRange error Message-ID: <5c6c57c8.1c69fb81.df682.aceb@mx.google.com> Author: Matti Picus Branch: Changeset: r96099:59c374d988c1 Date: 2019-02-19 21:22 +0200 http://bitbucket.org/pypy/pypy/changeset/59c374d988c1/ Log: use rutf8.OutOfRange error diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -403,7 +403,7 @@ try: builder.append_code(chr) pos += digits - except ValueError: + except rutf8.OutOfRange: message = "illegal Unicode character" res, pos = errorhandler( errors, encoding, message, s, pos - 2, pos + digits) From pypy.commits at gmail.com Tue Feb 19 15:40:21 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 19 Feb 2019 12:40:21 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix test_int_roundtrip in test_marshalimp.py Message-ID: <5c6c69b5.1c69fb81.ab856.1a36@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96102:c6cca0f534ca Date: 2019-02-19 21:36 +0100 http://bitbucket.org/pypy/pypy/changeset/c6cca0f534ca/ Log: fix test_int_roundtrip in test_marshalimp.py marshaling an int that doesn't fit into 32bit will now use the long bytecode. it should still turn into an int on unmarshaling diff --git a/pypy/objspace/std/marshal_impl.py b/pypy/objspace/std/marshal_impl.py --- a/pypy/objspace/std/marshal_impl.py +++ b/pypy/objspace/std/marshal_impl.py @@ -210,7 +210,11 @@ raise oefmt(space.w_ValueError, "bad marshal data") if negative: result = result.neg() - return space.newlong_from_rbigint(result) + # try to fit it into an int + try: + return space.newint(result.toint()) + except OverflowError: + return space.newlong_from_rbigint(result) def pack_float(f): From pypy.commits at gmail.com Wed Feb 20 02:47:44 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 23:47:44 -0800 (PST) Subject: [pypy-commit] pypy default: fix, use unicodehelper.wcharpsize2utf8 more widely Message-ID: <5c6d0620.1c69fb81.8027b.6537@mx.google.com> Author: Matti Picus Branch: Changeset: r96104:9ece3d54c956 Date: 2019-02-20 09:04 +0200 http://bitbucket.org/pypy/pypy/changeset/9ece3d54c956/ Log: fix, use unicodehelper.wcharpsize2utf8 more widely diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -537,14 +537,14 @@ def wcharpsize2utf8(space, wcharp, size): """Safe version of rffi.wcharpsize2utf8. - Raises app-level ValueError if any wchar value is outside the valid + Raises app-level rutf8.OutOfRange if any wchar value is outside the valid codepoint range. """ try: return rffi.wcharpsize2utf8(wcharp, size) - except ValueError: + except rutf8.OutOfRange as e: raise oefmt(space.w_ValueError, - "character is not in range [U+0000; U+10ffff]") + "character %s is not in range [U+0000; U+10ffff]", 'U+%x' % e.code) # ____________________________________________________________ diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -18,7 +18,6 @@ from pypy.module.cpyext.bytesobject import PyString_Check from pypy.module.sys.interp_encoding import setdefaultencoding from pypy.module._codecs.interp_codecs import CodecState -from pypy.interpreter import unicodehelper from pypy.objspace.std import unicodeobject import sys @@ -618,7 +617,7 @@ errors = None state = space.fromcache(CodecState) - result, _, length, byteorder = unicodehelper.str_decode_utf_32_helper( + result, _, length, byteorder = str_decode_utf_32_helper( string, errors, final=True, errorhandler=state.decode_error_handler, byteorder=byteorder) if pbyteorder is not None: @@ -641,7 +640,7 @@ Returns 0 on success, -1 on failure. """ - u = rffi.wcharpsize2utf8(s, length) + u = wcharpsize2utf8(space, s, length) if llerrors: errors = rffi.charp2str(llerrors) else: From pypy.commits at gmail.com Wed Feb 20 02:47:46 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 23:47:46 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch, use unicodehelper.wcharpsize2utf8 which can raise Message-ID: <5c6d0622.1c69fb81.70d81.0b7d@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96105:8c13dfd0c64b Date: 2019-02-20 09:15 +0200 http://bitbucket.org/pypy/pypy/changeset/8c13dfd0c64b/ Log: merge default into branch, use unicodehelper.wcharpsize2utf8 which can raise diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -660,14 +660,14 @@ def wcharpsize2utf8(space, wcharp, size): """Safe version of rffi.wcharpsize2utf8. - Raises app-level ValueError if any wchar value is outside the valid + Raises app-level rutf8.OutOfRange if any wchar value is outside the valid codepoint range. """ try: return rffi.wcharpsize2utf8(wcharp, size) - except ValueError: + except rutf8.OutOfRange as e: raise oefmt(space.w_ValueError, - "character is not in range [U+0000; U+10ffff]") + "character %s is not in range [U+0000; U+10ffff]", 'U+%x' % e.code) # ____________________________________________________________ diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py --- a/pypy/module/cpyext/unicodeobject.py +++ b/pypy/module/cpyext/unicodeobject.py @@ -88,14 +88,10 @@ def unicode_realize(space, py_obj): """ Creates the unicode in the interpreter. The PyUnicodeObject buffer must not - be modified after this call. + be modified after this call. Can raise in wcharpsize2utf8 """ lgt = get_wsize(py_obj) - try: - s_utf8 = rffi.wcharpsize2utf8(get_wbuffer(py_obj), lgt) - except rutf8.OutOfRange as e: - raise oefmt(space.w_ValueError, - 'character U+%x is not in range [U+0000; U+10ffff]' % e.code) + s_utf8 = wcharpsize2utf8(space, get_wbuffer(py_obj), lgt) w_type = from_ref(space, rffi.cast(PyObject, py_obj.c_ob_type)) w_obj = space.allocate_instance(unicodeobject.W_UnicodeObject, w_type) w_obj.__init__(s_utf8, lgt) @@ -300,8 +296,8 @@ maxchar = c if maxchar > MAX_UNICODE: raise oefmt(space.w_ValueError, - "Character U+%d is not in range [U+0000; U+10ffff]", - maxchar) + "Character U+%s is not in range [U+0000; U+10ffff]", + '%x' % maxchar) if maxchar < 256: ucs1_data = rffi.str2charp(value) set_data(py_obj, cts.cast('void*', ucs1_data)) @@ -916,7 +912,7 @@ Returns 0 on success, -1 on failure. """ - u = rffi.wcharpsize2utf8(s, length) + u = wcharpsize2utf8(space, s, length) if llerrors: errors = rffi.charp2str(llerrors) else: From pypy.commits at gmail.com Wed Feb 20 02:47:47 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 19 Feb 2019 23:47:47 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix translation and fix failing test Message-ID: <5c6d0623.1c69fb81.9ffaf.335e@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96106:c73741830344 Date: 2019-02-20 09:46 +0200 http://bitbucket.org/pypy/pypy/changeset/c73741830344/ Log: fix translation and fix failing test diff --git a/pypy/interpreter/mixedmodule.py b/pypy/interpreter/mixedmodule.py --- a/pypy/interpreter/mixedmodule.py +++ b/pypy/interpreter/mixedmodule.py @@ -198,7 +198,7 @@ return space.newtext_or_none(cls.__doc__) def setdictvalue_dont_introduce_cell(self, name, w_value): - """ inofficial interface on MixedModules to override an existing value + """ unofficial interface in MixedModules to override an existing value in the module but without introducing a cell (in the sense of celldict.py) for it. Should be used sparingly, since it will trigger a JIT recompile of all code that uses this module.""" diff --git a/pypy/interpreter/test/test_unicodehelper.py b/pypy/interpreter/test/test_unicodehelper.py --- a/pypy/interpreter/test/test_unicodehelper.py +++ b/pypy/interpreter/test/test_unicodehelper.py @@ -56,7 +56,7 @@ assert s[start:end] in [u'\udc80', u'\uD800\uDFFF'] return '', start, 'b' - assert pytest.raises(IndexError, utf8_encode_utf_8, u, 'strict', + assert pytest.raises(Exception, utf8_encode_utf_8, u, 'strict', errorhandler=errorhandler, allow_surrogates=False) def test_decode_utf8sp(): 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 @@ -195,8 +195,7 @@ dict_w = strategy.unerase(w_dict.dstorage) strategy.mutated() dict_w[name] = w_value # store without cell - return - w_obj.setdictvalue(space, name, w_value) + # nothing to do, not a W_DictMultiObject or wrong strategy create_iterator_classes(ModuleDictStrategy) From pypy.commits at gmail.com Wed Feb 20 03:32:23 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 20 Feb 2019 00:32:23 -0800 (PST) Subject: [pypy-commit] pypy default: add track_allocation to utf82wcharp Message-ID: <5c6d1097.1c69fb81.54295.6168@mx.google.com> Author: Matti Picus Branch: Changeset: r96107:ad61d87e47a5 Date: 2019-02-20 10:11 +0200 http://bitbucket.org/pypy/pypy/changeset/ad61d87e47a5/ Log: add track_allocation to utf82wcharp diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py --- a/rpython/rtyper/lltypesystem/rffi.py +++ b/rpython/rtyper/lltypesystem/rffi.py @@ -1052,16 +1052,20 @@ i += 1 return s.build(), i -def utf82wcharp(utf8, utf8len): +def utf82wcharp(utf8, utf8len, track_allocation=True): from rpython.rlib import rutf8 - w = lltype.malloc(CWCHARP.TO, utf8len + 1, flavor='raw') + if track_allocation: + w = lltype.malloc(CWCHARP.TO, utf8len + 1, flavor='raw', track_allocation=True) + else: + w = lltype.malloc(CWCHARP.TO, utf8len + 1, flavor='raw', track_allocation=False) index = 0 for ch in rutf8.Utf8StringIterator(utf8): w[index] = unichr(ch) index += 1 w[index] = unichr(0) return w +utf82charp._annenforceargs_ = [str, int, bool] # char** CCHARPP = lltype.Ptr(lltype.Array(CCHARP, hints={'nolength': True})) From pypy.commits at gmail.com Wed Feb 20 03:32:25 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 20 Feb 2019 00:32:25 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch Message-ID: <5c6d1099.1c69fb81.f02d8.9471@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96108:b7734909be86 Date: 2019-02-20 10:12 +0200 http://bitbucket.org/pypy/pypy/changeset/b7734909be86/ Log: merge default into branch diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py --- a/rpython/rtyper/lltypesystem/rffi.py +++ b/rpython/rtyper/lltypesystem/rffi.py @@ -1052,16 +1052,20 @@ i += 1 return s.build(), i -def utf82wcharp(utf8, utf8len): +def utf82wcharp(utf8, utf8len, track_allocation=True): from rpython.rlib import rutf8 - w = lltype.malloc(CWCHARP.TO, utf8len + 1, flavor='raw') + if track_allocation: + w = lltype.malloc(CWCHARP.TO, utf8len + 1, flavor='raw', track_allocation=True) + else: + w = lltype.malloc(CWCHARP.TO, utf8len + 1, flavor='raw', track_allocation=False) index = 0 for ch in rutf8.Utf8StringIterator(utf8): w[index] = unichr(ch) index += 1 w[index] = unichr(0) return w +utf82charp._annenforceargs_ = [str, int, bool] # char** CCHARPP = lltype.Ptr(lltype.Array(CCHARP, hints={'nolength': True})) From pypy.commits at gmail.com Wed Feb 20 04:23:31 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 20 Feb 2019 01:23:31 -0800 (PST) Subject: [pypy-commit] pypy default: fix typo Message-ID: <5c6d1c93.1c69fb81.f8681.eb1b@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96109:5dd4d5699b26 Date: 2019-02-20 10:22 +0100 http://bitbucket.org/pypy/pypy/changeset/5dd4d5699b26/ Log: fix typo diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py --- a/rpython/rtyper/lltypesystem/rffi.py +++ b/rpython/rtyper/lltypesystem/rffi.py @@ -1065,7 +1065,7 @@ index += 1 w[index] = unichr(0) return w -utf82charp._annenforceargs_ = [str, int, bool] +utf82wcharp._annenforceargs_ = [str, int, bool] # char** CCHARPP = lltype.Ptr(lltype.Array(CCHARP, hints={'nolength': True})) From pypy.commits at gmail.com Wed Feb 20 04:23:33 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 20 Feb 2019 01:23:33 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default Message-ID: <5c6d1c95.1c69fb81.1a19e.c0e2@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96110:8ee263d8a3b8 Date: 2019-02-20 10:22 +0100 http://bitbucket.org/pypy/pypy/changeset/8ee263d8a3b8/ Log: merge default diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py --- a/rpython/rtyper/lltypesystem/rffi.py +++ b/rpython/rtyper/lltypesystem/rffi.py @@ -1065,7 +1065,7 @@ index += 1 w[index] = unichr(0) return w -utf82charp._annenforceargs_ = [str, int, bool] +utf82wcharp._annenforceargs_ = [str, int, bool] # char** CCHARPP = lltype.Ptr(lltype.Array(CCHARP, hints={'nolength': True})) From pypy.commits at gmail.com Wed Feb 20 04:33:26 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 20 Feb 2019 01:33:26 -0800 (PST) Subject: [pypy-commit] pypy py3.6: add missing tests Message-ID: <5c6d1ee6.1c69fb81.df682.6c4e@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96111:09fa8b41bcf5 Date: 2019-02-20 10:32 +0100 http://bitbucket.org/pypy/pypy/changeset/09fa8b41bcf5/ Log: add missing tests diff --git a/lib-python/conftest.py b/lib-python/conftest.py --- a/lib-python/conftest.py +++ b/lib-python/conftest.py @@ -332,6 +332,14 @@ RegrTest('test_pathlib.py'), RegrTest('test_pdb.py'), RegrTest('test_peepholer.py'), + RegrTest('test_pep247.py'), + RegrTest('test_pep277.py'), + RegrTest('test_pep3120.py'), + RegrTest('test_pep3131.py'), + RegrTest('test_pep3151.py'), + RegrTest('test_pep352.py'), + RegrTest('test_pep380.py'), + RegrTest('test_pep479.py'), RegrTest('test_pickle.py', core=True), RegrTest('test_pickletools.py', core=False), RegrTest('test_pipes.py'), @@ -405,6 +413,7 @@ RegrTest('test_string.py', core=True), RegrTest('test_string_literals.py'), RegrTest('test_stringprep.py'), + RegrTest('test_strlit.py'), RegrTest('test_strptime.py'), RegrTest('test_strtod.py'), RegrTest('test_struct.py', usemodules='struct'), From pypy.commits at gmail.com Wed Feb 20 04:58:27 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 20 Feb 2019 01:58:27 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fixes Message-ID: <5c6d24c3.1c69fb81.43856.dbfd@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96112:20b185cf3f0c Date: 2019-02-20 11:03 +0200 http://bitbucket.org/pypy/pypy/changeset/20b185cf3f0c/ Log: fixes diff --git a/pypy/module/_cffi_backend/errorbox.py b/pypy/module/_cffi_backend/errorbox.py --- a/pypy/module/_cffi_backend/errorbox.py +++ b/pypy/module/_cffi_backend/errorbox.py @@ -90,7 +90,8 @@ w_text = self.space.call_function(w_done) p = rffi.utf82wcharp(self.space.utf8_w(w_text), - track_allocation=False) + self.space.len_w(w_text), + track_allocation=False) if self.text_p: rffi.free_wcharp(self.text_p, track_allocation=False) self.text_p = p # keepalive diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -488,7 +488,7 @@ "or str") try: builder.append_code(codepoint) - except ValueError: + except rutf8.OutOfRange: raise oefmt(space.w_ValueError, "character mapping must be in range(0x110000)") return self.from_utf8builder(builder) @@ -1927,7 +1927,7 @@ pass try: c = rutf8.unichr_as_utf8(r_uint(uchr)) - except ValueError: + except rutf8.OutOfRange: w_encoding = space.newtext('utf-8') w_start = space.newint(pos) w_end = space.newint(pos+1) diff --git a/rpython/rlib/test/test_rutf8.py b/rpython/rlib/test/test_rutf8.py --- a/rpython/rlib/test/test_rutf8.py +++ b/rpython/rlib/test/test_rutf8.py @@ -200,7 +200,7 @@ def test_utf8_string_builder_bad_code(): s = rutf8.Utf8StringBuilder() - with pytest.raises(ValueError): + with pytest.raises(rutf8.OutOfRange): s.append_code(0x110000) assert s.build() == '' assert s.getlength() == 0 From pypy.commits at gmail.com Wed Feb 20 08:05:59 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 20 Feb 2019 05:05:59 -0800 (PST) Subject: [pypy-commit] pypy py3.6: unlink deleted the test file that the other tests depended on Message-ID: <5c6d50b7.1c69fb81.aa96d.3785@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96113:dcf828c11e94 Date: 2019-02-20 13:38 +0100 http://bitbucket.org/pypy/pypy/changeset/dcf828c11e94/ Log: unlink deleted the test file that the other tests depended on diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -23,6 +23,8 @@ mod.path = udir.join('posixtestfile.txt') mod.path.write("this is a test") mod.path2 = udir.join('test_posix2-') + mod.path3 = udir.join('unlinktestfile.txt') + mod.path3.write("delete me!") pdir = udir.ensure('posixtestdir', dir=True) pdir.join('file1').write("test1") os.chmod(str(pdir.join('file1')), 0o600) @@ -66,6 +68,7 @@ cls.w_os = space.appexec([], "(): import os as m ; return m") cls.w_path = space.wrap(str(path)) cls.w_path2 = space.wrap(str(path2)) + cls.w_path3 = space.wrap(str(path3)) cls.w_pdir = space.wrap(str(pdir)) cls.w_bytes_dir = space.newbytes(str(bytes_dir)) cls.w_esurrogate_dir = space.newbytes(str(esurrogate_dir)) @@ -391,7 +394,7 @@ def test_unlink(self): os = self.posix - path = self.path + path = self.path3 with open(path, 'wb'): pass class Path: From pypy.commits at gmail.com Wed Feb 20 08:58:30 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 20 Feb 2019 05:58:30 -0800 (PST) Subject: [pypy-commit] pypy py3.6: add the same skip that test_curses.py uses Message-ID: <5c6d5d06.1c69fb81.d6551.eb70@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96114:888ecff170da Date: 2019-02-20 14:57 +0100 http://bitbucket.org/pypy/pypy/changeset/888ecff170da/ Log: add the same skip that test_curses.py uses diff --git a/pypy/module/readline/test/test_readline.py b/pypy/module/readline/test/test_readline.py --- a/pypy/module/readline/test/test_readline.py +++ b/pypy/module/readline/test/test_readline.py @@ -1,5 +1,14 @@ # -*- coding: utf-8 -*- import sys +import pytest + +def setup_module(mod): + try: + import curses + curses.setupterm() + except: + pytest.skip("Cannot test this here") + class AppTestReadline: spaceconfig = dict(usemodules=[ From pypy.commits at gmail.com Wed Feb 20 09:44:26 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 20 Feb 2019 06:44:26 -0800 (PST) Subject: [pypy-commit] pypy py3.6: adapt this completely amazing code that optimizes sys.exc_info() to wordcode Message-ID: <5c6d67ca.1c69fb81.641c9.419c@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96115:c8b336bec4ac Date: 2019-02-20 15:23 +0100 http://bitbucket.org/pypy/pypy/changeset/c8b336bec4ac/ Log: adapt this completely amazing code that optimizes sys.exc_info() to wordcode diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py --- a/pypy/module/sys/vm.py +++ b/pypy/module/sys/vm.py @@ -149,23 +149,21 @@ p = frame.last_instr if (ord(co[p]) == stdlib_opcode.CALL_FUNCTION or ord(co[p]) == stdlib_opcode.CALL_METHOD): - if ord(co[p+3]) == stdlib_opcode.LOAD_CONST: - lo = ord(co[p+4]) - hi = ord(co[p+5]) - w_constant = frame.getconstant_w((hi * 256) | lo) - if ord(co[p+6]) == stdlib_opcode.BINARY_SUBSCR: + if ord(co[p + 2]) == stdlib_opcode.LOAD_CONST: + lo = ord(co[p + 3]) + w_constant = frame.getconstant_w(lo) + if ord(co[p + 4]) == stdlib_opcode.BINARY_SUBSCR: if space.isinstance_w(w_constant, space.w_int): constant = space.int_w(w_constant) if -3 <= constant <= 1 and constant != -1: need_all_three_args = False - elif (ord(co[p+6]) == stdlib_opcode.LOAD_CONST and - ord(co[p+9]) == stdlib_opcode.BUILD_SLICE and - ord(co[p+12]) == stdlib_opcode.BINARY_SUBSCR): + elif (ord(co[p + 4]) == stdlib_opcode.LOAD_CONST and + ord(co[p + 6]) == stdlib_opcode.BUILD_SLICE and + ord(co[p + 8]) == stdlib_opcode.BINARY_SUBSCR): if (space.is_w(w_constant, space.w_None) or space.isinstance_w(w_constant, space.w_int)): - lo = ord(co[p+7]) - hi = ord(co[p+8]) - w_constant = frame.getconstant_w((hi * 256) | lo) + lo = ord(co[p + 5]) + w_constant = frame.getconstant_w(lo) if space.isinstance_w(w_constant, space.w_int): if space.int_w(w_constant) <= 2: need_all_three_args = False From pypy.commits at gmail.com Wed Feb 20 13:03:35 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 20 Feb 2019 10:03:35 -0800 (PST) Subject: [pypy-commit] pypy default: revert 6165ec8e5e76, broke translation on win32 Message-ID: <5c6d9677.1c69fb81.ab856.f843@mx.google.com> Author: Matti Picus Branch: Changeset: r96116:13b00df4c588 Date: 2019-02-20 19:51 +0200 http://bitbucket.org/pypy/pypy/changeset/13b00df4c588/ Log: revert 6165ec8e5e76, broke translation on win32 diff --git a/rpython/rlib/_os_support.py b/rpython/rlib/_os_support.py --- a/rpython/rlib/_os_support.py +++ b/rpython/rlib/_os_support.py @@ -64,6 +64,8 @@ assert path is not None if isinstance(path, unicode): return path + elif isinstance(path, str): + raise RuntimeError('str given where unicode expected') else: return path.as_unicode() diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -1273,6 +1273,8 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) + assert isinstance(path1, unicode) + assert isinstance(path2, unicode) if not win32traits.MoveFileEx(path1, path2, 0): raise rwin32.lastSavedWindowsError() @@ -1283,6 +1285,8 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) + assert isinstance(path1, unicode) + assert isinstance(path2, unicode) ret = win32traits.MoveFileEx(path1, path2, win32traits.MOVEFILE_REPLACE_EXISTING) if not ret: From pypy.commits at gmail.com Wed Feb 20 13:03:37 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 20 Feb 2019 10:03:37 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch Message-ID: <5c6d9679.1c69fb81.404c4.3d32@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96117:199b7e88fe60 Date: 2019-02-20 19:53 +0200 http://bitbucket.org/pypy/pypy/changeset/199b7e88fe60/ Log: merge default into branch diff --git a/rpython/rlib/_os_support.py b/rpython/rlib/_os_support.py --- a/rpython/rlib/_os_support.py +++ b/rpython/rlib/_os_support.py @@ -64,6 +64,8 @@ assert path is not None if isinstance(path, unicode): return path + elif isinstance(path, str): + raise RuntimeError('str given where unicode expected') else: return path.as_unicode() diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -1273,6 +1273,8 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) + assert isinstance(path1, unicode) + assert isinstance(path2, unicode) if not win32traits.MoveFileEx(path1, path2, 0): raise rwin32.lastSavedWindowsError() @@ -1283,6 +1285,8 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) + assert isinstance(path1, unicode) + assert isinstance(path2, unicode) ret = win32traits.MoveFileEx(path1, path2, win32traits.MOVEFILE_REPLACE_EXISTING) if not ret: From pypy.commits at gmail.com Wed Feb 20 13:03:40 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 20 Feb 2019 10:03:40 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge heads Message-ID: <5c6d967c.1c69fb81.77fc6.fa8f@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96118:02cb9ab27bd1 Date: 2019-02-20 20:05 +0200 http://bitbucket.org/pypy/pypy/changeset/02cb9ab27bd1/ Log: merge heads diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -23,6 +23,8 @@ mod.path = udir.join('posixtestfile.txt') mod.path.write("this is a test") mod.path2 = udir.join('test_posix2-') + mod.path3 = udir.join('unlinktestfile.txt') + mod.path3.write("delete me!") pdir = udir.ensure('posixtestdir', dir=True) pdir.join('file1').write("test1") os.chmod(str(pdir.join('file1')), 0o600) @@ -66,6 +68,7 @@ cls.w_os = space.appexec([], "(): import os as m ; return m") cls.w_path = space.wrap(str(path)) cls.w_path2 = space.wrap(str(path2)) + cls.w_path3 = space.wrap(str(path3)) cls.w_pdir = space.wrap(str(pdir)) cls.w_bytes_dir = space.newbytes(str(bytes_dir)) cls.w_esurrogate_dir = space.newbytes(str(esurrogate_dir)) @@ -391,7 +394,7 @@ def test_unlink(self): os = self.posix - path = self.path + path = self.path3 with open(path, 'wb'): pass class Path: diff --git a/pypy/module/readline/test/test_readline.py b/pypy/module/readline/test/test_readline.py --- a/pypy/module/readline/test/test_readline.py +++ b/pypy/module/readline/test/test_readline.py @@ -1,5 +1,14 @@ # -*- coding: utf-8 -*- import sys +import pytest + +def setup_module(mod): + try: + import curses + curses.setupterm() + except: + pytest.skip("Cannot test this here") + class AppTestReadline: spaceconfig = dict(usemodules=[ diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py --- a/pypy/module/sys/vm.py +++ b/pypy/module/sys/vm.py @@ -149,23 +149,21 @@ p = frame.last_instr if (ord(co[p]) == stdlib_opcode.CALL_FUNCTION or ord(co[p]) == stdlib_opcode.CALL_METHOD): - if ord(co[p+3]) == stdlib_opcode.LOAD_CONST: - lo = ord(co[p+4]) - hi = ord(co[p+5]) - w_constant = frame.getconstant_w((hi * 256) | lo) - if ord(co[p+6]) == stdlib_opcode.BINARY_SUBSCR: + if ord(co[p + 2]) == stdlib_opcode.LOAD_CONST: + lo = ord(co[p + 3]) + w_constant = frame.getconstant_w(lo) + if ord(co[p + 4]) == stdlib_opcode.BINARY_SUBSCR: if space.isinstance_w(w_constant, space.w_int): constant = space.int_w(w_constant) if -3 <= constant <= 1 and constant != -1: need_all_three_args = False - elif (ord(co[p+6]) == stdlib_opcode.LOAD_CONST and - ord(co[p+9]) == stdlib_opcode.BUILD_SLICE and - ord(co[p+12]) == stdlib_opcode.BINARY_SUBSCR): + elif (ord(co[p + 4]) == stdlib_opcode.LOAD_CONST and + ord(co[p + 6]) == stdlib_opcode.BUILD_SLICE and + ord(co[p + 8]) == stdlib_opcode.BINARY_SUBSCR): if (space.is_w(w_constant, space.w_None) or space.isinstance_w(w_constant, space.w_int)): - lo = ord(co[p+7]) - hi = ord(co[p+8]) - w_constant = frame.getconstant_w((hi * 256) | lo) + lo = ord(co[p + 5]) + w_constant = frame.getconstant_w(lo) if space.isinstance_w(w_constant, space.w_int): if space.int_w(w_constant) <= 2: need_all_three_args = False From pypy.commits at gmail.com Wed Feb 20 17:42:43 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 20 Feb 2019 14:42:43 -0800 (PST) Subject: [pypy-commit] pypy py3.6: call .keys first on the argument to OrderedDict Message-ID: <5c6dd7e3.1c69fb81.28c0c.822c@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96119:068ea0e91377 Date: 2019-02-20 22:44 +0100 http://bitbucket.org/pypy/pypy/changeset/068ea0e91377/ Log: call .keys first on the argument to OrderedDict diff --git a/pypy/module/_collections/app_odict.py b/pypy/module/_collections/app_odict.py --- a/pypy/module/_collections/app_odict.py +++ b/pypy/module/_collections/app_odict.py @@ -43,12 +43,12 @@ len(args)) if args: other = args[0] - if hasattr(other, 'items'): + if hasattr(other, "keys"): + for key in other.keys(): + self[key] = other[key] + elif hasattr(other, 'items'): for key, value in other.items(): self[key] = value - elif hasattr(other, "keys"): - for key in other.keys(): - self[key] = other[key] else: for key, value in other: self[key] = value diff --git a/pypy/module/_collections/test/test_ordereddict.py b/pypy/module/_collections/test/test_ordereddict.py --- a/pypy/module/_collections/test/test_ordereddict.py +++ b/pypy/module/_collections/test/test_ordereddict.py @@ -36,3 +36,18 @@ assert list(reversed(od.keys())) == [t[0] for t in reversed(pairs)] assert list(reversed(od.values())) == [t[1] for t in reversed(pairs)] assert list(reversed(od.items())) == list(reversed(pairs)) + + def test_call_key_first(self): + from _collections import OrderedDict + + calls = [] + class Spam: + def keys(self): + calls.append('keys') + return () + def items(self): + calls.append('items') + return () + + OrderedDict(Spam()) + assert calls == ['keys'] From pypy.commits at gmail.com Thu Feb 21 05:35:09 2019 From: pypy.commits at gmail.com (cfbolz) Date: Thu, 21 Feb 2019 02:35:09 -0800 (PST) Subject: [pypy-commit] pypy py3.6: copy slightly lengthy test from cpython to find the (crashing) bug in set_lineno Message-ID: <5c6e7edd.1c69fb81.591ba.5ce6@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96120:1d06586fec74 Date: 2019-02-21 11:26 +0100 http://bitbucket.org/pypy/pypy/changeset/1d06586fec74/ Log: copy slightly lengthy test from cpython to find the (crashing) bug in set_lineno diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py --- a/pypy/interpreter/pyframe.py +++ b/pypy/interpreter/pyframe.py @@ -739,6 +739,7 @@ delta_iblock = min_delta_iblock = 0 # see below for comment addr = min_addr while addr < max_addr: + assert addr & 1 == 0 op = ord(code[addr]) if op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH, @@ -749,10 +750,7 @@ if delta_iblock < min_delta_iblock: min_delta_iblock = delta_iblock - if op >= HAVE_ARGUMENT: - addr += 3 - else: - addr += 1 + addr += 2 # 'min_delta_iblock' is <= 0; its absolute value is the number of # blocks we exit. 'go_iblock' is the delta number of blocks diff --git a/pypy/interpreter/test/test_pyframe.py b/pypy/interpreter/test/test_pyframe.py --- a/pypy/interpreter/test/test_pyframe.py +++ b/pypy/interpreter/test/test_pyframe.py @@ -179,6 +179,61 @@ ('return', 5)] """ + def test_set_lineno_jump_out_of_block(self): + import sys + class JumpTracer: + def __init__(self, function): + self.function = function + self.jumpFrom = function.jump[0] + self.jumpTo = function.jump[1] + self.done = False + + def trace(self, frame, event, arg): + if not self.done and frame.f_code == self.function.__code__: + firstLine = frame.f_code.co_firstlineno + if event == 'line' and frame.f_lineno == firstLine + self.jumpFrom: + # Cope with non-integer self.jumpTo (because of + # no_jump_to_non_integers below). + try: + frame.f_lineno = firstLine + self.jumpTo + except TypeError: + frame.f_lineno = self.jumpTo + self.done = True + return self.trace + + def run_test(func): + tracer = JumpTracer(func) + sys.settrace(tracer.trace) + output = [] + func(output) + sys.settrace(None) + assert func.output == output + + # copied from cpython test suite + def jump_out_of_block_forwards(output): + for i in 1, 2: + output.append(2) + for j in [3]: # Also tests jumping over a block + output.append(4) + output.append(5) + + jump_out_of_block_forwards.jump = (3, 5) + jump_out_of_block_forwards.output = [2, 5] + #run_test(jump_out_of_block_forwards) + + def jump_out_of_block_backwards(output): + output.append(1) + for i in [1]: + output.append(3) + for j in [2]: # Also tests jumping over a block + output.append(5) + output.append(6) + output.append(7) + + jump_out_of_block_backwards.jump = (6, 1) + jump_out_of_block_backwards.output = [1, 3, 5, 1, 3, 5, 6, 7] + run_test(jump_out_of_block_backwards) + def test_f_back(self): import sys def f(): From pypy.commits at gmail.com Thu Feb 21 05:35:56 2019 From: pypy.commits at gmail.com (cfbolz) Date: Thu, 21 Feb 2019 02:35:56 -0800 (PST) Subject: [pypy-commit] pypy py3.5: close 3.5 branch to focus on py3.6 Message-ID: <5c6e7f0c.1c69fb81.9a979.ab28@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.5 Changeset: r96121:eb6e70a9658e Date: 2019-02-21 11:35 +0100 http://bitbucket.org/pypy/pypy/changeset/eb6e70a9658e/ Log: close 3.5 branch to focus on py3.6 From pypy.commits at gmail.com Thu Feb 21 05:57:33 2019 From: pypy.commits at gmail.com (mattip) Date: Thu, 21 Feb 2019 02:57:33 -0800 (PST) Subject: [pypy-commit] pypy default: fix Message-ID: <5c6e841d.1c69fb81.a1582.6532@mx.google.com> Author: Matti Picus Branch: Changeset: r96122:789d5650e3af Date: 2019-02-21 12:56 +0200 http://bitbucket.org/pypy/pypy/changeset/789d5650e3af/ Log: fix diff --git a/rpython/rlib/test/test_rutf8.py b/rpython/rlib/test/test_rutf8.py --- a/rpython/rlib/test/test_rutf8.py +++ b/rpython/rlib/test/test_rutf8.py @@ -200,7 +200,7 @@ def test_utf8_string_builder_bad_code(): s = rutf8.Utf8StringBuilder() - with pytest.raises(ValueError): + with pytest.raises(rutf8.OutOfRange): s.append_code(0x110000) assert s.build() == '' assert s.getlength() == 0 From pypy.commits at gmail.com Thu Feb 21 09:09:50 2019 From: pypy.commits at gmail.com (arigo) Date: Thu, 21 Feb 2019 06:09:50 -0800 (PST) Subject: [pypy-commit] cffi default: Issue #402 Message-ID: <5c6eb12e.1c69fb81.52090.7f7d@mx.google.com> Author: Armin Rigo Branch: Changeset: r3227:edac34833b64 Date: 2019-02-21 15:10 +0100 http://bitbucket.org/cffi/cffi/changeset/edac34833b64/ Log: Issue #402 Fix bug in documentation diff --git a/doc/source/ref.rst b/doc/source/ref.rst --- a/doc/source/ref.rst +++ b/doc/source/ref.rst @@ -157,8 +157,9 @@ - use ``file.write()`` and ``file.readinto()`` with such a buffer (for files opened in binary mode) -- use ``ffi.buffer(mystruct[0])[:] = socket.recv(len(buffer))`` to read - into a struct over a socket, rewriting the contents of mystruct[0] +- overwrite the content of a struct: if ``p`` is a cdata pointing to + it, use ``ffi.buffer(p)[:] = newcontent``, where ``newcontent`` is + a bytes object (``str`` in Python 2). Remember that like in C, you can use ``array + index`` to get the pointer to the index'th item of an array. (In C you might more naturally write @@ -175,7 +176,7 @@ - ``buf[:] = newstr``: change the original content (or ``buf[start:end] = newstr``) -- ``len(buf), buf[index], buf[index] = newchar``: access as a sequence +- ``len(buf)``, ``buf[index]``, ``buf[index] = newchar``: access as a sequence of characters. The buffer object returned by ``ffi.buffer(cdata)`` keeps alive the From pypy.commits at gmail.com Thu Feb 21 09:46:08 2019 From: pypy.commits at gmail.com (stevie_92) Date: Thu, 21 Feb 2019 06:46:08 -0800 (PST) Subject: [pypy-commit] pypy cpyext-gc-cycle: Implemented wrapper for tp_finalize Message-ID: <5c6eb9b0.1c69fb81.7c97b.61ce@mx.google.com> Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r96124:78d356baf93c Date: 2019-02-21 15:33 +0100 http://bitbucket.org/pypy/pypy/changeset/78d356baf93c/ Log: Implemented wrapper for tp_finalize 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 @@ -12,7 +12,7 @@ getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc, inquiry, ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc, cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc, - getbufferproc, ssizessizeobjargproc) + getbufferproc, ssizessizeobjargproc, destructor) from pypy.module.cpyext.pyobject import make_ref, from_ref, as_pyobj, decref from pypy.module.cpyext.pyerrors import PyErr_Occurred from pypy.module.cpyext.memoryobject import fill_Py_buffer @@ -438,6 +438,16 @@ fq.register_finalizer(buf) return buf.wrap(space) +class wrap_del(W_PyCWrapperObject): + def call(self, space, w_self, __args__): + self.check_args(__args__, 0) + func = self.get_func_to_call() + func_target = rffi.cast(destructor, func) + res = generic_cpy_call(space, func_target, w_self) + if res == -1: + space.fromcache(State).check_and_raise_exception(always=True) + return space.w_None + def get_richcmp_func(OP_CONST): class wrap_richcmp(W_PyCWrapperObject): def call(self, space, w_self, __args__): @@ -832,8 +842,21 @@ return 0 return slot_tp_descr_set + at slot_factory('tp_finalize') +def make_tp_finalize(space, typedef, name, attr): + w_type = space.gettypeobject(typedef) + new_fn = w_type.lookup('__del__') + if new_fn is None: + return -missing_wrappers = ['wrap_indexargfunc', 'wrap_del'] + @slot_function([PyObject], lltype.Void) + @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) + def slot_tp_finalize(space, w_self): + args = Arguments(space, []) + return space.call_args(w_self, args) + return slot_tp_finalize + +missing_wrappers = ['wrap_indexargfunc'] for name in missing_wrappers: assert name not in globals() class missing_wrapper(W_PyCWrapperObject): @@ -848,7 +871,6 @@ missing_builtin_slots = [ 'tp_print', 'tp_compare', 'tp_getattr', 'tp_setattr', 'tp_setattro', - 'tp_finalize', 'tp_richcompare', 'tp_del', 'tp_as_buffer.c_bf_getwritebuffer', 'tp_as_number.c_nb_bool', 'tp_as_number.c_nb_coerce', 'tp_as_number.c_nb_inplace_add', 'tp_as_number.c_nb_inplace_subtract', From pypy.commits at gmail.com Fri Feb 22 05:28:15 2019 From: pypy.commits at gmail.com (stevie_92) Date: Fri, 22 Feb 2019 02:28:15 -0800 (PST) Subject: [pypy-commit] pypy cpyext-gc-cycle: Added more tests for "modern" rawrefcount finalizers Message-ID: <5c6fcebf.1c69fb81.5c8e6.2805@mx.google.com> Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r96125:a3d95a9939f4 Date: 2019-02-22 11:27 +0100 http://bitbucket.org/pypy/pypy/changeset/a3d95a9939f4/ Log: Added more tests for "modern" rawrefcount finalizers diff --git a/rpython/memory/gc/test/dot/free_finalizer_nocycle.dot b/rpython/memory/gc/test/dot/free_finalizer_nocycle_1a.dot rename from rpython/memory/gc/test/dot/free_finalizer_nocycle.dot rename to rpython/memory/gc/test/dot/free_finalizer_nocycle_1a.dot diff --git a/rpython/memory/gc/test/dot/free_finalizer_nocycle.dot b/rpython/memory/gc/test/dot/free_finalizer_nocycle_1b.dot copy from rpython/memory/gc/test/dot/free_finalizer_nocycle.dot copy to rpython/memory/gc/test/dot/free_finalizer_nocycle_1b.dot --- a/rpython/memory/gc/test/dot/free_finalizer_nocycle.dot +++ b/rpython/memory/gc/test/dot/free_finalizer_nocycle_1b.dot @@ -1,7 +1,7 @@ digraph G { "a" [type=P, alive=n]; "b" [type=B, alive=n]; - "c" [type=C, alive=n, finalizer=modern]; + "c" [type=C, alive=n, finalizer=modern, delete="d"]; "d" [type=C, alive=n]; "e" [type=B, alive=n]; "f" [type=P, alive=n]; diff --git a/rpython/memory/gc/test/dot/free_finalizer_nocycle.dot b/rpython/memory/gc/test/dot/free_finalizer_nocycle_1c.dot copy from rpython/memory/gc/test/dot/free_finalizer_nocycle.dot copy to rpython/memory/gc/test/dot/free_finalizer_nocycle_1c.dot --- a/rpython/memory/gc/test/dot/free_finalizer_nocycle.dot +++ b/rpython/memory/gc/test/dot/free_finalizer_nocycle_1c.dot @@ -1,8 +1,8 @@ digraph G { "a" [type=P, alive=n]; "b" [type=B, alive=n]; - "c" [type=C, alive=n, finalizer=modern]; - "d" [type=C, alive=n]; + "c" [type=C, alive=n, finalizer=modern, delete="d"]; + "d" [type=C, alive=n, finalizer=modern, delete="e"]; "e" [type=B, alive=n]; "f" [type=P, alive=n]; "a" -> "b"; diff --git a/rpython/memory/gc/test/dot/free_finalizer_simple.dot b/rpython/memory/gc/test/dot/free_finalizer_simple_1a.dot rename from rpython/memory/gc/test/dot/free_finalizer_simple.dot rename to rpython/memory/gc/test/dot/free_finalizer_simple_1a.dot diff --git a/rpython/memory/gc/test/dot/free_finalizer_simple.dot b/rpython/memory/gc/test/dot/free_finalizer_simple_1b.dot copy from rpython/memory/gc/test/dot/free_finalizer_simple.dot copy to rpython/memory/gc/test/dot/free_finalizer_simple_1b.dot --- a/rpython/memory/gc/test/dot/free_finalizer_simple.dot +++ b/rpython/memory/gc/test/dot/free_finalizer_simple_1b.dot @@ -1,7 +1,7 @@ digraph G { "a" [type=P, alive=n]; "b" [type=B, alive=n]; - "c" [type=C, alive=n, finalizer=modern]; + "c" [type=C, alive=n, finalizer=modern, delete="d"]; "d" [type=C, alive=n]; "e" [type=B, alive=n]; "f" [type=P, alive=n]; diff --git a/rpython/memory/gc/test/dot/keep_finalizer_simple.dot b/rpython/memory/gc/test/dot/keep_finalizer_simple_1a.dot rename from rpython/memory/gc/test/dot/keep_finalizer_simple.dot rename to rpython/memory/gc/test/dot/keep_finalizer_simple_1a.dot diff --git a/rpython/memory/gc/test/dot/keep_finalizer_simple.dot b/rpython/memory/gc/test/dot/keep_finalizer_simple_1b.dot copy from rpython/memory/gc/test/dot/keep_finalizer_simple.dot copy to rpython/memory/gc/test/dot/keep_finalizer_simple_1b.dot --- a/rpython/memory/gc/test/dot/keep_finalizer_simple.dot +++ b/rpython/memory/gc/test/dot/keep_finalizer_simple_1b.dot @@ -1,7 +1,7 @@ digraph G { "a" [type=P, alive=y]; "b" [type=B, alive=y]; - "c" [type=C, alive=y, finalizer=modern, resurrect="d"]; + "c" [type=C, alive=y, finalizer=modern, resurrect="c"]; "d" [type=C, alive=y]; "e" [type=B, alive=y]; "f" [type=P, alive=y]; diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py --- a/rpython/memory/gc/test/test_rawrefcount.py +++ b/rpython/memory/gc/test/test_rawrefcount.py @@ -31,7 +31,9 @@ self.pyobjs = [] self.pyobj_refs = [] self.pyobj_finalizer = {} + self.pyobj_finalized = {} self.pyobj_resurrect = {} + self.pyobj_delete = {} def rawrefcount_tp_traverse(obj, callback, args): refs = self.pyobj_refs[self.pyobjs.index(obj)] @@ -85,6 +87,10 @@ refs = self.pyobj_resurrect[self.pyobjs.index(pyobj_source)] = [] refs.append(pyobj_target) + def _rawrefcount_add_delete(self, pyobj_source, pyobj_target): + refs = self.pyobj_delete[self.pyobjs.index(pyobj_source)] = [] + refs.append(pyobj_target) + def _rawrefcount_pypyobj(self, intval, rooted=False, create_old=True): p1 = self.malloc(S) p1.x = intval @@ -412,8 +418,6 @@ @py.test.mark.parametrize("file", dot_files) def test_dots(self, file): from rpython.memory.gc.test.dot import pydot - from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY - from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT class Node: def __init__(self, info): @@ -443,12 +447,14 @@ self.info = info class NodeInfo: - def __init__(self, type, alive, ext_refcnt, finalizer, resurrect): + def __init__(self, type, alive, ext_refcnt, finalizer, resurrect, + delete): self.type = type self.alive = alive self.ext_refcnt = ext_refcnt self.finalizer = finalizer self.resurrect = resurrect + self.delete = delete path = os.path.join(self.dot_dir, file) g = pydot.graph_from_dot_file(path)[0] @@ -468,7 +474,9 @@ if finalizer <> None: finalizers = True resurrect = attr['resurrect'] if 'resurrect' in attr else None - info = NodeInfo(type, alive, ext_refcnt, finalizer, resurrect) + delete = attr['delete'] if 'delete' in attr else None + info = NodeInfo(type, alive, ext_refcnt, finalizer, resurrect, + delete) if type == "C": r, raddr, check_alive = self._rawrefcount_pyobj() r.c_ob_refcnt += ext_refcnt @@ -509,10 +517,15 @@ if hasattr(n, "r"): index = self.pyobjs.index(n.r) resurrect = n.info.resurrect - if n.info.finalizer == "modern" and resurrect is not None: + delete = n.info.delete + if n.info.finalizer == "modern": self.pyobj_finalizer[index] = RAWREFCOUNT_FINALIZER_MODERN - self._rawrefcount_add_resurrect(n.r, nodes[resurrect].r) - nodes[resurrect].info.ext_refcnt += 1 + if resurrect is not None: + self._rawrefcount_add_resurrect(n.r, + nodes[resurrect].r) + nodes[resurrect].info.ext_refcnt += 1 + if delete is not None: + self._rawrefcount_add_delete(n.r, nodes[delete].r) else: self.pyobj_finalizer[index] = RAWREFCOUNT_FINALIZER_NONE @@ -534,18 +547,32 @@ def cleanup(): # do cleanup after collection (clear all dead pyobjects) + def finalize_modern(pyobj): + index = self.pyobjs.index(pyobj) + if not self.pyobj_finalizer.has_key(index) or \ + self.pyobj_finalizer[index] != \ + RAWREFCOUNT_FINALIZER_MODERN: + return + if self.pyobj_finalized.has_key(index): + return + self.pyobj_finalized[index] = True + if self.pyobj_resurrect.has_key(index): + resurrect = self.pyobj_resurrect[index] + for r in resurrect: + r.c_ob_refcnt += 1 + if self.pyobj_delete.has_key(index): + delete = self.pyobj_delete[index] + for r in delete: + self.pyobj_refs[index].remove(r) + decref(r, None) + def decref_children(pyobj): self.gc.rrc_tp_traverse(pyobj, decref, None) def decref(pyobj, ignore): pyobj.c_ob_refcnt -= 1 if pyobj.c_ob_refcnt == 0: - if self.pyobj_resurrect.has_key(self.pyobjs.index(pyobj)): - resurrect = self.pyobj_resurrect[ - self.pyobjs.index(pyobj)] - for r in resurrect: - r.c_ob_refcnt += 1 - resurrect.remove(r) + finalize_modern(pyobj) if pyobj.c_ob_refcnt == 0: gchdr = self.gc.rrc_pyobj_as_gc(pyobj) next = gchdr.c_gc_next @@ -567,12 +594,9 @@ while next <> llmemory.NULL: pyobj = llmemory.cast_adr_to_ptr(next, self.gc.PYOBJ_HDR_PTR) - if self.pyobj_resurrect.has_key(self.pyobjs.index(pyobj)): - resurrect = self.pyobj_resurrect[self.pyobjs.index(pyobj)] - for r in resurrect: - r.c_ob_refcnt += 1 - # TODO: improve test, use flag in gc_refs instead - resurrect.remove(r) + pyobj.c_ob_refcnt += 1 + finalize_modern(pyobj) + decref(pyobj, None) next = self.gc.rawrefcount_next_cyclic_isolate() next_dead = self.gc.rawrefcount_cyclic_garbage_head() From pypy.commits at gmail.com Fri Feb 22 06:20:52 2019 From: pypy.commits at gmail.com (arigo) Date: Fri, 22 Feb 2019 03:20:52 -0800 (PST) Subject: [pypy-commit] cffi default: Issue #402 Message-ID: <5c6fdb14.1c69fb81.4b173.4fde@mx.google.com> Author: Armin Rigo Branch: Changeset: r3228:b8f5fc1c699b Date: 2019-02-22 12:21 +0100 http://bitbucket.org/cffi/cffi/changeset/b8f5fc1c699b/ Log: Issue #402 More rewording diff --git a/doc/source/ref.rst b/doc/source/ref.rst --- a/doc/source/ref.rst +++ b/doc/source/ref.rst @@ -146,11 +146,17 @@ +++++++++++++++++++++++++++++++ **ffi.buffer(cdata, [size])**: return a buffer object that references -the raw C data pointed to by the given 'cdata', of 'size' bytes. The -'cdata' must be a pointer or an array. If unspecified, the size of the +the raw C data pointed to by the given 'cdata', of 'size' bytes. What +Python calls "a buffer", or more precisely "an object supporting the +buffer interface", is an object that represents some raw memory and +that can be passed around to various built-in or extension functions; +these built-in functions read from or write to the raw memory directly, +without needing an extra copy. + +The 'cdata' argument +must be a pointer or an array. If unspecified, the size of the buffer is either the size of what ``cdata`` points to, or the whole size -of the array. Getting a buffer is useful because you can read from it -without an extra copy, or write into it to change the original value. +of the array. Here are a few examples of where buffer() would be useful: @@ -165,15 +171,15 @@ to the index'th item of an array. (In C you might more naturally write ``&array[index]``, but that is equivalent.) -The returned object is not a built-in buffer nor memoryview object, -because these objects' API changes too much across Python versions. -Instead it has the following Python API (a subset of Python 2's -``buffer``): +The returned object's type is not the builtin ``buffer`` nor ``memoryview`` +types, because these types' API changes too much across Python versions. +Instead it has the following Python API (a subset of Python 2's ``buffer``) +in addition to supporting the buffer interface: -- ``buf[:]`` or ``bytes(buf)``: fetch a copy as a regular byte string (or - ``buf[start:end]`` for a part) +- ``buf[:]`` or ``bytes(buf)``: copy data out of the buffer, returning a + regular byte string (or ``buf[start:end]`` for a part) -- ``buf[:] = newstr``: change the original content (or ``buf[start:end] +- ``buf[:] = newstr``: copy data into the buffer (or ``buf[start:end] = newstr``) - ``len(buf)``, ``buf[index]``, ``buf[index] = newchar``: access as a sequence @@ -194,9 +200,11 @@ **ffi.from_buffer([cdecl,] python_buffer, require_writable=False)**: return an array cdata (by default a ````) that points to the data of the given Python object, which must support the -buffer interface. This is the opposite of ``ffi.buffer()``. It gives -a reference to the existing data, not a copy. -It is meant to be used on objects +buffer interface. Note that ``ffi.from_buffer()`` turns a generic +Python buffer object into a cdata object, whereas ``ffi.buffer()`` does +the opposite conversion. Both calls don't actually copy any data. + +``ffi.from_buffer()`` is meant to be used on objects containing large quantities of raw data, like bytearrays or ``array.array`` or numpy arrays. It supports both the old *buffer* API (in Python 2.x) and the From pypy.commits at gmail.com Fri Feb 22 06:25:56 2019 From: pypy.commits at gmail.com (arigo) Date: Fri, 22 Feb 2019 03:25:56 -0800 (PST) Subject: [pypy-commit] cffi default: typo Message-ID: <5c6fdc44.1c69fb81.a1135.5ee4@mx.google.com> Author: Armin Rigo Branch: Changeset: r3229:28096df0c342 Date: 2019-02-22 12:26 +0100 http://bitbucket.org/cffi/cffi/changeset/28096df0c342/ Log: typo diff --git a/doc/source/ref.rst b/doc/source/ref.rst --- a/doc/source/ref.rst +++ b/doc/source/ref.rst @@ -177,7 +177,7 @@ in addition to supporting the buffer interface: - ``buf[:]`` or ``bytes(buf)``: copy data out of the buffer, returning a - regular byte string (or ``buf[start:end]`` for a part) + regular byte string (or ``buf[start:end]`` for a part) - ``buf[:] = newstr``: copy data into the buffer (or ``buf[start:end] = newstr``) From pypy.commits at gmail.com Fri Feb 22 07:45:19 2019 From: pypy.commits at gmail.com (mattip) Date: Fri, 22 Feb 2019 04:45:19 -0800 (PST) Subject: [pypy-commit] pypy py3.6: define a compile arg, not a macro (arigato) Message-ID: <5c6feedf.1c69fb81.f5ec6.6641@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96126:323ddcb8a8c4 Date: 2019-02-22 14:44 +0200 http://bitbucket.org/pypy/pypy/changeset/323ddcb8a8c4/ Log: define a compile arg, not a macro (arigato) diff --git a/lib_pypy/_blake2/_blake2_build.py b/lib_pypy/_blake2/_blake2_build.py --- a/lib_pypy/_blake2/_blake2_build.py +++ b/lib_pypy/_blake2/_blake2_build.py @@ -5,11 +5,14 @@ from cffi import FFI IS_ARM = platform.machine().startswith('arm') +IS_WIN = sys.platform == 'win32' if IS_ARM: # XXX Choose neon accelaration - define_macros = [] + extra_compile_args = [] +elif IS_WIN: + extra_compile_args = ['/arch:SSE2'] else: - define_macros = [('__SSE2__', '1')] + extra_compile_args = ['-msse2'] @@ -82,7 +85,7 @@ sources=[os.path.join(_libdir, 'blake2b.c'), ], include_dirs=[_libdir], - define_macros=define_macros, + extra_compile_args=extra_compile_args, ) def _replace_b2s(src): @@ -98,7 +101,7 @@ sources=[os.path.join(_libdir, 'blake2s.c'), ], include_dirs=[_libdir], - define_macros=define_macros, + extra_compile_args=extra_compile_args, ) if __name__ == '__main__': From pypy.commits at gmail.com Fri Feb 22 11:03:52 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 22 Feb 2019 08:03:52 -0800 (PST) Subject: [pypy-commit] pypy py3.6: kill dead global Message-ID: <5c701d68.1c69fb81.2294a.59e3@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96127:18e9ae494869 Date: 2019-02-21 21:55 +0100 http://bitbucket.org/pypy/pypy/changeset/18e9ae494869/ Log: kill dead global diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py --- a/pypy/interpreter/pyframe.py +++ b/pypy/interpreter/pyframe.py @@ -25,7 +25,6 @@ for op in '''DUP_TOP POP_TOP SETUP_LOOP SETUP_EXCEPT SETUP_FINALLY SETUP_WITH SETUP_ASYNC_WITH POP_BLOCK END_FINALLY'''.split(): globals()[op] = stdlib_opcode.opmap[op] -HAVE_ARGUMENT = stdlib_opcode.HAVE_ARGUMENT class FrameDebugData(object): """ A small object that holds debug data for tracing From pypy.commits at gmail.com Fri Feb 22 11:03:54 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 22 Feb 2019 08:03:54 -0800 (PST) Subject: [pypy-commit] pypy py3.6: pypy's recursion limit is not precise Message-ID: <5c701d6a.1c69fb81.10bb6.58a8@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96128:aebd4e263fc5 Date: 2019-02-22 14:13 +0100 http://bitbucket.org/pypy/pypy/changeset/aebd4e263fc5/ Log: pypy's recursion limit is not precise diff --git a/lib-python/3/test/test_traceback.py b/lib-python/3/test/test_traceback.py --- a/lib-python/3/test/test_traceback.py +++ b/lib-python/3/test/test_traceback.py @@ -351,7 +351,8 @@ # Check the recursion count is roughly as expected rec_limit = sys.getrecursionlimit() - self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit)) + # PyPy's recursion limit is a lot less precise + self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-100, 2*rec_limit)) # Check a known (limited) number of recursive invocations def g(count=10): From pypy.commits at gmail.com Fri Feb 22 11:03:56 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 22 Feb 2019 08:03:56 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix changed module name Message-ID: <5c701d6c.1c69fb81.2a13b.aae7@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96129:905b81e90065 Date: 2019-02-22 14:23 +0100 http://bitbucket.org/pypy/pypy/changeset/905b81e90065/ Log: fix changed module name diff --git a/pypy/module/thread/os_local.py b/pypy/module/thread/os_local.py --- a/pypy/module/thread/os_local.py +++ b/pypy/module/thread/os_local.py @@ -87,7 +87,7 @@ Local.__init__(local, space, __args__) return local -Local.typedef = TypeDef("thread._local", +Local.typedef = TypeDef("_thread._local", __doc__ = "Thread-local data", __new__ = interp2app(Local.descr_local__new__.im_func), __dict__ = GetSetProperty(descr_get_dict, cls=Local), From pypy.commits at gmail.com Fri Feb 22 11:03:58 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 22 Feb 2019 08:03:58 -0800 (PST) Subject: [pypy-commit] pypy py3.6: this was actually a rename to test_redirector.py (which we have) Message-ID: <5c701d6e.1c69fb81.e2112.ad4c@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96130:1db0679fa78a Date: 2019-02-22 14:35 +0100 http://bitbucket.org/pypy/pypy/changeset/1db0679fa78a/ Log: this was actually a rename to test_redirector.py (which we have) diff --git a/lib-python/3/idlelib/idle_test/test_widgetredir.py b/lib-python/3/idlelib/idle_test/test_widgetredir.py deleted file mode 100644 --- a/lib-python/3/idlelib/idle_test/test_widgetredir.py +++ /dev/null @@ -1,125 +0,0 @@ -'''Test idlelib.WidgetRedirector. - -100% coverage -''' -from test.support import requires -import unittest -from idlelib.idle_test.mock_idle import Func -from tkinter import Tk, Text, TclError -from idlelib.WidgetRedirector import WidgetRedirector - - -class InitCloseTest(unittest.TestCase): - - @classmethod - def setUpClass(cls): - requires('gui') - cls.root = Tk() - cls.root.withdraw() - cls.text = Text(cls.root) - - @classmethod - def tearDownClass(cls): - del cls.text - cls.root.destroy() - del cls.root - - def test_init(self): - redir = WidgetRedirector(self.text) - self.assertEqual(redir.widget, self.text) - self.assertEqual(redir.tk, self.text.tk) - self.assertRaises(TclError, WidgetRedirector, self.text) - redir.close() # restore self.tk, self.text - - def test_close(self): - redir = WidgetRedirector(self.text) - redir.register('insert', Func) - redir.close() - self.assertEqual(redir._operations, {}) - self.assertFalse(hasattr(self.text, 'widget')) - - -class WidgetRedirectorTest(unittest.TestCase): - - @classmethod - def setUpClass(cls): - requires('gui') - cls.root = Tk() - cls.root.withdraw() - cls.text = Text(cls.root) - - @classmethod - def tearDownClass(cls): - del cls.text - cls.root.update_idletasks() - cls.root.destroy() - del cls.root - - def setUp(self): - self.redir = WidgetRedirector(self.text) - self.func = Func() - self.orig_insert = self.redir.register('insert', self.func) - self.text.insert('insert', 'asdf') # leaves self.text empty - - def tearDown(self): - self.text.delete('1.0', 'end') - self.redir.close() - - def test_repr(self): # partly for 100% coverage - self.assertIn('Redirector', repr(self.redir)) - self.assertIn('Original', repr(self.orig_insert)) - - def test_register(self): - self.assertEqual(self.text.get('1.0', 'end'), '\n') - self.assertEqual(self.func.args, ('insert', 'asdf')) - self.assertIn('insert', self.redir._operations) - self.assertIn('insert', self.text.__dict__) - self.assertEqual(self.text.insert, self.func) - - def test_original_command(self): - self.assertEqual(self.orig_insert.operation, 'insert') - self.assertEqual(self.orig_insert.tk_call, self.text.tk.call) - self.orig_insert('insert', 'asdf') - self.assertEqual(self.text.get('1.0', 'end'), 'asdf\n') - - def test_unregister(self): - self.assertIsNone(self.redir.unregister('invalid operation name')) - self.assertEqual(self.redir.unregister('insert'), self.func) - self.assertNotIn('insert', self.redir._operations) - self.assertNotIn('insert', self.text.__dict__) - - def test_unregister_no_attribute(self): - del self.text.insert - self.assertEqual(self.redir.unregister('insert'), self.func) - - def test_dispatch_intercept(self): - self.func.__init__(True) - self.assertTrue(self.redir.dispatch('insert', False)) - self.assertFalse(self.func.args[0]) - - def test_dispatch_bypass(self): - self.orig_insert('insert', 'asdf') - # tk.call returns '' where Python would return None - self.assertEqual(self.redir.dispatch('delete', '1.0', 'end'), '') - self.assertEqual(self.text.get('1.0', 'end'), '\n') - - def test_dispatch_error(self): - self.func.__init__(TclError()) - self.assertEqual(self.redir.dispatch('insert', False), '') - self.assertEqual(self.redir.dispatch('invalid'), '') - - def test_command_dispatch(self): - # Test that .__init__ causes redirection of tk calls - # through redir.dispatch - self.root.call(self.text._w, 'insert', 'hello') - self.assertEqual(self.func.args, ('hello',)) - self.assertEqual(self.text.get('1.0', 'end'), '\n') - # Ensure that called through redir .dispatch and not through - # self.text.insert by having mock raise TclError. - self.func.__init__(TclError()) - self.assertEqual(self.root.call(self.text._w, 'insert', 'boo'), '') - - - -if __name__ == '__main__': - unittest.main(verbosity=2) From pypy.commits at gmail.com Fri Feb 22 11:03:59 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 22 Feb 2019 08:03:59 -0800 (PST) Subject: [pypy-commit] pypy py3.6: follow more closely the complicated logic that CPython uses for code object equality Message-ID: <5c701d6f.1c69fb81.f2918.4a09@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96131:438c53ddd510 Date: 2019-02-22 17:00 +0100 http://bitbucket.org/pypy/pypy/changeset/438c53ddd510/ Log: follow more closely the complicated logic that CPython uses for code object equality diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py --- a/pypy/interpreter/pycode.py +++ b/pypy/interpreter/pycode.py @@ -350,7 +350,7 @@ return space.w_False for i in range(len(self.co_consts_w)): - if not space.eq_w(self.co_consts_w[i], w_other.co_consts_w[i]): + if not _code_const_eq(space, self.co_consts_w[i], w_other.co_consts_w[i]): return space.w_False return space.w_True @@ -454,3 +454,35 @@ return space.newtext(b'' % ( name, self.getaddrstring(space), fn, -1 if self.co_firstlineno == 0 else self.co_firstlineno)) + +def _code_const_eq(space, w_a, w_b): + # this is a mess! CPython has complicated logic for this. essentially this + # is supposed to be a "strong" equal, that takes types and signs of numbers + # into account, quite similar to how PyPy's 'is' behaves, but recursively + # in tuples and frozensets as well. Since PyPy already implements these + # rules correctly for ints, floats, bools, complex in 'is' and 'id', just + # use those. + return space.eq_w(_convert_const(space, w_a), _convert_const(space, w_b)) + +def _convert_const(space, w_a): + # use id to convert constants. for tuples and frozensets use tuples and + # frozensets of converted contents. + w_type = space.type(w_a) + if space.is_w(w_type, space.w_unicode): + # unicodes are supposed to compare by value + return w_a + if isinstance(w_a, PyCode): + # for code objects we use the logic recursively + return w_a + # for tuples and frozensets convert recursively + if space.is_w(w_type, space.w_tuple): + elements_w = [_convert_const(space, w_x) + for w_x in space.unpackiterable(w_a)] + return space.newtuple(elements_w) + if space.is_w(w_type, space.w_frozenset): + elements_w = [_convert_const(space, w_x) + for w_x in space.unpackiterable(w_a)] + return space.newfrozenset(elements_w) + # use id for the rest + return space.id(w_a) + diff --git a/pypy/interpreter/test/test_compiler.py b/pypy/interpreter/test/test_compiler.py --- a/pypy/interpreter/test/test_compiler.py +++ b/pypy/interpreter/test/test_compiler.py @@ -1053,7 +1053,7 @@ assert d['c1'] == tuple(sorted(d['c1'])) assert d['r1'] == d['r2'] == d['c1'] - def test_ast_equality(self): + def test_code_equality(self): import _ast sample_code = [ ['', 'x = 5'], diff --git a/pypy/interpreter/test/test_pycode.py b/pypy/interpreter/test/test_pycode.py --- a/pypy/interpreter/test/test_pycode.py +++ b/pypy/interpreter/test/test_pycode.py @@ -1,4 +1,5 @@ import sys, StringIO +from pypy.interpreter.pycode import _code_const_eq def test_dump(space): """test that pycode.dump kind of works with py3 opcodes""" @@ -17,3 +18,54 @@ assert ' 0 (7)' in output assert ' 4 (None)' in output assert ' 16 RETURN_VALUE' in output + + +def test_strong_const_equal(space): + # test that the stronger equal that code objects are supposed to use for + # consts works + s = 'Python' + values = [ + space.newint(1), + space.newfloat(0.0), + space.newfloat(-0.0), + space.newfloat(1.0), + space.newfloat(-1.0), + space.w_True, + space.w_False, + space.w_None, + space.w_Ellipsis, + space.newcomplex(0.0, 0.0), + space.newcomplex(0.0, -0.0), + space.newcomplex(-0.0, 0.0), + space.newcomplex(-0.0, -0.0), + space.newcomplex(1.0, 1.0), + space.newcomplex(1.0, -1.0), + space.newcomplex(-1.0, 1.0), + space.newcomplex(-1.0, -1.0), + space.newfrozenset(), + space.newtuple([]), + space.newutf8(s, len(s)), + ] + for w_a in values: + assert _code_const_eq(space, w_a, w_a) + assert _code_const_eq(space, space.newtuple([w_a]), + space.newtuple([w_a])) + assert _code_const_eq(space, space.newfrozenset([w_a]), + space.newfrozenset([w_a])) + for w_a in values: + for w_b in values: + if w_a is w_b: + continue + assert not _code_const_eq(space, w_a, w_b) + assert _code_const_eq(space, space.newtuple([w_a, w_b]), + space.newtuple([w_a, w_b])) + assert not _code_const_eq(space, space.newtuple([w_a]), + space.newtuple([w_b])) + assert not _code_const_eq(space, space.newtuple([w_a, w_b]), + space.newtuple([w_b, w_a])) + assert not _code_const_eq(space, space.newfrozenset([w_a]), + space.newfrozenset([w_b])) + s1 = 'Python' + str(1) + str(1) + s2 = 'Python' + str(11) + assert _code_const_eq(space, space.newutf8(s1, len(s1)), + space.newutf8(s2, len(s2))) From pypy.commits at gmail.com Fri Feb 22 11:22:25 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 22 Feb 2019 08:22:25 -0800 (PST) Subject: [pypy-commit] pypy py3.6: try to update _ctypes_test.c Message-ID: <5c7021c1.1c69fb81.9c6e8.0094@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96132:8e1f12b21406 Date: 2019-02-22 17:21 +0100 http://bitbucket.org/pypy/pypy/changeset/8e1f12b21406/ Log: try to update _ctypes_test.c diff --git a/lib-python/stdlib-upgrade.txt b/lib-python/stdlib-upgrade.txt --- a/lib-python/stdlib-upgrade.txt +++ b/lib-python/stdlib-upgrade.txt @@ -11,10 +11,11 @@ 2. upgrade the files there 2a. remove lib-python/2.7/ or lib-python/3/ 2b. copy the files from the cpython repo - 2c. hg add lib-python/2.7/ or lib-python/3/ - 2d. hg remove --after - 2e. show copied files in cpython repo by running `hg diff --git -r v -r v Lib | grep '^copy \(from\|to\)'` - 2f. fix copies / renames manually by running `hg copy --after ` for each copied file + 2c. copy _ctypes_test.c to lib_pypy/ + 2d. hg add lib-python/2.7/ or lib-python/3/ + 2e. hg remove --after + 2f. show copied files in cpython repo by running `hg diff --git -r v -r v Lib | grep '^copy \(from\|to\)'` + 2g. fix copies / renames manually by running `hg copy --after ` for each copied file 3. update stdlib-version.txt with the output of hg -id from the cpython repo 4. commit 5. update to default / py3k diff --git a/lib_pypy/_ctypes_test.c b/lib_pypy/_ctypes_test.c --- a/lib_pypy/_ctypes_test.c +++ b/lib_pypy/_ctypes_test.c @@ -1,4 +1,4 @@ -/* This is a Verbatim copy of _ctypes_test.c from CPython 3.5 */ +/* This is a verbatim copy of _ctypes_test.c from CPython 3.6 */ #include #ifdef MS_WIN32 @@ -45,6 +45,37 @@ func(in); } +/* + * See issue 29565. Update a structure passed by value; + * the caller should not see any change. + */ + +EXPORT(void) +_testfunc_large_struct_update_value(Test in) +{ + ((volatile Test *)&in)->first = 0x0badf00d; + ((volatile Test *)&in)->second = 0x0badf00d; + ((volatile Test *)&in)->third = 0x0badf00d; +} + +typedef struct { + unsigned int first; + unsigned int second; +} TestReg; + + +EXPORT(TestReg) last_tfrsuv_arg = {0}; + + +EXPORT(void) +_testfunc_reg_struct_update_value(TestReg in) +{ + last_tfrsuv_arg = in; + ((volatile TestReg *)&in)->first = 0x0badf00d; + ((volatile TestReg *)&in)->second = 0x0badf00d; +} + + EXPORT(void)testfunc_array(int values[4]) { printf("testfunc_array %d %d %d %d\n", @@ -234,16 +265,15 @@ return (*func)(table); } -#ifdef HAVE_LONG_LONG -EXPORT(PY_LONG_LONG) _testfunc_q_bhilfdq(signed char b, short h, int i, long l, float f, - double d, PY_LONG_LONG q) +EXPORT(long long) _testfunc_q_bhilfdq(signed char b, short h, int i, long l, float f, + double d, long long q) { - return (PY_LONG_LONG)(b + h + i + l + f + d + q); + return (long long)(b + h + i + l + f + d + q); } -EXPORT(PY_LONG_LONG) _testfunc_q_bhilfd(signed char b, short h, int i, long l, float f, double d) +EXPORT(long long) _testfunc_q_bhilfd(signed char b, short h, int i, long l, float f, double d) { - return (PY_LONG_LONG)(b + h + i + l + f + d); + return (long long)(b + h + i + l + f + d); } EXPORT(int) _testfunc_callback_i_if(int value, int (*func)(int)) @@ -256,10 +286,10 @@ return sum; } -EXPORT(PY_LONG_LONG) _testfunc_callback_q_qf(PY_LONG_LONG value, - PY_LONG_LONG (*func)(PY_LONG_LONG)) +EXPORT(long long) _testfunc_callback_q_qf(long long value, + long long (*func)(long long)) { - PY_LONG_LONG sum = 0; + long long sum = 0; while (value != 0) { sum += func(value); @@ -268,8 +298,6 @@ return sum; } -#endif - typedef struct { char *name; char *value; @@ -367,8 +395,7 @@ int i; if (!PyArg_ParseTuple(args, "si", &name, &i)) return NULL; - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } EXPORT(void) _py_func_si(char *s, int i) @@ -377,20 +404,26 @@ PyObject *py_func(PyObject *self, PyObject *args) { - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } EXPORT(void) _py_func(void) { } -EXPORT(PY_LONG_LONG) last_tf_arg_s; -EXPORT(unsigned PY_LONG_LONG) last_tf_arg_u; +EXPORT(long long) last_tf_arg_s = 0; +EXPORT(unsigned long long) last_tf_arg_u = 0; struct BITS { - int A: 1, B:2, C:3, D:4, E: 5, F: 6, G: 7, H: 8, I: 9; - short M: 1, N: 2, O: 3, P: 4, Q: 5, R: 6, S: 7; + signed int A: 1, B:2, C:3, D:4, E: 5, F: 6, G: 7, H: 8, I: 9; +/* + * The test case needs/uses "signed short" bitfields, but the + * IBM XLC compiler does not support this + */ +#ifndef __xlc__ +#define SIGNED_SHORT_BITFIELDS + short M: 1, N: 2, O: 3, P: 4, Q: 5, R: 6, S: 7; +#endif }; EXPORT(void) set_bitfields(struct BITS *bits, char name, int value) @@ -405,7 +438,7 @@ case 'G': bits->G = value; break; case 'H': bits->H = value; break; case 'I': bits->I = value; break; - +#ifdef SIGNED_SHORT_BITFIELDS case 'M': bits->M = value; break; case 'N': bits->N = value; break; case 'O': bits->O = value; break; @@ -413,6 +446,7 @@ case 'Q': bits->Q = value; break; case 'R': bits->R = value; break; case 'S': bits->S = value; break; +#endif } } @@ -429,6 +463,7 @@ case 'H': return bits->H; case 'I': return bits->I; +#ifdef SIGNED_SHORT_BITFIELDS case 'M': return bits->M; case 'N': return bits->N; case 'O': return bits->O; @@ -436,8 +471,9 @@ case 'Q': return bits->Q; case 'R': return bits->R; case 'S': return bits->S; +#endif } - return 0; + return 999; } static PyMethodDef module_methods[] = { @@ -449,8 +485,8 @@ { NULL, NULL, 0, NULL}, }; -#define S last_tf_arg_s = (PY_LONG_LONG)c -#define U last_tf_arg_u = (unsigned PY_LONG_LONG)c +#define S last_tf_arg_s = (long long)c +#define U last_tf_arg_u = (unsigned long long)c EXPORT(signed char) tf_b(signed char c) { S; return c/3; } EXPORT(unsigned char) tf_B(unsigned char c) { U; return c/3; } @@ -460,8 +496,8 @@ EXPORT(unsigned int) tf_I(unsigned int c) { U; return c/3; } EXPORT(long) tf_l(long c) { S; return c/3; } EXPORT(unsigned long) tf_L(unsigned long c) { U; return c/3; } -EXPORT(PY_LONG_LONG) tf_q(PY_LONG_LONG c) { S; return c/3; } -EXPORT(unsigned PY_LONG_LONG) tf_Q(unsigned PY_LONG_LONG c) { U; return c/3; } +EXPORT(long long) tf_q(long long c) { S; return c/3; } +EXPORT(unsigned long long) tf_Q(unsigned long long c) { U; return c/3; } EXPORT(float) tf_f(float c) { S; return c/3; } EXPORT(double) tf_d(double c) { S; return c/3; } EXPORT(long double) tf_D(long double c) { S; return c/3; } @@ -475,8 +511,8 @@ EXPORT(unsigned int) __stdcall s_tf_I(unsigned int c) { U; return c/3; } EXPORT(long) __stdcall s_tf_l(long c) { S; return c/3; } EXPORT(unsigned long) __stdcall s_tf_L(unsigned long c) { U; return c/3; } -EXPORT(PY_LONG_LONG) __stdcall s_tf_q(PY_LONG_LONG c) { S; return c/3; } -EXPORT(unsigned PY_LONG_LONG) __stdcall s_tf_Q(unsigned PY_LONG_LONG c) { U; return c/3; } +EXPORT(long long) __stdcall s_tf_q(long long c) { S; return c/3; } +EXPORT(unsigned long long) __stdcall s_tf_Q(unsigned long long c) { U; return c/3; } EXPORT(float) __stdcall s_tf_f(float c) { S; return c/3; } EXPORT(double) __stdcall s_tf_d(double c) { S; return c/3; } EXPORT(long double) __stdcall s_tf_D(long double c) { S; return c/3; } @@ -491,8 +527,8 @@ EXPORT(unsigned int) tf_bI(signed char x, unsigned int c) { U; return c/3; } EXPORT(long) tf_bl(signed char x, long c) { S; return c/3; } EXPORT(unsigned long) tf_bL(signed char x, unsigned long c) { U; return c/3; } -EXPORT(PY_LONG_LONG) tf_bq(signed char x, PY_LONG_LONG c) { S; return c/3; } -EXPORT(unsigned PY_LONG_LONG) tf_bQ(signed char x, unsigned PY_LONG_LONG c) { U; return c/3; } +EXPORT(long long) tf_bq(signed char x, long long c) { S; return c/3; } +EXPORT(unsigned long long) tf_bQ(signed char x, unsigned long long c) { U; return c/3; } EXPORT(float) tf_bf(signed char x, float c) { S; return c/3; } EXPORT(double) tf_bd(signed char x, double c) { S; return c/3; } EXPORT(long double) tf_bD(signed char x, long double c) { S; return c/3; } @@ -507,8 +543,8 @@ EXPORT(unsigned int) __stdcall s_tf_bI(signed char x, unsigned int c) { U; return c/3; } EXPORT(long) __stdcall s_tf_bl(signed char x, long c) { S; return c/3; } EXPORT(unsigned long) __stdcall s_tf_bL(signed char x, unsigned long c) { U; return c/3; } -EXPORT(PY_LONG_LONG) __stdcall s_tf_bq(signed char x, PY_LONG_LONG c) { S; return c/3; } -EXPORT(unsigned PY_LONG_LONG) __stdcall s_tf_bQ(signed char x, unsigned PY_LONG_LONG c) { U; return c/3; } +EXPORT(long long) __stdcall s_tf_bq(signed char x, long long c) { S; return c/3; } +EXPORT(unsigned long long) __stdcall s_tf_bQ(signed char x, unsigned long long c) { U; return c/3; } EXPORT(float) __stdcall s_tf_bf(signed char x, float c) { S; return c/3; } EXPORT(double) __stdcall s_tf_bd(signed char x, double c) { S; return c/3; } EXPORT(long double) __stdcall s_tf_bD(signed char x, long double c) { S; return c/3; } @@ -636,6 +672,200 @@ } #ifdef MS_WIN32 + +typedef struct { + char f1; +} Size1; + +typedef struct { + char f1; + char f2; +} Size2; + +typedef struct { + char f1; + char f2; + char f3; +} Size3; + +typedef struct { + char f1; + char f2; + char f3; + char f4; +} Size4; + +typedef struct { + char f1; + char f2; + char f3; + char f4; + char f5; +} Size5; + +typedef struct { + char f1; + char f2; + char f3; + char f4; + char f5; + char f6; +} Size6; + +typedef struct { + char f1; + char f2; + char f3; + char f4; + char f5; + char f6; + char f7; +} Size7; + +typedef struct { + char f1; + char f2; + char f3; + char f4; + char f5; + char f6; + char f7; + char f8; +} Size8; + +typedef struct { + char f1; + char f2; + char f3; + char f4; + char f5; + char f6; + char f7; + char f8; + char f9; +} Size9; + +typedef struct { + char f1; + char f2; + char f3; + char f4; + char f5; + char f6; + char f7; + char f8; + char f9; + char f10; +} Size10; + +EXPORT(Size1) TestSize1() { + Size1 f; + f.f1 = 'a'; + return f; +} + +EXPORT(Size2) TestSize2() { + Size2 f; + f.f1 = 'a'; + f.f2 = 'b'; + return f; +} + +EXPORT(Size3) TestSize3() { + Size3 f; + f.f1 = 'a'; + f.f2 = 'b'; + f.f3 = 'c'; + return f; +} + +EXPORT(Size4) TestSize4() { + Size4 f; + f.f1 = 'a'; + f.f2 = 'b'; + f.f3 = 'c'; + f.f4 = 'd'; + return f; +} + +EXPORT(Size5) TestSize5() { + Size5 f; + f.f1 = 'a'; + f.f2 = 'b'; + f.f3 = 'c'; + f.f4 = 'd'; + f.f5 = 'e'; + return f; +} + +EXPORT(Size6) TestSize6() { + Size6 f; + f.f1 = 'a'; + f.f2 = 'b'; + f.f3 = 'c'; + f.f4 = 'd'; + f.f5 = 'e'; + f.f6 = 'f'; + return f; +} + +EXPORT(Size7) TestSize7() { + Size7 f; + f.f1 = 'a'; + f.f2 = 'b'; + f.f3 = 'c'; + f.f4 = 'd'; + f.f5 = 'e'; + f.f6 = 'f'; + f.f7 = 'g'; + return f; +} + +EXPORT(Size8) TestSize8() { + Size8 f; + f.f1 = 'a'; + f.f2 = 'b'; + f.f3 = 'c'; + f.f4 = 'd'; + f.f5 = 'e'; + f.f6 = 'f'; + f.f7 = 'g'; + f.f8 = 'h'; + return f; +} + +EXPORT(Size9) TestSize9() { + Size9 f; + f.f1 = 'a'; + f.f2 = 'b'; + f.f3 = 'c'; + f.f4 = 'd'; + f.f5 = 'e'; + f.f6 = 'f'; + f.f7 = 'g'; + f.f8 = 'h'; + f.f9 = 'i'; + return f; +} + +EXPORT(Size10) TestSize10() { + Size10 f; + f.f1 = 'a'; + f.f2 = 'b'; + f.f3 = 'c'; + f.f4 = 'd'; + f.f5 = 'e'; + f.f6 = 'f'; + f.f7 = 'g'; + f.f8 = 'h'; + f.f9 = 'i'; + f.f10 = 'j'; + return f; +} + +#endif + +#ifdef MS_WIN32 EXPORT(S2H) __stdcall s_ret_2h_func(S2H inp) { return ret_2h_func(inp); } EXPORT(S8I) __stdcall s_ret_8i_func(S8I inp) { return ret_8i_func(inp); } #endif From pypy.commits at gmail.com Fri Feb 22 11:38:59 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 22 Feb 2019 08:38:59 -0800 (PST) Subject: [pypy-commit] pypy py3.6: try to update testcapimodule.c Message-ID: <5c7025a3.1c69fb81.526a8.3409@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96133:40c7bca434b8 Date: 2019-02-22 17:38 +0100 http://bitbucket.org/pypy/pypy/changeset/40c7bca434b8/ Log: try to update testcapimodule.c diff --git a/lib-python/stdlib-upgrade.txt b/lib-python/stdlib-upgrade.txt --- a/lib-python/stdlib-upgrade.txt +++ b/lib-python/stdlib-upgrade.txt @@ -11,7 +11,7 @@ 2. upgrade the files there 2a. remove lib-python/2.7/ or lib-python/3/ 2b. copy the files from the cpython repo - 2c. copy _ctypes_test.c to lib_pypy/ + 2c. copy _ctypes_test.c and _testcapimodule.c to lib_pypy/ 2d. hg add lib-python/2.7/ or lib-python/3/ 2e. hg remove --after 2f. show copied files in cpython repo by running `hg diff --git -r v -r v Lib | grep '^copy \(from\|to\)'` diff --git a/lib_pypy/_testcapimodule.c b/lib_pypy/_testcapimodule.c --- a/lib_pypy/_testcapimodule.c +++ b/lib_pypy/_testcapimodule.c @@ -21,6 +21,10 @@ # include /* struct timeval */ #endif +#ifdef HAVE_SYS_WAIT_H +#include /* For W_STOPCODE */ +#endif + #ifdef WITH_THREAD #include "pythread.h" #endif /* WITH_THREAD */ @@ -63,9 +67,7 @@ CHECK_SIZEOF(SIZEOF_LONG, long); CHECK_SIZEOF(SIZEOF_VOID_P, void*); CHECK_SIZEOF(SIZEOF_TIME_T, time_t); -#ifdef HAVE_LONG_LONG - CHECK_SIZEOF(SIZEOF_LONG_LONG, PY_LONG_LONG); -#endif + CHECK_SIZEOF(SIZEOF_LONG_LONG, long long); #undef CHECK_SIZEOF @@ -103,22 +105,14 @@ CHECK_SIGNNESS(Py_UCS1, 0); CHECK_SIGNNESS(Py_UCS2, 0); CHECK_SIGNNESS(Py_UCS4, 0); -#ifdef HAVE_INT32_T - CHECK_SIZEOF(PY_INT32_T, 4); - CHECK_SIGNNESS(PY_INT32_T, 1); -#endif -#ifdef HAVE_UINT32_T - CHECK_SIZEOF(PY_UINT32_T, 4); - CHECK_SIGNNESS(PY_UINT32_T, 0); -#endif -#ifdef HAVE_INT64_T - CHECK_SIZEOF(PY_INT64_T, 8); - CHECK_SIGNNESS(PY_INT64_T, 1); -#endif -#ifdef HAVE_UINT64_T - CHECK_SIZEOF(PY_UINT64_T, 8); - CHECK_SIGNNESS(PY_UINT64_T, 0); -#endif + CHECK_SIZEOF(int32_t, 4); + CHECK_SIGNNESS(int32_t, 1); + CHECK_SIZEOF(uint32_t, 4); + CHECK_SIGNNESS(uint32_t, 0); + CHECK_SIZEOF(int64_t, 8); + CHECK_SIGNNESS(int64_t, 1); + CHECK_SIZEOF(uint64_t, 8); + CHECK_SIGNNESS(uint64_t, 0); /* pointer/size types */ CHECK_SIZEOF(size_t, sizeof(void *)); @@ -126,10 +120,10 @@ CHECK_SIZEOF(Py_ssize_t, sizeof(void *)); CHECK_SIGNNESS(Py_ssize_t, 1); - CHECK_SIZEOF(Py_uintptr_t, sizeof(void *)); - CHECK_SIGNNESS(Py_uintptr_t, 0); - CHECK_SIZEOF(Py_intptr_t, sizeof(void *)); - CHECK_SIGNNESS(Py_intptr_t, 1); + CHECK_SIZEOF(uintptr_t, sizeof(void *)); + CHECK_SIGNNESS(uintptr_t, 0); + CHECK_SIZEOF(intptr_t, sizeof(void *)); + CHECK_SIGNNESS(intptr_t, 1); Py_INCREF(Py_None); return Py_None; @@ -252,13 +246,37 @@ } #ifndef PYPY_VERSION +static PyObject* +dict_getitem_knownhash(PyObject *self, PyObject *args) +{ + PyObject *mp, *key, *result; + Py_ssize_t hash; + + if (!PyArg_ParseTuple(args, "OOn:dict_getitem_knownhash", + &mp, &key, &hash)) { + return NULL; + } + + result = _PyDict_GetItem_KnownHash(mp, key, (Py_hash_t)hash); + if (result == NULL && !PyErr_Occurred()) { + _PyErr_SetKeyError(key); + return NULL; + } + + Py_XINCREF(result); + return result; +} static PyObject* dict_hassplittable(PyObject *self, PyObject *arg) { - if (!PyArg_Parse(arg, "O!:dict_hassplittable", &PyDict_Type, &arg)) { + if (!PyDict_Check(arg)) { + PyErr_Format(PyExc_TypeError, + "dict_hassplittable() argument must be dict, not '%s'", + arg->ob_type->tp_name); return NULL; } + return PyBool_FromLong(_PyDict_HasSplitTable((PyDictObject*)arg)); } #endif /* PYPY_VERSION */ @@ -371,12 +389,12 @@ } -/* Tests of PyLong_{As, From}{Unsigned,}Long(), and (#ifdef HAVE_LONG_LONG) +/* Tests of PyLong_{As, From}{Unsigned,}Long(), and PyLong_{As, From}{Unsigned,}LongLong(). Note that the meat of the test is contained in testcapi_long.h. This is revolting, but delicate code duplication is worse: "almost - exactly the same" code is needed to test PY_LONG_LONG, but the ubiquitous + exactly the same" code is needed to test long long, but the ubiquitous dependence on type names makes it impossible to use a parameterized function. A giant macro would be even worse than this. A C++ template would be perfect. @@ -416,8 +434,6 @@ #undef F_U_TO_PY #undef F_PY_TO_U -#ifdef HAVE_LONG_LONG - static PyObject * raise_test_longlong_error(const char* msg) { @@ -425,7 +441,7 @@ } #define TESTNAME test_longlong_api_inner -#define TYPENAME PY_LONG_LONG +#define TYPENAME long long #define F_S_TO_PY PyLong_FromLongLong #define F_PY_TO_S PyLong_AsLongLong #define F_U_TO_PY PyLong_FromUnsignedLongLong @@ -612,7 +628,7 @@ } /* Test the PyLong_AsLongLongAndOverflow API. General conversion to - PY_LONG_LONG is tested by test_long_api_inner. This test will + long long is tested by test_long_api_inner. This test will concentrate on proper handling of overflow. */ @@ -620,7 +636,7 @@ test_long_long_and_overflow(PyObject *self) { PyObject *num, *one, *temp; - PY_LONG_LONG value; + long long value; int overflow; /* Test that overflow is set properly for a large value. */ @@ -838,7 +854,7 @@ return Py_None; } -/* Test the L code for PyArg_ParseTuple. This should deliver a PY_LONG_LONG +/* Test the L code for PyArg_ParseTuple. This should deliver a long long for both long and int arguments. The test may leak a little memory if it fails. */ @@ -846,7 +862,7 @@ test_L_code(PyObject *self) { PyObject *tuple, *num; - PY_LONG_LONG value; + long long value; tuple = PyTuple_New(1); if (tuple == NULL) @@ -859,8 +875,9 @@ PyTuple_SET_ITEM(tuple, 0, num); value = -1; - if (PyArg_ParseTuple(tuple, "L:test_L_code", &value) < 0) - return NULL; + if (!PyArg_ParseTuple(tuple, "L:test_L_code", &value)) { + return NULL; + } if (value != 42) return raiseTestError("test_L_code", "L code returned wrong value for long 42"); @@ -873,8 +890,9 @@ PyTuple_SET_ITEM(tuple, 0, num); value = -1; - if (PyArg_ParseTuple(tuple, "L:test_L_code", &value) < 0) - return NULL; + if (!PyArg_ParseTuple(tuple, "L:test_L_code", &value)) { + return NULL; + } if (value != 42) return raiseTestError("test_L_code", "L code returned wrong value for int 42"); @@ -884,8 +902,6 @@ return Py_None; } -#endif /* ifdef HAVE_LONG_LONG */ - static PyObject * return_none(void *unused) { @@ -929,7 +945,7 @@ } static PyObject * -test_buildvalue_N(PyObject *self, PyObject *noargs) +test_buildvalue_N(PyObject *self, PyObject *Py_UNUSED(ignored)) { PyObject *arg, *res; @@ -999,7 +1015,7 @@ getargs_keywords(PyObject *self, PyObject *args, PyObject *kwargs) { static char *keywords[] = {"arg1","arg2","arg3","arg4","arg5", NULL}; - static char *fmt="(ii)i|(i(ii))(iii)i"; + static const char fmt[] = "(ii)i|(i(ii))(iii)i"; int int_args[10]={-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, fmt, keywords, @@ -1026,6 +1042,21 @@ return Py_BuildValue("iii", required, optional, keyword_only); } +/* test PyArg_ParseTupleAndKeywords positional-only arguments */ +static PyObject * +getargs_positional_only_and_keywords(PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *keywords[] = {"", "", "keyword", NULL}; + int required = -1; + int optional = -1; + int keyword = -1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|ii", keywords, + &required, &optional, &keyword)) + return NULL; + return Py_BuildValue("iii", required, optional, keyword); +} + /* Functions to call PyArg_ParseTuple with integer format codes, and return the result. */ @@ -1119,11 +1150,10 @@ return PyLong_FromLong(value); } -#ifdef HAVE_LONG_LONG static PyObject * getargs_L(PyObject *self, PyObject *args) { - PY_LONG_LONG value; + long long value; if (!PyArg_ParseTuple(args, "L", &value)) return NULL; return PyLong_FromLongLong(value); @@ -1132,12 +1162,11 @@ static PyObject * getargs_K(PyObject *self, PyObject *args) { - unsigned PY_LONG_LONG value; + unsigned long long value; if (!PyArg_ParseTuple(args, "K", &value)) return NULL; return PyLong_FromUnsignedLongLong(value); } -#endif /* This function not only tests the 'k' getargs code, but also the PyLong_AsUnsignedLongMask() and PyLong_AsUnsignedLongMask() functions. */ @@ -1159,13 +1188,14 @@ value = PyLong_AsUnsignedLongMask(num); if (value != ULONG_MAX) return raiseTestError("test_k_code", - "PyLong_AsUnsignedLongMask() returned wrong value for long 0xFFF...FFF"); + "PyLong_AsUnsignedLongMask() returned wrong value for long 0xFFF...FFF"); PyTuple_SET_ITEM(tuple, 0, num); value = 0; - if (PyArg_ParseTuple(tuple, "k:test_k_code", &value) < 0) - return NULL; + if (!PyArg_ParseTuple(tuple, "k:test_k_code", &value)) { + return NULL; + } if (value != ULONG_MAX) return raiseTestError("test_k_code", "k code returned wrong value for long 0xFFF...FFF"); @@ -1178,13 +1208,14 @@ value = PyLong_AsUnsignedLongMask(num); if (value != (unsigned long)-0x42) return raiseTestError("test_k_code", - "PyLong_AsUnsignedLongMask() returned wrong value for long 0xFFF...FFF"); + "PyLong_AsUnsignedLongMask() returned wrong value for long 0xFFF...FFF"); PyTuple_SET_ITEM(tuple, 0, num); value = 0; - if (PyArg_ParseTuple(tuple, "k:test_k_code", &value) < 0) - return NULL; + if (!PyArg_ParseTuple(tuple, "k:test_k_code", &value)) { + return NULL; + } if (value != (unsigned long)-0x42) return raiseTestError("test_k_code", "k code returned wrong value for long -0xFFF..000042"); @@ -1522,11 +1553,13 @@ /* These two blocks used to raise a TypeError: * "argument must be string without null bytes, not str" */ - if (PyArg_ParseTuple(tuple, "s:test_s_code1", &value) < 0) - return NULL; - - if (PyArg_ParseTuple(tuple, "z:test_s_code2", &value) < 0) - return NULL; + if (!PyArg_ParseTuple(tuple, "s:test_s_code1", &value)) { + return NULL; + } + + if (!PyArg_ParseTuple(tuple, "z:test_s_code2", &value)) { + return NULL; + } Py_DECREF(tuple); Py_RETURN_NONE; @@ -1628,14 +1661,16 @@ PyTuple_SET_ITEM(tuple, 0, obj); value = 0; - if (PyArg_ParseTuple(tuple, "u:test_u_code", &value) < 0) - return NULL; + if (!PyArg_ParseTuple(tuple, "u:test_u_code", &value)) { + return NULL; + } if (value != PyUnicode_AS_UNICODE(obj)) return raiseTestError("test_u_code", "u code returned wrong value for u'test'"); value = 0; - if (PyArg_ParseTuple(tuple, "u#:test_u_code", &value, &len) < 0) - return NULL; + if (!PyArg_ParseTuple(tuple, "u#:test_u_code", &value, &len)) { + return NULL; + } if (value != PyUnicode_AS_UNICODE(obj) || len != PyUnicode_GET_SIZE(obj)) return raiseTestError("test_u_code", @@ -1668,8 +1703,9 @@ value2 = PyUnicode_AS_UNICODE(obj); /* Test Z for both values */ - if (PyArg_ParseTuple(tuple, "ZZ:test_Z_code", &value1, &value2) < 0) - return NULL; + if (!PyArg_ParseTuple(tuple, "ZZ:test_Z_code", &value1, &value2)) { + return NULL; + } if (value1 != PyUnicode_AS_UNICODE(obj)) return raiseTestError("test_Z_code", "Z code returned wrong value for 'test'"); @@ -1683,9 +1719,11 @@ len2 = -1; /* Test Z# for both values */ - if (PyArg_ParseTuple(tuple, "Z#Z#:test_Z_code", &value1, &len1, - &value2, &len2) < 0) - return NULL; + if (!PyArg_ParseTuple(tuple, "Z#Z#:test_Z_code", &value1, &len1, + &value2, &len2)) + { + return NULL; + } if (value1 != PyUnicode_AS_UNICODE(obj) || len1 != PyUnicode_GET_SIZE(obj)) return raiseTestError("test_Z_code", @@ -1993,8 +2031,9 @@ tuple = PyTuple_New(0); if (!tuple) return NULL; - if ((result = PyArg_ParseTuple(tuple, "|:test_empty_argparse")) < 0) + if (!(result = PyArg_ParseTuple(tuple, "|:test_empty_argparse"))) { goto done; + } dict = PyDict_New(); if (!dict) goto done; @@ -2002,8 +2041,9 @@ done: Py_DECREF(tuple); Py_XDECREF(dict); - if (result < 0) - return NULL; + if (!result) { + return NULL; + } else { Py_RETURN_NONE; } @@ -2194,7 +2234,7 @@ PyObject *rc; int success; PyGILState_STATE s = PyGILState_Ensure(); - rc = PyObject_CallFunction((PyObject *)callable, ""); + rc = PyObject_CallFunction((PyObject *)callable, NULL); success = (rc != NULL); Py_XDECREF(rc); PyGILState_Release(s); @@ -2276,7 +2316,8 @@ /* The following requests n callbacks to _pending_callback. It can be * run from any python thread. */ -PyObject *pending_threadfunc(PyObject *self, PyObject *arg) +static PyObject * +pending_threadfunc(PyObject *self, PyObject *arg) { PyObject *callable; int r; @@ -2304,7 +2345,7 @@ /* Some tests of PyUnicode_FromFormat(). This needs more tests. */ static PyObject * -test_string_from_format(PyObject *self, PyObject *args) +test_string_from_format(PyObject *self, PyObject *Py_UNUSED(ignored)) { PyObject *result; char *msg; @@ -2330,10 +2371,8 @@ CHECK_1_FORMAT("%zu", size_t); /* "%lld" and "%llu" support added in Python 2.7. */ -#ifdef HAVE_LONG_LONG - CHECK_1_FORMAT("%llu", unsigned PY_LONG_LONG); - CHECK_1_FORMAT("%lld", PY_LONG_LONG); -#endif + CHECK_1_FORMAT("%llu", unsigned long long); + CHECK_1_FORMAT("%lld", long long); Py_RETURN_NONE; @@ -2379,7 +2418,7 @@ result = PyOS_string_to_double(STR, NULL, NULL); \ if (result == -1.0 && PyErr_Occurred()) \ return NULL; \ - if (result != expected) { \ + if (result != (double)expected) { \ msg = "conversion of " STR " to float failed"; \ goto fail; \ } @@ -2447,7 +2486,7 @@ } known_capsule; static PyObject * -test_capsule(PyObject *self, PyObject *args) +test_capsule(PyObject *self, PyObject *Py_UNUSED(ignored)) { PyObject *object; const char *error = NULL; @@ -2819,7 +2858,7 @@ } static PyObject * -test_from_contiguous(PyObject* self, PyObject *noargs) +test_from_contiguous(PyObject* self, PyObject *Py_UNUSED(ignored)) { int data[9] = {-1,-1,-1,-1,-1,-1,-1,-1,-1}; int init[5] = {0, 1, 2, 3, 4}; @@ -2872,7 +2911,7 @@ extern PyTypeObject _PyBytesIOBuffer_Type; static PyObject * -test_pep3118_obsolete_write_locks(PyObject* self, PyObject *noargs) +test_pep3118_obsolete_write_locks(PyObject* self, PyObject *Py_UNUSED(ignored)) { PyTypeObject *type = &_PyBytesIOBuffer_Type; PyObject *b; @@ -2973,7 +3012,10 @@ static int check_time_rounding(int round) { - if (round != _PyTime_ROUND_FLOOR && round != _PyTime_ROUND_CEILING) { + if (round != _PyTime_ROUND_FLOOR + && round != _PyTime_ROUND_CEILING + && round != _PyTime_ROUND_HALF_EVEN + && round != _PyTime_ROUND_UP) { PyErr_SetString(PyExc_ValueError, "invalid rounding"); return -1; } @@ -3397,6 +3439,130 @@ return test_setallocators(PYMEM_DOMAIN_OBJ); } + +/* Most part of the following code is inherited from the pyfailmalloc project + * written by Victor Stinner. */ +static struct { + int installed; + PyMemAllocatorEx raw; + PyMemAllocatorEx mem; + PyMemAllocatorEx obj; +} FmHook; + +static struct { + int start; + int stop; + Py_ssize_t count; +} FmData; + +static int +fm_nomemory(void) +{ + FmData.count++; + if (FmData.count > FmData.start && + (FmData.stop <= 0 || FmData.count <= FmData.stop)) { + return 1; + } + return 0; +} + +static void * +hook_fmalloc(void *ctx, size_t size) +{ + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; + if (fm_nomemory()) { + return NULL; + } + return alloc->malloc(alloc->ctx, size); +} + +static void * +hook_fcalloc(void *ctx, size_t nelem, size_t elsize) +{ + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; + if (fm_nomemory()) { + return NULL; + } + return alloc->calloc(alloc->ctx, nelem, elsize); +} + +static void * +hook_frealloc(void *ctx, void *ptr, size_t new_size) +{ + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; + if (fm_nomemory()) { + return NULL; + } + return alloc->realloc(alloc->ctx, ptr, new_size); +} + +static void +hook_ffree(void *ctx, void *ptr) +{ + PyMemAllocatorEx *alloc = (PyMemAllocatorEx *)ctx; + alloc->free(alloc->ctx, ptr); +} + +static void +fm_setup_hooks(void) +{ + PyMemAllocatorEx alloc; + + if (FmHook.installed) { + return; + } + FmHook.installed = 1; + + alloc.malloc = hook_fmalloc; + alloc.calloc = hook_fcalloc; + alloc.realloc = hook_frealloc; + alloc.free = hook_ffree; + PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &FmHook.raw); + PyMem_GetAllocator(PYMEM_DOMAIN_MEM, &FmHook.mem); + PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, &FmHook.obj); + + alloc.ctx = &FmHook.raw; + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &alloc); + + alloc.ctx = &FmHook.mem; + PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &alloc); + + alloc.ctx = &FmHook.obj; + PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &alloc); +} + +static void +fm_remove_hooks(void) +{ + if (FmHook.installed) { + FmHook.installed = 0; + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &FmHook.raw); + PyMem_SetAllocator(PYMEM_DOMAIN_MEM, &FmHook.mem); + PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &FmHook.obj); + } +} + +static PyObject* +set_nomemory(PyObject *self, PyObject *args) +{ + /* Memory allocation fails after 'start' allocation requests, and until + * 'stop' allocation requests except when 'stop' is negative or equal + * to 0 (default) in which case allocation failures never stop. */ + FmData.count = 0; + FmData.stop = 0; + if (!PyArg_ParseTuple(args, "i|i", &FmData.start, &FmData.stop)) { + return NULL; + } + fm_setup_hooks(); + Py_RETURN_NONE; +} + +static PyObject* +remove_mem_hooks(PyObject *self) +{ + fm_remove_hooks(); + Py_RETURN_NONE; +} #endif /* PYPY_VERSION */ PyDoc_STRVAR(docstring_empty, @@ -3474,7 +3640,7 @@ /* Allocate a Python thread state for this thread */ state = PyGILState_Ensure(); - res = PyObject_CallFunction(test_c_thread->callback, "", NULL); + res = PyObject_CallFunction(test_c_thread->callback, NULL); Py_CLEAR(test_c_thread->callback); if (res == NULL) { @@ -3549,8 +3715,9 @@ { int signum, err; - if (PyArg_ParseTuple(args, "i:raise_signal", &signum) < 0) - return NULL; + if (!PyArg_ParseTuple(args, "i:raise_signal", &signum)) { + return NULL; + } err = raise(signum); if (err) @@ -3769,7 +3936,7 @@ static PyObject * test_pytime_assecondsdouble(PyObject *self, PyObject *args) { - PY_LONG_LONG ns; + long long ns; _PyTime_t ts; double d; @@ -3783,7 +3950,7 @@ static PyObject * test_PyTime_AsTimeval(PyObject *self, PyObject *args) { - PY_LONG_LONG ns; + long long ns; int round; _PyTime_t t; struct timeval tv; @@ -3797,7 +3964,7 @@ if (_PyTime_AsTimeval(t, &tv, round) < 0) return NULL; - seconds = PyLong_FromLong((PY_LONG_LONG)tv.tv_sec); + seconds = PyLong_FromLongLong(tv.tv_sec); if (seconds == NULL) return NULL; return Py_BuildValue("Nl", seconds, tv.tv_usec); @@ -3807,7 +3974,7 @@ static PyObject * test_PyTime_AsTimespec(PyObject *self, PyObject *args) { - PY_LONG_LONG ns; + long long ns; _PyTime_t t; struct timespec ts; @@ -3823,7 +3990,7 @@ static PyObject * test_PyTime_AsMilliseconds(PyObject *self, PyObject *args) { - PY_LONG_LONG ns; + long long ns; int round; _PyTime_t t, ms; @@ -3841,7 +4008,7 @@ static PyObject * test_PyTime_AsMicroseconds(PyObject *self, PyObject *args) { - PY_LONG_LONG ns; + long long ns; int round; _PyTime_t t, ms; @@ -3866,6 +4033,289 @@ return PyLong_FromLong(tstate->recursion_depth - 1); } + + +static PyObject* +pymem_buffer_overflow(PyObject *self, PyObject *args) +{ + char *buffer; + + /* Deliberate buffer overflow to check that PyMem_Free() detects + the overflow when debug hooks are installed. */ + buffer = PyMem_Malloc(16); + buffer[16] = 'x'; + PyMem_Free(buffer); + + Py_RETURN_NONE; +} + +static PyObject* +pymem_api_misuse(PyObject *self, PyObject *args) +{ + char *buffer; + + /* Deliberate misusage of Python allocators: + allococate with PyMem but release with PyMem_Raw. */ + buffer = PyMem_Malloc(16); + PyMem_RawFree(buffer); + + Py_RETURN_NONE; +} + +static PyObject* +pymem_malloc_without_gil(PyObject *self, PyObject *args) +{ + char *buffer; + + /* Deliberate bug to test debug hooks on Python memory allocators: + call PyMem_Malloc() without holding the GIL */ + Py_BEGIN_ALLOW_THREADS + buffer = PyMem_Malloc(10); + Py_END_ALLOW_THREADS + + PyMem_Free(buffer); + + Py_RETURN_NONE; +} + +static PyObject* +pyobject_malloc_without_gil(PyObject *self, PyObject *args) +{ + char *buffer; + + /* Deliberate bug to test debug hooks on Python memory allocators: + call PyObject_Malloc() without holding the GIL */ + Py_BEGIN_ALLOW_THREADS + buffer = PyObject_Malloc(10); + Py_END_ALLOW_THREADS + + PyObject_Free(buffer); + + Py_RETURN_NONE; +} + +static PyObject * +tracemalloc_track(PyObject *self, PyObject *args) +{ + unsigned int domain; + PyObject *ptr_obj; + void *ptr; + Py_ssize_t size; + int release_gil = 0; + int res; + + if (!PyArg_ParseTuple(args, "IOn|i", &domain, &ptr_obj, &size, &release_gil)) + return NULL; + ptr = PyLong_AsVoidPtr(ptr_obj); + if (PyErr_Occurred()) + return NULL; + + if (release_gil) { + Py_BEGIN_ALLOW_THREADS + res = _PyTraceMalloc_Track(domain, (uintptr_t)ptr, size); + Py_END_ALLOW_THREADS + } + else { + res = _PyTraceMalloc_Track(domain, (uintptr_t)ptr, size); + } + + if (res < 0) { + PyErr_SetString(PyExc_RuntimeError, "_PyTraceMalloc_Track error"); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject * +tracemalloc_untrack(PyObject *self, PyObject *args) +{ + unsigned int domain; + PyObject *ptr_obj; + void *ptr; + int res; + + if (!PyArg_ParseTuple(args, "IO", &domain, &ptr_obj)) + return NULL; + ptr = PyLong_AsVoidPtr(ptr_obj); + if (PyErr_Occurred()) + return NULL; + + res = _PyTraceMalloc_Untrack(domain, (uintptr_t)ptr); + if (res < 0) { + PyErr_SetString(PyExc_RuntimeError, "_PyTraceMalloc_Track error"); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject * +tracemalloc_get_traceback(PyObject *self, PyObject *args) +{ + unsigned int domain; + PyObject *ptr_obj; + void *ptr; + + if (!PyArg_ParseTuple(args, "IO", &domain, &ptr_obj)) + return NULL; + ptr = PyLong_AsVoidPtr(ptr_obj); + if (PyErr_Occurred()) + return NULL; + + return _PyTraceMalloc_GetTraceback(domain, (uintptr_t)ptr); +} + +static PyObject * +dict_get_version(PyObject *self, PyObject *args) +{ + PyDictObject *dict; + uint64_t version; + + if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, &dict)) + return NULL; + + version = dict->ma_version_tag; + + Py_BUILD_ASSERT(sizeof(unsigned PY_LONG_LONG) >= sizeof(version)); + return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)version); +} + + +static int +fastcall_args(PyObject *args, PyObject ***stack, Py_ssize_t *nargs) +{ + if (args == Py_None) { + *stack = NULL; + *nargs = 0; + } + else if (PyTuple_Check(args)) { + *stack = &PyTuple_GET_ITEM(args, 0); + *nargs = PyTuple_GET_SIZE(args); + } + else { + PyErr_SetString(PyExc_TypeError, "args must be None or a tuple"); + return -1; + } + return 0; +} + + +static PyObject * +test_pyobject_fastcall(PyObject *self, PyObject *args) +{ + PyObject *func, *func_args; + PyObject **stack; + Py_ssize_t nargs; + + if (!PyArg_ParseTuple(args, "OO", &func, &func_args)) { + return NULL; + } + + if (fastcall_args(func_args, &stack, &nargs) < 0) { + return NULL; + } + return _PyObject_FastCall(func, stack, nargs); +} + + +static PyObject * +test_pyobject_fastcalldict(PyObject *self, PyObject *args) +{ + PyObject *func, *func_args, *kwargs; + PyObject **stack; + Py_ssize_t nargs; + + if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwargs)) { + return NULL; + } + + if (fastcall_args(func_args, &stack, &nargs) < 0) { + return NULL; + } + + if (kwargs == Py_None) { + kwargs = NULL; + } + else if (!PyDict_Check(kwargs)) { + PyErr_SetString(PyExc_TypeError, "kwnames must be None or a dict"); + return NULL; + } + + return _PyObject_FastCallDict(func, stack, nargs, kwargs); +} + + +static PyObject * +test_pyobject_fastcallkeywords(PyObject *self, PyObject *args) +{ + PyObject *func, *func_args, *kwnames = NULL; + PyObject **stack; + Py_ssize_t nargs, nkw; + + if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwnames)) { + return NULL; + } + + if (fastcall_args(func_args, &stack, &nargs) < 0) { + return NULL; + } + + if (kwnames == Py_None) { + kwnames = NULL; + } + else if (PyTuple_Check(kwnames)) { + nkw = PyTuple_GET_SIZE(kwnames); + if (nargs < nkw) { + PyErr_SetString(PyExc_ValueError, "kwnames longer than args"); + return NULL; + } + nargs -= nkw; + } + else { + PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple"); + return NULL; + } + return _PyObject_FastCallKeywords(func, stack, nargs, kwnames); +} + + +static PyObject * +raise_SIGINT_then_send_None(PyObject *self, PyObject *args) +{ + PyGenObject *gen; + + if (!PyArg_ParseTuple(args, "O!", &PyGen_Type, &gen)) + return NULL; + + /* This is used in a test to check what happens if a signal arrives just + as we're in the process of entering a yield from chain (see + bpo-30039). + + Needs to be done in C, because: + - we don't have a Python wrapper for raise() + - we need to make sure that the Python-level signal handler doesn't run + *before* we enter the generator frame, which is impossible in Python + because we check for signals before every bytecode operation. + */ + raise(SIGINT); + return _PyGen_Send(gen, Py_None); +} + + +#ifdef W_STOPCODE +static PyObject* +py_w_stopcode(PyObject *self, PyObject *args) +{ + int sig, status; + if (!PyArg_ParseTuple(args, "i", &sig)) { + return NULL; + } + status = W_STOPCODE(sig); + return PyLong_FromLong(status); +} +#endif + #endif /* PYPY_VERSION */ static PyMethodDef TestMethods[] = { @@ -3878,6 +4328,7 @@ {"test_list_api", (PyCFunction)test_list_api, METH_NOARGS}, {"test_dict_iteration", (PyCFunction)test_dict_iteration,METH_NOARGS}, #ifndef PYPY_VERSION + {"dict_getitem_knownhash", dict_getitem_knownhash, METH_VARARGS}, {"dict_hassplittable", dict_hassplittable, METH_O}, #endif {"test_lazy_hash_inheritance", (PyCFunction)test_lazy_hash_inheritance,METH_NOARGS}, @@ -3917,6 +4368,9 @@ METH_VARARGS|METH_KEYWORDS}, {"getargs_keyword_only", (PyCFunction)getargs_keyword_only, METH_VARARGS|METH_KEYWORDS}, + {"getargs_positional_only_and_keywords", + (PyCFunction)getargs_positional_only_and_keywords, + METH_VARARGS|METH_KEYWORDS}, {"getargs_b", getargs_b, METH_VARARGS}, {"getargs_B", getargs_B, METH_VARARGS}, {"getargs_h", getargs_h, METH_VARARGS}, @@ -3927,14 +4381,12 @@ {"getargs_l", getargs_l, METH_VARARGS}, {"getargs_n", getargs_n, METH_VARARGS}, {"getargs_p", getargs_p, METH_VARARGS}, -#ifdef HAVE_LONG_LONG {"getargs_L", getargs_L, METH_VARARGS}, {"getargs_K", getargs_K, METH_VARARGS}, {"test_longlong_api", test_longlong_api, METH_NOARGS}, {"test_long_long_and_overflow", (PyCFunction)test_long_long_and_overflow, METH_NOARGS}, {"test_L_code", (PyCFunction)test_L_code, METH_NOARGS}, -#endif {"getargs_f", getargs_f, METH_VARARGS}, {"getargs_d", getargs_d, METH_VARARGS}, {"getargs_D", getargs_D, METH_VARARGS}, @@ -4012,6 +4464,10 @@ (PyCFunction)test_pymem_setallocators, METH_NOARGS}, {"test_pyobject_setallocators", (PyCFunction)test_pyobject_setallocators, METH_NOARGS}, + {"set_nomemory", (PyCFunction)set_nomemory, METH_VARARGS, + PyDoc_STR("set_nomemory(start:int, stop:int = 0)")}, + {"remove_mem_hooks", (PyCFunction)remove_mem_hooks, METH_NOARGS, + PyDoc_STR("Remove memory hooks.")}, #endif {"no_docstring", (PyCFunction)test_with_docstring, METH_NOARGS}, @@ -4074,7 +4530,22 @@ {"PyTime_AsMilliseconds", test_PyTime_AsMilliseconds, METH_VARARGS}, {"PyTime_AsMicroseconds", test_PyTime_AsMicroseconds, METH_VARARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, + {"pymem_buffer_overflow", pymem_buffer_overflow, METH_NOARGS}, + {"pymem_api_misuse", pymem_api_misuse, METH_NOARGS}, + {"pymem_malloc_without_gil", pymem_malloc_without_gil, METH_NOARGS}, + {"pyobject_malloc_without_gil", pyobject_malloc_without_gil, METH_NOARGS}, + {"tracemalloc_track", tracemalloc_track, METH_VARARGS}, + {"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS}, + {"tracemalloc_get_traceback", tracemalloc_get_traceback, METH_VARARGS}, + {"dict_get_version", dict_get_version, METH_VARARGS}, + {"pyobject_fastcall", test_pyobject_fastcall, METH_VARARGS}, + {"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS}, + {"pyobject_fastcallkeywords", test_pyobject_fastcallkeywords, METH_VARARGS}, + {"raise_SIGINT_then_send_None", raise_SIGINT_then_send_None, METH_VARARGS}, +#ifdef W_STOPCODE + {"W_STOPCODE", py_w_stopcode, METH_VARARGS}, #endif +#endif /* PYPY_VERSION */ {NULL, NULL} /* sentinel */ }; @@ -4094,10 +4565,8 @@ float float_member; double double_member; char inplace_member[6]; -#ifdef HAVE_LONG_LONG - PY_LONG_LONG longlong_member; - unsigned PY_LONG_LONG ulonglong_member; -#endif + long long longlong_member; + unsigned long long ulonglong_member; } all_structmembers; typedef struct { @@ -4119,10 +4588,8 @@ {"T_FLOAT", T_FLOAT, offsetof(test_structmembers, structmembers.float_member), 0, NULL}, {"T_DOUBLE", T_DOUBLE, offsetof(test_structmembers, structmembers.double_member), 0, NULL}, {"T_STRING_INPLACE", T_STRING_INPLACE, offsetof(test_structmembers, structmembers.inplace_member), 0, NULL}, -#ifdef HAVE_LONG_LONG {"T_LONGLONG", T_LONGLONG, offsetof(test_structmembers, structmembers.longlong_member), 0, NULL}, {"T_ULONGLONG", T_ULONGLONG, offsetof(test_structmembers, structmembers.ulonglong_member), 0, NULL}, -#endif {NULL} }; @@ -4134,15 +4601,9 @@ "T_BOOL", "T_BYTE", "T_UBYTE", "T_SHORT", "T_USHORT", "T_INT", "T_UINT", "T_LONG", "T_ULONG", "T_PYSSIZET", "T_FLOAT", "T_DOUBLE", "T_STRING_INPLACE", -#ifdef HAVE_LONG_LONG "T_LONGLONG", "T_ULONGLONG", -#endif NULL}; - static char *fmt = "|bbBhHiIlknfds#" -#ifdef HAVE_LONG_LONG - "LK" -#endif - ; + static const char fmt[] = "|bbBhHiIlknfds#LK"; test_structmembers *ob; const char *s = NULL; Py_ssize_t string_len = 0; @@ -4164,10 +4625,8 @@ &ob->structmembers.float_member, &ob->structmembers.double_member, &s, &string_len -#ifdef HAVE_LONG_LONG , &ob->structmembers.longlong_member, &ob->structmembers.ulonglong_member -#endif )) { Py_DECREF(ob); return NULL; @@ -4430,6 +4889,61 @@ }; +static int recurse_infinitely_error_init(PyObject *, PyObject *, PyObject *); + +static PyTypeObject PyRecursingInfinitelyError_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "RecursingInfinitelyError", /* tp_name */ + sizeof(PyBaseExceptionObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Instantiating this exception starts infinite recursion.", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)recurse_infinitely_error_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +static int +recurse_infinitely_error_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *type = (PyObject *)&PyRecursingInfinitelyError_Type; + + /* Instantiating this exception starts infinite recursion. */ + Py_INCREF(type); + PyErr_SetObject(type, NULL); + return -1; +} + + static struct PyModuleDef _testcapimodule = { PyModuleDef_HEAD_INIT, "_testcapi", @@ -4471,6 +4985,14 @@ Py_INCREF(&awaitType); PyModule_AddObject(m, "awaitType", (PyObject *)&awaitType); + PyRecursingInfinitelyError_Type.tp_base = (PyTypeObject *)PyExc_Exception; + if (PyType_Ready(&PyRecursingInfinitelyError_Type) < 0) { + return NULL; + } + Py_INCREF(&PyRecursingInfinitelyError_Type); + PyModule_AddObject(m, "RecursingInfinitelyError", + (PyObject *)&PyRecursingInfinitelyError_Type); + PyModule_AddObject(m, "CHAR_MAX", PyLong_FromLong(CHAR_MAX)); PyModule_AddObject(m, "CHAR_MIN", PyLong_FromLong(CHAR_MIN)); PyModule_AddObject(m, "UCHAR_MAX", PyLong_FromLong(UCHAR_MAX)); @@ -4493,6 +5015,7 @@ PyModule_AddObject(m, "PY_SSIZE_T_MAX", PyLong_FromSsize_t(PY_SSIZE_T_MAX)); PyModule_AddObject(m, "PY_SSIZE_T_MIN", PyLong_FromSsize_t(PY_SSIZE_T_MIN)); PyModule_AddObject(m, "SIZEOF_PYGC_HEAD", PyLong_FromSsize_t(sizeof(PyGC_Head))); + PyModule_AddObject(m, "SIZEOF_TIME_T", PyLong_FromSsize_t(sizeof(time_t))); Py_INCREF(&PyInstanceMethod_Type); PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type); From pypy.commits at gmail.com Fri Feb 22 15:59:28 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 22 Feb 2019 12:59:28 -0800 (PST) Subject: [pypy-commit] pypy default: a test that fails on py3.6 Message-ID: <5c7062b0.1c69fb81.d65fb.114f@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96136:b71c018270cf Date: 2019-02-22 21:50 +0100 http://bitbucket.org/pypy/pypy/changeset/b71c018270cf/ Log: a test that fails on py3.6 diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -1276,3 +1276,6 @@ assert str(e.value) == 'decoding Unicode is not supported' e = raises(TypeError, unicode, z, 'supposedly_the_encoding') assert str(e.value) == 'decoding Unicode is not supported' + + def test_newlist_utf8_non_ascii(self): + 'ä'.split("\n")[0] # does not crash \ No newline at end of file From pypy.commits at gmail.com Fri Feb 22 15:59:30 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 22 Feb 2019 12:59:30 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default Message-ID: <5c7062b2.1c69fb81.1b4d2.34d6@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96137:b0af8614d286 Date: 2019-02-22 21:51 +0100 http://bitbucket.org/pypy/pypy/changeset/b0af8614d286/ Log: merge default diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -1345,3 +1345,6 @@ assert next(it3) == u"e" assert next(it3) == u"f" + + def test_newlist_utf8_non_ascii(self): + 'ä'.split("\n")[0] # does not crash \ No newline at end of file From pypy.commits at gmail.com Fri Feb 22 15:59:32 2019 From: pypy.commits at gmail.com (cfbolz) Date: Fri, 22 Feb 2019 12:59:32 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix TODO: no, ignoring is_ascii wasn't correct :-( Message-ID: <5c7062b4.1c69fb81.64890.23ab@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96138:64cbcb808e45 Date: 2019-02-22 21:56 +0100 http://bitbucket.org/pypy/pypy/changeset/64cbcb808e45/ Log: fix TODO: no, ignoring is_ascii wasn't correct :-( diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py --- a/pypy/objspace/std/objspace.py +++ b/pypy/objspace/std/objspace.py @@ -329,11 +329,13 @@ return W_ListObject.newlist_bytes(self, list_s) def newlist_text(self, list_t): - return self.newlist_utf8([decode_utf8sp(self, s)[0] for s in list_t]) + return self.newlist_utf8([decode_utf8sp(self, s)[0] for s in list_t], False) - def newlist_utf8(self, list_u, is_ascii=True): - # TODO ignoring is_ascii, is that correct? - return W_ListObject.newlist_utf8(self, list_u) + def newlist_utf8(self, list_u, is_ascii): + if is_ascii: + return W_ListObject.newlist_utf8(self, list_u) + return ObjSpace.newlist_utf8(self, list_u, False) + def newlist_int(self, list_i): return W_ListObject.newlist_int(self, list_i) From pypy.commits at gmail.com Fri Feb 22 20:49:27 2019 From: pypy.commits at gmail.com (rlamy) Date: Fri, 22 Feb 2019 17:49:27 -0800 (PST) Subject: [pypy-commit] pypy py3.6: Ignore _cffi_backend when checking for usemodules in -A tests Message-ID: <5c70a6a7.1c69fb81.9937e.1fed@mx.google.com> Author: Ronan Lamy Branch: py3.6 Changeset: r96140:ed559ffaf9d1 Date: 2019-02-23 01:48 +0000 http://bitbucket.org/pypy/pypy/changeset/ed559ffaf9d1/ Log: Ignore _cffi_backend when checking for usemodules in -A tests diff --git a/pypy/tool/pytest/apptest.py b/pypy/tool/pytest/apptest.py --- a/pypy/tool/pytest/apptest.py +++ b/pypy/tool/pytest/apptest.py @@ -147,7 +147,7 @@ # They may be extension modules on CPython name = None for name in missing.copy(): - if name == 'cpyext': + if name in ['cpyext', '_cffi_backend']: missing.remove(name) continue try: From pypy.commits at gmail.com Sat Feb 23 17:39:57 2019 From: pypy.commits at gmail.com (rlamy) Date: Sat, 23 Feb 2019 14:39:57 -0800 (PST) Subject: [pypy-commit] pypy default: Correctly initialize the d_type and d_name members of builtin descriptors. Message-ID: <5c71cbbd.1c69fb81.a1115.bb77@mx.google.com> Author: Ronan Lamy Branch: Changeset: r96141:5f79cbf51fda Date: 2019-02-23 19:06 +0000 http://bitbucket.org/pypy/pypy/changeset/5f79cbf51fda/ Log: Correctly initialize the d_type and d_name members of builtin descriptors. This fixes a segfault related to classmethods in Cython. 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 @@ -100,7 +100,7 @@ def call_o(self, space, w_self, __args__): func = self.ml.c_ml_meth - w_o = __args__.arguments_w[0] + w_o = __args__.arguments_w[0] return generic_cpy_call(space, func, w_self, w_o) def call_varargs(self, space, w_self, __args__): 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 @@ -146,8 +146,11 @@ def test_cython_fake_classmethod(self): module = self.import_module(name='foo') - print(module.fooType.fake_classmeth) - print(type(module.fooType.fake_classmeth)) + + # Check that objects are printable + print(module.fooType.fake_classmeth) # bound method on the class + print(module.fooType.__dict__['fake_classmeth']) # raw descriptor + assert module.fooType.fake_classmeth() is module.fooType def test_new(self): @@ -447,7 +450,7 @@ pass obj = Sub() assert module.hack_tp_dict(obj, "b") == 2 - + def test_tp_descr_get(self): module = self.import_extension('foo', [ 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 @@ -131,31 +131,58 @@ basestruct=cts.gettype('PyMemberDescrObject'), attach=memberdescr_attach, realize=memberdescr_realize, + dealloc=descr_dealloc, ) make_typedescr(W_GetSetPropertyEx.typedef, basestruct=cts.gettype('PyGetSetDescrObject'), attach=getsetdescr_attach, + dealloc=descr_dealloc, ) make_typedescr(W_PyCClassMethodObject.typedef, basestruct=cts.gettype('PyMethodDescrObject'), attach=methoddescr_attach, realize=classmethoddescr_realize, + dealloc=descr_dealloc, ) make_typedescr(W_PyCMethodObject.typedef, basestruct=cts.gettype('PyMethodDescrObject'), attach=methoddescr_attach, realize=methoddescr_realize, + dealloc=descr_dealloc, ) +def init_descr(space, py_obj, w_type, c_name): + """Initialises the common fields in a PyDescrObject + + Arguments: + py_obj: PyObject* pointer to a PyDescrObject + w_type: W_TypeObject + c_name: char* + """ + from pypy.module.cpyext.unicodeobject import PyUnicode_FromString + py_descr = cts.cast('PyDescrObject*', py_obj) + py_descr.c_d_type = cts.cast( + 'PyTypeObject*', make_ref(space, w_type)) + py_descr.c_d_name = make_ref( + space, PyUnicode_FromString(space, c_name)) + + at slot_function([PyObject], lltype.Void) +def descr_dealloc(space, py_obj): + from pypy.module.cpyext.object import _dealloc + py_descr = cts.cast('PyDescrObject*', py_obj) + decref(space, py_descr.c_d_type) + decref(space, py_descr.c_d_name) + _dealloc(space, py_obj) + def memberdescr_attach(space, py_obj, w_obj, w_userdata=None): """ Fills a newly allocated PyMemberDescrObject with the given W_MemberDescr object. The values must not be modified. """ py_memberdescr = cts.cast('PyMemberDescrObject*', py_obj) - # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_MemberDescr) py_memberdescr.c_d_member = w_obj.member + init_descr(space, py_obj, w_obj.w_type, w_obj.member.c_name) def memberdescr_realize(space, obj): # XXX NOT TESTED When is this ever called? @@ -178,15 +205,15 @@ w_obj = W_GetSetPropertyEx(py_getsetdef, w_userdata) # now w_obj.getset is py_getsetdef, which was freshly allocated # XXX how is this ever released? - # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_GetSetPropertyEx) py_getsetdescr.c_d_getset = w_obj.getset + init_descr(space, py_obj, w_obj.w_type, w_obj.getset.c_name) def methoddescr_attach(space, py_obj, w_obj, w_userdata=None): py_methoddescr = cts.cast('PyMethodDescrObject*', py_obj) - # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_PyCFunctionObject) py_methoddescr.c_d_method = w_obj.ml + init_descr(space, py_obj, w_obj.w_objclass, w_obj.ml.c_ml_name) def classmethoddescr_realize(space, obj): # XXX NOT TESTED When is this ever called? @@ -206,6 +233,7 @@ track_reference(space, obj, w_obj) return w_obj + def convert_getset_defs(space, dict_w, getsets, w_type): getsets = rffi.cast(rffi.CArrayPtr(PyGetSetDef), getsets) if getsets: From pypy.commits at gmail.com Sat Feb 23 17:39:59 2019 From: pypy.commits at gmail.com (rlamy) Date: Sat, 23 Feb 2019 14:39:59 -0800 (PST) Subject: [pypy-commit] pypy py3.6: hg merge default Message-ID: <5c71cbbf.1c69fb81.e52ac.d8b9@mx.google.com> Author: Ronan Lamy Branch: py3.6 Changeset: r96142:26a7e7f30d93 Date: 2019-02-23 20:01 +0000 http://bitbucket.org/pypy/pypy/changeset/26a7e7f30d93/ Log: hg merge default 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 @@ -133,7 +133,7 @@ def call_o(self, space, w_self, __args__): func = self.ml.c_ml_meth - w_o = __args__.arguments_w[0] + w_o = __args__.arguments_w[0] return generic_cpy_call(space, func, w_self, w_o) def call_varargs(self, space, w_self, __args__): 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 @@ -147,8 +147,11 @@ def test_cython_fake_classmethod(self): module = self.import_module(name='foo') - print(module.fooType.fake_classmeth) - print(type(module.fooType.fake_classmeth)) + + # Check that objects are printable + print(module.fooType.fake_classmeth) # bound method on the class + print(module.fooType.__dict__['fake_classmeth']) # raw descriptor + assert module.fooType.fake_classmeth() is module.fooType def test_new(self): @@ -422,7 +425,7 @@ pass obj = Sub() assert module.hack_tp_dict(obj, "b") == 2 - + def test_tp_descr_get(self): module = self.import_extension('foo', [ 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 @@ -134,31 +134,58 @@ basestruct=cts.gettype('PyMemberDescrObject'), attach=memberdescr_attach, realize=memberdescr_realize, + dealloc=descr_dealloc, ) make_typedescr(W_GetSetPropertyEx.typedef, basestruct=cts.gettype('PyGetSetDescrObject'), attach=getsetdescr_attach, + dealloc=descr_dealloc, ) make_typedescr(W_PyCClassMethodObject.typedef, basestruct=cts.gettype('PyMethodDescrObject'), attach=methoddescr_attach, realize=classmethoddescr_realize, + dealloc=descr_dealloc, ) make_typedescr(W_PyCMethodObject.typedef, basestruct=cts.gettype('PyMethodDescrObject'), attach=methoddescr_attach, realize=methoddescr_realize, + dealloc=descr_dealloc, ) +def init_descr(space, py_obj, w_type, c_name): + """Initialises the common fields in a PyDescrObject + + Arguments: + py_obj: PyObject* pointer to a PyDescrObject + w_type: W_TypeObject + c_name: char* + """ + from pypy.module.cpyext.unicodeobject import PyUnicode_FromString + py_descr = cts.cast('PyDescrObject*', py_obj) + py_descr.c_d_type = cts.cast( + 'PyTypeObject*', make_ref(space, w_type)) + py_descr.c_d_name = make_ref( + space, PyUnicode_FromString(space, c_name)) + + at slot_function([PyObject], lltype.Void) +def descr_dealloc(space, py_obj): + from pypy.module.cpyext.object import _dealloc + py_descr = cts.cast('PyDescrObject*', py_obj) + decref(space, py_descr.c_d_type) + decref(space, py_descr.c_d_name) + _dealloc(space, py_obj) + def memberdescr_attach(space, py_obj, w_obj, w_userdata=None): """ Fills a newly allocated PyMemberDescrObject with the given W_MemberDescr object. The values must not be modified. """ py_memberdescr = cts.cast('PyMemberDescrObject*', py_obj) - # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_MemberDescr) py_memberdescr.c_d_member = w_obj.member + init_descr(space, py_obj, w_obj.w_type, w_obj.member.c_name) def memberdescr_realize(space, obj): # XXX NOT TESTED When is this ever called? @@ -181,15 +208,15 @@ w_obj = W_GetSetPropertyEx(py_getsetdef, w_userdata) # now w_obj.getset is py_getsetdef, which was freshly allocated # XXX how is this ever released? - # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_GetSetPropertyEx) py_getsetdescr.c_d_getset = w_obj.getset + init_descr(space, py_obj, w_obj.w_type, w_obj.getset.c_name) def methoddescr_attach(space, py_obj, w_obj, w_userdata=None): py_methoddescr = cts.cast('PyMethodDescrObject*', py_obj) - # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_PyCFunctionObject) py_methoddescr.c_d_method = w_obj.ml + init_descr(space, py_obj, w_obj.w_objclass, w_obj.ml.c_ml_name) def classmethoddescr_realize(space, obj): # XXX NOT TESTED When is this ever called? @@ -209,6 +236,7 @@ track_reference(space, obj, w_obj) return w_obj + def convert_getset_defs(space, dict_w, getsets, w_type): getsets = rffi.cast(rffi.CArrayPtr(PyGetSetDef), getsets) if getsets: From pypy.commits at gmail.com Sun Feb 24 06:38:38 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 24 Feb 2019 03:38:38 -0800 (PST) Subject: [pypy-commit] pypy default: use name instead of c_name to fix translation Message-ID: <5c72823e.1c69fb81.da2fc.aec2@mx.google.com> Author: Matti Picus Branch: Changeset: r96143:8d4ef4fd2096 Date: 2019-02-24 13:37 +0200 http://bitbucket.org/pypy/pypy/changeset/8d4ef4fd2096/ Log: use name instead of c_name to fix translation 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 @@ -62,7 +62,7 @@ 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)) + self.name = rffi.charp2str(rffi.cast(rffi.CCHARP, self.ml.c_ml_name)) self.flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags) self.w_self = w_self self.w_module = w_module 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 @@ -151,7 +151,7 @@ dealloc=descr_dealloc, ) -def init_descr(space, py_obj, w_type, c_name): +def init_descr(space, py_obj, w_type, name): """Initialises the common fields in a PyDescrObject Arguments: @@ -159,12 +159,10 @@ w_type: W_TypeObject c_name: char* """ - from pypy.module.cpyext.unicodeobject import PyUnicode_FromString py_descr = cts.cast('PyDescrObject*', py_obj) py_descr.c_d_type = cts.cast( 'PyTypeObject*', make_ref(space, w_type)) - py_descr.c_d_name = make_ref( - space, PyUnicode_FromString(space, c_name)) + py_descr.c_d_name = make_ref(space, space.newtext(name)) @slot_function([PyObject], lltype.Void) def descr_dealloc(space, py_obj): @@ -182,7 +180,7 @@ py_memberdescr = cts.cast('PyMemberDescrObject*', py_obj) assert isinstance(w_obj, W_MemberDescr) py_memberdescr.c_d_member = w_obj.member - init_descr(space, py_obj, w_obj.w_type, w_obj.member.c_name) + init_descr(space, py_obj, w_obj.w_type, w_obj.name) def memberdescr_realize(space, obj): # XXX NOT TESTED When is this ever called? @@ -207,13 +205,13 @@ # XXX how is this ever released? assert isinstance(w_obj, W_GetSetPropertyEx) py_getsetdescr.c_d_getset = w_obj.getset - init_descr(space, py_obj, w_obj.w_type, w_obj.getset.c_name) + init_descr(space, py_obj, w_obj.w_type, w_obj.name) def methoddescr_attach(space, py_obj, w_obj, w_userdata=None): py_methoddescr = cts.cast('PyMethodDescrObject*', py_obj) assert isinstance(w_obj, W_PyCFunctionObject) py_methoddescr.c_d_method = w_obj.ml - init_descr(space, py_obj, w_obj.w_objclass, w_obj.ml.c_ml_name) + init_descr(space, py_obj, w_obj.w_objclass, w_obj.name) def classmethoddescr_realize(space, obj): # XXX NOT TESTED When is this ever called? From pypy.commits at gmail.com Sun Feb 24 07:03:40 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 24 Feb 2019 04:03:40 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch Message-ID: <5c72881c.1c69fb81.54493.a5a9@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96144:363f2ef2fd93 Date: 2019-02-24 13:51 +0200 http://bitbucket.org/pypy/pypy/changeset/363f2ef2fd93/ Log: merge default into branch 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 @@ -154,7 +154,7 @@ dealloc=descr_dealloc, ) -def init_descr(space, py_obj, w_type, c_name): +def init_descr(space, py_obj, w_type, name): """Initialises the common fields in a PyDescrObject Arguments: @@ -162,12 +162,10 @@ w_type: W_TypeObject c_name: char* """ - from pypy.module.cpyext.unicodeobject import PyUnicode_FromString py_descr = cts.cast('PyDescrObject*', py_obj) py_descr.c_d_type = cts.cast( 'PyTypeObject*', make_ref(space, w_type)) - py_descr.c_d_name = make_ref( - space, PyUnicode_FromString(space, c_name)) + py_descr.c_d_name = make_ref(space, space.newtext(name)) @slot_function([PyObject], lltype.Void) def descr_dealloc(space, py_obj): @@ -185,7 +183,7 @@ py_memberdescr = cts.cast('PyMemberDescrObject*', py_obj) assert isinstance(w_obj, W_MemberDescr) py_memberdescr.c_d_member = w_obj.member - init_descr(space, py_obj, w_obj.w_type, w_obj.member.c_name) + init_descr(space, py_obj, w_obj.w_type, w_obj.name) def memberdescr_realize(space, obj): # XXX NOT TESTED When is this ever called? @@ -210,13 +208,13 @@ # XXX how is this ever released? assert isinstance(w_obj, W_GetSetPropertyEx) py_getsetdescr.c_d_getset = w_obj.getset - init_descr(space, py_obj, w_obj.w_type, w_obj.getset.c_name) + init_descr(space, py_obj, w_obj.w_type, w_obj.name) def methoddescr_attach(space, py_obj, w_obj, w_userdata=None): py_methoddescr = cts.cast('PyMethodDescrObject*', py_obj) assert isinstance(w_obj, W_PyCFunctionObject) py_methoddescr.c_d_method = w_obj.ml - init_descr(space, py_obj, w_obj.w_objclass, w_obj.ml.c_ml_name) + init_descr(space, py_obj, w_obj.w_objclass, w_obj.name) def classmethoddescr_realize(space, obj): # XXX NOT TESTED When is this ever called? From pypy.commits at gmail.com Sun Feb 24 09:10:01 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sun, 24 Feb 2019 06:10:01 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix test_decimal Message-ID: <5c72a5b9.1c69fb81.8581a.873f@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96145:3b7015c3581a Date: 2019-02-24 15:06 +0100 http://bitbucket.org/pypy/pypy/changeset/3b7015c3581a/ Log: fix test_decimal diff --git a/lib_pypy/_decimal.py b/lib_pypy/_decimal.py --- a/lib_pypy/_decimal.py +++ b/lib_pypy/_decimal.py @@ -1295,6 +1295,13 @@ raise RuntimeError("Invalid error flag", trapped) def create_decimal(self, num="0"): + """Creates a new Decimal instance but using self as context. + + This method implements the to-number operation of the + IBM Decimal specification.""" + + if isinstance(num, str) and (num != num.strip() or '_' in num): + num = '' # empty string triggers ConversionSyntax return Decimal._from_object(num, self, exact=False) def create_decimal_from_float(self, f): From pypy.commits at gmail.com Sun Feb 24 09:22:59 2019 From: pypy.commits at gmail.com (cfbolz) Date: Sun, 24 Feb 2019 06:22:59 -0800 (PST) Subject: [pypy-commit] pypy py3.6: add missing file Message-ID: <5c72a8c3.1c69fb81.44637.4e06@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96146:57e5774b430e Date: 2019-02-24 15:22 +0100 http://bitbucket.org/pypy/pypy/changeset/57e5774b430e/ Log: add missing file diff --git a/lib-python/3/test/mod_generics_cache.py b/lib-python/3/test/mod_generics_cache.py new file mode 100644 --- /dev/null +++ b/lib-python/3/test/mod_generics_cache.py @@ -0,0 +1,53 @@ +"""Module for testing the behavior of generics across different modules.""" + +import sys +from textwrap import dedent +from typing import TypeVar, Generic, Optional + + +if sys.version_info[:2] >= (3, 6): + exec(dedent(""" + default_a: Optional['A'] = None + default_b: Optional['B'] = None + + T = TypeVar('T') + + + class A(Generic[T]): + some_b: 'B' + + + class B(Generic[T]): + class A(Generic[T]): + pass + + my_inner_a1: 'B.A' + my_inner_a2: A + my_outer_a: 'A' # unless somebody calls get_type_hints with localns=B.__dict__ + """)) +else: # This should stay in sync with the syntax above. + __annotations__ = dict( + default_a=Optional['A'], + default_b=Optional['B'], + ) + default_a = None + default_b = None + + T = TypeVar('T') + + + class A(Generic[T]): + __annotations__ = dict( + some_b='B' + ) + + + class B(Generic[T]): + class A(Generic[T]): + pass + + __annotations__ = dict( + my_inner_a1='B.A', + my_inner_a2=A, + my_outer_a='A' # unless somebody calls get_type_hints with localns=B.__dict__ + ) From pypy.commits at gmail.com Sun Feb 24 09:42:17 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 24 Feb 2019 06:42:17 -0800 (PST) Subject: [pypy-commit] pypy default: len('3.6') is 3, fix comparison Message-ID: <5c72ad49.1c69fb81.44637.5703@mx.google.com> Author: Matti Picus Branch: Changeset: r96147:fa5202d97bc9 Date: 2019-02-24 16:40 +0200 http://bitbucket.org/pypy/pypy/changeset/fa5202d97bc9/ Log: len('3.6') is 3, fix comparison diff --git a/pypy/conftest.py b/pypy/conftest.py --- a/pypy/conftest.py +++ b/pypy/conftest.py @@ -10,7 +10,7 @@ except ImportError: pass else: - if __version__[:2] < '3.6': + if __version__[:3] < '3.6': s = settings(deadline=None) settings.register_profile('default', s) else: From pypy.commits at gmail.com Sun Feb 24 09:42:19 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 24 Feb 2019 06:42:19 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch Message-ID: <5c72ad4b.1c69fb81.16aae.ddb1@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96148:0aa14088b8a4 Date: 2019-02-24 16:41 +0200 http://bitbucket.org/pypy/pypy/changeset/0aa14088b8a4/ Log: merge default into branch diff --git a/pypy/conftest.py b/pypy/conftest.py --- a/pypy/conftest.py +++ b/pypy/conftest.py @@ -19,7 +19,7 @@ except ImportError: pass else: - if __version__[:2] < '3.6': + if __version__[:3] < '3.6': s = settings(deadline=None) settings.register_profile('default', s) else: From pypy.commits at gmail.com Sun Feb 24 13:28:51 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 24 Feb 2019 10:28:51 -0800 (PST) Subject: [pypy-commit] pypy default: fix when malloc() returns an address with the last bit set (arigato) Message-ID: <5c72e263.1c69fb81.46096.628c@mx.google.com> Author: Matti Picus Branch: Changeset: r96149:0bfaff4207c3 Date: 2019-02-24 20:25 +0200 http://bitbucket.org/pypy/pypy/changeset/0bfaff4207c3/ Log: fix when malloc() returns an address with the last bit set (arigato) diff --git a/rpython/rtyper/lltypesystem/llarena.py b/rpython/rtyper/lltypesystem/llarena.py --- a/rpython/rtyper/lltypesystem/llarena.py +++ b/rpython/rtyper/lltypesystem/llarena.py @@ -252,7 +252,7 @@ def _cast_to_int(self, symbolic=False): assert not symbolic - return self.arena._getid() + self.offset + return rffi.cast(lltype.Signed, self.arena._getid() + self.offset) def getfakearenaaddress(addr): From pypy.commits at gmail.com Sun Feb 24 13:28:53 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 24 Feb 2019 10:28:53 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch Message-ID: <5c72e265.1c69fb81.dd946.f493@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96150:edc5179ca4b1 Date: 2019-02-24 20:28 +0200 http://bitbucket.org/pypy/pypy/changeset/edc5179ca4b1/ Log: merge default into branch diff --git a/rpython/rtyper/lltypesystem/llarena.py b/rpython/rtyper/lltypesystem/llarena.py --- a/rpython/rtyper/lltypesystem/llarena.py +++ b/rpython/rtyper/lltypesystem/llarena.py @@ -252,7 +252,7 @@ def _cast_to_int(self, symbolic=False): assert not symbolic - return self.arena._getid() + self.offset + return rffi.cast(lltype.Signed, self.arena._getid() + self.offset) def getfakearenaaddress(addr): From pypy.commits at gmail.com Sun Feb 24 16:22:17 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Sun, 24 Feb 2019 13:22:17 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: Fixed first test case for windows events Message-ID: <5c730b09.1c69fb81.9c6e8.b38d@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r96151:aadc1d859506 Date: 2019-02-24 21:19 +0000 http://bitbucket.org/pypy/pypy/changeset/aadc1d859506/ Log: Fixed first test case for windows events diff --git a/lib_pypy/_overlapped.py b/lib_pypy/_overlapped.py --- a/lib_pypy/_overlapped.py +++ b/lib_pypy/_overlapped.py @@ -62,21 +62,25 @@ self.type = OverlappedType.TYPE_NONE self.overlapped[0].hEvent = \ _kernel32.CreateEventW(NULL, True, False, NULL) - self.address = _ffi.addressof(self.overlapped) + self.address = _ffi.addressof(self.overlapped[0]) def __del__(self): + ###if (!HasOverlappedIoCompleted(&self->overlapped) && + ### self->type != TYPE_NOT_STARTED) + ### + xxx # do this somehow else - xxx - err = _kernel32.GetLastError() - bytes = _ffi.new('DWORD[1]') - o = overlapped[0] - if overlapped[0].pending: - if _kernel32.CancelIoEx(o.handle, o.overlapped) & \ - self.GetOverlappedResult(o.handle, o.overlapped, _ffi.addressof(bytes), True): - # The operation is no longer pending, nothing to do - pass - else: - raise RuntimeError('deleting an overlapped struct with a pending operation not supported') + #err = _kernel32.GetLastError() + #bytes = _ffi.new('DWORD[1]') + #o = self.overlapped[0] + #if self.overlapped[0].pending: + # if _kernel32.CancelIoEx(o.handle, o.overlapped) & \ + # self.GetOverlappedResult(o.handle, o.overlapped, _ffi.addressof(bytes), True): + # # The operation is no longer pending, nothing to do + # pass + # else: + # raise RuntimeError('deleting an overlapped struct with a pending operation not supported') + @property def event(self): @@ -84,24 +88,34 @@ def GetOverlappedResult(self, wait): transferred = _ffi.new('DWORD[1]', [0]) + + res = _kernel32.GetOverlappedResult(self.handle, self.overlapped, transferred, wait != 0) if res: err = _winapi.ERROR_SUCCESS else: - err = GetLastError() + err = _kernel32.GetLastError() if err in (_winapi.ERROR_SUCCESS, _winapi.ERROR_MORE_DATA, _winapi.ERROR_OPERATION_ABORTED): self.completed = 1 self.pending = 0 - elif res == _winapi.ERROR_IO_INCOMPLETE: + elif err == _winapi.ERROR_IO_INCOMPLETE: pass else: self.pending = 0 raise _winapi._WinError() - if self.completed and self.allocated_buffer: - import pdb; pdb.set_trace() - if transferred[0] != len(self.allocated_buffer[0]): - raise _winapi._WinError() - return transferred[0], err + if self.type == OverlappedType.TYPE_READ: + if self.completed and self.allocated_buffer: + if transferred[0] != len(self.allocated_buffer): + ### Do a resize + result = _ffi.new("CHAR[]", transferred[0]) + _ffi.memmove(result, self.allocated_buffer, transferred[0]) + return result + else: + return b'' + else: + return b'' + else: + return transferred[0] def getbuffer(self): xxx @@ -124,29 +138,33 @@ self.type = OverlappedType.TYPE_READ self.handle = handle - self.allocated_buffer = _ffi.new("BYTE[]", max(1,size)) + self.allocated_buffer = _ffi.new("CHAR[]", max(1,size)) return self.do_WSARecv(handle, self.allocated_buffer, size, flags) def do_WSARecv(self, handle, allocatedbuffer, size, flags): nread = _ffi.new("LPDWORD") wsabuff = _ffi.new("WSABUF[1]") buffercount = _ffi.new("DWORD[1]", [1]) + pflags = _ffi.new("LPDWORD") + pflags[0] = flags + wsabuff[0].len = size wsabuff[0].buf = allocatedbuffer - result = _winsock2.WSARecv(handle, wsabuff, _int2dword(1), nread, _ffi.cast("LPDWORD",flags), self.overlapped, _ffi.NULL) - if result: - self.error = _winapi.ERROR_SUCCESS + + result = _winsock2.WSARecv(handle, wsabuff, _int2dword(1), nread, pflags, self.overlapped, _ffi.NULL) + if result < 0: + self.error = _kernel32.GetLastError() else: - self.error = _kernel32.GetLastError() + self.error = _winapi.ERROR_SUCCESS if self.error == _winapi.ERROR_BROKEN_PIPE: mark_as_completed(self.overlapped) - return SetFromWindowsErr(err) + raise _winapi._WinError() elif self.error in [_winapi.ERROR_SUCCESS, _winapi.ERROR_MORE_DATA, _winapi.ERROR_IO_PENDING] : return None else: self.type = OverlappedType.TYPE_NOT_STARTED - return SetFromWindowsErr(err) + raise _winapi._WinError() def getresult(self, wait=False): return self.GetOverlappedResult(wait) @@ -167,6 +185,7 @@ ov = Overlapped(handle) else: ov = Overlapped(None) + success = _kernel32.ConnectNamedPipe(handle, ov.overlapped) if overlapped: # Overlapped ConnectNamedPipe never returns a success code @@ -193,25 +212,33 @@ existingcompletionport, completionkey, numberofconcurrentthreads) - if not result: + if result == _ffi.NULL: raise _winapi._WinError() return result def GetQueuedCompletionStatus(completionport, milliseconds): numberofbytes = _ffi.new('DWORD[1]', [0]) - completionkey = _ffi.new('ULONG *', 0) + completionkey = _ffi.new('ULONG **') if completionport is None: raise _winapi._WinError() - overlapped = _ffi.new('OVERLAPPED*') + overlapped = _ffi.new("OVERLAPPED **") result = _kernel32.GetQueuedCompletionStatus(completionport, numberofbytes, completionkey, overlapped, milliseconds) - err = _kernel32.GetLastError() - return (err, numberofbytes, completionkey, overlapped) + if result: + err = _winapi.ERROR_SUCCESS + else: + err = _kernel32.GetLastError() + if overlapped[0] == _ffi.NULL: + if err == _winapi.WAIT_TIMEOUT: + return None + return SetFromWindowsErr(err) + + return (err, numberofbytes, completionkey[0], overlapped[0]) @_ffi.callback("void(void*, bool)") def post_to_queue_callback(lpparameter, timerorwaitfired): @@ -223,7 +250,6 @@ def RegisterWaitWithQueue(object, completionport, ovaddress, miliseconds): data = _ffi.new('PostCallbackData[1]') newwaitobject = _ffi.new("HANDLE[1]") - data[0].hCompletionPort = completionport data[0].Overlapped = ovaddress[0] success = _kernel32.RegisterWaitForSingleObject(newwaitobject, diff --git a/lib_pypy/_pypy_winbase_build.py b/lib_pypy/_pypy_winbase_build.py --- a/lib_pypy/_pypy_winbase_build.py +++ b/lib_pypy/_pypy_winbase_build.py @@ -139,7 +139,7 @@ BOOL WINAPI RegisterWaitForSingleObject(PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, ULONG, ULONG); BOOL WINAPI PostQueuedCompletionStatus(HANDLE, DWORD, ULONG_PTR, LPOVERLAPPED); -BOOL WINAPI GetQueuedCompletionStatus(HANDLE, LPDWORD, PULONG_PTR, LPOVERLAPPED, DWORD); +BOOL WINAPI GetQueuedCompletionStatus(HANDLE, LPDWORD, ULONG**, LPOVERLAPPED*, DWORD); HANDLE WINAPI CreateIoCompletionPort(HANDLE, HANDLE, ULONG_PTR, DWORD); #define WT_EXECUTEINWAITTHREAD 0x00000004 diff --git a/lib_pypy/_pypy_winbase_cffi.py b/lib_pypy/_pypy_winbase_cffi.py --- a/lib_pypy/_pypy_winbase_cffi.py +++ b/lib_pypy/_pypy_winbase_cffi.py @@ -3,7 +3,7 @@ ffi = _cffi_backend.FFI('_pypy_winbase_cffi', _version = 0x2601, - _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xE6\x03\x00\x00\x13\x11\x00\x00\xEC\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xE4\x03\x00\x00\xE0\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xDB\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xDF\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xE5\x03\x00\x00\x0A\x01\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x31\x11\x00\x00\xD3\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x31\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x8F\x03\x00\x00\x6D\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x6D\x11\x00\x00\x6D\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x4A\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x6D\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x8A\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x8A\x0D\x00\x00\x00\x0F\x00\x00\x8A\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xE3\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xE6\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x92\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x8F\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x95\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x95\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x6D\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x9B\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xEC\x0D\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x00\x0F\x00\x00\xEC\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xEC\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xE2\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x07\x09\x00\x00\x02\x01\x00\x00\x39\x03\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xEB\x03\x00\x00\x04\x01\x00\x00\x00\x01', + _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xE6\x03\x00\x00\x13\x11\x00\x00\xEC\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xE4\x03\x00\x00\xE0\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xDB\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xDF\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xE5\x03\x00\x00\x0A\x01\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x31\x11\x00\x00\xD3\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x03\x00\x00\x31\x03\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x8F\x03\x00\x00\x6D\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x6D\x11\x00\x00\x6D\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x4A\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x6D\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x8A\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x8A\x0D\x00\x00\x00\x0F\x00\x00\x8A\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xE3\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xE6\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x92\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x8F\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x95\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x95\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x6D\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x9B\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xEC\x0D\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x00\x0F\x00\x00\xEC\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xEC\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xE2\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x07\x09\x00\x00\x02\x01\x00\x00\x39\x03\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xEB\x03\x00\x00\x04\x01\x00\x00\x00\x01', _globals = (b'\x00\x00\x2C\x23CancelIo',0,b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x91\x23CreateEventA',0,b'\x00\x00\x97\x23CreateEventW',0,b'\x00\x00\x9D\x23CreateFileA',0,b'\x00\x00\xCA\x23CreateFileW',0,b'\x00\x00\xB8\x23CreateIoCompletionPort',0,b'\x00\x00\xA6\x23CreateNamedPipeA',0,b'\x00\x00\xC0\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x6C\x23CreateProcessW',0,b'\x00\x00\x63\x23DuplicateHandle',0,b'\x00\x00\xBE\x23GetCurrentProcess',0,b'\x00\x00\x4C\x23GetExitCodeProcess',0,b'\x00\x00\x87\x23GetLastError',0,b'\x00\x00\x82\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x50\x23GetQueuedCompletionStatus',0,b'\x00\x00\xB5\x23GetStdHandle',0,b'\x00\x00\x87\x23GetVersion',0,b'\x00\x00\x5D\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x7B\x23SetErrorMode',0,b'\x00\x00\xD8\x23SetEvent',0,b'\x00\x00\x57\x23SetNamedPipeHandleState',0,b'\x00\x00\x48\x23TerminateProcess',0,b'\x00\x00\x3F\x23WSARecv',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x7E\x23WaitForSingleObject',0,b'\x00\x00\x78\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x8C\x23_getwch',0,b'\x00\x00\x8C\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x8E\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x89\x23_ungetwch',0,b'\x00\x00\xB0\x23socket',0), _struct_unions = ((b'\x00\x00\x00\xE9\x00\x00\x00\x03$1',b'\x00\x00\xE8\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xE8\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xE0\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xE4\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x8A\x11wShowWindow',b'\x00\x00\x8A\x11cbReserved2',b'\x00\x00\xEA\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xDF\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xE9\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xE2\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x31\x11Overlapped'),(b'\x00\x00\x00\xE3\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle'),(b'\x00\x00\x00\xE5\x00\x00\x00\x02_WSABUF',b'\x00\x00\x18\x11len',b'\x00\x00\x13\x11buf')), _typenames = (b'\x00\x00\x00\xE7LPFN_DISCONNECTEX',b'\x00\x00\x00\x31LPOVERLAPPED',b'\x00\x00\x00\x46LPOVERLAPPED_COMPLETION_ROUTINE',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xE1LPPostCallbackData',b'\x00\x00\x00\x92LPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\x41LPWSABUF',b'\x00\x00\x00\xDFOVERLAPPED',b'\x00\x00\x00\xE0PROCESS_INFORMATION',b'\x00\x00\x00\x92PSECURITY_ATTRIBUTES',b'\x00\x00\x00\xE2PostCallbackData',b'\x00\x00\x00\xE3SECURITY_ATTRIBUTES',b'\x00\x00\x00\x15SOCKET',b'\x00\x00\x00\xE4STARTUPINFO',b'\x00\x00\x00\x27WAITORTIMERCALLBACK',b'\x00\x00\x00\xE5WSABUF',b'\x00\x00\x00\x8Awint_t'), From pypy.commits at gmail.com Mon Feb 25 02:25:15 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 24 Feb 2019 23:25:15 -0800 (PST) Subject: [pypy-commit] pypy default: fix return values Message-ID: <5c73985b.1c69fb81.8271b.57ec@mx.google.com> Author: Matti Picus Branch: Changeset: r96152:b7859ce44de0 Date: 2019-02-25 08:23 +0200 http://bitbucket.org/pypy/pypy/changeset/b7859ce44de0/ Log: fix return values diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -73,7 +73,7 @@ result_borrowed=True) def PyDict_GetItem(space, w_dict, w_key): if not isinstance(w_dict, W_DictMultiObject): - raise PyErr_BadInternalCall(space) + return None # NOTE: this works so far because all our dict strategies store # *values* as full objects, which stay alive as long as the dict is # alive and not modified. So we can return a borrowed ref. @@ -83,14 +83,14 @@ @cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_SetItem(space, w_dict, w_key, w_obj): if not isinstance(w_dict, W_DictMultiObject): - raise PyErr_BadInternalCall(space) + PyErr_BadInternalCall(space) w_dict.setitem(w_key, w_obj) return 0 @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_DelItem(space, w_dict, w_key): if not isinstance(w_dict, W_DictMultiObject): - raise PyErr_BadInternalCall(space) + PyErr_BadInternalCall(space) w_dict.descr_delitem(space, w_key) return 0 @@ -98,7 +98,7 @@ def PyDict_SetItemString(space, w_dict, key_ptr, w_obj): w_key = space.newtext(rffi.charp2str(key_ptr)) if not isinstance(w_dict, W_DictMultiObject): - raise PyErr_BadInternalCall(space) + PyErr_BadInternalCall(space) w_dict.setitem(w_key, w_obj) return 0 @@ -109,7 +109,7 @@ char*, rather than a PyObject*.""" w_key = space.newtext(rffi.charp2str(key)) if not isinstance(w_dict, W_DictMultiObject): - raise PyErr_BadInternalCall(space) + return None # NOTE: this works so far because all our dict strategies store # *values* as full objects, which stay alive as long as the dict is # alive and not modified. So we can return a borrowed ref. From pypy.commits at gmail.com Mon Feb 25 02:25:18 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 24 Feb 2019 23:25:18 -0800 (PST) Subject: [pypy-commit] pypy default: use rpython/conftest.py logic to skip deadline Message-ID: <5c73985e.1c69fb81.c0396.84b1@mx.google.com> Author: Matti Picus Branch: Changeset: r96153:44e3e9067f96 Date: 2019-02-25 09:18 +0200 http://bitbucket.org/pypy/pypy/changeset/44e3e9067f96/ Log: use rpython/conftest.py logic to skip deadline diff --git a/pypy/conftest.py b/pypy/conftest.py --- a/pypy/conftest.py +++ b/pypy/conftest.py @@ -10,11 +10,12 @@ except ImportError: pass else: - if __version__[:3] < '3.6': - s = settings(deadline=None) - settings.register_profile('default', s) - else: + try: settings.register_profile('default', deadline=None) + except Exception: + import warnings + warnings.warn("Version of hypothesis too old, " + "cannot set the deadline to None") settings.load_profile('default') # PyPy's command line extra options (these are added From pypy.commits at gmail.com Mon Feb 25 02:25:19 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 24 Feb 2019 23:25:19 -0800 (PST) Subject: [pypy-commit] pypy default: skip vectorize tests on 32 bit Message-ID: <5c73985f.1c69fb81.203e.c604@mx.google.com> Author: Matti Picus Branch: Changeset: r96154:5d31ca57807b Date: 2019-02-25 09:19 +0200 http://bitbucket.org/pypy/pypy/changeset/5d31ca57807b/ Log: skip vectorize tests on 32 bit diff --git a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py --- a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py +++ b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py @@ -1,12 +1,14 @@ import py import sys +import platform from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC from rpython.rlib.rawstorage import misaligned_is_fine def no_vector_backend(): - import platform if platform.machine().startswith('x86'): from rpython.jit.backend.x86.detect_feature import detect_sse4_2 + if sys.maxsize < 2**31: + return True return not detect_sse4_2() if platform.machine().startswith('ppc'): from rpython.jit.backend.ppc.detect_feature import detect_vsx From pypy.commits at gmail.com Mon Feb 25 02:25:21 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 24 Feb 2019 23:25:21 -0800 (PST) Subject: [pypy-commit] pypy newmemoryview-app-level: add newmemoryview via IndirectView backported from py3.6, use in _ctypes/array Message-ID: <5c739861.1c69fb81.26af1.09de@mx.google.com> Author: Matti Picus Branch: newmemoryview-app-level Changeset: r96155:8972b398d0ba Date: 2019-02-24 22:12 +0200 http://bitbucket.org/pypy/pypy/changeset/8972b398d0ba/ Log: add newmemoryview via IndirectView backported from py3.6, use in _ctypes/array diff --git a/lib_pypy/_ctypes/array.py b/lib_pypy/_ctypes/array.py --- a/lib_pypy/_ctypes/array.py +++ b/lib_pypy/_ctypes/array.py @@ -4,6 +4,7 @@ from _ctypes.basics import _CData, cdata_from_address, _CDataMeta, sizeof from _ctypes.basics import keepalive_key, store_reference, ensure_objects from _ctypes.basics import CArgObject, as_ffi_pointer +import sys, __pypy__ class ArrayMeta(_CDataMeta): def __new__(self, name, cls, typedict): @@ -241,6 +242,21 @@ def _as_ffi_pointer_(self, ffitype): return as_ffi_pointer(self, ffitype) + def __buffer__(self, flags): + shape = [] + obj = self + while 1: + shape.append(obj._length_) + try: + obj[0]._length_ + except AttributeError: + break + obj = obj[0] + + fmt = get_format_str(obj._type_) + itemsize = len(buffer(obj[0])) + return __pypy__.newmemoryview(memoryview(self._buffer), itemsize, fmt, shape) + ARRAY_CACHE = {} def create_array_type(base, length): @@ -260,3 +276,25 @@ cls = ArrayMeta(name, (Array,), tpdict) ARRAY_CACHE[key] = cls return cls + +byteorder = {'little': '>', 'big': '<'} +swappedorder = {'little': '<', 'big': '>'} + +def get_format_str(typ): + if hasattr(typ, '_fields_'): + if hasattr(typ, '_swappedbytes_'): + bo = swappedorder[sys.byteorder] + else: + bo = byteorder[sys.byteorder] + flds = [] + for name, obj in typ._fields_: + flds.append(bo) + flds.append(get_format_str(obj)) + flds.append(':') + flds.append(name) + flds.append(':') + return 'T{' + ''.join(flds) + '}' + elif hasattr(typ, '_type_'): + return typ._type_ + else: + raise ValueError('cannot get format string for %r' % typ) diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py --- a/pypy/module/__pypy__/__init__.py +++ b/pypy/module/__pypy__/__init__.py @@ -110,6 +110,7 @@ 'side_effects_ok' : 'interp_magic.side_effects_ok', 'stack_almost_full' : 'interp_magic.stack_almost_full', 'pyos_inputhook' : 'interp_magic.pyos_inputhook', + 'newmemoryview' : 'newmemoryview.newmemoryview', } if sys.platform == 'win32': interpleveldefs['get_console_cp'] = 'interp_magic.get_console_cp' diff --git a/pypy/module/__pypy__/newmemoryview.py b/pypy/module/__pypy__/newmemoryview.py new file mode 100644 --- /dev/null +++ b/pypy/module/__pypy__/newmemoryview.py @@ -0,0 +1,99 @@ +# +# An app-level interface to tp_as_buffer->bf_getbuffer. +# + +from pypy.interpreter.error import oefmt +from pypy.interpreter.gateway import unwrap_spec +from pypy.objspace.std.memoryobject import BufferViewND + +pep3118_size_map = { + # from struct documentation https://docs.python.org/3/library/struct.html + 'x': 1, #padding + '?': 1, + 'c': 1, + 'b': 1, + 'B': 1, + 'h': 2, + 'H': 2, + 'i': 4, + 'I': 4, + 'l': 4, + 'L': 4, + 'q': 8, + 'Q': 8, + 'e': 2, + 'f': 4, + 'd': 8, + # pep 3118 extensions + # https://www.python.org/dev/peps/pep-3118/#additions-to-the-struct-string-syntax + 'g': 16, # long double - is this platform dependent? + 'Zf': 8, + 'Zd':16, + 'Zg':32, + # Unhandled: 's', 'w' (UCS-4), 'c' (usc-1), 'u' (usc-2), 'O' (PyObject*), +} + + at unwrap_spec(itemsize=int, format='text') +def newmemoryview(space, w_obj, itemsize, format, w_shape=None, w_strides=None): + ''' + newmemoryview(buf, itemsize, format, shape=None, strides=None) + ''' + if not space.isinstance_w(w_obj, space.w_memoryview): + raise oefmt(space.w_ValueError, "memoryview expected") + # minimal error checking + lgt = space.len_w(w_obj) + old_size = w_obj.getitemsize() + nbytes = lgt * old_size + if w_shape: + tot = 1 + shape = [] + for w_v in space.listview(w_shape): + v = space.int_w(w_v) + shape.append(v) + tot *= v + if tot * itemsize != nbytes: + raise oefmt(space.w_ValueError, + "shape/itemsize %s/%d does not match obj len/itemsize %d/%d", + str(shape), itemsize, lgt, old_size) + else: + if nbytes % itemsize != 0: + raise oefmt(space.w_ValueError, + "itemsize %d does not match obj len/itemsize %d/%d", + itemsize, lgt, old_size) + shape = [nbytes / itemsize,] + ndim = len(shape) + if w_strides: + strides = [] + for w_v in space.listview(w_strides): + v = space.int_w(w_v) + strides.append(v) + if not w_shape and len(strides) != 1: + raise oefmt(space.w_ValueError, + "strides must have one value if shape not provided") + if len(strides) != ndim: + raise oefmt(space.w_ValueError, + "shape %s does not match strides %s", + str(shape), str(strides)) + else: + # start from the right, c-order layout + strides = [itemsize] * ndim + for v in range(ndim - 2, -1, -1): + strides[v] = strides[v + 1] * shape[v + 1] + # check that the strides are not too big + for i in range(ndim): + if strides[i] * shape[i] > nbytes: + raise oefmt(space.w_ValueError, + "shape %s and strides %s exceed object size %d", + shape, strides, nbytes) + view = space.buffer_w(w_obj, 0) + return space.newmemoryview(FormatBufferViewND(view, format, ndim, shape, strides)) + +class FormatBufferViewND(BufferViewND): + _immutable_ = True + _attrs_ = ['readonly', 'parent', 'ndim', 'shape', 'strides', 'format'] + def __init__(self, parent, format, ndim, shape, strides): + BufferViewND.__init__(self, parent, ndim, shape, strides) + self.format = format + + def getformat(self): + return self.format diff --git a/pypy/module/__pypy__/test/test_newmemoryview.py b/pypy/module/__pypy__/test/test_newmemoryview.py new file mode 100644 --- /dev/null +++ b/pypy/module/__pypy__/test/test_newmemoryview.py @@ -0,0 +1,17 @@ + + +class AppTestMinimal: + spaceconfig = dict(usemodules=['__pypy__']) + + def test_newmemoryview(self): + from __pypy__ import newmemoryview + b = bytearray(12) + # The format can be anything, we only verify shape, strides, and itemsize + m = newmemoryview(memoryview(b), 2, 'T{ Author: Matti Picus Branch: newmemoryview-app-level Changeset: r96156:96f4200d74ac Date: 2019-02-24 22:41 +0200 http://bitbucket.org/pypy/pypy/changeset/96f4200d74ac/ Log: cleanup, add test diff --git a/extra_tests/ctypes_tests/test_structures.py b/extra_tests/ctypes_tests/test_structures.py --- a/extra_tests/ctypes_tests/test_structures.py +++ b/extra_tests/ctypes_tests/test_structures.py @@ -190,3 +190,20 @@ assert sizeof(s) == 3 * sizeof(c_int) assert s.a == 4 # 256 + 4 assert s.b == -123 + +def test_memoryview(): + class S(Structure): + _fields_ = [('a', c_int16), + ('b', c_int16), + ] + + S3 = S * 3 + c_array = (2 * S3)( + S3(S(a=0, b=1), S(a=2, b=3), S(a=4, b=5)), + S3(S(a=6, b=7), S(a=8, b=9), S(a=10, b=11)), + ) + + mv = memoryview(c_array) + assert mv.format == 'T{ Author: Matti Picus Branch: newmemoryview-app-level Changeset: r96157:42a82e91006e Date: 2019-02-24 22:42 +0200 http://bitbucket.org/pypy/pypy/changeset/42a82e91006e/ Log: w_userdata was unused, make sure view.obj is not NULL 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 @@ -41,7 +41,9 @@ fill_Py_buffer(space, w_obj.view, view) try: view.c_buf = rffi.cast(rffi.VOIDP, w_obj.view.get_raw_address()) - view.c_obj = make_ref(space, w_userdata) + # not used in PyPy to keep something alive, + # but some c-extensions check the type without checking for NULL + view.c_obj = make_ref(space.w_None) rffi.setintfield(view, 'c_readonly', w_obj.view.readonly) except ValueError: w_s = w_obj.descr_tobytes(space) From pypy.commits at gmail.com Mon Feb 25 02:25:26 2019 From: pypy.commits at gmail.com (mattip) Date: Sun, 24 Feb 2019 23:25:26 -0800 (PST) Subject: [pypy-commit] pypy newmemoryview-app-level: add itemsize to the new view Message-ID: <5c739866.1c69fb81.a1ae8.b745@mx.google.com> Author: Matti Picus Branch: newmemoryview-app-level Changeset: r96158:2eee79cfc69a Date: 2019-02-24 23:15 +0200 http://bitbucket.org/pypy/pypy/changeset/2eee79cfc69a/ Log: add itemsize to the new view diff --git a/pypy/module/__pypy__/newmemoryview.py b/pypy/module/__pypy__/newmemoryview.py --- a/pypy/module/__pypy__/newmemoryview.py +++ b/pypy/module/__pypy__/newmemoryview.py @@ -59,14 +59,20 @@ "shape %s and strides %s exceed object size %d", shape, strides, nbytes) view = space.buffer_w(w_obj, 0) - return space.newmemoryview(FormatBufferViewND(view, format, ndim, shape, strides)) + return space.newmemoryview(FormatBufferViewND(view, itemsize, format, ndim, + shape, strides)) class FormatBufferViewND(BufferViewND): _immutable_ = True - _attrs_ = ['readonly', 'parent', 'ndim', 'shape', 'strides', 'format'] - def __init__(self, parent, format, ndim, shape, strides): + _attrs_ = ['readonly', 'parent', 'ndim', 'shape', 'strides', + 'format', 'itemsize'] + def __init__(self, parent, itemsize, format, ndim, shape, strides): BufferViewND.__init__(self, parent, ndim, shape, strides) - self.format = format + self.format = format + self.itemsize = itemsize def getformat(self): return self.format + + def getitemsize(self): + return self.itemsize diff --git a/pypy/module/__pypy__/test/test_newmemoryview.py b/pypy/module/__pypy__/test/test_newmemoryview.py --- a/pypy/module/__pypy__/test/test_newmemoryview.py +++ b/pypy/module/__pypy__/test/test_newmemoryview.py @@ -13,5 +13,4 @@ strides=(6, 2)) assert m.strides == (6, 2) assert m.format == 'T{ Author: Matti Picus Branch: newmemoryview-app-level Changeset: r96159:c71067a1a79a Date: 2019-02-24 23:51 +0200 http://bitbucket.org/pypy/pypy/changeset/c71067a1a79a/ Log: fix 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 @@ -43,7 +43,7 @@ view.c_buf = rffi.cast(rffi.VOIDP, w_obj.view.get_raw_address()) # not used in PyPy to keep something alive, # but some c-extensions check the type without checking for NULL - view.c_obj = make_ref(space.w_None) + view.c_obj = make_ref(space, space.w_None) rffi.setintfield(view, 'c_readonly', w_obj.view.readonly) except ValueError: w_s = w_obj.descr_tobytes(space) From pypy.commits at gmail.com Mon Feb 25 02:52:17 2019 From: pypy.commits at gmail.com (arigo) Date: Sun, 24 Feb 2019 23:52:17 -0800 (PST) Subject: [pypy-commit] cffi default: potential fix for Py 3.8 Message-ID: <5c739eb1.1c69fb81.27885.3453@mx.google.com> Author: Armin Rigo Branch: Changeset: r3230:07d1803cb17b Date: 2019-02-25 08:52 +0100 http://bitbucket.org/cffi/cffi/changeset/07d1803cb17b/ Log: potential fix for Py 3.8 diff --git a/c/call_python.c b/c/call_python.c --- a/c/call_python.c +++ b/c/call_python.c @@ -1,3 +1,9 @@ +#if PY_VERSION_HEX >= 0x03080000 +# define Py_BUILD_CORE +/* for access to the fields of PyInterpreterState */ +# include "internal/pycore_pystate.h" +# undef Py_BUILD_CORE +#endif static PyObject *_get_interpstate_dict(void) { From pypy.commits at gmail.com Mon Feb 25 04:21:05 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 25 Feb 2019 01:21:05 -0800 (PST) Subject: [pypy-commit] pypy default: update expected response Message-ID: <5c73b381.1c69fb81.2accb.55fe@mx.google.com> Author: Matti Picus Branch: Changeset: r96160:ca68946053a3 Date: 2019-02-25 11:17 +0200 http://bitbucket.org/pypy/pypy/changeset/ca68946053a3/ Log: update expected response diff --git a/rpython/translator/sandbox/test/test_sandlib.py b/rpython/translator/sandbox/test/test_sandlib.py --- a/rpython/translator/sandbox/test/test_sandlib.py +++ b/rpython/translator/sandbox/test/test_sandlib.py @@ -114,7 +114,7 @@ proc = SocketProc([exe]) output, error = proc.communicate("") - assert output.startswith('HTTP/1.1 301 Moved Permanently') + assert output.startswith('HTTP/1.0 400 Bad request') def test_oserror(): def entry_point(argv): From pypy.commits at gmail.com Mon Feb 25 04:21:07 2019 From: pypy.commits at gmail.com (mattip) Date: Mon, 25 Feb 2019 01:21:07 -0800 (PST) Subject: [pypy-commit] pypy default: update list of expected addresses Message-ID: <5c73b383.1c69fb81.83e6a.5331@mx.google.com> Author: Matti Picus Branch: Changeset: r96161:e1a64d19e7f4 Date: 2019-02-25 11:20 +0200 http://bitbucket.org/pypy/pypy/changeset/e1a64d19e7f4/ Log: update list of expected addresses diff --git a/rpython/rlib/test/test_rsocket.py b/rpython/rlib/test/test_rsocket.py --- a/rpython/rlib/test/test_rsocket.py +++ b/rpython/rlib/test/test_rsocket.py @@ -416,7 +416,7 @@ assert isinstance(lst, list) found = False for family, socktype, protocol, canonname, addr in lst: - if addr.get_host() in ('104.130.43.121', '23.253.135.79'): + if addr.get_host() in ('104.130.43.121', '23.253.135.79', '45.55.99.72'): found = True elif family == AF_INET: print 'pydotorg changed to', addr.get_host() From pypy.commits at gmail.com Mon Feb 25 07:04:54 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 25 Feb 2019 04:04:54 -0800 (PST) Subject: [pypy-commit] pypy py3.6: a few test methods were duplicated (merge problem?) Message-ID: <5c73d9e6.1c69fb81.37952.071e@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96162:7fd78ec00476 Date: 2019-02-25 12:50 +0100 http://bitbucket.org/pypy/pypy/changeset/7fd78ec00476/ Log: a few test methods were duplicated (merge problem?) diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -1277,28 +1277,6 @@ assert u'A\u03a3\u0345'.lower() == u'a\u03c2\u0345' assert u'\u03a3\u0345 '.lower() == u'\u03c3\u0345 ' - def test_encode_wrong_errors(self): - assert ''.encode(errors='some_wrong_name') == b'' - - def test_casefold(self): - assert u'hello'.casefold() == u'hello' - assert u'hELlo'.casefold() == u'hello' - assert u'ß'.casefold() == u'ss' - assert u'fi'.casefold() == u'fi' - assert u'\u03a3'.casefold() == u'\u03c3' - assert u'A\u0345\u03a3'.casefold() == u'a\u03b9\u03c3' - assert u'\u00b5'.casefold() == u'\u03bc' - - def test_lower_3a3(self): - # Special case for GREEK CAPITAL LETTER SIGMA U+03A3 - assert u'\u03a3'.lower() == u'\u03c3' - assert u'\u0345\u03a3'.lower() == u'\u0345\u03c3' - assert u'A\u0345\u03a3'.lower() == u'a\u0345\u03c2' - assert u'A\u0345\u03a3a'.lower() == u'a\u0345\u03c3a' - assert u'A\u0345\u03a3'.lower() == u'a\u0345\u03c2' - assert u'A\u03a3\u0345'.lower() == u'a\u03c2\u0345' - assert u'\u03a3\u0345 '.lower() == u'\u03c3\u0345 ' - def test_unicode_constructor_misc(self): x = u'foo' x += u'bar' @@ -1347,4 +1325,4 @@ def test_newlist_utf8_non_ascii(self): - 'ä'.split("\n")[0] # does not crash \ No newline at end of file + 'ä'.split("\n")[0] # does not crash From pypy.commits at gmail.com Mon Feb 25 07:04:56 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 25 Feb 2019 04:04:56 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix corner case in unicode.title Message-ID: <5c73d9e8.1c69fb81.12a53.fa7b@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96163:fd6330354b33 Date: 2019-02-25 13:02 +0100 http://bitbucket.org/pypy/pypy/changeset/fd6330354b33/ Log: fix corner case in unicode.title diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -1,12 +1,11 @@ # -*- encoding: utf-8 -*- import py -import sys try: from hypothesis import given, strategies, settings, example HAS_HYPOTHESIS = True except ImportError: HAS_HYPOTHESIS = False - + from rpython.rlib import rutf8 from pypy.interpreter.error import OperationError @@ -356,6 +355,9 @@ assert (chr(0x345) + u'abc').title() == u'\u0399abc' assert (chr(0x345) + u'ABC').title() == u'\u0399abc' + def test_title_bug(self): + assert (chr(496) + "abc").title() == 'J̌abc' + def test_istitle(self): assert u"".istitle() == False assert u"!".istitle() == False diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -431,7 +431,7 @@ codes = unicodedb.tolower_full(ch) for c in codes: builder.append_code(c) - previous_is_cased = unicodedb.iscased(codes[-1]) + previous_is_cased = unicodedb.iscased(ch) i += 1 return self.from_utf8builder(builder) From pypy.commits at gmail.com Mon Feb 25 10:34:16 2019 From: pypy.commits at gmail.com (cfbolz) Date: Mon, 25 Feb 2019 07:34:16 -0800 (PST) Subject: [pypy-commit] pypy py3.6: python3.6 (not 2.7) uses 'utf-8' in UnicodeDecodeError messages Message-ID: <5c740af8.1c69fb81.16aae.05fb@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96164:008d52d161c2 Date: 2019-02-25 16:33 +0100 http://bitbucket.org/pypy/pypy/changeset/008d52d161c2/ Log: python3.6 (not 2.7) uses 'utf-8' in UnicodeDecodeError messages diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -375,7 +375,7 @@ continue if ordch1 <= 0xC1: - r, pos, rettype = errorhandler(errors, "utf8", "invalid start byte", + r, pos, rettype = errorhandler(errors, "utf-8", "invalid start byte", s, pos, pos + 1) res.append(r) continue @@ -387,14 +387,14 @@ if not final: pos -= 1 break - r, pos, rettype = errorhandler(errors, "utf8", "unexpected end of data", + r, pos, rettype = errorhandler(errors, "utf-8", "unexpected end of data", s, pos - 1, pos) res.append(r) continue ordch2 = ord(s[pos]) if rutf8._invalid_byte_2_of_2(ordch2): - r, pos, rettype = errorhandler(errors, "utf8", "invalid continuation byte", + r, pos, rettype = errorhandler(errors, "utf-8", "invalid continuation byte", s, pos - 1, pos) res.append(r) continue @@ -412,11 +412,11 @@ if (pos) < end and rutf8._invalid_byte_2_of_3(ordch1, ord(s[pos]), allow_surrogates): msg = "invalid continuation byte" - r, pos, rettype = errorhandler(errors, "utf8", msg, s, + r, pos, rettype = errorhandler(errors, "utf-8", msg, s, pos - 1, pos) else: msg = "unexpected end of data" - r, pos, rettype = errorhandler(errors, "utf8", msg, s, + r, pos, rettype = errorhandler(errors, "utf-8", msg, s, pos - 1, pos) pos = end res.append(r) @@ -425,12 +425,12 @@ ordch3 = ord(s[pos + 1]) if rutf8._invalid_byte_2_of_3(ordch1, ordch2, allow_surrogates): - r, pos, rettype = errorhandler(errors, "utf8", "invalid continuation byte", + r, pos, rettype = errorhandler(errors, "utf-8", "invalid continuation byte", s, pos - 1, pos) res.append(r) continue elif rutf8._invalid_byte_3_of_3(ordch3): - r, pos, rettype = errorhandler(errors, "utf8", "invalid continuation byte", + r, pos, rettype = errorhandler(errors, "utf-8", "invalid continuation byte", s, pos - 1, pos + 1) res.append(r) continue @@ -449,16 +449,16 @@ break if pos < end and rutf8._invalid_byte_2_of_4(ordch1, ord(s[pos])): msg = "invalid continuation byte" - r, pos, rettype = errorhandler(errors, "utf8", msg, s, + r, pos, rettype = errorhandler(errors, "utf-8", msg, s, pos - 1, pos) elif pos + 1 < end and rutf8._invalid_byte_3_of_4(ord(s[pos + 1])): msg = "invalid continuation byte" pos += 1 - r, pos, rettype = errorhandler(errors, "utf8", msg, s, + r, pos, rettype = errorhandler(errors, "utf-8", msg, s, pos - 2, pos) else: msg = "unexpected end of data" - r, pos, rettype = errorhandler(errors, "utf8", msg, s, + r, pos, rettype = errorhandler(errors, "utf-8", msg, s, pos - 1, pos) pos = end res.append(r) @@ -467,17 +467,17 @@ ordch3 = ord(s[pos + 1]) ordch4 = ord(s[pos + 2]) if rutf8._invalid_byte_2_of_4(ordch1, ordch2): - r, pos, rettype = errorhandler(errors, "utf8", "invalid continuation byte", + r, pos, rettype = errorhandler(errors, "utf-8", "invalid continuation byte", s, pos - 1, pos) res.append(r) continue elif rutf8._invalid_byte_3_of_4(ordch3): - r, pos, rettype = errorhandler(errors, "utf8", "invalid continuation byte", + r, pos, rettype = errorhandler(errors, "utf-8", "invalid continuation byte", s, pos - 1, pos + 1) res.append(r) continue elif rutf8._invalid_byte_4_of_4(ordch4): - r, pos, rettype = errorhandler(errors, "utf8", "invalid continuation byte", + r, pos, rettype = errorhandler(errors, "utf-8", "invalid continuation byte", s, pos - 1, pos + 2) res.append(r) continue @@ -490,7 +490,7 @@ res.append(chr(ordch4)) continue - r, pos, rettype = errorhandler(errors, "utf8", "invalid start byte", + r, pos, rettype = errorhandler(errors, "utf-8", "invalid start byte", s, pos - 1, pos) res.append(r) diff --git a/pypy/module/_codecs/test/test_codecs.py b/pypy/module/_codecs/test/test_codecs.py --- a/pypy/module/_codecs/test/test_codecs.py +++ b/pypy/module/_codecs/test/test_codecs.py @@ -59,6 +59,10 @@ assert str(UnicodeDecodeError( "ascii", b"g\xfcrk", 1, 3, "ouch")) == "'ascii' codec can't decode bytes in position 1-2: ouch" + def test_unicodedecodeerror_utf8(self): + error = raises(UnicodeDecodeError, b'\xf6'.decode, "utf-8").value + assert str(error) == "'utf-8' codec can't decode byte 0xf6 in position 0: invalid start byte" + def test_unicodetranslateerror(self): import sys assert str(UnicodeTranslateError( From pypy.commits at gmail.com Tue Feb 26 09:57:29 2019 From: pypy.commits at gmail.com (arigo) Date: Tue, 26 Feb 2019 06:57:29 -0800 (PST) Subject: [pypy-commit] cffi default: bump version number to 1.12.2 Message-ID: <5c7553d9.1c69fb81.9f0ca.7459@mx.google.com> Author: Armin Rigo Branch: Changeset: r3231:6b31f9a47c79 Date: 2019-02-26 15:58 +0100 http://bitbucket.org/cffi/cffi/changeset/6b31f9a47c79/ Log: bump version number to 1.12.2 diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -2,7 +2,7 @@ #include #include "structmember.h" -#define CFFI_VERSION "1.12.1" +#define CFFI_VERSION "1.12.2" #ifdef MS_WIN32 #include diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -12,7 +12,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.12.1", ("This test_c.py file is for testing a version" +assert __version__ == "1.12.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,): diff --git a/cffi/__init__.py b/cffi/__init__.py --- a/cffi/__init__.py +++ b/cffi/__init__.py @@ -5,8 +5,8 @@ from .error import CDefError, FFIError, VerificationError, VerificationMissing from .error import PkgConfigError -__version__ = "1.12.1" -__version_info__ = (1, 12, 1) +__version__ = "1.12.2" +__version_info__ = (1, 12, 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/cffi/_embedding.h b/cffi/_embedding.h --- a/cffi/_embedding.h +++ b/cffi/_embedding.h @@ -221,7 +221,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.12.1" + "\ncompiled with cffi version: 1.12.2" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff --git a/doc/source/conf.py b/doc/source/conf.py --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -47,7 +47,7 @@ # The short X.Y version. version = '1.12' # The full version, including alpha/beta/rc tags. -release = '1.12.1' +release = '1.12.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -52,13 +52,13 @@ * https://pypi.python.org/pypi/cffi -* Checksums of the "source" package version 1.12.1: +* Checksums of the "source" package version 1.12.2: - - MD5: d6d5c4805bbce844cf1368702b056e3c + - MD5: ... - - SHA: b4d8d74a22d0574cb056502daa2c377898f0a910 + - SHA: ... - - SHA256: 9b6f7ba4e78c52c1a291d0c0c0bd745d19adde1a9e1c03cb899f0c6efd6f8033 + - SHA256: ... * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -198,7 +198,7 @@ `Mailing list `_ """, - version='1.12.1', + version='1.12.2', packages=['cffi'] if cpython else [], package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', '_embedding.h', '_cffi_errors.h']} From pypy.commits at gmail.com Tue Feb 26 10:00:32 2019 From: pypy.commits at gmail.com (arigo) Date: Tue, 26 Feb 2019 07:00:32 -0800 (PST) Subject: [pypy-commit] cffi default: document Message-ID: <5c755490.1c69fb81.542f.ff1b@mx.google.com> Author: Armin Rigo Branch: Changeset: r3232:2224b65a8b65 Date: 2019-02-26 16:00 +0100 http://bitbucket.org/cffi/cffi/changeset/2224b65a8b65/ Log: document diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -3,6 +3,12 @@ ====================== +v1.12.2 +======= + +* Added temporary workaround to compile on CPython 3.8.0a2. + + v1.12.1 ======= From pypy.commits at gmail.com Tue Feb 26 10:00:34 2019 From: pypy.commits at gmail.com (arigo) Date: Tue, 26 Feb 2019 07:00:34 -0800 (PST) Subject: [pypy-commit] cffi release-1.12: hg merge default Message-ID: <5c755492.1c69fb81.dd946.a52b@mx.google.com> Author: Armin Rigo Branch: release-1.12 Changeset: r3233:2c4239852fde Date: 2019-02-26 16:00 +0100 http://bitbucket.org/cffi/cffi/changeset/2c4239852fde/ Log: hg merge default diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -2,7 +2,7 @@ #include #include "structmember.h" -#define CFFI_VERSION "1.12.1" +#define CFFI_VERSION "1.12.2" #ifdef MS_WIN32 #include diff --git a/c/call_python.c b/c/call_python.c --- a/c/call_python.c +++ b/c/call_python.c @@ -1,3 +1,9 @@ +#if PY_VERSION_HEX >= 0x03080000 +# define Py_BUILD_CORE +/* for access to the fields of PyInterpreterState */ +# include "internal/pycore_pystate.h" +# undef Py_BUILD_CORE +#endif static PyObject *_get_interpstate_dict(void) { diff --git a/c/test_c.py b/c/test_c.py --- a/c/test_c.py +++ b/c/test_c.py @@ -12,7 +12,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.12.1", ("This test_c.py file is for testing a version" +assert __version__ == "1.12.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,): diff --git a/cffi/__init__.py b/cffi/__init__.py --- a/cffi/__init__.py +++ b/cffi/__init__.py @@ -5,8 +5,8 @@ from .error import CDefError, FFIError, VerificationError, VerificationMissing from .error import PkgConfigError -__version__ = "1.12.1" -__version_info__ = (1, 12, 1) +__version__ = "1.12.2" +__version_info__ = (1, 12, 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/cffi/_embedding.h b/cffi/_embedding.h --- a/cffi/_embedding.h +++ b/cffi/_embedding.h @@ -221,7 +221,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.12.1" + "\ncompiled with cffi version: 1.12.2" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff --git a/doc/source/conf.py b/doc/source/conf.py --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -47,7 +47,7 @@ # The short X.Y version. version = '1.12' # The full version, including alpha/beta/rc tags. -release = '1.12.1' +release = '1.12.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -52,13 +52,13 @@ * https://pypi.python.org/pypi/cffi -* Checksums of the "source" package version 1.12.1: +* Checksums of the "source" package version 1.12.2: - - MD5: d6d5c4805bbce844cf1368702b056e3c + - MD5: ... - - SHA: b4d8d74a22d0574cb056502daa2c377898f0a910 + - SHA: ... - - SHA256: 9b6f7ba4e78c52c1a291d0c0c0bd745d19adde1a9e1c03cb899f0c6efd6f8033 + - SHA256: ... * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` diff --git a/doc/source/ref.rst b/doc/source/ref.rst --- a/doc/source/ref.rst +++ b/doc/source/ref.rst @@ -146,36 +146,43 @@ +++++++++++++++++++++++++++++++ **ffi.buffer(cdata, [size])**: return a buffer object that references -the raw C data pointed to by the given 'cdata', of 'size' bytes. The -'cdata' must be a pointer or an array. If unspecified, the size of the +the raw C data pointed to by the given 'cdata', of 'size' bytes. What +Python calls "a buffer", or more precisely "an object supporting the +buffer interface", is an object that represents some raw memory and +that can be passed around to various built-in or extension functions; +these built-in functions read from or write to the raw memory directly, +without needing an extra copy. + +The 'cdata' argument +must be a pointer or an array. If unspecified, the size of the buffer is either the size of what ``cdata`` points to, or the whole size -of the array. Getting a buffer is useful because you can read from it -without an extra copy, or write into it to change the original value. +of the array. Here are a few examples of where buffer() would be useful: - use ``file.write()`` and ``file.readinto()`` with such a buffer (for files opened in binary mode) -- use ``ffi.buffer(mystruct[0])[:] = socket.recv(len(buffer))`` to read - into a struct over a socket, rewriting the contents of mystruct[0] +- overwrite the content of a struct: if ``p`` is a cdata pointing to + it, use ``ffi.buffer(p)[:] = newcontent``, where ``newcontent`` is + a bytes object (``str`` in Python 2). Remember that like in C, you can use ``array + index`` to get the pointer to the index'th item of an array. (In C you might more naturally write ``&array[index]``, but that is equivalent.) -The returned object is not a built-in buffer nor memoryview object, -because these objects' API changes too much across Python versions. -Instead it has the following Python API (a subset of Python 2's -``buffer``): +The returned object's type is not the builtin ``buffer`` nor ``memoryview`` +types, because these types' API changes too much across Python versions. +Instead it has the following Python API (a subset of Python 2's ``buffer``) +in addition to supporting the buffer interface: -- ``buf[:]`` or ``bytes(buf)``: fetch a copy as a regular byte string (or - ``buf[start:end]`` for a part) +- ``buf[:]`` or ``bytes(buf)``: copy data out of the buffer, returning a + regular byte string (or ``buf[start:end]`` for a part) -- ``buf[:] = newstr``: change the original content (or ``buf[start:end] +- ``buf[:] = newstr``: copy data into the buffer (or ``buf[start:end] = newstr``) -- ``len(buf), buf[index], buf[index] = newchar``: access as a sequence +- ``len(buf)``, ``buf[index]``, ``buf[index] = newchar``: access as a sequence of characters. The buffer object returned by ``ffi.buffer(cdata)`` keeps alive the @@ -193,9 +200,11 @@ **ffi.from_buffer([cdecl,] python_buffer, require_writable=False)**: return an array cdata (by default a ````) that points to the data of the given Python object, which must support the -buffer interface. This is the opposite of ``ffi.buffer()``. It gives -a reference to the existing data, not a copy. -It is meant to be used on objects +buffer interface. Note that ``ffi.from_buffer()`` turns a generic +Python buffer object into a cdata object, whereas ``ffi.buffer()`` does +the opposite conversion. Both calls don't actually copy any data. + +``ffi.from_buffer()`` is meant to be used on objects containing large quantities of raw data, like bytearrays or ``array.array`` or numpy arrays. It supports both the old *buffer* API (in Python 2.x) and the diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -3,6 +3,12 @@ ====================== +v1.12.2 +======= + +* Added temporary workaround to compile on CPython 3.8.0a2. + + v1.12.1 ======= diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -198,7 +198,7 @@ `Mailing list `_ """, - version='1.12.1', + version='1.12.2', packages=['cffi'] if cpython else [], package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', '_embedding.h', '_cffi_errors.h']} From pypy.commits at gmail.com Tue Feb 26 10:02:57 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 26 Feb 2019 07:02:57 -0800 (PST) Subject: [pypy-commit] =?utf-8?q?pypy_py3=2E6=3A_fix_behaviour_of_=CE=A3_?= =?utf-8?q?in_combination_with_title?= Message-ID: <5c755521.1c69fb81.d7f82.176c@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96167:dca96cba7aee Date: 2019-02-26 14:54 +0100 http://bitbucket.org/pypy/pypy/changeset/dca96cba7aee/ Log: fix behaviour of Σ in combination with title diff --git a/pypy/objspace/std/test/test_unicodeobject.py b/pypy/objspace/std/test/test_unicodeobject.py --- a/pypy/objspace/std/test/test_unicodeobject.py +++ b/pypy/objspace/std/test/test_unicodeobject.py @@ -1279,6 +1279,26 @@ assert u'A\u03a3\u0345'.lower() == u'a\u03c2\u0345' assert u'\u03a3\u0345 '.lower() == u'\u03c3\u0345 ' + def test_title_3a3(self): + # Special case for GREEK CAPITAL LETTER SIGMA U+03A3 + assert u'\u03a3abc'.title() == u'\u03a3abc' + assert u'\u03a3'.title() == u'Σ' + assert u'\u0345\u03a3'.title() == u'Ισ' + assert u'A\u0345\u03a3'.title() == u'Aͅς' + assert u'A\u0345\u03a3a'.title() == u'Aͅσa' + assert u'A\u0345\u03a3'.title() == u'Aͅς' + assert u'A\u03a3\u0345'.title() == u'Aςͅ' + assert u'\u03a3\u0345 '.title() == u'Σͅ ' + + assert u'ääää \u03a3'.title() == u'Ääää Σ' + assert u'ääää \u0345\u03a3'.title() == u'Ääää Ισ' + assert u'ääää A\u0345\u03a3'.title() == u'Ääää Aͅς' + assert u'ääää A\u0345\u03a3a'.title() == u'Ääää Aͅσa' + assert u'ääää A\u0345\u03a3'.title() == u'Ääää Aͅς' + assert u'ääää A\u03a3\u0345'.title() == u'Ääää Aςͅ' + assert u'ääää \u03a3\u0345 '.title() == u'Ääää Σͅ ' + + def test_unicode_constructor_misc(self): x = u'foo' x += u'bar' diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -398,10 +398,7 @@ i = 0 for ch in rutf8.Utf8StringIterator(value): if unicodedb.isupper(ch): - if ch == 0x3a3: - codes = [self._handle_capital_sigma(value, i),] - else: - codes = unicodedb.tolower_full(ch) + codes = self._lower_char(ch, value, i) elif unicodedb.islower(ch): codes = unicodedb.toupper_full(ch) else: @@ -423,18 +420,22 @@ previous_is_cased = False i = 0 for ch in rutf8.Utf8StringIterator(input): - if ch == 0x3a3: - codes = [self._handle_capital_sigma(input, i),] - elif not previous_is_cased: + if previous_is_cased: + codes = self._lower_char(ch, value, i) + else: codes = unicodedb.totitle_full(ch) - else: - codes = unicodedb.tolower_full(ch) for c in codes: builder.append_code(c) previous_is_cased = unicodedb.iscased(ch) i += 1 return self.from_utf8builder(builder) + def _lower_char(self, ch, value, i): + if ch == 0x3a3: + return [self._handle_capital_sigma(value, i), ] + else: + return unicodedb.tolower_full(ch) + def _handle_capital_sigma(self, value, i): # U+03A3 is in the Final_Sigma context when, it is found like this: #\p{cased} \p{case-ignorable}* U+03A3 not(\p{case-ignorable}* \p{cased}) @@ -598,10 +599,7 @@ builder = rutf8.Utf8StringBuilder(len(value)) i = 0 for ch in rutf8.Utf8StringIterator(value): - if ch == 0x3a3: - codes = [self._handle_capital_sigma(value, i),] - else: - codes = unicodedb.tolower_full(ch) + codes = self._lower_char(ch, value, i) for c in codes: builder.append_code(c) i += 1 @@ -889,10 +887,7 @@ builder.append_code(c) i = 1 for ch in it: - if ch == 0x3a3: - codes = [self._handle_capital_sigma(value, i),] - else: - codes = unicodedb.tolower_full(ch) + codes = self._lower_char(ch, value, i) for c in codes: builder.append_code(c) i += 1 From pypy.commits at gmail.com Tue Feb 26 10:02:59 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 26 Feb 2019 07:02:59 -0800 (PST) Subject: [pypy-commit] pypy default: Utf8StringPosIterator to iterate over character, position pairs Message-ID: <5c755523.1c69fb81.2d270.ea7a@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96168:5512e76a8a54 Date: 2019-02-26 15:57 +0100 http://bitbucket.org/pypy/pypy/changeset/5512e76a8a54/ Log: Utf8StringPosIterator to iterate over character, position pairs diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -153,6 +153,7 @@ """Gives the position of the previous codepoint. 'pos' must not be zero. """ + assert pos != 0 pos -= 1 if pos >= len(code): # for the case where pos - 1 == len(code): assert pos >= 0 @@ -811,6 +812,18 @@ (0xF0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 ) assert False, "unreachable" +class Utf8StringPosIterator(object): + def __init__(self, utf8s): + self.it = Utf8StringIterator(utf8s) + + def __iter__(self): + return self + + @always_inline + def next(self): + pos = self.it.get_pos() + return (self.it.next(), pos) + def decode_latin_1(s): if len(s) == 0: diff --git a/rpython/rlib/test/test_rutf8.py b/rpython/rlib/test/test_rutf8.py --- a/rpython/rlib/test/test_rutf8.py +++ b/rpython/rlib/test/test_rutf8.py @@ -212,3 +212,16 @@ for c in u: l.append(unichr(c)) assert list(arg) == l + + at given(strategies.text()) +def test_utf8_iterator_pos(arg): + utf8s = arg.encode('utf8') + u = rutf8.Utf8StringPosIterator(utf8s) + l = [] + i = 0 + for c, pos in u: + l.append(unichr(c)) + assert c == rutf8.codepoint_at_pos(utf8s, pos) + assert pos == i + i = rutf8.next_codepoint_pos(utf8s, i) + assert list(arg) == l From pypy.commits at gmail.com Tue Feb 26 10:03:00 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 26 Feb 2019 07:03:00 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default Message-ID: <5c755524.1c69fb81.23574.eded@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96169:ff451dfc00d9 Date: 2019-02-26 15:57 +0100 http://bitbucket.org/pypy/pypy/changeset/ff451dfc00d9/ Log: merge default diff --git a/pypy/conftest.py b/pypy/conftest.py --- a/pypy/conftest.py +++ b/pypy/conftest.py @@ -19,11 +19,12 @@ except ImportError: pass else: - if __version__[:3] < '3.6': - s = settings(deadline=None) - settings.register_profile('default', s) - else: + try: settings.register_profile('default', deadline=None) + except Exception: + import warnings + warnings.warn("Version of hypothesis too old, " + "cannot set the deadline to None") settings.load_profile('default') # PyPy's command line extra options (these are added diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -73,7 +73,7 @@ result_borrowed=True) def PyDict_GetItem(space, w_dict, w_key): if not isinstance(w_dict, W_DictMultiObject): - raise PyErr_BadInternalCall(space) + return None # NOTE: this works so far because all our dict strategies store # *values* as full objects, which stay alive as long as the dict is # alive and not modified. So we can return a borrowed ref. @@ -83,14 +83,14 @@ @cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_SetItem(space, w_dict, w_key, w_obj): if not isinstance(w_dict, W_DictMultiObject): - raise PyErr_BadInternalCall(space) + PyErr_BadInternalCall(space) w_dict.setitem(w_key, w_obj) return 0 @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1) def PyDict_DelItem(space, w_dict, w_key): if not isinstance(w_dict, W_DictMultiObject): - raise PyErr_BadInternalCall(space) + PyErr_BadInternalCall(space) w_dict.descr_delitem(space, w_key) return 0 @@ -98,7 +98,7 @@ def PyDict_SetItemString(space, w_dict, key_ptr, w_obj): w_key = space.newtext(rffi.charp2str(key_ptr)) if not isinstance(w_dict, W_DictMultiObject): - raise PyErr_BadInternalCall(space) + PyErr_BadInternalCall(space) w_dict.setitem(w_key, w_obj) return 0 @@ -109,7 +109,7 @@ char*, rather than a PyObject*.""" w_key = space.newtext(rffi.charp2str(key)) if not isinstance(w_dict, W_DictMultiObject): - raise PyErr_BadInternalCall(space) + return None # NOTE: this works so far because all our dict strategies store # *values* as full objects, which stay alive as long as the dict is # alive and not modified. So we can return a borrowed ref. diff --git a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py --- a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py +++ b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py @@ -1,12 +1,14 @@ import py import sys +import platform from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC from rpython.rlib.rawstorage import misaligned_is_fine def no_vector_backend(): - import platform if platform.machine().startswith('x86'): from rpython.jit.backend.x86.detect_feature import detect_sse4_2 + if sys.maxsize < 2**31: + return True return not detect_sse4_2() if platform.machine().startswith('ppc'): from rpython.jit.backend.ppc.detect_feature import detect_vsx diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -153,6 +153,7 @@ """Gives the position of the previous codepoint. 'pos' must not be zero. """ + assert pos != 0 pos -= 1 if pos >= len(code): # for the case where pos - 1 == len(code): assert pos >= 0 @@ -811,6 +812,18 @@ (0xF0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 ) assert False, "unreachable" +class Utf8StringPosIterator(object): + def __init__(self, utf8s): + self.it = Utf8StringIterator(utf8s) + + def __iter__(self): + return self + + @always_inline + def next(self): + pos = self.it.get_pos() + return (self.it.next(), pos) + def decode_latin_1(s): if len(s) == 0: diff --git a/rpython/rlib/test/test_rsocket.py b/rpython/rlib/test/test_rsocket.py --- a/rpython/rlib/test/test_rsocket.py +++ b/rpython/rlib/test/test_rsocket.py @@ -416,7 +416,7 @@ assert isinstance(lst, list) found = False for family, socktype, protocol, canonname, addr in lst: - if addr.get_host() in ('104.130.43.121', '23.253.135.79'): + if addr.get_host() in ('104.130.43.121', '23.253.135.79', '45.55.99.72'): found = True elif family == AF_INET: print 'pydotorg changed to', addr.get_host() diff --git a/rpython/rlib/test/test_rutf8.py b/rpython/rlib/test/test_rutf8.py --- a/rpython/rlib/test/test_rutf8.py +++ b/rpython/rlib/test/test_rutf8.py @@ -212,3 +212,16 @@ for c in u: l.append(unichr(c)) assert list(arg) == l + + at given(strategies.text()) +def test_utf8_iterator_pos(arg): + utf8s = arg.encode('utf8') + u = rutf8.Utf8StringPosIterator(utf8s) + l = [] + i = 0 + for c, pos in u: + l.append(unichr(c)) + assert c == rutf8.codepoint_at_pos(utf8s, pos) + assert pos == i + i = rutf8.next_codepoint_pos(utf8s, i) + assert list(arg) == l diff --git a/rpython/translator/sandbox/test/test_sandlib.py b/rpython/translator/sandbox/test/test_sandlib.py --- a/rpython/translator/sandbox/test/test_sandlib.py +++ b/rpython/translator/sandbox/test/test_sandlib.py @@ -114,7 +114,7 @@ proc = SocketProc([exe]) output, error = proc.communicate("") - assert output.startswith('HTTP/1.1 301 Moved Permanently') + assert output.startswith('HTTP/1.0 400 Bad request') def test_oserror(): def entry_point(argv): From pypy.commits at gmail.com Tue Feb 26 10:03:02 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 26 Feb 2019 07:03:02 -0800 (PST) Subject: [pypy-commit] pypy py3.6: make performance of lower/upper/title/swapcase not terrible for strings Message-ID: <5c755526.1c69fb81.fa9f4.1356@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96170:f2a689373046 Date: 2019-02-26 16:01 +0100 http://bitbucket.org/pypy/pypy/changeset/f2a689373046/ Log: make performance of lower/upper/title/swapcase not terrible for strings containing Σ diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -395,73 +395,70 @@ def descr_swapcase(self, space): value = self._utf8 builder = rutf8.Utf8StringBuilder(len(value)) - i = 0 - for ch in rutf8.Utf8StringIterator(value): + for ch, pos in rutf8.Utf8StringPosIterator(value): if unicodedb.isupper(ch): - codes = self._lower_char(ch, value, i) + codes = self._lower_char(ch, value, pos) elif unicodedb.islower(ch): codes = unicodedb.toupper_full(ch) else: codes = [ch,] for c in codes: builder.append_code(c) - i += 1 return self.from_utf8builder(builder) def descr_title(self, space): if len(self._utf8) == 0: return self - return self.title_unicode(self._utf8) + return self.title_unicode() @jit.elidable - def title_unicode(self, value): - input = self._utf8 - builder = rutf8.Utf8StringBuilder(len(input)) + def title_unicode(self): + value = self._utf8 + builder = rutf8.Utf8StringBuilder(len(value)) previous_is_cased = False - i = 0 - for ch in rutf8.Utf8StringIterator(input): + for ch, pos in rutf8.Utf8StringPosIterator(value): if previous_is_cased: - codes = self._lower_char(ch, value, i) + codes = self._lower_char(ch, value, pos) else: codes = unicodedb.totitle_full(ch) for c in codes: builder.append_code(c) previous_is_cased = unicodedb.iscased(ch) - i += 1 return self.from_utf8builder(builder) - def _lower_char(self, ch, value, i): + def _lower_char(self, ch, value, bytepos): if ch == 0x3a3: - return [self._handle_capital_sigma(value, i), ] + return [self._handle_capital_sigma(value, bytepos), ] else: return unicodedb.tolower_full(ch) - def _handle_capital_sigma(self, value, i): + def _handle_capital_sigma(self, value, bytepos): # U+03A3 is in the Final_Sigma context when, it is found like this: #\p{cased} \p{case-ignorable}* U+03A3 not(\p{case-ignorable}* \p{cased}) # where \p{xxx} is a character with property xxx. - # TODO: find a better way for utf8 -> codepoints - value = [ch for ch in rutf8.Utf8StringIterator(value)] - j = i - 1 final_sigma = False - while j >= 0: - ch = value[j] - if unicodedb.iscaseignorable(ch): - j -= 1 - continue - final_sigma = unicodedb.iscased(ch) - break - if final_sigma: - j = i + 1 - length = len(value) - while j < length: - ch = value[j] + if bytepos > 0: + j = rutf8.prev_codepoint_pos(value, bytepos) + while j >= 0: + ch = rutf8.codepoint_at_pos(value, j) if unicodedb.iscaseignorable(ch): - j += 1 + if j == 0: + break + j = rutf8.prev_codepoint_pos(value, j) continue - final_sigma = not unicodedb.iscased(ch) + final_sigma = unicodedb.iscased(ch) break + if final_sigma and bytepos < len(value): + j = rutf8.next_codepoint_pos(value, bytepos) + length = len(value) + while j < length: + ch = rutf8.codepoint_at_pos(value, j) + if unicodedb.iscaseignorable(ch): + j = rutf8.next_codepoint_pos(value, j) + continue + final_sigma = not unicodedb.iscased(ch) + break if final_sigma: return 0x3C2 else: @@ -597,12 +594,10 @@ def descr_lower(self, space): value = self._utf8 builder = rutf8.Utf8StringBuilder(len(value)) - i = 0 - for ch in rutf8.Utf8StringIterator(value): - codes = self._lower_char(ch, value, i) + for ch, pos in rutf8.Utf8StringPosIterator(value): + codes = self._lower_char(ch, value, pos) for c in codes: builder.append_code(c) - i += 1 return self.from_utf8builder(builder) def descr_isdecimal(self, space): @@ -879,18 +874,16 @@ value = self._utf8 builder = rutf8.Utf8StringBuilder(len(value)) - it = rutf8.Utf8StringIterator(value) - uchar = it.next() + it = rutf8.Utf8StringPosIterator(value) + uchar, _ = it.next() codes = unicodedb.toupper_full(uchar) # can sometimes give more than one, like for omega-with-Ypogegrammeni, 8179 for c in codes: builder.append_code(c) - i = 1 - for ch in it: - codes = self._lower_char(ch, value, i) + for ch, pos in it: + codes = self._lower_char(ch, value, pos) for c in codes: builder.append_code(c) - i += 1 return self.from_utf8builder(builder) @unwrap_spec(width=int, w_fillchar=WrappedDefault(u' ')) From pypy.commits at gmail.com Tue Feb 26 10:08:19 2019 From: pypy.commits at gmail.com (arigo) Date: Tue, 26 Feb 2019 07:08:19 -0800 (PST) Subject: [pypy-commit] cffi release-1.12: md5/sha Message-ID: <5c755663.1c69fb81.8db89.081a@mx.google.com> Author: Armin Rigo Branch: release-1.12 Changeset: r3234:e0c76668ecf5 Date: 2019-02-26 16:08 +0100 http://bitbucket.org/cffi/cffi/changeset/e0c76668ecf5/ Log: md5/sha diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -54,11 +54,11 @@ * Checksums of the "source" package version 1.12.2: - - MD5: ... + - MD5: 4d7dcb6c7c738c15d2ece9bd4c5f86da - - SHA: ... + - SHA: 5f579d4980cbcc8aac592721f714ef6a64370ab1 - - SHA256: ... + - SHA256: e113878a446c6228669144ae8a56e268c91b7f1fafae927adc4879d9849e0ea7 * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` From pypy.commits at gmail.com Tue Feb 26 10:08:21 2019 From: pypy.commits at gmail.com (arigo) Date: Tue, 26 Feb 2019 07:08:21 -0800 (PST) Subject: [pypy-commit] cffi default: hg merge release-1.12 Message-ID: <5c755665.1c69fb81.2f559.73fa@mx.google.com> Author: Armin Rigo Branch: Changeset: r3235:3f7e50919c84 Date: 2019-02-26 16:08 +0100 http://bitbucket.org/cffi/cffi/changeset/3f7e50919c84/ Log: hg merge release-1.12 diff --git a/doc/source/installation.rst b/doc/source/installation.rst --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -54,11 +54,11 @@ * Checksums of the "source" package version 1.12.2: - - MD5: ... + - MD5: 4d7dcb6c7c738c15d2ece9bd4c5f86da - - SHA: ... + - SHA: 5f579d4980cbcc8aac592721f714ef6a64370ab1 - - SHA256: ... + - SHA256: e113878a446c6228669144ae8a56e268c91b7f1fafae927adc4879d9849e0ea7 * Or grab the most current version from the `Bitbucket page`_: ``hg clone https://bitbucket.org/cffi/cffi`` From pypy.commits at gmail.com Tue Feb 26 10:09:16 2019 From: pypy.commits at gmail.com (arigo) Date: Tue, 26 Feb 2019 07:09:16 -0800 (PST) Subject: [pypy-commit] cffi release-1.12: Added tag v1.12.2 for changeset e0c76668ecf5 Message-ID: <5c75569c.1c69fb81.6688f.5651@mx.google.com> Author: Armin Rigo Branch: release-1.12 Changeset: r3236:85148822c1f5 Date: 2019-02-26 16:09 +0100 http://bitbucket.org/cffi/cffi/changeset/85148822c1f5/ Log: Added tag v1.12.2 for changeset e0c76668ecf5 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -19,3 +19,4 @@ 48416163071ed48300c3ae4358cc7fd841912413 v1.11.5 170fb3d1f0b30d32c9794ea40dbb51836dc7d947 v1.12.0 ac7cb142e8f5dc73033d89c54c54e1b1a6413946 v1.12.1 +e0c76668ecf5ab1ad005799cfe1b7cb58610f155 v1.12.2 From pypy.commits at gmail.com Tue Feb 26 10:09:18 2019 From: pypy.commits at gmail.com (arigo) Date: Tue, 26 Feb 2019 07:09:18 -0800 (PST) Subject: [pypy-commit] cffi default: hg merge release-1.12 Message-ID: <5c75569e.1c69fb81.a85dc.5860@mx.google.com> Author: Armin Rigo Branch: Changeset: r3237:d765c36df047 Date: 2019-02-26 16:09 +0100 http://bitbucket.org/cffi/cffi/changeset/d765c36df047/ Log: hg merge release-1.12 diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -19,3 +19,4 @@ 48416163071ed48300c3ae4358cc7fd841912413 v1.11.5 170fb3d1f0b30d32c9794ea40dbb51836dc7d947 v1.12.0 ac7cb142e8f5dc73033d89c54c54e1b1a6413946 v1.12.1 +e0c76668ecf5ab1ad005799cfe1b7cb58610f155 v1.12.2 From pypy.commits at gmail.com Tue Feb 26 10:46:20 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 26 Feb 2019 07:46:20 -0800 (PST) Subject: [pypy-commit] pypy py3.6: make posix2 tests run to completion on win32 Message-ID: <5c755f4c.1c69fb81.4b173.277d@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96171:f9760faab53a Date: 2019-02-26 17:23 +0200 http://bitbucket.org/pypy/pypy/changeset/f9760faab53a/ Log: make posix2 tests run to completion on win32 diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -10,6 +10,7 @@ from pypy.interpreter.gateway import interp2app from rpython.translator.c.test.test_extfunc import need_sparse_files from rpython.rlib import rposix +from rpython.rlib.objectmodel import we_are_translated USEMODULES = ['binascii', 'posix', 'signal', 'struct', 'time'] # py3k os.open uses subprocess, requiring the following per platform @@ -291,6 +292,7 @@ assert 0 def test_functions_raise_error(self): + import sys def ex(func, *args): try: func(*args) @@ -308,10 +310,12 @@ ex(self.posix.write, UNUSEDFD, b"x") ex(self.posix.close, UNUSEDFD) #UMPF cpython raises IOError ex(self.posix.ftruncate, UNUSEDFD, 123) - ex(self.posix.fstat, UNUSEDFD) - ex(self.posix.stat, "qweqwehello") - # how can getcwd() raise? - ex(self.posix.dup, UNUSEDFD) + if sys.platform == 'win32' and not we_are_translated(): + # XXX kills the host interpreter untranslated + ex(self.posix.fstat, UNUSEDFD) + ex(self.posix.stat, "qweqwehello") + # how can getcwd() raise? + ex(self.posix.dup, UNUSEDFD) def test_getcwd(self): os, posix = self.os, self.posix @@ -594,7 +598,6 @@ # using startfile in app_startfile creates global state test_popen.dont_track_allocations = True test_popen_with.dont_track_allocations = True - test_popen_child_fds.dont_track_allocations = True if hasattr(__import__(os.name), '_getfullpathname'): def test__getfullpathname(self): @@ -1073,8 +1076,10 @@ os.closerange(start, stop) for fd in fds: os.close(fd) # should not have been closed - for fd in range(start, stop): - raises(OSError, os.fstat, fd) # should have been closed + if sys.platform == 'win32' and not we_are_translated(): + # XXX kills the host interpreter untranslated + for fd in range(start, stop): + raises(OSError, os.fstat, fd) # should have been closed if hasattr(os, 'chown'): def test_chown(self): @@ -1455,6 +1460,11 @@ fname = self.path2 + 'rename.txt' with open(fname, "w") as f: f.write("this is a rename test") + str_name = str(self.pdir) + '/test_rename.txt' + os.rename(self.path, str_name) + with open(str_name) as f: + assert f.read() == 'this is a rename test' + os.rename(str_name, fname) unicode_name = str(self.udir) + u'/test\u03be.txt' os.rename(fname, unicode_name) with open(unicode_name) as f: diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -109,9 +109,6 @@ wchar_t const* file, unsigned int line, uintptr_t pReserved) { - wprintf(L"Invalid parameter detected in function %s." - L" File: %s Line: %d\\n", function, file, line); - wprintf(L"Expression: %s\\n", expression); } RPY_EXTERN void* enter_suppress_iph(void) From pypy.commits at gmail.com Tue Feb 26 10:46:22 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 26 Feb 2019 07:46:22 -0800 (PST) Subject: [pypy-commit] pypy default: test, fix for win32. Message-ID: <5c755f4e.1c69fb81.1efd4.f3db@mx.google.com> Author: Matti Picus Branch: Changeset: r96172:25b25fdd6d5c Date: 2019-02-26 11:18 +0200 http://bitbucket.org/pypy/pypy/changeset/25b25fdd6d5c/ Log: test, fix for win32. The asserts should be py3.6 only, and theny point to unfinished utf8 changes diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -1190,6 +1190,11 @@ os = self.posix with open(self.path, "w") as f: f.write("this is a rename test") + str_name = str(self.pdir) + '/test_rename.txt' + os.rename(self.path, str_name) + with open(str_name) as f: + assert f.read() == 'this is a rename test' + os.rename(str_name, self.path) unicode_name = str(self.udir) + u'/test\u03be.txt' os.rename(self.path, unicode_name) with open(unicode_name) as f: diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -1273,8 +1273,6 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) - assert isinstance(path1, unicode) - assert isinstance(path2, unicode) if not win32traits.MoveFileEx(path1, path2, 0): raise rwin32.lastSavedWindowsError() @@ -1285,8 +1283,6 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) - assert isinstance(path1, unicode) - assert isinstance(path2, unicode) ret = win32traits.MoveFileEx(path1, path2, win32traits.MOVEFILE_REPLACE_EXISTING) if not ret: From pypy.commits at gmail.com Tue Feb 26 10:46:23 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 26 Feb 2019 07:46:23 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default into branch Message-ID: <5c755f4f.1c69fb81.728f5.d1a8@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96173:a16d58959cb9 Date: 2019-02-26 17:45 +0200 http://bitbucket.org/pypy/pypy/changeset/a16d58959cb9/ Log: merge default into branch diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -1270,8 +1270,6 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) - assert isinstance(path1, unicode) - assert isinstance(path2, unicode) if not win32traits.MoveFileEx(path1, path2, 0): raise rwin32.lastSavedWindowsError() @@ -1282,8 +1280,6 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) - assert isinstance(path1, unicode) - assert isinstance(path2, unicode) ret = win32traits.MoveFileEx(path1, path2, win32traits.MOVEFILE_REPLACE_EXISTING) if not ret: From pypy.commits at gmail.com Tue Feb 26 10:46:25 2019 From: pypy.commits at gmail.com (mattip) Date: Tue, 26 Feb 2019 07:46:25 -0800 (PST) Subject: [pypy-commit] pypy py3.6: redo unicode assert, this should use utf8 instead Message-ID: <5c755f51.1c69fb81.56400.2309@mx.google.com> Author: Matti Picus Branch: py3.6 Changeset: r96174:ea62fc146783 Date: 2019-02-26 17:47 +0200 http://bitbucket.org/pypy/pypy/changeset/ea62fc146783/ Log: redo unicode assert, this should use utf8 instead diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -1270,6 +1270,8 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) + assert isinstance(path1, unicode) + assert isinstance(path2, unicode) if not win32traits.MoveFileEx(path1, path2, 0): raise rwin32.lastSavedWindowsError() @@ -1280,6 +1282,8 @@ win32traits = make_win32_traits(traits) path1 = traits.as_str0(path1) path2 = traits.as_str0(path2) + assert isinstance(path1, unicode) + assert isinstance(path2, unicode) ret = win32traits.MoveFileEx(path1, path2, win32traits.MOVEFILE_REPLACE_EXISTING) if not ret: From pypy.commits at gmail.com Tue Feb 26 13:02:01 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 26 Feb 2019 10:02:01 -0800 (PST) Subject: [pypy-commit] pypy default: inlining breaks Message-ID: <5c757f19.1c69fb81.88d20.137a@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96175:2a3bb59a81cb Date: 2019-02-26 16:24 +0100 http://bitbucket.org/pypy/pypy/changeset/2a3bb59a81cb/ Log: inlining breaks diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -819,7 +819,6 @@ def __iter__(self): return self - @always_inline def next(self): pos = self.it.get_pos() return (self.it.next(), pos) From pypy.commits at gmail.com Tue Feb 26 13:02:03 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 26 Feb 2019 10:02:03 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default Message-ID: <5c757f1b.1c69fb81.4fe78.11c7@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96176:243d81c93b41 Date: 2019-02-26 16:24 +0100 http://bitbucket.org/pypy/pypy/changeset/243d81c93b41/ Log: merge default diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -819,7 +819,6 @@ def __iter__(self): return self - @always_inline def next(self): pos = self.it.get_pos() return (self.it.next(), pos) From pypy.commits at gmail.com Tue Feb 26 13:02:05 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 26 Feb 2019 10:02:05 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge Message-ID: <5c757f1d.1c69fb81.291f5.e2a6@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96177:ec74b34bb0fb Date: 2019-02-26 19:01 +0100 http://bitbucket.org/pypy/pypy/changeset/ec74b34bb0fb/ Log: merge diff --git a/pypy/module/posix/test/test_posix2.py b/pypy/module/posix/test/test_posix2.py --- a/pypy/module/posix/test/test_posix2.py +++ b/pypy/module/posix/test/test_posix2.py @@ -10,6 +10,7 @@ from pypy.interpreter.gateway import interp2app from rpython.translator.c.test.test_extfunc import need_sparse_files from rpython.rlib import rposix +from rpython.rlib.objectmodel import we_are_translated USEMODULES = ['binascii', 'posix', 'signal', 'struct', 'time'] # py3k os.open uses subprocess, requiring the following per platform @@ -291,6 +292,7 @@ assert 0 def test_functions_raise_error(self): + import sys def ex(func, *args): try: func(*args) @@ -308,10 +310,12 @@ ex(self.posix.write, UNUSEDFD, b"x") ex(self.posix.close, UNUSEDFD) #UMPF cpython raises IOError ex(self.posix.ftruncate, UNUSEDFD, 123) - ex(self.posix.fstat, UNUSEDFD) - ex(self.posix.stat, "qweqwehello") - # how can getcwd() raise? - ex(self.posix.dup, UNUSEDFD) + if sys.platform == 'win32' and not we_are_translated(): + # XXX kills the host interpreter untranslated + ex(self.posix.fstat, UNUSEDFD) + ex(self.posix.stat, "qweqwehello") + # how can getcwd() raise? + ex(self.posix.dup, UNUSEDFD) def test_getcwd(self): os, posix = self.os, self.posix @@ -594,7 +598,6 @@ # using startfile in app_startfile creates global state test_popen.dont_track_allocations = True test_popen_with.dont_track_allocations = True - test_popen_child_fds.dont_track_allocations = True if hasattr(__import__(os.name), '_getfullpathname'): def test__getfullpathname(self): @@ -1073,8 +1076,10 @@ os.closerange(start, stop) for fd in fds: os.close(fd) # should not have been closed - for fd in range(start, stop): - raises(OSError, os.fstat, fd) # should have been closed + if sys.platform == 'win32' and not we_are_translated(): + # XXX kills the host interpreter untranslated + for fd in range(start, stop): + raises(OSError, os.fstat, fd) # should have been closed if hasattr(os, 'chown'): def test_chown(self): @@ -1455,6 +1460,11 @@ fname = self.path2 + 'rename.txt' with open(fname, "w") as f: f.write("this is a rename test") + str_name = str(self.pdir) + '/test_rename.txt' + os.rename(self.path, str_name) + with open(str_name) as f: + assert f.read() == 'this is a rename test' + os.rename(str_name, fname) unicode_name = str(self.udir) + u'/test\u03be.txt' os.rename(fname, unicode_name) with open(unicode_name) as f: diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -109,9 +109,6 @@ wchar_t const* file, unsigned int line, uintptr_t pReserved) { - wprintf(L"Invalid parameter detected in function %s." - L" File: %s Line: %d\\n", function, file, line); - wprintf(L"Expression: %s\\n", expression); } RPY_EXTERN void* enter_suppress_iph(void) From pypy.commits at gmail.com Tue Feb 26 13:02:08 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 26 Feb 2019 10:02:08 -0800 (PST) Subject: [pypy-commit] pypy default: merge Message-ID: <5c757f20.1c69fb81.8cec.b745@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96178:dbdf453749f7 Date: 2019-02-26 19:01 +0100 http://bitbucket.org/pypy/pypy/changeset/dbdf453749f7/ Log: merge diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -819,7 +819,6 @@ def __iter__(self): return self - @always_inline def next(self): pos = self.it.get_pos() return (self.it.next(), pos) From pypy.commits at gmail.com Tue Feb 26 15:16:16 2019 From: pypy.commits at gmail.com (cfbolz) Date: Tue, 26 Feb 2019 12:16:16 -0800 (PST) Subject: [pypy-commit] pypy py3.6: more places that give the name of the encoding as 'utf8' Message-ID: <5c759e90.1c69fb81.d29cc.1104@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96179:2eeaa559be67 Date: 2019-02-26 21:15 +0100 http://bitbucket.org/pypy/pypy/changeset/2eeaa559be67/ Log: more places that give the name of the encoding as 'utf8' diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -161,7 +161,7 @@ try: return rutf8.check_utf8(string, True, start, end) except rutf8.CheckError as e: - decode_error_handler(space)('strict', 'utf8', + decode_error_handler(space)('strict', 'utf-8', 'unexpected end of data', string, e.pos, e.pos + 1) @@ -251,7 +251,7 @@ uchr = rutf8.codepoint_at_pos(s, pos) if 0xDC00 <= uchr <= 0xDFFF: delta += 1 - res, newindex, rettype = errorhandler(errors, 'utf8', + res, newindex, rettype = errorhandler(errors, 'utf-8', 'surrogates not allowed', s, upos, upos + delta) if rettype == 'u': for cp in rutf8.Utf8StringIterator(res): From pypy.commits at gmail.com Tue Feb 26 19:36:01 2019 From: pypy.commits at gmail.com (andrewjlawrence) Date: Tue, 26 Feb 2019 16:36:01 -0800 (PST) Subject: [pypy-commit] pypy winoverlapped: Fixed a another test Message-ID: <5c75db71.1c69fb81.a42c1.1f12@mx.google.com> Author: andrewjlawrence Branch: winoverlapped Changeset: r96184:a98c0892bf12 Date: 2019-02-27 00:30 +0000 http://bitbucket.org/pypy/pypy/changeset/a98c0892bf12/ Log: Fixed a another test diff --git a/lib_pypy/_overlapped.py b/lib_pypy/_overlapped.py --- a/lib_pypy/_overlapped.py +++ b/lib_pypy/_overlapped.py @@ -65,22 +65,20 @@ self.address = _ffi.addressof(self.overlapped[0]) def __del__(self): - ###if (!HasOverlappedIoCompleted(&self->overlapped) && - ### self->type != TYPE_NOT_STARTED) - ### - xxx - # do this somehow else - #err = _kernel32.GetLastError() - #bytes = _ffi.new('DWORD[1]') - #o = self.overlapped[0] - #if self.overlapped[0].pending: - # if _kernel32.CancelIoEx(o.handle, o.overlapped) & \ - # self.GetOverlappedResult(o.handle, o.overlapped, _ffi.addressof(bytes), True): - # # The operation is no longer pending, nothing to do - # pass - # else: - # raise RuntimeError('deleting an overlapped struct with a pending operation not supported') - + bytes = _ffi.new("DWORD") + olderr = _winapi.GetLastError() + if not HasOverlappedIoCompleted(self.overlapped) and self.type != TYPE_NOT_STARTED: + wait = _kernel32.CancelIoEx(self.handle, self.overlapped) + ret = self.GetOverlappedResult(wait) + err = _winapi.ERROR_SUCCESS + if not ret: + err = _winapi.GetLastError() + if err != _winapi.ERROR_SUCCESS and \ + err != _winapi.ERROR_NOT_FOUND and \ + err != _winapi.ERROR_OPERATION_ABORTED: + raise _winapi._WinError() + if self.overlapped.hEvent: + _winapi.CloseHandle(self.overlapped.hEvent) @property def event(self): @@ -104,16 +102,7 @@ self.pending = 0 raise _winapi._WinError() if self.type == OverlappedType.TYPE_READ: - if self.completed and self.allocated_buffer: - if transferred[0] != len(self.allocated_buffer): - ### Do a resize - result = _ffi.new("CHAR[]", transferred[0]) - _ffi.memmove(result, self.allocated_buffer, transferred[0]) - return result - else: - return b'' - else: - return b'' + return _ffi.unpack(self.allocated_buffer, transferred[0]) else: return transferred[0] @@ -251,7 +240,7 @@ data = _ffi.new('PostCallbackData[1]') newwaitobject = _ffi.new("HANDLE[1]") data[0].hCompletionPort = completionport - data[0].Overlapped = ovaddress[0] + data[0].Overlapped = _ffi.new("OVERLAPPED *",ovaddress[0]) success = _kernel32.RegisterWaitForSingleObject(newwaitobject, object, _ffi.cast("WAITORTIMERCALLBACK",post_to_queue_callback), @@ -261,7 +250,27 @@ return newwaitobject +def ConnectPipe(address): + handle = _kernel32.CreateFileW(address, + _winapi.GENERIC_READ | _winapi.GENERIC_WRITE, + 0, + _ffi.NULL, + _winapi.OPEN_EXISTING, + _winapi.FILE_FLAG_OVERLAPPED, + _ffi.NULL) + return handle + + # In CPython this function converts a windows error into a python object # Not sure what we should do here. def SetFromWindowsErr(error): - return error \ No newline at end of file + return error + +def HasOverlappedIoCompleted(overlapped): + return (overlapped.Internal != STATUS_PENDING) + + +# +# Error Codes +# +ERROR_PIPE_BUSY = 231 \ No newline at end of file diff --git a/lib_pypy/_pypy_winbase_cffi.py b/lib_pypy/_pypy_winbase_cffi.py --- a/lib_pypy/_pypy_winbase_cffi.py +++ b/lib_pypy/_pypy_winbase_cffi.py @@ -3,8 +3,8 @@ ffi = _cffi_backend.FFI('_pypy_winbase_cffi', _version = 0x2601, - _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xE6\x03\x00\x00\x13\x11\x00\x00\xEC\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xE4\x03\x00\x00\xE0\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xDB\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xDF\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xE5\x03\x00\x00\x0A\x01\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x31\x11\x00\x00\xD3\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x03\x00\x00\x31\x03\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x8F\x03\x00\x00\x6D\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x6D\x11\x00\x00\x6D\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x4A\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x6D\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x8A\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x8A\x0D\x00\x00\x00\x0F\x00\x00\x8A\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xE3\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xE6\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x92\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x8F\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x95\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x95\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x6D\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x9B\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xEC\x0D\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x00\x0F\x00\x00\xEC\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xEC\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xE2\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x07\x09\x00\x00\x02\x01\x00\x00\x39\x03\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xEB\x03\x00\x00\x04\x01\x00\x00\x00\x01', - _globals = (b'\x00\x00\x2C\x23CancelIo',0,b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x91\x23CreateEventA',0,b'\x00\x00\x97\x23CreateEventW',0,b'\x00\x00\x9D\x23CreateFileA',0,b'\x00\x00\xCA\x23CreateFileW',0,b'\x00\x00\xB8\x23CreateIoCompletionPort',0,b'\x00\x00\xA6\x23CreateNamedPipeA',0,b'\x00\x00\xC0\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x6C\x23CreateProcessW',0,b'\x00\x00\x63\x23DuplicateHandle',0,b'\x00\x00\xBE\x23GetCurrentProcess',0,b'\x00\x00\x4C\x23GetExitCodeProcess',0,b'\x00\x00\x87\x23GetLastError',0,b'\x00\x00\x82\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x50\x23GetQueuedCompletionStatus',0,b'\x00\x00\xB5\x23GetStdHandle',0,b'\x00\x00\x87\x23GetVersion',0,b'\x00\x00\x5D\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x7B\x23SetErrorMode',0,b'\x00\x00\xD8\x23SetEvent',0,b'\x00\x00\x57\x23SetNamedPipeHandleState',0,b'\x00\x00\x48\x23TerminateProcess',0,b'\x00\x00\x3F\x23WSARecv',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x7E\x23WaitForSingleObject',0,b'\x00\x00\x78\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x8C\x23_getwch',0,b'\x00\x00\x8C\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x8E\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x89\x23_ungetwch',0,b'\x00\x00\xB0\x23socket',0), - _struct_unions = ((b'\x00\x00\x00\xE9\x00\x00\x00\x03$1',b'\x00\x00\xE8\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xE8\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xE0\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xE4\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x8A\x11wShowWindow',b'\x00\x00\x8A\x11cbReserved2',b'\x00\x00\xEA\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xDF\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xE9\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xE2\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x31\x11Overlapped'),(b'\x00\x00\x00\xE3\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle'),(b'\x00\x00\x00\xE5\x00\x00\x00\x02_WSABUF',b'\x00\x00\x18\x11len',b'\x00\x00\x13\x11buf')), - _typenames = (b'\x00\x00\x00\xE7LPFN_DISCONNECTEX',b'\x00\x00\x00\x31LPOVERLAPPED',b'\x00\x00\x00\x46LPOVERLAPPED_COMPLETION_ROUTINE',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xE1LPPostCallbackData',b'\x00\x00\x00\x92LPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\x41LPWSABUF',b'\x00\x00\x00\xDFOVERLAPPED',b'\x00\x00\x00\xE0PROCESS_INFORMATION',b'\x00\x00\x00\x92PSECURITY_ATTRIBUTES',b'\x00\x00\x00\xE2PostCallbackData',b'\x00\x00\x00\xE3SECURITY_ATTRIBUTES',b'\x00\x00\x00\x15SOCKET',b'\x00\x00\x00\xE4STARTUPINFO',b'\x00\x00\x00\x27WAITORTIMERCALLBACK',b'\x00\x00\x00\xE5WSABUF',b'\x00\x00\x00\x8Awint_t'), + _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x09\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x19\x01\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\x00\x0F\x00\x00\x01\x0D\x00\x00\xE9\x03\x00\x00\x13\x11\x00\x00\xEF\x03\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x13\x11\x00\x00\x13\x11\x00\x00\xE7\x03\x00\x00\xE3\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x03\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x1F\x11\x00\x00\x15\x11\x00\x00\xDE\x03\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xE2\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x18\x03\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x31\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\xE8\x03\x00\x00\x0A\x01\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x31\x11\x00\x00\xD6\x03\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x03\x00\x00\x31\x03\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x36\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x31\x11\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x1F\x11\x00\x00\x0A\x01\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x01\x0D\x00\x00\x8F\x03\x00\x00\x6D\x11\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x0A\x01\x00\x00\x6D\x11\x00\x00\x6D\x11\x00\x00\x1B\x11\x00\x00\x1C\x11\x00\x00\x02\x0F\x00\x00\x0D\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x4A\x0D\x00\x00\x08\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x15\x11\x00\x00\x6D\x11\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x18\x0D\x00\x00\x02\x0F\x00\x00\x8A\x0D\x00\x00\x06\x01\x00\x00\x00\x0F\x00\x00\x8A\x0D\x00\x00\x00\x0F\x00\x00\x8A\x0D\x00\x00\x10\x01\x00\x00\x00\x0F\x00\x00\x15\x0D\x00\x00\xE6\x03\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\xE9\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x92\x11\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x8F\x03\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x95\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x95\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x15\x11\x00\x00\x15\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x6D\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x02\x0F\x00\x00\x15\x0D\x00\x00\x9B\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x92\x11\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xEF\x0D\x00\x00\x31\x11\x00\x00\x00\x0F\x00\x00\xEF\x0D\x00\x00\x0A\x01\x00\x00\x0A\x01\x00\x00\x15\x11\x00\x00\x00\x0F\x00\x00\xEF\x0D\x00\x00\x15\x11\x00\x00\x02\x0F\x00\x00\xEF\x0D\x00\x00\x15\x11\x00\x00\x07\x01\x00\x00\x02\x0F\x00\x00\x04\x09\x00\x00\x02\x09\x00\x00\xE5\x03\x00\x00\x05\x09\x00\x00\x06\x09\x00\x00\x03\x09\x00\x00\x07\x09\x00\x00\x02\x01\x00\x00\x39\x03\x00\x00\x01\x09\x00\x00\x00\x09\x00\x00\xEE\x03\x00\x00\x04\x01\x00\x00\x00\x01', + _globals = (b'\x00\x00\x2C\x23CancelIo',0,b'\x00\x00\x2F\x23CancelIoEx',0,b'\x00\x00\x2C\x23CloseHandle',0,b'\x00\x00\x2F\x23ConnectNamedPipe',0,b'\x00\x00\x91\x23CreateEventA',0,b'\x00\x00\x97\x23CreateEventW',0,b'\x00\x00\x9D\x23CreateFileA',0,b'\x00\x00\xCA\x23CreateFileW',0,b'\x00\x00\xB8\x23CreateIoCompletionPort',0,b'\x00\x00\xA6\x23CreateNamedPipeA',0,b'\x00\x00\xC0\x23CreateNamedPipeW',0,b'\x00\x00\x1E\x23CreatePipe',0,b'\x00\x00\x12\x23CreateProcessA',0,b'\x00\x00\x6C\x23CreateProcessW',0,b'\x00\x00\x63\x23DuplicateHandle',0,b'\x00\x00\xBE\x23GetCurrentProcess',0,b'\x00\x00\x4C\x23GetExitCodeProcess',0,b'\x00\x00\x87\x23GetLastError',0,b'\x00\x00\x82\x23GetModuleFileNameW',0,b'\x00\x00\x33\x23GetOverlappedResult',0,b'\x00\x00\x50\x23GetQueuedCompletionStatus',0,b'\x00\x00\xB5\x23GetStdHandle',0,b'\x00\x00\x87\x23GetVersion',0,b'\x00\x00\xD3\x23HasOverlappedIoCompleted',0,b'\x00\x00\x5D\x23PostQueuedCompletionStatus',0,b'\x00\x00\x24\x23RegisterWaitForSingleObject',0,b'\xFF\xFF\xFF\x1FSEM_FAILCRITICALERRORS',1,b'\xFF\xFF\xFF\x1FSEM_NOALIGNMENTFAULTEXCEPT',4,b'\xFF\xFF\xFF\x1FSEM_NOGPFAULTERRORBOX',2,b'\xFF\xFF\xFF\x1FSEM_NOOPENFILEERRORBOX',32768,b'\x00\x00\x7B\x23SetErrorMode',0,b'\x00\x00\xDB\x23SetEvent',0,b'\x00\x00\x57\x23SetNamedPipeHandleState',0,b'\x00\x00\x48\x23TerminateProcess',0,b'\x00\x00\x3F\x23WSARecv',0,b'\xFF\xFF\xFF\x1FWT_EXECUTEINWAITTHREAD',4,b'\xFF\xFF\xFF\x1FWT_EXECUTEONLYONCE',8,b'\x00\x00\x7E\x23WaitForSingleObject',0,b'\x00\x00\x78\x23_get_osfhandle',0,b'\x00\x00\x10\x23_getch',0,b'\x00\x00\x10\x23_getche',0,b'\x00\x00\x8C\x23_getwch',0,b'\x00\x00\x8C\x23_getwche',0,b'\x00\x00\x10\x23_kbhit',0,b'\x00\x00\x07\x23_locking',0,b'\x00\x00\x0C\x23_open_osfhandle',0,b'\x00\x00\x00\x23_putch',0,b'\x00\x00\x8E\x23_putwch',0,b'\x00\x00\x03\x23_setmode',0,b'\x00\x00\x00\x23_ungetch',0,b'\x00\x00\x89\x23_ungetwch',0,b'\x00\x00\xB0\x23socket',0), + _struct_unions = ((b'\x00\x00\x00\xEC\x00\x00\x00\x03$1',b'\x00\x00\xEB\x11DUMMYSTRUCTNAME',b'\x00\x00\x15\x11Pointer'),(b'\x00\x00\x00\xEB\x00\x00\x00\x02$2',b'\x00\x00\x18\x11Offset',b'\x00\x00\x18\x11OffsetHigh'),(b'\x00\x00\x00\xE3\x00\x00\x00\x02$PROCESS_INFORMATION',b'\x00\x00\x15\x11hProcess',b'\x00\x00\x15\x11hThread',b'\x00\x00\x18\x11dwProcessId',b'\x00\x00\x18\x11dwThreadId'),(b'\x00\x00\x00\xE7\x00\x00\x00\x02$STARTUPINFO',b'\x00\x00\x18\x11cb',b'\x00\x00\x13\x11lpReserved',b'\x00\x00\x13\x11lpDesktop',b'\x00\x00\x13\x11lpTitle',b'\x00\x00\x18\x11dwX',b'\x00\x00\x18\x11dwY',b'\x00\x00\x18\x11dwXSize',b'\x00\x00\x18\x11dwYSize',b'\x00\x00\x18\x11dwXCountChars',b'\x00\x00\x18\x11dwYCountChars',b'\x00\x00\x18\x11dwFillAttribute',b'\x00\x00\x18\x11dwFlags',b'\x00\x00\x8A\x11wShowWindow',b'\x00\x00\x8A\x11cbReserved2',b'\x00\x00\xED\x11lpReserved2',b'\x00\x00\x15\x11hStdInput',b'\x00\x00\x15\x11hStdOutput',b'\x00\x00\x15\x11hStdError'),(b'\x00\x00\x00\xE2\x00\x00\x00\x02_OVERLAPPED',b'\x00\x00\x18\x11Internal',b'\x00\x00\x18\x11InternalHigh',b'\x00\x00\xEC\x11DUMMYUNIONNAME',b'\x00\x00\x15\x11hEvent'),(b'\x00\x00\x00\xE5\x00\x00\x00\x02_PostCallbackData',b'\x00\x00\x15\x11hCompletionPort',b'\x00\x00\x31\x11Overlapped'),(b'\x00\x00\x00\xE6\x00\x00\x00\x02_SECURITY_ATTRIBUTES',b'\x00\x00\x18\x11nLength',b'\x00\x00\x15\x11lpSecurityDescriptor',b'\x00\x00\x01\x11bInheritHandle'),(b'\x00\x00\x00\xE8\x00\x00\x00\x02_WSABUF',b'\x00\x00\x18\x11len',b'\x00\x00\x13\x11buf')), + _typenames = (b'\x00\x00\x00\xEALPFN_DISCONNECTEX',b'\x00\x00\x00\x31LPOVERLAPPED',b'\x00\x00\x00\x46LPOVERLAPPED_COMPLETION_ROUTINE',b'\x00\x00\x00\x1CLPPROCESS_INFORMATION',b'\x00\x00\x00\xE4LPPostCallbackData',b'\x00\x00\x00\x92LPSECURITY_ATTRIBUTES',b'\x00\x00\x00\x1BLPSTARTUPINFO',b'\x00\x00\x00\x41LPWSABUF',b'\x00\x00\x00\xE2OVERLAPPED',b'\x00\x00\x00\xE3PROCESS_INFORMATION',b'\x00\x00\x00\x92PSECURITY_ATTRIBUTES',b'\x00\x00\x00\xE5PostCallbackData',b'\x00\x00\x00\xE6SECURITY_ATTRIBUTES',b'\x00\x00\x00\x15SOCKET',b'\x00\x00\x00\xE7STARTUPINFO',b'\x00\x00\x00\x27WAITORTIMERCALLBACK',b'\x00\x00\x00\xE8WSABUF',b'\x00\x00\x00\x8Awint_t'), ) diff --git a/lib_pypy/_winapi.py b/lib_pypy/_winapi.py --- a/lib_pypy/_winapi.py +++ b/lib_pypy/_winapi.py @@ -222,6 +222,8 @@ PIPE_ACCEPT_REMOTE_CLIENTS = 0x00000000 PIPE_REJECT_REMOTE_CLIENTS = 0x00000008 +PIPE_UNLIMITED_INSTANCES = 255 + GENERIC_READ = 0x80000000 GENERIC_WRITE = 0x40000000 GENERIC_EXECUTE= 0x20000000 From pypy.commits at gmail.com Wed Feb 27 10:17:03 2019 From: pypy.commits at gmail.com (stevie_92) Date: Wed, 27 Feb 2019 07:17:03 -0800 (PST) Subject: [pypy-commit] pypy cpyext-gc-cycle: Implemented interface for gc.garbage list Message-ID: <5c76a9ef.1c69fb81.d441d.335f@mx.google.com> Author: Stefan Beyer Branch: cpyext-gc-cycle Changeset: r96185:38a9f6496360 Date: 2019-02-23 15:34 +0100 http://bitbucket.org/pypy/pypy/changeset/38a9f6496360/ Log: Implemented interface for gc.garbage list diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py --- a/pypy/module/cpyext/state.py +++ b/pypy/module/cpyext/state.py @@ -78,7 +78,9 @@ if not self.space.config.translating: def dealloc_trigger(): from pypy.module.cpyext.pyobject import PyObject, decref, \ - incref, cts, finalize + incref, cts, finalize, from_ref + w_list = space.getattr(space.builtin_modules['gc'], + space.newtext('garbage')) print 'dealloc_trigger...' while True: ob = rawrefcount.next_dead(PyObject) @@ -113,6 +115,12 @@ if adr_int == llmemory.cast_adr_to_int( llmemory.cast_ptr_to_adr(head)): rawrefcount.cyclic_garbage_remove() + while True: + py_obj = rawrefcount.next_garbage(PyObject) + if not py_obj: + break + w_obj = from_ref(space, py_obj) + w_list.append(w_obj) print 'dealloc_trigger DONE' return "RETRY" def tp_traverse(pyobj_ptr, callback, args): @@ -274,7 +282,12 @@ def _rawrefcount_perform(space): - from pypy.module.cpyext.pyobject import PyObject, incref, decref, finalize + from pypy.module.cpyext.pyobject import (PyObject, incref, decref, + finalize, from_ref) + + w_list = space.getattr(space.builtin_modules['gc'], + space.newtext('garbage')) + while True: py_obj = rawrefcount.next_dead(PyObject) if not py_obj: @@ -291,18 +304,23 @@ py_obj = rawrefcount.cyclic_garbage_head(PyObject) if not py_obj: break - pyobj = rffi.cast(PyObject, py_obj) adr_int = llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(pyobj)) if pyobj.c_ob_type.c_tp_clear: incref(space, py_obj) pyobj.c_ob_type.c_tp_clear(pyobj) decref(space, py_obj) - head = rawrefcount.cyclic_garbage_head(PyObject) if adr_int == llmemory.cast_adr_to_int(llmemory.cast_ptr_to_adr(head)): rawrefcount.cyclic_garbage_remove() + while True: + py_obj = rawrefcount.next_garbage(PyObject) + if not py_obj: + break + w_obj = from_ref(space, py_obj) + w_list.append(w_obj) + class PyObjDeallocAction(executioncontext.AsyncAction): """An action that invokes _Py_Dealloc() on the dying PyObjects. """ diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py --- a/rpython/memory/gc/incminimark.py +++ b/rpython/memory/gc/incminimark.py @@ -3217,6 +3217,10 @@ gchdr.c_gc_next = next next.c_gc_prev = gchdr + def rawrefcount_next_garbage(self): + return llmemory.NULL + + def rrc_invoke_callback(self): if self.rrc_enabled and (self.rrc_dealloc_pending.non_empty() or self.rrc_pyobj_isolate_list.c_gc_next <> diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py --- a/rpython/memory/gctransform/framework.py +++ b/rpython/memory/gctransform/framework.py @@ -518,6 +518,9 @@ self.rawrefcount_cyclic_garbage_remove_ptr = getfn( GCClass.rawrefcount_cyclic_garbage_remove, [s_gc], annmodel.s_None, inline = True) + self.rawrefcount_next_garbage_ptr = getfn( + GCClass.rawrefcount_next_garbage, [s_gc], + SomeAddress(), inline = True) if GCClass.can_usually_pin_objects: self.pin_ptr = getfn(GCClass.pin, @@ -1426,6 +1429,12 @@ [self.rawrefcount_cyclic_garbage_remove_ptr, self.c_const_gc]) + def gct_gc_rawrefcount_next_garbage(self, hop): + assert hop.spaceop.result.concretetype == llmemory.Address + hop.genop("direct_call", + [self.rawrefcount_next_garbage_ptr, self.c_const_gc], + resultvar=hop.spaceop.result) + def _set_into_gc_array_part(self, op): if op.opname == 'setarrayitem': return op.args[1] diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py --- a/rpython/rlib/rawrefcount.py +++ b/rpython/rlib/rawrefcount.py @@ -146,6 +146,10 @@ return lltype.nullptr(OB_PTR_TYPE.TO) @not_rpython +def next_garbage(OB_PTR_TYPE): + return lltype.nullptr(OB_PTR_TYPE.TO) + + at not_rpython def _collect(track_allocation=True): """for tests only. Emulates a GC collection. Will invoke dealloc_trigger_callback() once if there are objects @@ -367,7 +371,8 @@ return _spec_p(hop, v_p) class Entry(ExtRegistryEntry): - _about_ = (next_dead, cyclic_garbage_head, next_cyclic_isolate) + _about_ = (next_dead, cyclic_garbage_head, next_cyclic_isolate, + next_garbage) def compute_result_annotation(self, s_OB_PTR_TYPE): from rpython.rtyper.llannotation import lltype_to_annotation @@ -381,6 +386,8 @@ name = 'gc_rawrefcount_cyclic_garbage_head' elif self.instance is next_cyclic_isolate: name = 'gc_rawrefcount_next_cyclic_isolate' + elif self.instance is next_garbage: + name = 'gc_rawrefcount_next_garbage' hop.exception_cannot_occur() v_ob = hop.genop(name, [], resulttype = llmemory.Address) return _spec_ob(hop, v_ob) diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py --- a/rpython/rtyper/llinterp.py +++ b/rpython/rtyper/llinterp.py @@ -995,6 +995,9 @@ def op_gc_rawrefcount_cyclic_garbage_remove(self, *args): raise NotImplementedError("gc_rawrefcount_cyclic_garbage_remove") + def op_gc_rawrefcount_next_garbage(self, *args): + raise NotImplementedError("gc_rawrefcount_next_garbage") + def op_do_malloc_fixedsize(self): raise NotImplementedError("do_malloc_fixedsize") def op_do_malloc_fixedsize_clear(self): diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py --- a/rpython/rtyper/lltypesystem/lloperation.py +++ b/rpython/rtyper/lltypesystem/lloperation.py @@ -532,6 +532,7 @@ 'gc_rawrefcount_next_cyclic_isolate': LLOp(), 'gc_rawrefcount_cyclic_garbage_head': LLOp(sideeffects=False), 'gc_rawrefcount_cyclic_garbage_remove': LLOp(), + 'gc_rawrefcount_next_garbage': LLOp(), 'gc_move_out_of_nursery': LLOp(), From pypy.commits at gmail.com Wed Feb 27 10:53:54 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 27 Feb 2019 07:53:54 -0800 (PST) Subject: [pypy-commit] pypy default: test (fails on py3) to check for the bytecode optimization of constant tuples Message-ID: <5c76b292.1c69fb81.6caf2.a9ce@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: Changeset: r96186:4d37ac959895 Date: 2019-02-27 10:46 +0100 http://bitbucket.org/pypy/pypy/changeset/4d37ac959895/ Log: test (fails on py3) to check for the bytecode optimization of constant tuples diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py --- a/pypy/interpreter/astcompiler/test/test_compiler.py +++ b/pypy/interpreter/astcompiler/test/test_compiler.py @@ -1204,3 +1204,17 @@ source = 'def f(): %s' % source counts = self.count_instructions(source) assert ops.BINARY_POWER not in counts + + def test_constant_tuples(self): + source = """def f(): + return ((u"a", 1), 2) + """ + counts = self.count_instructions(source) + assert ops.BUILD_TUPLE not in counts + # also for bytes + source = """def f(): + return ((b"a", 5), 5, 7, 8) + """ + counts = self.count_instructions(source) + assert ops.BUILD_TUPLE not in counts + From pypy.commits at gmail.com Wed Feb 27 10:53:58 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 27 Feb 2019 07:53:58 -0800 (PST) Subject: [pypy-commit] pypy py3.6: merge default Message-ID: <5c76b296.1c69fb81.2f6b0.2977@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96187:8317fe5138ca Date: 2019-02-27 10:47 +0100 http://bitbucket.org/pypy/pypy/changeset/8317fe5138ca/ Log: merge default diff too long, truncating to 2000 out of 203255 lines diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py --- a/pypy/interpreter/astcompiler/test/test_compiler.py +++ b/pypy/interpreter/astcompiler/test/test_compiler.py @@ -1528,3 +1528,18 @@ a: list = [j for j in range(10)] """ generate_function_code(source, self.space) + + def test_constant_tuples(self): + source = """def f(): + return ((u"a", 1), 2) + """ + counts = self.count_instructions(source) + assert ops.BUILD_TUPLE not in counts + # also for bytes + source = """def f(): + return ((b"a", 5), 5, 7, 8) + """ + counts = self.count_instructions(source) + assert ops.BUILD_TUPLE not in counts + + diff --git a/rpython/rlib/unicodedata/generate_unicodedb.py b/rpython/rlib/unicodedata/generate_unicodedb.py old mode 100755 new mode 100644 --- a/rpython/rlib/unicodedata/generate_unicodedb.py +++ b/rpython/rlib/unicodedata/generate_unicodedb.py @@ -505,43 +505,36 @@ if base_mod is None: triegenerator.build_compression_tree(outfile, names) print >> outfile, "# the following dictionary is used by modules that take this as a base" + print >> outfile, "# only used by generate_unicodedb, not after translation" print >> outfile, "_orig_names = {" for name, code in sorted_names_codes: print >> outfile, "%r: %r," % (name, code) print >> outfile, "}" else: - print >> outfile, '_names = {' + corrected_names = [] + for name, code in sorted_names_codes: try: if base_mod.lookup_charcode(code) == name: continue except KeyError: pass - print >> outfile, '%r: %r,' % (code, name) - print >> outfile, '}' + corrected_names.append((name, code)) + corrected_names_dict = dict(corrected_names) + triegenerator.build_compression_tree(outfile, corrected_names_dict) - - print >> outfile, '_names_corrected = {' + removed_names = [] for name, code in sorted(base_mod._orig_names.iteritems()): if name not in names: - print >> outfile, '%r: None,' % code - print >> outfile, '}' - - print >> outfile, '_code_by_name = {' - corrected = {} - for name, code in sorted_names_codes: - try: - if base_mod.lookup_charcode(code) == name: - continue - except KeyError: - pass - print >> outfile, '%r: %r,' % (name, code) + removed_names.append((name, code)) + print >> outfile, '_names_corrected = {' + for name, code in removed_names: + print >> outfile, '%r: None,' % code print >> outfile, '}' print >> outfile, '_code_by_name_corrected = {' - for name, code in sorted(base_mod._orig_names.iteritems()): - if name not in names: - print >> outfile, '%r: None,' % name + for name, code in removed_names: + print >> outfile, '%r: None,' % name print >> outfile, '}' @@ -657,7 +650,7 @@ code = trie_lookup(name) else: try: - code = _code_by_name[name] + code = trie_lookup(name) except KeyError: if name not in _code_by_name_corrected: code = base_mod.trie_lookup(name) @@ -686,7 +679,7 @@ return lookup_charcode(code) else: try: - return _names[code] + return lookup_charcode(code) except KeyError: if code not in _names_corrected: return base_mod.lookup_charcode(code) @@ -895,7 +888,7 @@ else: return None ''' % dict(start=table.NAMED_SEQUENCES_START) - + # aliases print >> outfile, '_name_aliases = [' for name, char in table.aliases: diff --git a/rpython/rlib/unicodedata/triegenerator.py b/rpython/rlib/unicodedata/triegenerator.py --- a/rpython/rlib/unicodedata/triegenerator.py +++ b/rpython/rlib/unicodedata/triegenerator.py @@ -76,7 +76,9 @@ charnode = 0 while 0 <= charnode < 0xffff: # 16bit number, 0xffff = None charnode *= 3 - leftright, parentstr, codepoint = _charnodes[charnode:charnode+3] + leftright = _charnodes[charnode] + parentstr = _charnodes[charnode + 1] + codepoint = _charnodes[charnode + 2] if leftright < 0: # XXX assumes msb is sign @@ -91,7 +93,7 @@ else: parent = (parentstr & 0x7fffffff) >> %(STRIDXBITS)d stridx = parentstr & ((1 << %(STRIDXBITS)d) - 1) - + strlen = ord(_stringtable[stridx]) substring = _stringtable[stridx+1:stridx+1+strlen] @@ -109,7 +111,9 @@ prevnode = -1 while 0 <= charnode < 0xffff: # 16bit number, 0xffff = None charnode *= 3 - leftright, parentstr, codepoint = _charnodes[charnode:charnode+3] + leftright = _charnodes[charnode] + parentstr = _charnodes[charnode + 1] + codepoint = _charnodes[charnode + 2] if leftright < 0: # XXX assumes msg is sign @@ -128,13 +132,14 @@ stridx = parentstr & ((1<<%(STRIDXBITS)d)-1) strlen = ord(_stringtable[stridx]) substring = _stringtable[stridx+1:stridx+1+strlen] - res.insert(0, substring) + res.append(substring) prevnode = charnode // 3 charnode = parent + res.reverse() return ''.join(res) - + """ % globals() def findranges(d): @@ -165,6 +170,11 @@ return collapsed def build_compression_tree(outfile, ucdata): + print >> outfile, "#" + "_" * 60 + print >> outfile, "# output from build_compression_tree" + if not ucdata: + print >> outfile, empty_trie_functions + return print >> outfile, classdef reversedict = {} @@ -180,7 +190,7 @@ print >> outfile, "%r" % (chr(strlen) + string) stringidx[string] = stridx stridx += strlen + 1 - + print >> outfile, ")" assert stridx < (1< %d chars" % ( @@ -190,7 +200,7 @@ nodelist = [] maxidx = 0 nodes = [rootnode] - + while nodes: n = nodes.pop() nodelist.append(n) @@ -198,28 +208,31 @@ nodes.append(n.left) if n.right: nodes.append(n.right) - + nodelist.sort(key=lambda x: x.index) newnodes = [] map(newnodes.extend, (n.as_list(stringidx) for n in nodelist)) print >> outfile, "_charnodes =", pprint.pprint(newnodes, stream=outfile) - + function = ["def lookup_charcode(code):", " res = -1"] ranges = collapse_ranges(findranges(reversedict)) + prefix = "" for low, high in ranges: if high - low <= MINLIST: for code in range(low, high + 1): if code in reversedict: function.append( - " if code == %d: res = %s" % - (code, reversedict[code].index)) + " %sif code == %d: res = %s" % + (prefix, code, reversedict[code].index)) + prefix = "el" continue function.append( - " if %d <= code <= %d: res = _charnames_%d[code-%d]" % ( - low, high, low, low)) + " %sif %d <= code <= %d: res = _charnames_%d[code-%d]" % ( + prefix, low, high, low, low)) + prefix = "el" print >> outfile, "_charnames_%d = [" % (low,) for code in range(low, high + 1): @@ -234,6 +247,8 @@ "", ]) print >> outfile, '\n'.join(function) + print >> outfile, "# end output from build_compression_tree" + print >> outfile, "#" + "_" * 60 return rootnode def gen_compression_tree(stringlist, ucdata, reversedict, parent=None, parent_str="", left=False): @@ -244,8 +259,8 @@ for string in stringlist: for stop in range(1, len(string) + 1): codes[string[:stop]] = codes.get(string[:stop], 0) + 1 - - s = [((freq), code) for (code, freq) in codes.iteritems()] + + s = [((freq), code) for (code, freq) in codes.iteritems()] s.sort() if not s: return None @@ -306,3 +321,10 @@ import sys build_compression_tree(sys.stdout, testdata) + +empty_trie_functions = """ +def trie_lookup(name): + raise KeyError +def lookup_charcode(code): + raise KeyError +""" diff --git a/rpython/rlib/unicodedata/unicodedb_3_2_0.py b/rpython/rlib/unicodedata/unicodedb_3_2_0.py --- a/rpython/rlib/unicodedata/unicodedb_3_2_0.py +++ b/rpython/rlib/unicodedata/unicodedb_3_2_0.py @@ -8,8 +8,14 @@ import unicodedb_5_2_0 as base_mod version = '3.2.0' -_names = { -} +#____________________________________________________________ +# output from build_compression_tree + +def trie_lookup(name): + raise KeyError +def lookup_charcode(code): + raise KeyError + _names_corrected = { 9190: None, 65794: None, @@ -8370,8 +8376,6 @@ 11825: None, 983048: None, } -_code_by_name = { -} _code_by_name_corrected = { 'AC CURRENT': None, 'AEGEAN CHECK MARK': None, @@ -16798,7 +16802,7 @@ code = trie_lookup(name) else: try: - code = _code_by_name[name] + code = trie_lookup(name) except KeyError: if name not in _code_by_name_corrected: code = base_mod.trie_lookup(name) @@ -16827,7 +16831,7 @@ return lookup_charcode(code) else: try: - return _names[code] + return lookup_charcode(code) except KeyError: if code not in _names_corrected: return base_mod.lookup_charcode(code) diff --git a/rpython/rlib/unicodedata/unicodedb_5_2_0.py b/rpython/rlib/unicodedata/unicodedb_5_2_0.py --- a/rpython/rlib/unicodedata/unicodedb_5_2_0.py +++ b/rpython/rlib/unicodedata/unicodedb_5_2_0.py @@ -8,12 +8,16 @@ base_mod = None version = '5.2.0' +#____________________________________________________________ +# output from build_compression_tree def trie_lookup(name): charnode = 0 while 0 <= charnode < 0xffff: # 16bit number, 0xffff = None charnode *= 3 - leftright, parentstr, codepoint = _charnodes[charnode:charnode+3] + leftright = _charnodes[charnode] + parentstr = _charnodes[charnode + 1] + codepoint = _charnodes[charnode + 2] if leftright < 0: # XXX assumes msb is sign @@ -28,7 +32,7 @@ else: parent = (parentstr & 0x7fffffff) >> 16 stridx = parentstr & ((1 << 16) - 1) - + strlen = ord(_stringtable[stridx]) substring = _stringtable[stridx+1:stridx+1+strlen] @@ -46,7 +50,9 @@ prevnode = -1 while 0 <= charnode < 0xffff: # 16bit number, 0xffff = None charnode *= 3 - leftright, parentstr, codepoint = _charnodes[charnode:charnode+3] + leftright = _charnodes[charnode] + parentstr = _charnodes[charnode + 1] + codepoint = _charnodes[charnode + 2] if leftright < 0: # XXX assumes msg is sign @@ -65,13 +71,14 @@ stridx = parentstr & ((1<<16)-1) strlen = ord(_stringtable[stridx]) substring = _stringtable[stridx+1:stridx+1+strlen] - res.insert(0, substring) + res.append(substring) prevnode = charnode // 3 charnode = parent + res.reverse() return ''.join(res) - + _stringtable = ( '\x01 ' @@ -114455,149 +114462,152 @@ def lookup_charcode(code): res = -1 if 32 <= code <= 126: res = _charnames_32[code-32] - if 160 <= code <= 1317: res = _charnames_160[code-160] - if 1329 <= code <= 1479: res = _charnames_1329[code-1329] - if 1488 <= code <= 1524: res = _charnames_1488[code-1488] - if 1536 <= code <= 1969: res = _charnames_1536[code-1536] - if 1984 <= code <= 2110: res = _charnames_1984[code-1984] - if 2304 <= code <= 2510: res = _charnames_2304[code-2304] - if 2519 <= code <= 2641: res = _charnames_2519[code-2519] - if code == 2649: res = 22369 - if code == 2650: res = 22374 - if code == 2651: res = 22402 - if code == 2652: res = 22383 - if code == 2654: res = 22408 - if 2662 <= code <= 2677: res = _charnames_2662[code-2662] - if 2689 <= code <= 2768: res = _charnames_2689[code-2689] - if 2784 <= code <= 2801: res = _charnames_2784[code-2784] - if 2817 <= code <= 2893: res = _charnames_2817[code-2817] - if 2902 <= code <= 2929: res = _charnames_2902[code-2902] - if 2946 <= code <= 3031: res = _charnames_2946[code-2946] - if 3046 <= code <= 3149: res = _charnames_3046[code-3046] - if 3157 <= code <= 3183: res = _charnames_3157[code-3157] - if 3192 <= code <= 3277: res = _charnames_3192[code-3192] - if code == 3285: res = 23172 - if code == 3286: res = 23215 - if 3294 <= code <= 3314: res = _charnames_3294[code-3294] - if 3330 <= code <= 3405: res = _charnames_3330[code-3330] - if code == 3415: res = 6443 - if 3424 <= code <= 3551: res = _charnames_3424[code-3424] - if code == 3570: res = 19930 - if code == 3571: res = 19929 - if code == 3572: res = 19944 - if 3585 <= code <= 3675: res = _charnames_3585[code-3585] - if 3713 <= code <= 3805: res = _charnames_3713[code-3713] - if 3840 <= code <= 4056: res = _charnames_3840[code-3840] - if 4096 <= code <= 4293: res = _charnames_4096[code-4096] - if 4304 <= code <= 5108: res = _charnames_4304[code-4304] - if 5120 <= code <= 5872: res = _charnames_5120[code-5120] - if 5888 <= code <= 5908: res = _charnames_5888[code-5888] - if 5920 <= code <= 5942: res = _charnames_5920[code-5920] - if 5952 <= code <= 5971: res = _charnames_5952[code-5952] - if 5984 <= code <= 6003: res = _charnames_5984[code-5984] - if 6016 <= code <= 6263: res = _charnames_6016[code-6016] - if 6272 <= code <= 6389: res = _charnames_6272[code-6272] - if 6400 <= code <= 6516: res = _charnames_6400[code-6400] - if 6528 <= code <= 6829: res = _charnames_6528[code-6528] - if 6912 <= code <= 7097: res = _charnames_6912[code-6912] - if 7168 <= code <= 7295: res = _charnames_7168[code-7168] - if 7376 <= code <= 7410: res = _charnames_7376[code-7376] - if 7424 <= code <= 7654: res = _charnames_7424[code-7424] - if 7677 <= code <= 8340: res = _charnames_7677[code-7677] - if 8352 <= code <= 8376: res = _charnames_8352[code-8352] - if 8400 <= code <= 8432: res = _charnames_8400[code-8400] - if 8448 <= code <= 9192: res = _charnames_8448[code-8448] - if 9216 <= code <= 9254: res = _charnames_9216[code-9216] - if 9280 <= code <= 9290: res = _charnames_9280[code-9280] - if 9312 <= code <= 11097: res = _charnames_9312[code-9312] - if 11264 <= code <= 11505: res = _charnames_11264[code-11264] - if 11513 <= code <= 11557: res = _charnames_11513[code-11513] - if 11568 <= code <= 11621: res = _charnames_11568[code-11568] - if code == 11631: res = 13355 - if 11648 <= code <= 11670: res = _charnames_11648[code-11648] - if 11680 <= code <= 11825: res = _charnames_11680[code-11680] - if 11904 <= code <= 12019: res = _charnames_11904[code-11904] - if 12032 <= code <= 12245: res = _charnames_12032[code-12032] - if 12272 <= code <= 12727: res = _charnames_12272[code-12272] - if 12736 <= code <= 12771: res = _charnames_12736[code-12736] - if 12784 <= code <= 13311: res = _charnames_12784[code-12784] - if 19904 <= code <= 19967: res = _charnames_19904[code-19904] - if 40960 <= code <= 42182: res = _charnames_40960[code-40960] - if 42192 <= code <= 42539: res = _charnames_42192[code-42192] - if 42560 <= code <= 42611: res = _charnames_42560[code-42560] - if 42620 <= code <= 42647: res = _charnames_42620[code-42620] - if 42656 <= code <= 42743: res = _charnames_42656[code-42656] - if 42752 <= code <= 42892: res = _charnames_42752[code-42752] - if 43003 <= code <= 43127: res = _charnames_43003[code-43003] - if 43136 <= code <= 43204: res = _charnames_43136[code-43136] - if 43214 <= code <= 43347: res = _charnames_43214[code-43214] - if 43359 <= code <= 43487: res = _charnames_43359[code-43359] - if 43520 <= code <= 43574: res = _charnames_43520[code-43520] - if 43584 <= code <= 43714: res = _charnames_43584[code-43584] - if code == 43739: res = 12781 - if code == 43740: res = 12784 - if code == 43741: res = 12783 - if code == 43742: res = 12785 - if code == 43743: res = 12782 - if 43968 <= code <= 44025: res = _charnames_43968[code-43968] - if 55216 <= code <= 55291: res = _charnames_55216[code-55216] - if 63744 <= code <= 64217: res = _charnames_63744[code-63744] - if 64256 <= code <= 64262: res = _charnames_64256[code-64256] - if 64275 <= code <= 64433: res = _charnames_64275[code-64275] - if 64467 <= code <= 64831: res = _charnames_64467[code-64467] - if 64848 <= code <= 64967: res = _charnames_64848[code-64848] - if 65008 <= code <= 65062: res = _charnames_65008[code-65008] - if 65072 <= code <= 65518: res = _charnames_65072[code-65072] - if 65529 <= code <= 65629: res = _charnames_65529[code-65529] - if 65664 <= code <= 65947: res = _charnames_65664[code-65664] - if 66000 <= code <= 66045: res = _charnames_66000[code-66000] - if 66176 <= code <= 66256: res = _charnames_66176[code-66176] - if 66304 <= code <= 66339: res = _charnames_66304[code-66304] - if 66352 <= code <= 66378: res = _charnames_66352[code-66352] - if 66432 <= code <= 66517: res = _charnames_66432[code-66432] - if 66560 <= code <= 66729: res = _charnames_66560[code-66560] - if 67584 <= code <= 67679: res = _charnames_67584[code-67584] - if 67840 <= code <= 67903: res = _charnames_67840[code-67840] - if 68096 <= code <= 68167: res = _charnames_68096[code-68096] - if 68176 <= code <= 68184: res = _charnames_68176[code-68176] - if 68192 <= code <= 68223: res = _charnames_68192[code-68192] - if 68352 <= code <= 68479: res = _charnames_68352[code-68352] - if 68608 <= code <= 68680: res = _charnames_68608[code-68608] - if 69216 <= code <= 69246: res = _charnames_69216[code-69216] - if 69760 <= code <= 69825: res = _charnames_69760[code-69760] - if 73728 <= code <= 74606: res = _charnames_73728[code-73728] - if 74752 <= code <= 74850: res = _charnames_74752[code-74752] - if code == 74864: res = 2527 - if code == 74865: res = 2526 - if code == 74866: res = 2525 - if code == 74867: res = 2524 - if 77824 <= code <= 78894: res = _charnames_77824[code-77824] - if 118784 <= code <= 119029: res = _charnames_118784[code-118784] - if 119040 <= code <= 119261: res = _charnames_119040[code-119040] - if 119296 <= code <= 119365: res = _charnames_119296[code-119296] - if 119552 <= code <= 119638: res = _charnames_119552[code-119552] - if 119648 <= code <= 119665: res = _charnames_119648[code-119648] - if 119808 <= code <= 120831: res = _charnames_119808[code-119808] - if 126976 <= code <= 127123: res = _charnames_126976[code-126976] - if 127232 <= code <= 127281: res = _charnames_127232[code-127232] - if 127293 <= code <= 127310: res = _charnames_127293[code-127293] - if code == 127319: res = 27110 - if code == 127327: res = 27109 - if 127353 <= code <= 127359: res = _charnames_127353[code-127353] - if 127370 <= code <= 127376: res = _charnames_127370[code-127370] - if code == 127488: res = 19008 - if 127504 <= code <= 127537: res = _charnames_127504[code-127504] - if 127552 <= code <= 127560: res = _charnames_127552[code-127552] - if 194560 <= code <= 195101: res = _charnames_194560[code-194560] - if code == 917505: res = 9289 - if 917536 <= code <= 917631: res = _charnames_917536[code-917536] - if 917760 <= code <= 917999: res = _charnames_917760[code-917760] - if 983040 <= code <= 983050: res = _charnames_983040[code-983040] - if 983552 <= code <= 983945: res = _charnames_983552[code-983552] + elif 160 <= code <= 1317: res = _charnames_160[code-160] + elif 1329 <= code <= 1479: res = _charnames_1329[code-1329] + elif 1488 <= code <= 1524: res = _charnames_1488[code-1488] + elif 1536 <= code <= 1969: res = _charnames_1536[code-1536] + elif 1984 <= code <= 2110: res = _charnames_1984[code-1984] + elif 2304 <= code <= 2510: res = _charnames_2304[code-2304] + elif 2519 <= code <= 2641: res = _charnames_2519[code-2519] + elif code == 2649: res = 22369 + elif code == 2650: res = 22374 + elif code == 2651: res = 22402 + elif code == 2652: res = 22383 + elif code == 2654: res = 22408 + elif 2662 <= code <= 2677: res = _charnames_2662[code-2662] + elif 2689 <= code <= 2768: res = _charnames_2689[code-2689] + elif 2784 <= code <= 2801: res = _charnames_2784[code-2784] + elif 2817 <= code <= 2893: res = _charnames_2817[code-2817] + elif 2902 <= code <= 2929: res = _charnames_2902[code-2902] + elif 2946 <= code <= 3031: res = _charnames_2946[code-2946] + elif 3046 <= code <= 3149: res = _charnames_3046[code-3046] + elif 3157 <= code <= 3183: res = _charnames_3157[code-3157] + elif 3192 <= code <= 3277: res = _charnames_3192[code-3192] + elif code == 3285: res = 23172 + elif code == 3286: res = 23215 + elif 3294 <= code <= 3314: res = _charnames_3294[code-3294] + elif 3330 <= code <= 3405: res = _charnames_3330[code-3330] + elif code == 3415: res = 6443 + elif 3424 <= code <= 3551: res = _charnames_3424[code-3424] + elif code == 3570: res = 19930 + elif code == 3571: res = 19929 + elif code == 3572: res = 19944 + elif 3585 <= code <= 3675: res = _charnames_3585[code-3585] + elif 3713 <= code <= 3805: res = _charnames_3713[code-3713] + elif 3840 <= code <= 4056: res = _charnames_3840[code-3840] + elif 4096 <= code <= 4293: res = _charnames_4096[code-4096] + elif 4304 <= code <= 5108: res = _charnames_4304[code-4304] + elif 5120 <= code <= 5872: res = _charnames_5120[code-5120] + elif 5888 <= code <= 5908: res = _charnames_5888[code-5888] + elif 5920 <= code <= 5942: res = _charnames_5920[code-5920] + elif 5952 <= code <= 5971: res = _charnames_5952[code-5952] + elif 5984 <= code <= 6003: res = _charnames_5984[code-5984] + elif 6016 <= code <= 6263: res = _charnames_6016[code-6016] + elif 6272 <= code <= 6389: res = _charnames_6272[code-6272] + elif 6400 <= code <= 6516: res = _charnames_6400[code-6400] + elif 6528 <= code <= 6829: res = _charnames_6528[code-6528] + elif 6912 <= code <= 7097: res = _charnames_6912[code-6912] + elif 7168 <= code <= 7295: res = _charnames_7168[code-7168] + elif 7376 <= code <= 7410: res = _charnames_7376[code-7376] + elif 7424 <= code <= 7654: res = _charnames_7424[code-7424] + elif 7677 <= code <= 8340: res = _charnames_7677[code-7677] + elif 8352 <= code <= 8376: res = _charnames_8352[code-8352] + elif 8400 <= code <= 8432: res = _charnames_8400[code-8400] + elif 8448 <= code <= 9192: res = _charnames_8448[code-8448] + elif 9216 <= code <= 9254: res = _charnames_9216[code-9216] + elif 9280 <= code <= 9290: res = _charnames_9280[code-9280] + elif 9312 <= code <= 11097: res = _charnames_9312[code-9312] + elif 11264 <= code <= 11505: res = _charnames_11264[code-11264] + elif 11513 <= code <= 11557: res = _charnames_11513[code-11513] + elif 11568 <= code <= 11621: res = _charnames_11568[code-11568] + elif code == 11631: res = 13355 + elif 11648 <= code <= 11670: res = _charnames_11648[code-11648] + elif 11680 <= code <= 11825: res = _charnames_11680[code-11680] + elif 11904 <= code <= 12019: res = _charnames_11904[code-11904] + elif 12032 <= code <= 12245: res = _charnames_12032[code-12032] + elif 12272 <= code <= 12727: res = _charnames_12272[code-12272] + elif 12736 <= code <= 12771: res = _charnames_12736[code-12736] + elif 12784 <= code <= 13311: res = _charnames_12784[code-12784] + elif 19904 <= code <= 19967: res = _charnames_19904[code-19904] + elif 40960 <= code <= 42182: res = _charnames_40960[code-40960] + elif 42192 <= code <= 42539: res = _charnames_42192[code-42192] + elif 42560 <= code <= 42611: res = _charnames_42560[code-42560] + elif 42620 <= code <= 42647: res = _charnames_42620[code-42620] + elif 42656 <= code <= 42743: res = _charnames_42656[code-42656] + elif 42752 <= code <= 42892: res = _charnames_42752[code-42752] + elif 43003 <= code <= 43127: res = _charnames_43003[code-43003] + elif 43136 <= code <= 43204: res = _charnames_43136[code-43136] + elif 43214 <= code <= 43347: res = _charnames_43214[code-43214] + elif 43359 <= code <= 43487: res = _charnames_43359[code-43359] + elif 43520 <= code <= 43574: res = _charnames_43520[code-43520] + elif 43584 <= code <= 43714: res = _charnames_43584[code-43584] + elif code == 43739: res = 12781 + elif code == 43740: res = 12784 + elif code == 43741: res = 12783 + elif code == 43742: res = 12785 + elif code == 43743: res = 12782 + elif 43968 <= code <= 44025: res = _charnames_43968[code-43968] + elif 55216 <= code <= 55291: res = _charnames_55216[code-55216] + elif 63744 <= code <= 64217: res = _charnames_63744[code-63744] + elif 64256 <= code <= 64262: res = _charnames_64256[code-64256] + elif 64275 <= code <= 64433: res = _charnames_64275[code-64275] + elif 64467 <= code <= 64831: res = _charnames_64467[code-64467] + elif 64848 <= code <= 64967: res = _charnames_64848[code-64848] + elif 65008 <= code <= 65062: res = _charnames_65008[code-65008] + elif 65072 <= code <= 65518: res = _charnames_65072[code-65072] + elif 65529 <= code <= 65629: res = _charnames_65529[code-65529] + elif 65664 <= code <= 65947: res = _charnames_65664[code-65664] + elif 66000 <= code <= 66045: res = _charnames_66000[code-66000] + elif 66176 <= code <= 66256: res = _charnames_66176[code-66176] + elif 66304 <= code <= 66339: res = _charnames_66304[code-66304] + elif 66352 <= code <= 66378: res = _charnames_66352[code-66352] + elif 66432 <= code <= 66517: res = _charnames_66432[code-66432] + elif 66560 <= code <= 66729: res = _charnames_66560[code-66560] + elif 67584 <= code <= 67679: res = _charnames_67584[code-67584] + elif 67840 <= code <= 67903: res = _charnames_67840[code-67840] + elif 68096 <= code <= 68167: res = _charnames_68096[code-68096] + elif 68176 <= code <= 68184: res = _charnames_68176[code-68176] + elif 68192 <= code <= 68223: res = _charnames_68192[code-68192] + elif 68352 <= code <= 68479: res = _charnames_68352[code-68352] + elif 68608 <= code <= 68680: res = _charnames_68608[code-68608] + elif 69216 <= code <= 69246: res = _charnames_69216[code-69216] + elif 69760 <= code <= 69825: res = _charnames_69760[code-69760] + elif 73728 <= code <= 74606: res = _charnames_73728[code-73728] + elif 74752 <= code <= 74850: res = _charnames_74752[code-74752] + elif code == 74864: res = 2527 + elif code == 74865: res = 2526 + elif code == 74866: res = 2525 + elif code == 74867: res = 2524 + elif 77824 <= code <= 78894: res = _charnames_77824[code-77824] + elif 118784 <= code <= 119029: res = _charnames_118784[code-118784] + elif 119040 <= code <= 119261: res = _charnames_119040[code-119040] + elif 119296 <= code <= 119365: res = _charnames_119296[code-119296] + elif 119552 <= code <= 119638: res = _charnames_119552[code-119552] + elif 119648 <= code <= 119665: res = _charnames_119648[code-119648] + elif 119808 <= code <= 120831: res = _charnames_119808[code-119808] + elif 126976 <= code <= 127123: res = _charnames_126976[code-126976] + elif 127232 <= code <= 127281: res = _charnames_127232[code-127232] + elif 127293 <= code <= 127310: res = _charnames_127293[code-127293] + elif code == 127319: res = 27110 + elif code == 127327: res = 27109 + elif 127353 <= code <= 127359: res = _charnames_127353[code-127353] + elif 127370 <= code <= 127376: res = _charnames_127370[code-127370] + elif code == 127488: res = 19008 + elif 127504 <= code <= 127537: res = _charnames_127504[code-127504] + elif 127552 <= code <= 127560: res = _charnames_127552[code-127552] + elif 194560 <= code <= 195101: res = _charnames_194560[code-194560] + elif code == 917505: res = 9289 + elif 917536 <= code <= 917631: res = _charnames_917536[code-917536] + elif 917760 <= code <= 917999: res = _charnames_917760[code-917760] + elif 983040 <= code <= 983050: res = _charnames_983040[code-983040] + elif 983552 <= code <= 983945: res = _charnames_983552[code-983552] if res == -1: raise KeyError, code return name_of_node(res) +# end output from build_compression_tree +#____________________________________________________________ # the following dictionary is used by modules that take this as a base +# only used by generate_unicodedb, not after translation _orig_names = { 'AC CURRENT': 9190, 'ACCOUNT OF': 8448, @@ -136813,7 +136823,7 @@ code = trie_lookup(name) else: try: - code = _code_by_name[name] + code = trie_lookup(name) except KeyError: if name not in _code_by_name_corrected: code = base_mod.trie_lookup(name) @@ -136842,7 +136852,7 @@ return lookup_charcode(code) else: try: - return _names[code] + return lookup_charcode(code) except KeyError: if code not in _names_corrected: return base_mod.lookup_charcode(code) diff --git a/rpython/rlib/unicodedata/unicodedb_6_0_0.py b/rpython/rlib/unicodedata/unicodedb_6_0_0.py --- a/rpython/rlib/unicodedata/unicodedb_6_0_0.py +++ b/rpython/rlib/unicodedata/unicodedb_6_0_0.py @@ -8,4460 +8,13026 @@ import unicodedb_5_2_0 as base_mod version = '6.0.0' -_names = { -128673: 'AERIAL TRAMWAY', -9200: 'ALARM CLOCK', -128769: 'ALCHEMICAL SYMBOL FOR AIR', -128874: 'ALCHEMICAL SYMBOL FOR ALEMBIC', -128822: 'ALCHEMICAL SYMBOL FOR ALKALI', -128823: 'ALCHEMICAL SYMBOL FOR ALKALI-2', -128837: 'ALCHEMICAL SYMBOL FOR ALUM', -128859: 'ALCHEMICAL SYMBOL FOR AMALGAM', -128811: 'ALCHEMICAL SYMBOL FOR ANTIMONY ORE', -128774: 'ALCHEMICAL SYMBOL FOR AQUA REGIA', -128775: 'ALCHEMICAL SYMBOL FOR AQUA REGIA-2', -128776: 'ALCHEMICAL SYMBOL FOR AQUA VITAE', -128777: 'ALCHEMICAL SYMBOL FOR AQUA VITAE-2', -128773: 'ALCHEMICAL SYMBOL FOR AQUAFORTIS', -128826: 'ALCHEMICAL SYMBOL FOR ARSENIC', -128855: 'ALCHEMICAL SYMBOL FOR ASHES', -128829: 'ALCHEMICAL SYMBOL FOR AURIPIGMENT', -128875: 'ALCHEMICAL SYMBOL FOR BATH OF MARY', -128876: 'ALCHEMICAL SYMBOL FOR BATH OF VAPOURS', -128830: 'ALCHEMICAL SYMBOL FOR BISMUTH ORE', -128783: 'ALCHEMICAL SYMBOL FOR BLACK SULFUR', -128834: 'ALCHEMICAL SYMBOL FOR BORAX', -128835: 'ALCHEMICAL SYMBOL FOR BORAX-2', -128836: 'ALCHEMICAL SYMBOL FOR BORAX-3', -128857: 'ALCHEMICAL SYMBOL FOR BRICK', -128848: 'ALCHEMICAL SYMBOL FOR CADUCEUS', -128844: 'ALCHEMICAL SYMBOL FOR CALX', -128846: 'ALCHEMICAL SYMBOL FOR CAPUT MORTUUM', -128787: 'ALCHEMICAL SYMBOL FOR CINNABAR', -128805: 'ALCHEMICAL SYMBOL FOR COPPER ANTIMONIATE', -128800: 'ALCHEMICAL SYMBOL FOR COPPER ORE', -128803: 'ALCHEMICAL SYMBOL FOR CROCUS OF COPPER', -128804: 'ALCHEMICAL SYMBOL FOR CROCUS OF COPPER-2', -128798: 'ALCHEMICAL SYMBOL FOR CROCUS OF IRON', -128869: 'ALCHEMICAL SYMBOL FOR CRUCIBLE', -128870: 'ALCHEMICAL SYMBOL FOR CRUCIBLE-2', -128871: 'ALCHEMICAL SYMBOL FOR CRUCIBLE-3', -128872: 'ALCHEMICAL SYMBOL FOR CRUCIBLE-4', -128873: 'ALCHEMICAL SYMBOL FOR CRUCIBLE-5', -128880: 'ALCHEMICAL SYMBOL FOR DAY-NIGHT', -128865: 'ALCHEMICAL SYMBOL FOR DISSOLVE', -128866: 'ALCHEMICAL SYMBOL FOR DISSOLVE-2', -128864: 'ALCHEMICAL SYMBOL FOR DISTILL', -128771: 'ALCHEMICAL SYMBOL FOR EARTH', -128770: 'ALCHEMICAL SYMBOL FOR FIRE', -128794: 'ALCHEMICAL SYMBOL FOR GOLD', -128841: 'ALCHEMICAL SYMBOL FOR GUM', -128882: 'ALCHEMICAL SYMBOL FOR HALF DRAM', -128883: 'ALCHEMICAL SYMBOL FOR HALF OUNCE', -128854: 'ALCHEMICAL SYMBOL FOR HORSE DUNG', -128878: 'ALCHEMICAL SYMBOL FOR HOUR', -128796: 'ALCHEMICAL SYMBOL FOR IRON ORE', -128797: 'ALCHEMICAL SYMBOL FOR IRON ORE-2', -128801: 'ALCHEMICAL SYMBOL FOR IRON-COPPER ORE', -128810: 'ALCHEMICAL SYMBOL FOR LEAD ORE', -128851: 'ALCHEMICAL SYMBOL FOR LODESTONE', -128824: 'ALCHEMICAL SYMBOL FOR MARCASITE', -128784: 'ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE', -128785: 'ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE-2', -128786: 'ALCHEMICAL SYMBOL FOR MERCURY SUBLIMATE-3', -128881: 'ALCHEMICAL SYMBOL FOR MONTH', -128879: 'ALCHEMICAL SYMBOL FOR NIGHT', -128789: 'ALCHEMICAL SYMBOL FOR NITRE', -128838: 'ALCHEMICAL SYMBOL FOR OIL', -128782: 'ALCHEMICAL SYMBOL FOR PHILOSOPHERS SULFUR', -128856: 'ALCHEMICAL SYMBOL FOR POT ASHES', -128843: 'ALCHEMICAL SYMBOL FOR POWDER', -128858: 'ALCHEMICAL SYMBOL FOR POWDERED BRICK', -128863: 'ALCHEMICAL SYMBOL FOR PRECIPITATE', -128867: 'ALCHEMICAL SYMBOL FOR PURIFY', -128868: 'ALCHEMICAL SYMBOL FOR PUTREFACTION', -128833: 'ALCHEMICAL SYMBOL FOR QUICK LIME', -128768: 'ALCHEMICAL SYMBOL FOR QUINTESSENCE', -128827: 'ALCHEMICAL SYMBOL FOR REALGAR', -128828: 'ALCHEMICAL SYMBOL FOR REALGAR-2', -128818: 'ALCHEMICAL SYMBOL FOR REGULUS', -128816: 'ALCHEMICAL SYMBOL FOR REGULUS OF ANTIMONY', -128817: 'ALCHEMICAL SYMBOL FOR REGULUS OF ANTIMONY-2', -128799: 'ALCHEMICAL SYMBOL FOR REGULUS OF IRON', -128819: 'ALCHEMICAL SYMBOL FOR REGULUS-2', -128820: 'ALCHEMICAL SYMBOL FOR REGULUS-3', -128821: 'ALCHEMICAL SYMBOL FOR REGULUS-4', -128877: 'ALCHEMICAL SYMBOL FOR RETORT', -128792: 'ALCHEMICAL SYMBOL FOR ROCK SALT', -128793: 'ALCHEMICAL SYMBOL FOR ROCK SALT-2', -128825: 'ALCHEMICAL SYMBOL FOR SAL-AMMONIAC', -128788: 'ALCHEMICAL SYMBOL FOR SALT', -128813: 'ALCHEMICAL SYMBOL FOR SALT OF ANTIMONY', -128806: 'ALCHEMICAL SYMBOL FOR SALT OF COPPER ANTIMONIATE', -128847: 'ALCHEMICAL SYMBOL FOR SCEPTER OF JOVE', -128795: 'ALCHEMICAL SYMBOL FOR SILVER', -128852: 'ALCHEMICAL SYMBOL FOR SOAP', -128839: 'ALCHEMICAL SYMBOL FOR SPIRIT', -128850: 'ALCHEMICAL SYMBOL FOR STARRED TRIDENT', -128860: 'ALCHEMICAL SYMBOL FOR STRATUM SUPER STRATUM', -128861: 'ALCHEMICAL SYMBOL FOR STRATUM SUPER STRATUM-2', -128812: 'ALCHEMICAL SYMBOL FOR SUBLIMATE OF ANTIMONY', -128802: 'ALCHEMICAL SYMBOL FOR SUBLIMATE OF COPPER', -128814: 'ALCHEMICAL SYMBOL FOR SUBLIMATE OF SALT OF ANTIMONY', -128807: 'ALCHEMICAL SYMBOL FOR SUBLIMATE OF SALT OF COPPER', -128862: 'ALCHEMICAL SYMBOL FOR SUBLIMATION', -128781: 'ALCHEMICAL SYMBOL FOR SULFUR', -128831: 'ALCHEMICAL SYMBOL FOR TARTAR', -128832: 'ALCHEMICAL SYMBOL FOR TARTAR-2', -128809: 'ALCHEMICAL SYMBOL FOR TIN ORE', -128840: 'ALCHEMICAL SYMBOL FOR TINCTURE', -128849: 'ALCHEMICAL SYMBOL FOR TRIDENT', -128845: 'ALCHEMICAL SYMBOL FOR TUTTY', -128853: 'ALCHEMICAL SYMBOL FOR URINE', -128808: 'ALCHEMICAL SYMBOL FOR VERDIGRIS', -128778: 'ALCHEMICAL SYMBOL FOR VINEGAR', -128815: 'ALCHEMICAL SYMBOL FOR VINEGAR OF ANTIMONY', -128779: 'ALCHEMICAL SYMBOL FOR VINEGAR-2', -128780: 'ALCHEMICAL SYMBOL FOR VINEGAR-3', -128790: 'ALCHEMICAL SYMBOL FOR VITRIOL', -128791: 'ALCHEMICAL SYMBOL FOR VITRIOL-2', -128772: 'ALCHEMICAL SYMBOL FOR WATER', -128842: 'ALCHEMICAL SYMBOL FOR WAX', -128126: 'ALIEN MONSTER', -128657: 'AMBULANCE', -127944: 'AMERICAN FOOTBALL', -128162: 'ANGER SYMBOL', -128544: 'ANGRY FACE', -128028: 'ANT', -128246: 'ANTENNA WITH BARS', -128260: 'ANTICLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS', -1568: 'ARABIC LETTER KASHMIRI YEH', -64434: 'ARABIC SYMBOL DOT ABOVE', -64435: 'ARABIC SYMBOL DOT BELOW', -64444: 'ARABIC SYMBOL DOUBLE VERTICAL BAR BELOW', -64442: 'ARABIC SYMBOL FOUR DOTS ABOVE', -64443: 'ARABIC SYMBOL FOUR DOTS BELOW', -64447: 'ARABIC SYMBOL RING', -64448: 'ARABIC SYMBOL SMALL TAH ABOVE', -64449: 'ARABIC SYMBOL SMALL TAH BELOW', -64438: 'ARABIC SYMBOL THREE DOTS ABOVE', -64439: 'ARABIC SYMBOL THREE DOTS BELOW', -64440: 'ARABIC SYMBOL THREE DOTS POINTING DOWNWARDS ABOVE', -64441: 'ARABIC SYMBOL THREE DOTS POINTING DOWNWARDS BELOW', -64436: 'ARABIC SYMBOL TWO DOTS ABOVE', -64437: 'ARABIC SYMBOL TWO DOTS BELOW', -64445: 'ARABIC SYMBOL TWO DOTS VERTICALLY ABOVE', -64446: 'ARABIC SYMBOL TWO DOTS VERTICALLY BELOW', -1631: 'ARABIC WAVY HAMZA BELOW', -128667: 'ARTICULATED LORRY', -127912: 'ARTIST PALETTE', -128562: 'ASTONISHED FACE', -9954: 'ASTRONOMICAL SYMBOL FOR URANUS', -128095: 'ATHLETIC SHOE', -127814: 'AUBERGINE', -127975: 'AUTOMATED TELLER MACHINE', -128663: 'AUTOMOBILE', -128118: 'BABY', -128124: 'BABY ANGEL', -127868: 'BABY BOTTLE', -128036: 'BABY CHICK', -128700: 'BABY SYMBOL', -128281: 'BACK WITH LEFTWARDS ARROW ABOVE', -128043: 'BACTRIAN CAMEL', -128708: 'BAGGAGE CLAIM', -127880: 'BALLOON', -92217: 'BAMUM LETTER PHASE-A FIRI', -92161: 'BAMUM LETTER PHASE-A GBIEE FON', -92193: 'BAMUM LETTER PHASE-A GHEUAEGHEUAE', -92188: 'BAMUM LETTER PHASE-A GHEUAERAE', -92199: 'BAMUM LETTER PHASE-A KAFA', -92240: 'BAMUM LETTER PHASE-A KAQ', -92211: 'BAMUM LETTER PHASE-A KET', -92179: 'BAMUM LETTER PHASE-A KEUKEUTNDA', -92219: 'BAMUM LETTER PHASE-A KPOQ', -92213: 'BAMUM LETTER PHASE-A KUOQ', -92183: 'BAMUM LETTER PHASE-A LAPAQ', -92184: 'BAMUM LETTER PHASE-A LET KUT', -92216: 'BAMUM LETTER PHASE-A LOMMAE', -92243: 'BAMUM LETTER PHASE-A LU', -92207: 'BAMUM LETTER PHASE-A LUAEP', -92186: 'BAMUM LETTER PHASE-A MAEKEUP', -92238: 'BAMUM LETTER PHASE-A MAEM', -92171: 'BAMUM LETTER PHASE-A MAEMBGBIEE', -92203: 'BAMUM LETTER PHASE-A MAEMKPEN', -92174: 'BAMUM LETTER PHASE-A MAEMVEUX', -92210: 'BAMUM LETTER PHASE-A MAENYI', -92230: 'BAMUM LETTER PHASE-A MAESI', -92175: 'BAMUM LETTER PHASE-A MANSUAE', -92221: 'BAMUM LETTER PHASE-A MAP PIEET', -92232: 'BAMUM LETTER PHASE-A MBANYI', -92246: 'BAMUM LETTER PHASE-A MBAQ', -92197: 'BAMUM LETTER PHASE-A MEUNJOMNDEUQ', -92196: 'BAMUM LETTER PHASE-A MGBASA', -92190: 'BAMUM LETTER PHASE-A MON NGGEUAET', -92214: 'BAMUM LETTER PHASE-A MOOMEUT', -92198: 'BAMUM LETTER PHASE-A MOOMPUQ', -92176: 'BAMUM LETTER PHASE-A MVEUAENGAM', -92164: 'BAMUM LETTER PHASE-A NAA MFON', -92245: 'BAMUM LETTER PHASE-A NAQ', -92201: 'BAMUM LETTER PHASE-A NDA LEERAEWA', -92212: 'BAMUM LETTER PHASE-A NDAANGGEUAET', -92244: 'BAMUM LETTER PHASE-A NEN', -92173: 'BAMUM LETTER PHASE-A NGANGU', -92229: 'BAMUM LETTER PHASE-A NGGEN', -92160: 'BAMUM LETTER PHASE-A NGKUE MFON', -92182: 'BAMUM LETTER PHASE-A NGKUENZEUM', -92204: 'BAMUM LETTER PHASE-A NIKA', -92231: 'BAMUM LETTER PHASE-A NJAM', -92227: 'BAMUM LETTER PHASE-A NKAARAE', -92180: 'BAMUM LETTER PHASE-A NKINDI', -92241: 'BAMUM LETTER PHASE-A NSHA', -92237: 'BAMUM LETTER PHASE-A NSHIEE', -92223: 'BAMUM LETTER PHASE-A NTAP', -92185: 'BAMUM LETTER PHASE-A NTAP MFAA', -92194: 'BAMUM LETTER PHASE-A NTAP NTAA', -92178: 'BAMUM LETTER PHASE-A NTOQPEN', -92233: 'BAMUM LETTER PHASE-A NYET', -92239: 'BAMUM LETTER PHASE-A NYI', -92225: 'BAMUM LETTER PHASE-A NYIT MONGKEUAEQ', -92167: 'BAMUM LETTER PHASE-A NZA MFON', -92191: 'BAMUM LETTER PHASE-A NZUN MEUT', -92200: 'BAMUM LETTER PHASE-A PA LEERAEWA', -92236: 'BAMUM LETTER PHASE-A PAAM', -92226: 'BAMUM LETTER PHASE-A PAARAE', -92189: 'BAMUM LETTER PHASE-A PAMSHAE', -92187: 'BAMUM LETTER PHASE-A PASHAE', -92202: 'BAMUM LETTER PHASE-A PET', -92163: 'BAMUM LETTER PHASE-A PON MFON PIPAEMBA', -92162: 'BAMUM LETTER PHASE-A PON MFON PIPAEMGBIEE', -92170: 'BAMUM LETTER PHASE-A PON PA NJI PIPAEMBA', -92169: 'BAMUM LETTER PHASE-A PON PA NJI PIPAEMGBIEE', -92205: 'BAMUM LETTER PHASE-A PUP', -92218: 'BAMUM LETTER PHASE-A ROM', -92177: 'BAMUM LETTER PHASE-A SEUNYAM', -92168: 'BAMUM LETTER PHASE-A SHINDA PA NJI', -92222: 'BAMUM LETTER PHASE-A SHIRAE', -92224: 'BAMUM LETTER PHASE-A SHOQ NSHUT YUM', -92165: 'BAMUM LETTER PHASE-A SHUENSHUET', -92215: 'BAMUM LETTER PHASE-A SHUM', -92195: 'BAMUM LETTER PHASE-A SISA', -92208: 'BAMUM LETTER PHASE-A SONJAM', -92220: 'BAMUM LETTER PHASE-A SOQ', -92235: 'BAMUM LETTER PHASE-A SOT', -92181: 'BAMUM LETTER PHASE-A SUU', -92234: 'BAMUM LETTER PHASE-A TEUAEN', -92209: 'BAMUM LETTER PHASE-A TEUTEUWEN', -92166: 'BAMUM LETTER PHASE-A TITA MFON', -92172: 'BAMUM LETTER PHASE-A TU MAEMBA', -92206: 'BAMUM LETTER PHASE-A TUAEP', -92192: 'BAMUM LETTER PHASE-A U YUQ NAE', -92228: 'BAMUM LETTER PHASE-A UNKNOWN', -92242: 'BAMUM LETTER PHASE-A VEE', -92293: 'BAMUM LETTER PHASE-B FEE', -92291: 'BAMUM LETTER PHASE-B FEUX', -92271: 'BAMUM LETTER PHASE-B GHEUGHEN', -92262: 'BAMUM LETTER PHASE-B GHEUGHEUAEM', -92256: 'BAMUM LETTER PHASE-B KAM', -92294: 'BAMUM LETTER PHASE-B KEUAEM', -92270: 'BAMUM LETTER PHASE-B KEUPUQ', -92272: 'BAMUM LETTER PHASE-B KEUYEUX', -92253: 'BAMUM LETTER PHASE-B KIEEM', -92301: 'BAMUM LETTER PHASE-B KIQ', -92273: 'BAMUM LETTER PHASE-B LAANAE', -92259: 'BAMUM LETTER PHASE-B LAM NSHUT NYAM', -92297: 'BAMUM LETTER PHASE-B LET', -92251: 'BAMUM LETTER PHASE-B LOM NTEUM', -92300: 'BAMUM LETTER PHASE-B MA', -92295: 'BAMUM LETTER PHASE-B MA NJEUAENA', -92296: 'BAMUM LETTER PHASE-B MA NJUQA', -92252: 'BAMUM LETTER PHASE-B MBA MAELEE', -92255: 'BAMUM LETTER PHASE-B MBAARAE', -92286: 'BAMUM LETTER PHASE-B MBEURI', -92268: 'BAMUM LETTER PHASE-B MBIT MBAAKET', -92292: 'BAMUM LETTER PHASE-B MBUOQ', -92281: 'BAMUM LETTER PHASE-B MEUQ', -92290: 'BAMUM LETTER PHASE-B MEUT NGGEET', -92284: 'BAMUM LETTER PHASE-B MFIYAQ', -92267: 'BAMUM LETTER PHASE-B MFON TEUAEQ', -92287: 'BAMUM LETTER PHASE-B MONTIEEN', -92261: 'BAMUM LETTER PHASE-B NDU NJAA', -92298: 'BAMUM LETTER PHASE-B NGGAAM', -92277: 'BAMUM LETTER PHASE-B NGGEU MBU', -92282: 'BAMUM LETTER PHASE-B NGGUOQ', -92283: 'BAMUM LETTER PHASE-B NGGUOQ LARGE', -92276: 'BAMUM LETTER PHASE-B NGKINDI MVOP', -92302: 'BAMUM LETTER PHASE-B NGOM', -92299: 'BAMUM LETTER PHASE-B NSEN', -92247: 'BAMUM LETTER PHASE-B NSHUET', -92260: 'BAMUM LETTER PHASE-B NTIEE SHEUOQ', -92288: 'BAMUM LETTER PHASE-B NYAEMAE', -92269: 'BAMUM LETTER PHASE-B NYI NTEUM', -92274: 'BAMUM LETTER PHASE-B PARUM', -92257: 'BAMUM LETTER PHASE-B PEESHI', -92263: 'BAMUM LETTER PHASE-B PIT', -92289: 'BAMUM LETTER PHASE-B PUNGAAM', -92279: 'BAMUM LETTER PHASE-B SAKEUAE', -92250: 'BAMUM LETTER PHASE-B SET TU', -92265: 'BAMUM LETTER PHASE-B SHET NJAQ', -92266: 'BAMUM LETTER PHASE-B SHEUAEQTU', -92249: 'BAMUM LETTER PHASE-B SIEE', -92285: 'BAMUM LETTER PHASE-B SUE', -92280: 'BAMUM LETTER PHASE-B TAAM', -92248: 'BAMUM LETTER PHASE-B TU MAEMGBIEE', -92264: 'BAMUM LETTER PHASE-B TU NSIEE', -92275: 'BAMUM LETTER PHASE-B VEUM', -92278: 'BAMUM LETTER PHASE-B WUAET', -92258: 'BAMUM LETTER PHASE-B YAFU LEERAEWA', -92254: 'BAMUM LETTER PHASE-B YEURAE', -92352: 'BAMUM LETTER PHASE-C BUNG', -92348: 'BAMUM LETTER PHASE-C FUE', -92312: 'BAMUM LETTER PHASE-C GBAYI', -92320: 'BAMUM LETTER PHASE-C GHAP', -92310: 'BAMUM LETTER PHASE-C GHARAE', -92329: 'BAMUM LETTER PHASE-C KAA', -92394: 'BAMUM LETTER PHASE-C KEN FATIGUE', -92393: 'BAMUM LETTER PHASE-C KEN LAW', -92361: 'BAMUM LETTER PHASE-C KET', -92321: 'BAMUM LETTER PHASE-C KEUKAQ', -92386: 'BAMUM LETTER PHASE-C KEUM', -92384: 'BAMUM LETTER PHASE-C KEUSEUX', -92319: 'BAMUM LETTER PHASE-C KEUSHEUAEP', -92328: 'BAMUM LETTER PHASE-C KPARAQ', -92373: 'BAMUM LETTER PHASE-C KUOP NKAARAE', -92366: 'BAMUM LETTER PHASE-C KUT', -92357: 'BAMUM LETTER PHASE-C LAM', -92342: 'BAMUM LETTER PHASE-C LAP', -92397: 'BAMUM LETTER PHASE-C LIQ', -92365: 'BAMUM LETTER PHASE-C LU', -92339: 'BAMUM LETTER PHASE-C MA KEUAERI', -92376: 'BAMUM LETTER PHASE-C MA NSIEE', -92382: 'BAMUM LETTER PHASE-C MAEMBA', -92363: 'BAMUM LETTER PHASE-C MAESI', -92318: 'BAMUM LETTER PHASE-C MBAA CABBAGE-TREE', -92387: 'BAMUM LETTER PHASE-C MBAA PICKET', -92383: 'BAMUM LETTER PHASE-C MBANYI', -92311: 'BAMUM LETTER PHASE-C MBEEKEET', -92354: 'BAMUM LETTER PHASE-C MBERAE', -92315: 'BAMUM LETTER PHASE-C MBEUM', -92385: 'BAMUM LETTER PHASE-C MBEUX', -92381: 'BAMUM LETTER PHASE-C MBI', -92343: 'BAMUM LETTER PHASE-C MBIRIEEN', -92326: 'BAMUM LETTER PHASE-C MBIT', -92364: 'BAMUM LETTER PHASE-C MBUAEM', -92324: 'BAMUM LETTER PHASE-C MBUE', -92344: 'BAMUM LETTER PHASE-C MGBASAQ', -92390: 'BAMUM LETTER PHASE-C MIEE', -92391: 'BAMUM LETTER PHASE-C MUAE', -92338: 'BAMUM LETTER PHASE-C NANSANAQ', -92396: 'BAMUM LETTER PHASE-C NAQ', -92375: 'BAMUM LETTER PHASE-C NDAM', -92378: 'BAMUM LETTER PHASE-C NDAP', -92308: 'BAMUM LETTER PHASE-C NDEUAEREE', -92349: 'BAMUM LETTER PHASE-C NDEUT', -92331: 'BAMUM LETTER PHASE-C NDIDA', -92317: 'BAMUM LETTER PHASE-C NDOMBU', -92395: 'BAMUM LETTER PHASE-C NGAQ', -92307: 'BAMUM LETTER PHASE-C NGGEN', -92362: 'BAMUM LETTER PHASE-C NGGU', -92336: 'BAMUM LETTER PHASE-C NGGUAEN NYAM', -92370: 'BAMUM LETTER PHASE-C NGGUEET', -92347: 'BAMUM LETTER PHASE-C NGGUM', -92341: 'BAMUM LETTER PHASE-C NGGUON', -92309: 'BAMUM LETTER PHASE-C NGKAQ', -92303: 'BAMUM LETTER PHASE-C NGKUE MAEMBA', -92368: 'BAMUM LETTER PHASE-C NGOM', -92356: 'BAMUM LETTER PHASE-C NJAEM', -92367: 'BAMUM LETTER PHASE-C NJAM', -92360: 'BAMUM LETTER PHASE-C NJEEEE', -92389: 'BAMUM LETTER PHASE-C NJEUX', -92333: 'BAMUM LETTER PHASE-C NJUEQ', -92350: 'BAMUM LETTER PHASE-C NSA', -92325: 'BAMUM LETTER PHASE-C NSEUAEN', -92351: 'BAMUM LETTER PHASE-C NSHAQ', -92371: 'BAMUM LETTER PHASE-C NSOM', -92374: 'BAMUM LETTER PHASE-C NSUN', -92359: 'BAMUM LETTER PHASE-C NSUOT NGOM', -92340: 'BAMUM LETTER PHASE-C NTAA', -92372: 'BAMUM LETTER PHASE-C NTEN', -92345: 'BAMUM LETTER PHASE-C NTEUNGBA', -92314: 'BAMUM LETTER PHASE-C NTU MBIT', -92313: 'BAMUM LETTER PHASE-C NYIR MKPARAQ MEUN', -92304: 'BAMUM LETTER PHASE-C NZA', -92323: 'BAMUM LETTER PHASE-C NZEUM', -92399: 'BAMUM LETTER PHASE-C PEN', -92398: 'BAMUM LETTER PHASE-C PIN', -92316: 'BAMUM LETTER PHASE-C PIRIEEN', -92355: 'BAMUM LETTER PHASE-C RU', -92380: 'BAMUM LETTER PHASE-C SETFON', -92330: 'BAMUM LETTER PHASE-C SEUX', -92392: 'BAMUM LETTER PHASE-C SHIQ', -92379: 'BAMUM LETTER PHASE-C SHUEQ', -92335: 'BAMUM LETTER PHASE-C SUAET', -92332: 'BAMUM LETTER PHASE-C TAASHAE', -92400: 'BAMUM LETTER PHASE-C TET', -92346: 'BAMUM LETTER PHASE-C TEUTEUX', -92334: 'BAMUM LETTER PHASE-C TITA YUE', -92358: 'BAMUM LETTER PHASE-C TITUAEP', -92353: 'BAMUM LETTER PHASE-C VEUAEPEN', -92337: 'BAMUM LETTER PHASE-C VEUX', -92306: 'BAMUM LETTER PHASE-C WANGKUOQ', -92369: 'BAMUM LETTER PHASE-C WUP', -92377: 'BAMUM LETTER PHASE-C YAA', -92327: 'BAMUM LETTER PHASE-C YEUQ', -92322: 'BAMUM LETTER PHASE-C YU MUOMAE', -92305: 'BAMUM LETTER PHASE-C YUM', -92388: 'BAMUM LETTER PHASE-C YUWOQ', -92517: 'BAMUM LETTER PHASE-D FAA', -92436: 'BAMUM LETTER PHASE-D FEUFEUAET', -92434: 'BAMUM LETTER PHASE-D GHAA', -92488: 'BAMUM LETTER PHASE-D GHEUAE', -92420: 'BAMUM LETTER PHASE-D KET', -92415: 'BAMUM LETTER PHASE-D KEUAETMEUN', -92454: 'BAMUM LETTER PHASE-D KEUM', -92431: 'BAMUM LETTER PHASE-D KEUOT MBUAE', -92460: 'BAMUM LETTER PHASE-D KEUP', -92489: 'BAMUM LETTER PHASE-D KU', -92474: 'BAMUM LETTER PHASE-D KUN', -92422: 'BAMUM LETTER PHASE-D KUOM', -92479: 'BAMUM LETTER PHASE-D KUQ', -92449: 'BAMUM LETTER PHASE-D KWAET', -92502: 'BAMUM LETTER PHASE-D KYEE', -92495: 'BAMUM LETTER PHASE-D LEEEE', -92464: 'BAMUM LETTER PHASE-D LET', -92439: 'BAMUM LETTER PHASE-D LEUAEP', -92484: 'BAMUM LETTER PHASE-D LEUM', -92406: 'BAMUM LETTER PHASE-D LIEE', -92511: 'BAMUM LETTER PHASE-D LOQ', -92446: 'BAMUM LETTER PHASE-D LUM', -92497: 'BAMUM LETTER PHASE-D M', -92482: 'BAMUM LETTER PHASE-D MAENJET', -92426: 'BAMUM LETTER PHASE-D MALEERI', -92448: 'BAMUM LETTER PHASE-D MBAA', -92515: 'BAMUM LETTER PHASE-D MBAA SEVEN', -92401: 'BAMUM LETTER PHASE-D MBUO', -92496: 'BAMUM LETTER PHASE-D MEEEE', -92478: 'BAMUM LETTER PHASE-D MEUN', -92427: 'BAMUM LETTER PHASE-D MEUT', -92458: 'BAMUM LETTER PHASE-D MFEUAE', -92424: 'BAMUM LETTER PHASE-D MFEUT', -92466: 'BAMUM LETTER PHASE-D MFIEE', -92445: 'BAMUM LETTER PHASE-D MFO', -92404: 'BAMUM LETTER PHASE-D MFON', -92442: 'BAMUM LETTER PHASE-D MGBEUN', -92444: 'BAMUM LETTER PHASE-D MGBIEE', -92438: 'BAMUM LETTER PHASE-D MGBOFUM', -92441: 'BAMUM LETTER PHASE-D MONI', -92499: 'BAMUM LETTER PHASE-D MU', -92510: 'BAMUM LETTER PHASE-D MVOP', -92471: 'BAMUM LETTER PHASE-D NDAM', -92437: 'BAMUM LETTER PHASE-D NDEE', -92425: 'BAMUM LETTER PHASE-D NDEUX', -92440: 'BAMUM LETTER PHASE-D NDON', -92465: 'BAMUM LETTER PHASE-D NGGAAM', -92409: 'BAMUM LETTER PHASE-D NGGAAMAE', -92483: 'BAMUM LETTER PHASE-D NGGAP', -92475: 'BAMUM LETTER PHASE-D NGGEUX', -92485: 'BAMUM LETTER PHASE-D NGGUOM', -92467: 'BAMUM LETTER PHASE-D NGGWAEN', -92414: 'BAMUM LETTER PHASE-D NGKAP', -92457: 'BAMUM LETTER PHASE-D NGKEUAEQ', -92432: 'BAMUM LETTER PHASE-D NGKEURI', -92476: 'BAMUM LETTER PHASE-D NGKIEE', -92412: 'BAMUM LETTER PHASE-D NGKUN', -92435: 'BAMUM LETTER PHASE-D NGKYEE', -92507: 'BAMUM LETTER PHASE-D NI', -92418: 'BAMUM LETTER PHASE-D NJAP', -92430: 'BAMUM LETTER PHASE-D NJEUAEM', -92407: 'BAMUM LETTER PHASE-D NJEUT', -92403: 'BAMUM LETTER PHASE-D NJI', -92405: 'BAMUM LETTER PHASE-D NJIEE', -92487: 'BAMUM LETTER PHASE-D NJUEQ', -92408: 'BAMUM LETTER PHASE-D NSHEE', -92486: 'BAMUM LETTER PHASE-D NSHUT', -92447: 'BAMUM LETTER PHASE-D NSIEEP', -92459: 'BAMUM LETTER PHASE-D NSIEET', -92480: 'BAMUM LETTER PHASE-D NSUM', -92505: 'BAMUM LETTER PHASE-D NTEE', -92472: 'BAMUM LETTER PHASE-D NTEUM', -92514: 'BAMUM LETTER PHASE-D NTUU', -92503: 'BAMUM LETTER PHASE-D NU', -92410: 'BAMUM LETTER PHASE-D NYAM', -92450: 'BAMUM LETTER PHASE-D NYET', -92493: 'BAMUM LETTER PHASE-D NYI', -92463: 'BAMUM LETTER PHASE-D NYUE', -92469: 'BAMUM LETTER PHASE-D PAP', -92506: 'BAMUM LETTER PHASE-D PEE', -92462: 'BAMUM LETTER PHASE-D PEUTAE', -92461: 'BAMUM LETTER PHASE-D PIP', -92509: 'BAMUM LETTER PHASE-D PUQ', -92443: 'BAMUM LETTER PHASE-D PUUT', -92455: 'BAMUM LETTER PHASE-D RAEM', -92512: 'BAMUM LETTER PHASE-D REN MUCH', -92490: 'BAMUM LETTER PHASE-D REN OLD', -92494: 'BAMUM LETTER PHASE-D RII', -92423: 'BAMUM LETTER PHASE-D SAP', -92516: 'BAMUM LETTER PHASE-D SAQ', -92428: 'BAMUM LETTER PHASE-D SEUAEQ', -92413: 'BAMUM LETTER PHASE-D SHEE', -92417: 'BAMUM LETTER PHASE-D SHEUAE', -92501: 'BAMUM LETTER PHASE-D SHEUX', -92500: 'BAMUM LETTER PHASE-D SHII', -92508: 'BAMUM LETTER PHASE-D SHOQ', -92504: 'BAMUM LETTER PHASE-D SHU', -92452: 'BAMUM LETTER PHASE-D SOT', -92473: 'BAMUM LETTER PHASE-D SUAE', -92419: 'BAMUM LETTER PHASE-D SUE', -92498: 'BAMUM LETTER PHASE-D SUU', -92491: 'BAMUM LETTER PHASE-D TAE', -92456: 'BAMUM LETTER PHASE-D TEEEE', -92451: 'BAMUM LETTER PHASE-D TEUAEN', -92481: 'BAMUM LETTER PHASE-D TEUN', -92416: 'BAMUM LETTER PHASE-D TEUT', -92513: 'BAMUM LETTER PHASE-D TI', -92492: 'BAMUM LETTER PHASE-D TOQ', -92433: 'BAMUM LETTER PHASE-D TU', -92477: 'BAMUM LETTER PHASE-D TUOT', -92402: 'BAMUM LETTER PHASE-D WAP', -92411: 'BAMUM LETTER PHASE-D WUAEN', -92421: 'BAMUM LETTER PHASE-D YAEMMAE', -92429: 'BAMUM LETTER PHASE-D YEN', -92468: 'BAMUM LETTER PHASE-D YUOM', -92470: 'BAMUM LETTER PHASE-D YUOP', -92453: 'BAMUM LETTER PHASE-D YUWOQ', -92629: 'BAMUM LETTER PHASE-E A', -92603: 'BAMUM LETTER PHASE-E FA', -92673: 'BAMUM LETTER PHASE-E FAQ', -92651: 'BAMUM LETTER PHASE-E FEE', -92627: 'BAMUM LETTER PHASE-E FOM', -92626: 'BAMUM LETTER PHASE-E FU CALL', -92616: 'BAMUM LETTER PHASE-E FU I', -92661: 'BAMUM LETTER PHASE-E FU REMEDY', -92596: 'BAMUM LETTER PHASE-E FUE', -92612: 'BAMUM LETTER PHASE-E FUET', -92585: 'BAMUM LETTER PHASE-E GBET', -92597: 'BAMUM LETTER PHASE-E GBEUX', -92578: 'BAMUM LETTER PHASE-E GHAAMAE', -92602: 'BAMUM LETTER PHASE-E GHET', -92615: 'BAMUM LETTER PHASE-E GHEUAE', -92565: 'BAMUM LETTER PHASE-E GHEUN', -92552: 'BAMUM LETTER PHASE-E GHEUX', -92674: 'BAMUM LETTER PHASE-E GHOM', -92632: 'BAMUM LETTER PHASE-E I', -92599: 'BAMUM LETTER PHASE-E KET', -92570: 'BAMUM LETTER PHASE-E KEUAE', -92646: 'BAMUM LETTER PHASE-E KEUX', -92670: 'BAMUM LETTER PHASE-E KI', -92665: 'BAMUM LETTER PHASE-E KO', -92532: 'BAMUM LETTER PHASE-E KPEUX', -92587: 'BAMUM LETTER PHASE-E KUET', -92538: 'BAMUM LETTER PHASE-E KUOP', -92620: 'BAMUM LETTER PHASE-E KUT', -92575: 'BAMUM LETTER PHASE-E LAAM', -92521: 'BAMUM LETTER PHASE-E LAP', -92633: 'BAMUM LETTER PHASE-E LAQ', -92595: 'BAMUM LETTER PHASE-E LEUAEM', -92539: 'BAMUM LETTER PHASE-E LOM', -92523: 'BAMUM LETTER PHASE-E LOON', -92556: 'BAMUM LETTER PHASE-E LOOT', -92664: 'BAMUM LETTER PHASE-E LOQ', -92653: 'BAMUM LETTER PHASE-E LU', -92667: 'BAMUM LETTER PHASE-E MA', -92600: 'BAMUM LETTER PHASE-E MAE', -92542: 'BAMUM LETTER PHASE-E MAEM', -92555: 'BAMUM LETTER PHASE-E MAP', -92668: 'BAMUM LETTER PHASE-E MAQ', -92582: 'BAMUM LETTER PHASE-E MBEE', -92520: 'BAMUM LETTER PHASE-E MBEUM', -92666: 'BAMUM LETTER PHASE-E MEN', -92591: 'BAMUM LETTER PHASE-E MFEUQ', -92551: 'BAMUM LETTER PHASE-E MGBA', -92581: 'BAMUM LETTER PHASE-E MGBEN', -92654: 'BAMUM LETTER PHASE-E MI', -92611: 'BAMUM LETTER PHASE-E MIEE', -92671: 'BAMUM LETTER PHASE-E MON', -92614: 'BAMUM LETTER PHASE-E MUAE', -92617: 'BAMUM LETTER PHASE-E MVI', -92662: 'BAMUM LETTER PHASE-E NA', -92613: 'BAMUM LETTER PHASE-E NAE', -92637: 'BAMUM LETTER PHASE-E NDAA MY HOUSE', -92562: 'BAMUM LETTER PHASE-E NDAA SOFTNESS', -92518: 'BAMUM LETTER PHASE-E NDAP', -92592: 'BAMUM LETTER PHASE-E NDIAQ', -92558: 'BAMUM LETTER PHASE-E NDIQ', -92528: 'BAMUM LETTER PHASE-E NDUN', -92658: 'BAMUM LETTER PHASE-E NGA', -92579: 'BAMUM LETTER PHASE-E NGEUREUT', -92557: 'BAMUM LETTER PHASE-E NGGEEEE', -92607: 'BAMUM LETTER PHASE-E NGGEUAE', -92535: 'BAMUM LETTER PHASE-E NGGEUAET', -92563: 'BAMUM LETTER PHASE-E NGGUAESHAE NYAM', -92624: 'BAMUM LETTER PHASE-E NGGUP', -92550: 'BAMUM LETTER PHASE-E NGGURAE', -92531: 'BAMUM LETTER PHASE-E NGKA', -92601: 'BAMUM LETTER PHASE-E NGKAAMI', -92553: 'BAMUM LETTER PHASE-E NGKEUAEM', -92543: 'BAMUM LETTER PHASE-E NGKEUX', -92619: 'BAMUM LETTER PHASE-E NGKUM', -92598: 'BAMUM LETTER PHASE-E NGKUP', -92541: 'BAMUM LETTER PHASE-E NGOP', -92544: 'BAMUM LETTER PHASE-E NGOQ', -92640: 'BAMUM LETTER PHASE-E NGUAE', -92657: 'BAMUM LETTER PHASE-E NGUAET', -92554: 'BAMUM LETTER PHASE-E NJAEMLI', -92628: 'BAMUM LETTER PHASE-E NJEE', -92648: 'BAMUM LETTER PHASE-E NJEE EPOCH', -92547: 'BAMUM LETTER PHASE-E NJEUX', -92584: 'BAMUM LETTER PHASE-E NKOM', -92540: 'BAMUM LETTER PHASE-E NSHIEE', -92545: 'BAMUM LETTER PHASE-E NSHUE', -92527: 'BAMUM LETTER PHASE-E NSHUOP', -92622: 'BAMUM LETTER PHASE-E NTAP', -92604: 'BAMUM LETTER PHASE-E NTUM', -92608: 'BAMUM LETTER PHASE-E NYI BETWEEN', -92589: 'BAMUM LETTER PHASE-E NYI CLEAVER', -92583: 'BAMUM LETTER PHASE-E NZAQ', -92609: 'BAMUM LETTER PHASE-E NZUQ', -92631: 'BAMUM LETTER PHASE-E O', -92625: 'BAMUM LETTER PHASE-E PA PEOPLE', -92634: 'BAMUM LETTER PHASE-E PA PLURAL', -92524: 'BAMUM LETTER PHASE-E PAA', -92536: 'BAMUM LETTER PHASE-E PAAM', -92548: 'BAMUM LETTER PHASE-E PEEM', -92605: 'BAMUM LETTER PHASE-E PEUT', -92647: 'BAMUM LETTER PHASE-E PEUX', -92663: 'BAMUM LETTER PHASE-E PI', -92593: 'BAMUM LETTER PHASE-E PIEEQ', -92621: 'BAMUM LETTER PHASE-E PIET', -92568: 'BAMUM LETTER PHASE-E PO', -92610: 'BAMUM LETTER PHASE-E POON', -92576: 'BAMUM LETTER PHASE-E PU', -92529: 'BAMUM LETTER PHASE-E PUAE', -92618: 'BAMUM LETTER PHASE-E PUAQ', -92649: 'BAMUM LETTER PHASE-E PUE', -92561: 'BAMUM LETTER PHASE-E PUM', -92656: 'BAMUM LETTER PHASE-E RAE', -92526: 'BAMUM LETTER PHASE-E RAQ', -92655: 'BAMUM LETTER PHASE-E REUX', -92546: 'BAMUM LETTER PHASE-E RIMGBA', -92549: 'BAMUM LETTER PHASE-E SAA', -92534: 'BAMUM LETTER PHASE-E SEE', -92560: 'BAMUM LETTER PHASE-E SET', -92580: 'BAMUM LETTER PHASE-E SHEUAEQ', -92638: 'BAMUM LETTER PHASE-E SHIQ', -92659: 'BAMUM LETTER PHASE-E SHO', -92660: 'BAMUM LETTER PHASE-E SHOQ', -92525: 'BAMUM LETTER PHASE-E SOM', -92571: 'BAMUM LETTER PHASE-E SUAEN', -92635: 'BAMUM LETTER PHASE-E TAA', -92577: 'BAMUM LETTER PHASE-E TAAQ', -92559: 'BAMUM LETTER PHASE-E TAEN NTEUM', -92530: 'BAMUM LETTER PHASE-E TAM', -92636: 'BAMUM LETTER PHASE-E TAQ', -92672: 'BAMUM LETTER PHASE-E TEN', -92669: 'BAMUM LETTER PHASE-E TEU', -92572: 'BAMUM LETTER PHASE-E TEUAEQ', -92537: 'BAMUM LETTER PHASE-E TOO', -92519: 'BAMUM LETTER PHASE-E TOON', -92630: 'BAMUM LETTER PHASE-E TOQ', -92566: 'BAMUM LETTER PHASE-E TUAE', -92586: 'BAMUM LETTER PHASE-E TUM', -92569: 'BAMUM LETTER PHASE-E TUMAE', -92652: 'BAMUM LETTER PHASE-E VEE', -92573: 'BAMUM LETTER PHASE-E VEUAE', -92522: 'BAMUM LETTER PHASE-E VOM', -92574: 'BAMUM LETTER PHASE-E WEUX', -92650: 'BAMUM LETTER PHASE-E WUE', -92533: 'BAMUM LETTER PHASE-E WUO', -92588: 'BAMUM LETTER PHASE-E YAP', -92567: 'BAMUM LETTER PHASE-E YEUAE', -92623: 'BAMUM LETTER PHASE-E YEUAET', -92606: 'BAMUM LETTER PHASE-E YEUM', -92639: 'BAMUM LETTER PHASE-E YEUX', -92564: 'BAMUM LETTER PHASE-E YIEE', -92590: 'BAMUM LETTER PHASE-E YIT', -92643: 'BAMUM LETTER PHASE-E YOQ COVER', -92642: 'BAMUM LETTER PHASE-E YOQ SWIMMING', -92641: 'BAMUM LETTER PHASE-E YUAEN', -92594: 'BAMUM LETTER PHASE-E YUEQ', -92645: 'BAMUM LETTER PHASE-E YUN', -92644: 'BAMUM LETTER PHASE-E YUQ', -92678: 'BAMUM LETTER PHASE-F EE', -92715: 'BAMUM LETTER PHASE-F FOM', -92675: 'BAMUM LETTER PHASE-F KA', -92710: 'BAMUM LETTER PHASE-F KEN', -92695: 'BAMUM LETTER PHASE-F KET', -92719: 'BAMUM LETTER PHASE-F KO', -92726: 'BAMUM LETTER PHASE-F KPA', -92677: 'BAMUM LETTER PHASE-F KU', -92694: 'BAMUM LETTER PHASE-F KYEE', -92682: 'BAMUM LETTER PHASE-F LA', -92717: 'BAMUM LETTER PHASE-F LI', -92718: 'BAMUM LETTER PHASE-F LOQ', -92689: 'BAMUM LETTER PHASE-F M', -92722: 'BAMUM LETTER PHASE-F MA', -92724: 'BAMUM LETTER PHASE-F MBAA', -92720: 'BAMUM LETTER PHASE-F MBEN', -92685: 'BAMUM LETTER PHASE-F MEEEE', -92723: 'BAMUM LETTER PHASE-F MO', -92687: 'BAMUM LETTER PHASE-F NDAA', -92712: 'BAMUM LETTER PHASE-F NGGA', -92711: 'BAMUM LETTER PHASE-F NGKWAEN', -92708: 'BAMUM LETTER PHASE-F NI', -92688: 'BAMUM LETTER PHASE-F NJAEM', -92698: 'BAMUM LETTER PHASE-F NJUAE', -92702: 'BAMUM LETTER PHASE-F NSHA', -92704: 'BAMUM LETTER PHASE-F NTEE', -92697: 'BAMUM LETTER PHASE-F NU', -92696: 'BAMUM LETTER PHASE-F NUAE', -92681: 'BAMUM LETTER PHASE-F NYI', -92706: 'BAMUM LETTER PHASE-F PEE', -92703: 'BAMUM LETTER PHASE-F PEUX', -92714: 'BAMUM LETTER PHASE-F PUAE', -92679: 'BAMUM LETTER PHASE-F REE', -92721: 'BAMUM LETTER PHASE-F REN', -92709: 'BAMUM LETTER PHASE-F REUX', -92684: 'BAMUM LETTER PHASE-F RIEE', -92683: 'BAMUM LETTER PHASE-F RII', -92707: 'BAMUM LETTER PHASE-F RU', -92727: 'BAMUM LETTER PHASE-F SAMBA', -92693: 'BAMUM LETTER PHASE-F SEUX', -92691: 'BAMUM LETTER PHASE-F SHII', -92713: 'BAMUM LETTER PHASE-F SHO', -92700: 'BAMUM LETTER PHASE-F SHU', -92692: 'BAMUM LETTER PHASE-F SI', -92690: 'BAMUM LETTER PHASE-F SUU', -92686: 'BAMUM LETTER PHASE-F TAA', -92680: 'BAMUM LETTER PHASE-F TAE', -92725: 'BAMUM LETTER PHASE-F TET', -92676: 'BAMUM LETTER PHASE-F U', -92728: 'BAMUM LETTER PHASE-F VUEQ', -92716: 'BAMUM LETTER PHASE-F WA', -92705: 'BAMUM LETTER PHASE-F WUE', -92701: 'BAMUM LETTER PHASE-F YA', -92699: 'BAMUM LETTER PHASE-F YOQ', -127820: 'BANANA', -127974: 'BANK', -128181: 'BANKNOTE WITH DOLLAR SIGN', -128182: 'BANKNOTE WITH EURO SIGN', -128183: 'BANKNOTE WITH POUND SIGN', -128180: 'BANKNOTE WITH YEN SIGN', -128202: 'BAR CHART', -128136: 'BARBER POLE', -127936: 'BASKETBALL AND HOOP', -7153: 'BATAK CONSONANT SIGN H', -7152: 'BATAK CONSONANT SIGN NG', -7104: 'BATAK LETTER A', -7109: 'BATAK LETTER BA', -7137: 'BATAK LETTER CA', -7121: 'BATAK LETTER DA', -7118: 'BATAK LETTER GA', -7106: 'BATAK LETTER HA', -7140: 'BATAK LETTER I', -7120: 'BATAK LETTER JA', -7110: 'BATAK LETTER KARO BA', -7134: 'BATAK LETTER LA', -7124: 'BATAK LETTER MA', -7108: 'BATAK LETTER MANDAILING HA', -7114: 'BATAK LETTER MANDAILING NA', -7130: 'BATAK LETTER MANDAILING SA', -7139: 'BATAK LETTER MBA', -7113: 'BATAK LETTER NA', -7138: 'BATAK LETTER NDA', -7133: 'BATAK LETTER NGA', -7127: 'BATAK LETTER NORTHERN TA', -7136: 'BATAK LETTER NYA', -7111: 'BATAK LETTER PA', -7117: 'BATAK LETTER PAKPAK WA', -7122: 'BATAK LETTER RA', -7128: 'BATAK LETTER SA', -7105: 'BATAK LETTER SIMALUNGUN A', -7119: 'BATAK LETTER SIMALUNGUN GA', -7107: 'BATAK LETTER SIMALUNGUN HA', -7135: 'BATAK LETTER SIMALUNGUN LA', -7125: 'BATAK LETTER SIMALUNGUN MA', -7112: 'BATAK LETTER SIMALUNGUN PA', -7123: 'BATAK LETTER SIMALUNGUN RA', -7129: 'BATAK LETTER SIMALUNGUN SA', -7116: 'BATAK LETTER SIMALUNGUN WA', -7132: 'BATAK LETTER SIMALUNGUN YA', -7126: 'BATAK LETTER SOUTHERN TA', -7141: 'BATAK LETTER U', -7115: 'BATAK LETTER WA', -7131: 'BATAK LETTER YA', -7154: 'BATAK PANGOLAT', -7155: 'BATAK PANONGONAN', -7142: 'BATAK SIGN TOMPI', -7166: 'BATAK SYMBOL BINDU JUDUL', -7164: 'BATAK SYMBOL BINDU NA METEK', -7167: 'BATAK SYMBOL BINDU PANGOLAT', -7165: 'BATAK SYMBOL BINDU PINARBORAS', -7143: 'BATAK VOWEL SIGN E', -7145: 'BATAK VOWEL SIGN EE', -7146: 'BATAK VOWEL SIGN I', -7147: 'BATAK VOWEL SIGN KARO I', -7149: 'BATAK VOWEL SIGN KARO O', -7148: 'BATAK VOWEL SIGN O', -7144: 'BATAK VOWEL SIGN PAKPAK E', -7150: 'BATAK VOWEL SIGN U', -7151: 'BATAK VOWEL SIGN U FOR SIMALUNGUN SA', -128704: 'BATH', -128705: 'BATHTUB', -128267: 'BATTERY', -128059: 'BEAR FACE', -128147: 'BEATING HEART', -127866: 'BEER MUG', -128276: 'BELL', -128277: 'BELL WITH CANCELLATION STROKE', -983621: 'BENGALI LETTER KHINYA', -127857: 'BENTO BOX', -128690: 'BICYCLE', -128692: 'BICYCLIST', -128089: 'BIKINI', -127921: 'BILLIARDS', -128038: 'BIRD', -127874: 'BIRTHDAY CAKE', -9196: 'BLACK DOWN-POINTING DOUBLE TRIANGLE', -9194: 'BLACK LEFT-POINTING DOUBLE TRIANGLE', -9198: 'BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR', -10067: 'BLACK QUESTION MARK ORNAMENT', -9193: 'BLACK RIGHT-POINTING DOUBLE TRIANGLE', -9197: 'BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR', -9199: 'BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR', -128306: 'BLACK SQUARE BUTTON', -9195: 'BLACK UP-POINTING DOUBLE TRIANGLE', -127804: 'BLOSSOM', -128033: 'BLOWFISH', -128216: 'BLUE BOOK', -128153: 'BLUE HEART', -128023: 'BOAR', -128163: 'BOMB', -128278: 'BOOKMARK', -128209: 'BOOKMARK TABS', -128218: 'BOOKS', -12728: 'BOPOMOFO LETTER GH', -12729: 'BOPOMOFO LETTER LH', -12730: 'BOPOMOFO LETTER ZY', -128144: 'BOUQUET', -127923: 'BOWLING', -128102: 'BOY', -69703: 'BRAHMI DANDA', -69742: 'BRAHMI DIGIT EIGHT', -69739: 'BRAHMI DIGIT FIVE', -69738: 'BRAHMI DIGIT FOUR', -69743: 'BRAHMI DIGIT NINE', -69735: 'BRAHMI DIGIT ONE', -69741: 'BRAHMI DIGIT SEVEN', -69740: 'BRAHMI DIGIT SIX', -69737: 'BRAHMI DIGIT THREE', -69736: 'BRAHMI DIGIT TWO', -69734: 'BRAHMI DIGIT ZERO', -69704: 'BRAHMI DOUBLE DANDA', -69637: 'BRAHMI LETTER A', -69638: 'BRAHMI LETTER AA', -69648: 'BRAHMI LETTER AI', -69650: 'BRAHMI LETTER AU', -69673: 'BRAHMI LETTER BA', -69674: 'BRAHMI LETTER BHA', -69656: 'BRAHMI LETTER CA', -69657: 'BRAHMI LETTER CHA', -69668: 'BRAHMI LETTER DA', -69663: 'BRAHMI LETTER DDA', -69664: 'BRAHMI LETTER DDHA', -69669: 'BRAHMI LETTER DHA', -69647: 'BRAHMI LETTER E', -69653: 'BRAHMI LETTER GA', -69654: 'BRAHMI LETTER GHA', -69683: 'BRAHMI LETTER HA', -69639: 'BRAHMI LETTER I', -69640: 'BRAHMI LETTER II', -69658: 'BRAHMI LETTER JA', -69659: 'BRAHMI LETTER JHA', -69651: 'BRAHMI LETTER KA', -69652: 'BRAHMI LETTER KHA', -69678: 'BRAHMI LETTER LA', -69684: 'BRAHMI LETTER LLA', -69675: 'BRAHMI LETTER MA', -69670: 'BRAHMI LETTER NA', -69655: 'BRAHMI LETTER NGA', -69665: 'BRAHMI LETTER NNA', -69660: 'BRAHMI LETTER NYA', -69649: 'BRAHMI LETTER O', -69685: 'BRAHMI LETTER OLD TAMIL LLLA', -69687: 'BRAHMI LETTER OLD TAMIL NNNA', -69686: 'BRAHMI LETTER OLD TAMIL RRA', -69671: 'BRAHMI LETTER PA', -69672: 'BRAHMI LETTER PHA', -69677: 'BRAHMI LETTER RA', -69682: 'BRAHMI LETTER SA', -69680: 'BRAHMI LETTER SHA', -69681: 'BRAHMI LETTER SSA', -69666: 'BRAHMI LETTER TA', -69667: 'BRAHMI LETTER THA', -69661: 'BRAHMI LETTER TTA', -69662: 'BRAHMI LETTER TTHA', -69641: 'BRAHMI LETTER U', -69642: 'BRAHMI LETTER UU', -69679: 'BRAHMI LETTER VA', -69645: 'BRAHMI LETTER VOCALIC L', -69646: 'BRAHMI LETTER VOCALIC LL', -69643: 'BRAHMI LETTER VOCALIC R', -69644: 'BRAHMI LETTER VOCALIC RR', -69676: 'BRAHMI LETTER YA', -69721: 'BRAHMI NUMBER EIGHT', -69730: 'BRAHMI NUMBER EIGHTY', -69727: 'BRAHMI NUMBER FIFTY', -69718: 'BRAHMI NUMBER FIVE', -69726: 'BRAHMI NUMBER FORTY', -69717: 'BRAHMI NUMBER FOUR', -69722: 'BRAHMI NUMBER NINE', -69731: 'BRAHMI NUMBER NINETY', -69714: 'BRAHMI NUMBER ONE', -69732: 'BRAHMI NUMBER ONE HUNDRED', -69733: 'BRAHMI NUMBER ONE THOUSAND', -69720: 'BRAHMI NUMBER SEVEN', -69729: 'BRAHMI NUMBER SEVENTY', -69719: 'BRAHMI NUMBER SIX', -69728: 'BRAHMI NUMBER SIXTY', -69723: 'BRAHMI NUMBER TEN', -69725: 'BRAHMI NUMBER THIRTY', -69716: 'BRAHMI NUMBER THREE', -69724: 'BRAHMI NUMBER TWENTY', -69715: 'BRAHMI NUMBER TWO', -69708: 'BRAHMI PUNCTUATION CRESCENT BAR', -69705: 'BRAHMI PUNCTUATION DOT', -69706: 'BRAHMI PUNCTUATION DOUBLE DOT', -69707: 'BRAHMI PUNCTUATION LINE', -69709: 'BRAHMI PUNCTUATION LOTUS', -69633: 'BRAHMI SIGN ANUSVARA', -69632: 'BRAHMI SIGN CANDRABINDU', -69635: 'BRAHMI SIGN JIHVAMULIYA', -69636: 'BRAHMI SIGN UPADHMANIYA', -69634: 'BRAHMI SIGN VISARGA', -69702: 'BRAHMI VIRAMA', -69688: 'BRAHMI VOWEL SIGN AA', -69699: 'BRAHMI VOWEL SIGN AI', -69701: 'BRAHMI VOWEL SIGN AU', -69689: 'BRAHMI VOWEL SIGN BHATTIPROLU AA', -69698: 'BRAHMI VOWEL SIGN E', -69690: 'BRAHMI VOWEL SIGN I', -69691: 'BRAHMI VOWEL SIGN II', -69700: 'BRAHMI VOWEL SIGN O', -69692: 'BRAHMI VOWEL SIGN U', -69693: 'BRAHMI VOWEL SIGN UU', -69696: 'BRAHMI VOWEL SIGN VOCALIC L', -69697: 'BRAHMI VOWEL SIGN VOCALIC LL', -69694: 'BRAHMI VOWEL SIGN VOCALIC R', -69695: 'BRAHMI VOWEL SIGN VOCALIC RR', -127838: 'BREAD', -128112: 'BRIDE WITH VEIL', -127753: 'BRIDGE AT NIGHT', -128188: 'BRIEFCASE', -128148: 'BROKEN HEART', -128027: 'BUG', -128652: 'BUS', -128655: 'BUS STOP', -128100: 'BUST IN SILHOUETTE', -128101: 'BUSTS IN SILHOUETTE', -127797: 'CACTUS', -128197: 'CALENDAR', -128247: 'CAMERA', -127852: 'CANDY', -128199: 'CARD INDEX', -127904: 'CAROUSEL HORSE', -127887: 'CARP STREAMER', -128008: 'CAT', -128049: 'CAT FACE', -128569: 'CAT FACE WITH TEARS OF JOY', -128572: 'CAT FACE WITH WRY SMILE', -128201: 'CHART WITH DOWNWARDS TREND', -128200: 'CHART WITH UPWARDS TREND', -128185: 'CHART WITH UPWARDS TREND AND YEN SIGN', -128227: 'CHEERING MEGAPHONE', -127937: 'CHEQUERED FLAG', -127826: 'CHERRIES', -127800: 'CHERRY BLOSSOM', -127792: 'CHESTNUT', -128020: 'CHICKEN', -128696: 'CHILDREN CROSSING', -127851: 'CHOCOLATE BAR', -127876: 'CHRISTMAS TREE', -127910: 'CINEMA', -127569: 'CIRCLED IDEOGRAPH ACCEPT', -127568: 'CIRCLED IDEOGRAPH ADVANTAGE', -127914: 'CIRCUS TENT', -127750: 'CITYSCAPE AT DUSK', -127916: 'CLAPPER BOARD', -128079: 'CLAPPING HANDS SIGN', -127867: 'CLINKING BEER MUGS', -128203: 'CLIPBOARD', -128343: 'CLOCK FACE EIGHT OCLOCK', -128355: 'CLOCK FACE EIGHT-THIRTY', -128346: 'CLOCK FACE ELEVEN OCLOCK', -128358: 'CLOCK FACE ELEVEN-THIRTY', -128340: 'CLOCK FACE FIVE OCLOCK', -128352: 'CLOCK FACE FIVE-THIRTY', -128339: 'CLOCK FACE FOUR OCLOCK', -128351: 'CLOCK FACE FOUR-THIRTY', -128344: 'CLOCK FACE NINE OCLOCK', -128356: 'CLOCK FACE NINE-THIRTY', -128336: 'CLOCK FACE ONE OCLOCK', -128348: 'CLOCK FACE ONE-THIRTY', -128342: 'CLOCK FACE SEVEN OCLOCK', -128354: 'CLOCK FACE SEVEN-THIRTY', -128341: 'CLOCK FACE SIX OCLOCK', -128353: 'CLOCK FACE SIX-THIRTY', -128345: 'CLOCK FACE TEN OCLOCK', -128357: 'CLOCK FACE TEN-THIRTY', -128338: 'CLOCK FACE THREE OCLOCK', -128350: 'CLOCK FACE THREE-THIRTY', -128347: 'CLOCK FACE TWELVE OCLOCK', -128359: 'CLOCK FACE TWELVE-THIRTY', -128337: 'CLOCK FACE TWO OCLOCK', -128349: 'CLOCK FACE TWO-THIRTY', -128259: 'CLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS', -128257: 'CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS', -128258: 'CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY', -128213: 'CLOSED BOOK', -128272: 'CLOSED LOCK WITH KEY', -128234: 'CLOSED MAILBOX WITH LOWERED FLAG', -128235: 'CLOSED MAILBOX WITH RAISED FLAG', -127746: 'CLOSED UMBRELLA', -127864: 'COCKTAIL GLASS', -128165: 'COLLISION SYMBOL', -7676: 'COMBINING DOUBLE INVERTED BREVE BELOW', -127882: 'CONFETTI BALL', -128534: 'CONFOUNDED FACE', -128679: 'CONSTRUCTION SIGN', -128119: 'CONSTRUCTION WORKER', -127978: 'CONVENIENCE STORE', -127834: 'COOKED RICE', -127850: 'COOKIE', -127859: 'COOKING', -128145: 'COUPLE WITH HEART', -128004: 'COW', -128046: 'COW FACE', -128179: 'CREDIT CARD', -127769: 'CRESCENT MOON', -128010: 'CROCODILE', -10060: 'CROSS MARK', -127884: 'CROSSED FLAGS', -128081: 'CROWN', -128575: 'CRYING CAT FACE', -128546: 'CRYING FACE', -128302: 'CRYSTAL BALL', -10160: 'CURLY LOOP', -128177: 'CURRENCY EXCHANGE', -127835: 'CURRY AND RICE', -127854: 'CUSTARD', -128707: 'CUSTOMS', -127744: 'CYCLONE', -42592: 'CYRILLIC CAPITAL LETTER REVERSED TSE', -1318: 'CYRILLIC CAPITAL LETTER SHHA WITH DESCENDER', -42593: 'CYRILLIC SMALL LETTER REVERSED TSE', -1319: 'CYRILLIC SMALL LETTER SHHA WITH DESCENDER', -128131: 'DANCER', -127841: 'DANGO', -128168: 'DASH SYMBOL', -127795: 'DECIDUOUS TREE', -128666: 'DELIVERY TRUCK', -127980: 'DEPARTMENT STORE', -2421: 'DEVANAGARI LETTER AW', -2419: 'DEVANAGARI LETTER OE', -2420: 'DEVANAGARI LETTER OOE', -2422: 'DEVANAGARI LETTER UE', -2423: 'DEVANAGARI LETTER UUE', -2383: 'DEVANAGARI VOWEL SIGN AW', -2362: 'DEVANAGARI VOWEL SIGN OE', -2363: 'DEVANAGARI VOWEL SIGN OOE', -2390: 'DEVANAGARI VOWEL SIGN UE', -2391: 'DEVANAGARI VOWEL SIGN UUE', -128160: 'DIAMOND SHAPE WITH A DOT INSIDE', -127919: 'DIRECT HIT', -128549: 'DISAPPOINTED BUT RELIEVED FACE', -128542: 'DISAPPOINTED FACE', -128565: 'DIZZY FACE', -128171: 'DIZZY SYMBOL', -128687: 'DO NOT LITTER SYMBOL', -128021: 'DOG', -128054: 'DOG FACE', -128044: 'DOLPHIN', -128682: 'DOOR', -10175: 'DOUBLE CURLY LOOP', -127849: 'DOUGHNUT', -128315: 'DOWN-POINTING RED TRIANGLE', -128317: 'DOWN-POINTING SMALL RED TRIANGLE', -128009: 'DRAGON', -128050: 'DRAGON FACE', -128087: 'DRESS', -128042: 'DROMEDARY CAMEL', -128167: 'DROPLET', -128192: 'DVD', -128231: 'E-MAIL SYMBOL', -128066: 'EAR', -127805: 'EAR OF MAIZE', -127806: 'EAR OF RICE', -127758: 'EARTH GLOBE AMERICAS', -127759: 'EARTH GLOBE ASIA-AUSTRALIA', -127757: 'EARTH GLOBE EUROPE-AFRICA', -128161: 'ELECTRIC LIGHT BULB', -128268: 'ELECTRIC PLUG', -128294: 'ELECTRIC TORCH', -128024: 'ELEPHANT', -128282: 'END WITH LEFTWARDS ARROW ABOVE', -128233: 'ENVELOPE WITH DOWNWARDS ARROW ABOVE', -4957: 'ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK', -4958: 'ETHIOPIC COMBINING VOWEL LENGTH MARK', -43816: 'ETHIOPIC SYLLABLE BBA', -43819: 'ETHIOPIC SYLLABLE BBAA', -43821: 'ETHIOPIC SYLLABLE BBE', -43820: 'ETHIOPIC SYLLABLE BBEE', -43818: 'ETHIOPIC SYLLABLE BBI', -43822: 'ETHIOPIC SYLLABLE BBO', -43817: 'ETHIOPIC SYLLABLE BBU', -43808: 'ETHIOPIC SYLLABLE CCHHA', -43811: 'ETHIOPIC SYLLABLE CCHHAA', -43813: 'ETHIOPIC SYLLABLE CCHHE', -43812: 'ETHIOPIC SYLLABLE CCHHEE', -43810: 'ETHIOPIC SYLLABLE CCHHI', -43814: 'ETHIOPIC SYLLABLE CCHHO', -43809: 'ETHIOPIC SYLLABLE CCHHU', -43787: 'ETHIOPIC SYLLABLE DDHAA', -43789: 'ETHIOPIC SYLLABLE DDHE', -43788: 'ETHIOPIC SYLLABLE DDHEE', -43786: 'ETHIOPIC SYLLABLE DDHI', -43790: 'ETHIOPIC SYLLABLE DDHO', -43785: 'ETHIOPIC SYLLABLE DDHU', -43795: 'ETHIOPIC SYLLABLE DZAA', -43797: 'ETHIOPIC SYLLABLE DZE', -43796: 'ETHIOPIC SYLLABLE DZEE', -43794: 'ETHIOPIC SYLLABLE DZI', -43798: 'ETHIOPIC SYLLABLE DZO', -43793: 'ETHIOPIC SYLLABLE DZU', -43779: 'ETHIOPIC SYLLABLE TTHAA', -43781: 'ETHIOPIC SYLLABLE TTHE', -43780: 'ETHIOPIC SYLLABLE TTHEE', -43778: 'ETHIOPIC SYLLABLE TTHI', -43782: 'ETHIOPIC SYLLABLE TTHO', -43777: 'ETHIOPIC SYLLABLE TTHU', -127984: 'EUROPEAN CASTLE', -127972: 'EUROPEAN POST OFFICE', -127794: 'EVERGREEN TREE', -128125: 'EXTRATERRESTRIAL ALIEN', -128083: 'EYEGLASSES', -128064: 'EYES', -128134: 'FACE MASSAGE', -128523: 'FACE SAVOURING DELICIOUS FOOD', -128561: 'FACE SCREAMING IN FEAR', -128536: 'FACE THROWING A KISS', -128531: 'FACE WITH COLD SWEAT', -128548: 'FACE WITH LOOK OF TRIUMPH', -128567: 'FACE WITH MEDICAL MASK', -128581: 'FACE WITH NO GOOD GESTURE', -128582: 'FACE WITH OK GESTURE', -128560: 'FACE WITH OPEN MOUTH AND COLD SWEAT', -128541: 'FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES', -128540: 'FACE WITH STUCK-OUT TONGUE AND WINKING EYE', -128514: 'FACE WITH TEARS OF JOY', -128566: 'FACE WITHOUT MOUTH', -127981: 'FACTORY', -127810: 'FALLEN LEAF', -128106: 'FAMILY', -127877: 'FATHER CHRISTMAS', -128224: 'FAX MACHINE', -128552: 'FEARFUL FACE', -127905: 'FERRIS WHEEL', -128193: 'FILE FOLDER', -128293: 'FIRE', -128658: 'FIRE ENGINE', -127879: 'FIREWORK SPARKLER', -127878: 'FIREWORKS', -127763: 'FIRST QUARTER MOON SYMBOL', -127771: 'FIRST QUARTER MOON WITH FACE', -128031: 'FISH', -127845: 'FISH CAKE WITH SWIRL DESIGN', -127907: 'FISHING POLE AND FISH', -128074: 'FISTED HAND SIGN', -128170: 'FLEXED BICEPS', -128190: 'FLOPPY DISK', -127924: 'FLOWER PLAYING CARDS', -128563: 'FLUSHED FACE', -127745: 'FOGGY', -128099: 'FOOTPRINTS', -127860: 'FORK AND KNIFE', -127808: 'FOUR LEAF CLOVER', -127839: 'FRENCH FRIES', -127844: 'FRIED SHRIMP', -128056: 'FROG FACE', -128037: 'FRONT-FACING BABY CHICK', -127765: 'FULL MOON SYMBOL', -127773: 'FULL MOON WITH FACE', -127922: 'GAME DIE', -128142: 'GEM STONE', -983912: 'GEORGIAN LETTER U-BRJGU', -128123: 'GHOST', -128103: 'GIRL', -127760: 'GLOBE WITH MERIDIANS', -127775: 'GLOWING STAR', -128016: 'GOAT', -127891: 'GRADUATION CAP', -127815: 'GRAPES', -127823: 'GREEN APPLE', -128215: 'GREEN BOOK', -128154: 'GREEN HEART', -128568: 'GRINNING CAT FACE WITH SMILING EYES', -128513: 'GRINNING FACE WITH SMILING EYES', -128151: 'GROWING HEART', -128130: 'GUARDSMAN', -127928: 'GUITAR', -128135: 'HAIRCUT', -127828: 'HAMBURGER', -128296: 'HAMMER', -128057: 'HAMSTER FACE', -128092: 'HANDBAG', -128587: 'HAPPY PERSON RAISING ONE HAND', -128035: 'HATCHING CHICK', -127911: 'HEADPHONE', -128585: 'HEAR-NO-EVIL MONKEY', -128159: 'HEART DECORATION', -128152: 'HEART WITH ARROW', -128157: 'HEART WITH RIBBON', -10135: 'HEAVY DIVISION SIGN', -128178: 'HEAVY DOLLAR SIGN', -10080: 'HEAVY LOW DOUBLE COMMA QUOTATION MARK ORNAMENT', -10079: 'HEAVY LOW SINGLE COMMA QUOTATION MARK ORNAMENT', -10134: 'HEAVY MINUS SIGN', -10133: 'HEAVY PLUS SIGN', -128641: 'HELICOPTER', -127807: 'HERB', -127802: 'HIBISCUS', -128262: 'HIGH BRIGHTNESS SYMBOL', -128096: 'HIGH-HEELED SHOE', -128644: 'HIGH-SPEED TRAIN', -128645: 'HIGH-SPEED TRAIN WITH BULLET NOSE', -110593: 'HIRAGANA LETTER ARCHAIC YE', -983954: 'HIRAGANA LETTER BIDAKUON NGA', -983957: 'HIRAGANA LETTER BIDAKUON NGE', -983955: 'HIRAGANA LETTER BIDAKUON NGI', -983958: 'HIRAGANA LETTER BIDAKUON NGO', -983956: 'HIRAGANA LETTER BIDAKUON NGU', -128298: 'HOCHO', -127855: 'HONEY POT', -128029: 'HONEYBEE', -128677: 'HORIZONTAL TRAFFIC LIGHT', -128014: 'HORSE', -128052: 'HORSE FACE', -127943: 'HORSE RACING', -127973: 'HOSPITAL', -127976: 'HOTEL', -9203: 'HOURGLASS WITH FLOWING SAND', -127968: 'HOUSE BUILDING', -127969: 'HOUSE WITH GARDEN', -128175: 'HUNDRED POINTS SYMBOL', -127848: 'ICE CREAM', -128127: 'IMP', -128229: 'INBOX TRAY', -128232: 'INCOMING ENVELOPE', -8377: 'INDIAN RUPEE SIGN', -128129: 'INFORMATION DESK PERSON', -128288: 'INPUT SYMBOL FOR LATIN CAPITAL LETTERS', -128292: 'INPUT SYMBOL FOR LATIN LETTERS', -128289: 'INPUT SYMBOL FOR LATIN SMALL LETTERS', -128290: 'INPUT SYMBOL FOR NUMBERS', -128291: 'INPUT SYMBOL FOR SYMBOLS', -9959: 'INVERTED PENTAGRAM', -127982: 'IZAKAYA LANTERN', -127875: 'JACK-O-LANTERN', -127983: 'JAPANESE CASTLE', -127886: 'JAPANESE DOLLS', -128122: 'JAPANESE GOBLIN', -128121: 'JAPANESE OGRE', -127971: 'JAPANESE POST OFFICE', -128304: 'JAPANESE SYMBOL FOR BEGINNER', -128086: 'JEANS', -983964: 'KATAKANA LETTER AINU CE', -983967: 'KATAKANA LETTER AINU P', -983966: 'KATAKANA LETTER AINU TO', -983965: 'KATAKANA LETTER AINU TU', -110592: 'KATAKANA LETTER ARCHAIC E', -983959: 'KATAKANA LETTER BIDAKUON NGA', -983962: 'KATAKANA LETTER BIDAKUON NGE', -983960: 'KATAKANA LETTER BIDAKUON NGI', -983963: 'KATAKANA LETTER BIDAKUON NGO', -983961: 'KATAKANA LETTER BIDAKUON NGU', -128273: 'KEY', -128287: 'KEYCAP TEN', -983933: 'KHMER CONSONANT SIGN COENG BA', -983918: 'KHMER CONSONANT SIGN COENG CA', -983919: 'KHMER CONSONANT SIGN COENG CHA', -983921: 'KHMER CONSONANT SIGN COENG CHO', -983920: 'KHMER CONSONANT SIGN COENG CO', -983923: 'KHMER CONSONANT SIGN COENG DA', -983925: 'KHMER CONSONANT SIGN COENG DO', -983945: 'KHMER CONSONANT SIGN COENG HA', -983913: 'KHMER CONSONANT SIGN COENG KA', -983914: 'KHMER CONSONANT SIGN COENG KHA', -983916: 'KHMER CONSONANT SIGN COENG KHO', -983915: 'KHMER CONSONANT SIGN COENG KO', -983946: 'KHMER CONSONANT SIGN COENG LA', -983940: 'KHMER CONSONANT SIGN COENG LO', -983937: 'KHMER CONSONANT SIGN COENG MO', -983927: 'KHMER CONSONANT SIGN COENG NA', -983917: 'KHMER CONSONANT SIGN COENG NGO', -983932: 'KHMER CONSONANT SIGN COENG NO', -983922: 'KHMER CONSONANT SIGN COENG NYO', -983934: 'KHMER CONSONANT SIGN COENG PHA', -983936: 'KHMER CONSONANT SIGN COENG PHO', -983935: 'KHMER CONSONANT SIGN COENG PO', -983939: 'KHMER CONSONANT SIGN COENG RO', -983944: 'KHMER CONSONANT SIGN COENG SA', -983942: 'KHMER CONSONANT SIGN COENG SHA', -983943: 'KHMER CONSONANT SIGN COENG SSA', -983928: 'KHMER CONSONANT SIGN COENG TA', -983929: 'KHMER CONSONANT SIGN COENG THA', -983931: 'KHMER CONSONANT SIGN COENG THO', -983930: 'KHMER CONSONANT SIGN COENG TO', -983924: 'KHMER CONSONANT SIGN COENG TTHA', -983926: 'KHMER CONSONANT SIGN COENG TTHO', -983941: 'KHMER CONSONANT SIGN COENG VO', -983938: 'KHMER CONSONANT SIGN COENG YO', -983951: 'KHMER INDEPENDENT VOWEL SIGN COENG QE', From pypy.commits at gmail.com Wed Feb 27 10:54:00 2019 From: pypy.commits at gmail.com (cfbolz) Date: Wed, 27 Feb 2019 07:54:00 -0800 (PST) Subject: [pypy-commit] pypy py3.6: fix test_constant_tuples: bytes were not considered constantifiable Message-ID: <5c76b298.1c69fb81.ff635.d6dc@mx.google.com> Author: Carl Friedrich Bolz-Tereick Branch: py3.6 Changeset: r96188:2ff7e1e52fe2 Date: 2019-02-27 10:50 +0100 http://bitbucket.org/pypy/pypy/changeset/2ff7e1e52fe2/ Log: fix test_constant_tuples: bytes were not considered constantifiable diff --git a/pypy/interpreter/astcompiler/optimize.py b/pypy/interpreter/astcompiler/optimize.py --- a/pypy/interpreter/astcompiler/optimize.py +++ b/pypy/interpreter/astcompiler/optimize.py @@ -57,6 +57,12 @@ return self.s +class __extend__(ast.Bytes): + + def as_constant(self): + return self.s + + class __extend__(ast.Ellipsis): def as_constant_truth(self, space): From pypy.commits at gmail.com Wed Feb 27 17:47:44 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 27 Feb 2019 14:47:44 -0800 (PST) Subject: [pypy-commit] pypy newmemoryview-app-level: fix 2/3 compatibility Message-ID: <5c771390.1c69fb81.2a13b.c5cd@mx.google.com> Author: Matti Picus Branch: newmemoryview-app-level Changeset: r96189:05a3ee709f7c Date: 2019-02-26 21:23 +0200 http://bitbucket.org/pypy/pypy/changeset/05a3ee709f7c/ Log: fix 2/3 compatibility diff --git a/lib_pypy/_ctypes/array.py b/lib_pypy/_ctypes/array.py --- a/lib_pypy/_ctypes/array.py +++ b/lib_pypy/_ctypes/array.py @@ -254,7 +254,7 @@ obj = obj[0] fmt = get_format_str(obj._type_) - itemsize = len(buffer(obj[0])) + itemsize = len(memoryview(obj[0])) return __pypy__.newmemoryview(memoryview(self._buffer), itemsize, fmt, shape) ARRAY_CACHE = {} @@ -288,8 +288,12 @@ bo = byteorder[sys.byteorder] flds = [] for name, obj in typ._fields_: - flds.append(bo) - flds.append(get_format_str(obj)) + ch = get_format_str(obj) + if (ch) == 'B': + flds.append(byteorder[sys.byteorder]) + else: + flds.append(bo) + flds.append(ch) flds.append(':') flds.append(name) flds.append(':') From pypy.commits at gmail.com Wed Feb 27 17:47:46 2019 From: pypy.commits at gmail.com (mattip) Date: Wed, 27 Feb 2019 14:47:46 -0800 (PST) Subject: [pypy-commit] pypy newmemoryview-app-level: test, add a class which fills in tp_as_buffer.bf_getbuffer via __buffer__ Message-ID: <5c771392.1c69fb81.c2a9e.2608@mx.google.com> Author: Matti Picus Branch: newmemoryview-app-level Changeset: r96190:ecee4043385d Date: 2019-02-27 21:54 +0200 http://bitbucket.org/pypy/pypy/changeset/ecee4043385d/ Log: test, add a class which fills in tp_as_buffer.bf_getbuffer via __buffer__ diff --git a/lib_pypy/_ctypes/array.py b/lib_pypy/_ctypes/array.py --- a/lib_pypy/_ctypes/array.py +++ b/lib_pypy/_ctypes/array.py @@ -277,8 +277,8 @@ ARRAY_CACHE[key] = cls return cls -byteorder = {'little': '>', 'big': '<'} -swappedorder = {'little': '<', 'big': '>'} +byteorder = {'little': '<', 'big': '>'} +swappedorder = {'little': '>', 'big': '<'} def get_format_str(typ): if hasattr(typ, '_fields_'): diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py --- a/pypy/module/__pypy__/__init__.py +++ b/pypy/module/__pypy__/__init__.py @@ -62,11 +62,18 @@ class PyPyDateTime(MixedModule): appleveldefs = {} interpleveldefs = { - 'dateinterop': 'interp_pypydatetime.W_DateTime_Date', - 'timeinterop' : 'interp_pypydatetime.W_DateTime_Time', - 'deltainterop' : 'interp_pypydatetime.W_DateTime_Delta', + 'dateinterop' : 'interp_pypydatetime.W_DateTime_Date', + 'timeinterop' : 'interp_pypydatetime.W_DateTime_Time', + 'deltainterop' : 'interp_pypydatetime.W_DateTime_Delta', } +class PyPyBufferable(MixedModule): + appleveldefs = {} + interpleveldefs = { + 'bufferable': 'interp_buffer.W_Bufferable', + } + + class Module(MixedModule): """ PyPy specific "magic" functions. A lot of them are experimental and subject to change, many are internal. """ @@ -110,7 +117,7 @@ 'side_effects_ok' : 'interp_magic.side_effects_ok', 'stack_almost_full' : 'interp_magic.stack_almost_full', 'pyos_inputhook' : 'interp_magic.pyos_inputhook', - 'newmemoryview' : 'newmemoryview.newmemoryview', + 'newmemoryview' : 'interp_buffer.newmemoryview', } if sys.platform == 'win32': interpleveldefs['get_console_cp'] = 'interp_magic.get_console_cp' @@ -122,6 +129,7 @@ "intop": IntOpModule, "os": OsModule, '_pypydatetime': PyPyDateTime, + 'bufferable': PyPyBufferable, } def setup_after_space_initialization(self): diff --git a/pypy/module/__pypy__/newmemoryview.py b/pypy/module/__pypy__/interp_buffer.py rename from pypy/module/__pypy__/newmemoryview.py rename to pypy/module/__pypy__/interp_buffer.py --- a/pypy/module/__pypy__/newmemoryview.py +++ b/pypy/module/__pypy__/interp_buffer.py @@ -3,8 +3,26 @@ # from pypy.interpreter.error import oefmt -from pypy.interpreter.gateway import unwrap_spec +from pypy.interpreter.gateway import unwrap_spec, interp2app from pypy.objspace.std.memoryobject import BufferViewND +from pypy.interpreter.baseobjspace import W_Root +from pypy.interpreter.typedef import TypeDef, generic_new_descr + +class W_Bufferable(W_Root): + def __init__(self, space): + pass + + def descr_buffer(self, space, w_flags): + if self is W_Bufferable: + raise oefmt(space.w_ValueError, "override __buffer__ in a subclass") + return space.call_method(self, '__buffer__', w_flags) + +W_Bufferable.typedef = TypeDef("Bufferable", + __doc__ = """a helper class for a app-level class (like _ctypes.Array) +that want to support tp_as_buffer.bf_getbuffer via a __buffer__ method""", + __new__ = generic_new_descr(W_Bufferable), + __buffer__ = interp2app(W_Bufferable.descr_buffer), +) @unwrap_spec(itemsize=int, format='text') def newmemoryview(space, w_obj, itemsize, format, w_shape=None, w_strides=None): 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 @@ -29,6 +29,7 @@ from pypy.module.__builtin__.descriptor import W_Property from pypy.module.__builtin__.interp_classobj import W_ClassObject from pypy.module.micronumpy.base import W_NDimArray +from pypy.module.__pypy__.interp_buffer import W_Bufferable from rpython.rlib.entrypoint import entrypoint_lowlevel from rpython.rlib.rposix import FdValidator from rpython.rlib.unroll import unrolling_iterable @@ -719,6 +720,7 @@ '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)', + 'PyBufferable_Type': 'space.gettypeobject(W_Bufferable.typedef)', }.items(): register_global(cpyname, 'PyTypeObject*', pypyexpr, header=pypy_decl) 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 @@ -34,6 +34,21 @@ decref(space, ref) decref(space, c_memoryview) + def test_class_with___buffer__(self, space, api): + w_obj = space.appexec([], """(): + from __pypy__.bufferable import bufferable + class B(bufferable): + def __init__(self): + self.buf = bytearray(10) + + def __buffer__(self, flags): + return memoryview(self.buf) + return B()""") + py_obj = make_ref(space, w_obj) + assert py_obj.c_ob_type.c_tp_as_buffer + assert py_obj.c_ob_type.c_tp_as_buffer.c_bf_getbuffer + + class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase): def test_fillWithObject(self): module = self.import_extension('foo', [ From pypy.commits at gmail.com Thu Feb 28 12:39:59 2019 From: pypy.commits at gmail.com (mattip) Date: Thu, 28 Feb 2019 09:39:59 -0800 (PST) Subject: [pypy-commit] pypy newmemoryview-app-level: fix translation, extend class for python2 and use it in _ctypes Message-ID: <5c781cef.1c69fb81.5c281.3133@mx.google.com> Author: Matti Picus Branch: newmemoryview-app-level Changeset: r96193:6118585acf9c Date: 2019-02-28 15:27 +0200 http://bitbucket.org/pypy/pypy/changeset/6118585acf9c/ Log: fix translation, extend class for python2 and use it in _ctypes diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py --- a/lib_pypy/_ctypes/basics.py +++ b/lib_pypy/_ctypes/basics.py @@ -2,8 +2,15 @@ from _rawffi import alt as _ffi import sys -try: from __pypy__ import builtinify -except ImportError: builtinify = lambda f: f +try: + from __pypy__ import builtinify +except ImportError: + builtinify = lambda f: f + +try: + from __pypy__.bufferable import bufferable +except ImportError: + bufferable = object keepalive_key = str # XXX fix this when provided with test @@ -64,7 +71,7 @@ 'resbuffer' is a _rawffi array of length 1 containing the value, and this returns a general Python object that corresponds. """ - res = object.__new__(self) + res = bufferable.__new__(self) res.__class__ = self res.__dict__['_buffer'] = resbuffer if base is not None: @@ -158,7 +165,7 @@ def __ne__(self, other): return self._obj != other -class _CData(object): +class _CData(bufferable): """ The most basic object for all ctypes types """ __metaclass__ = _CDataMeta diff --git a/pypy/module/__pypy__/interp_buffer.py b/pypy/module/__pypy__/interp_buffer.py --- a/pypy/module/__pypy__/interp_buffer.py +++ b/pypy/module/__pypy__/interp_buffer.py @@ -13,11 +13,11 @@ pass def descr_buffer(self, space, w_flags): - if self is W_Bufferable: + if type(self) is W_Bufferable: raise oefmt(space.w_ValueError, "override __buffer__ in a subclass") return space.call_method(self, '__buffer__', w_flags) -W_Bufferable.typedef = TypeDef("Bufferable", +W_Bufferable.typedef = TypeDef("Bufferable", None, None, 'read-write', __doc__ = """a helper class for a app-level class (like _ctypes.Array) that want to support tp_as_buffer.bf_getbuffer via a __buffer__ method""", __new__ = generic_new_descr(W_Bufferable), 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 @@ -47,6 +47,8 @@ py_obj = make_ref(space, w_obj) assert py_obj.c_ob_type.c_tp_as_buffer assert py_obj.c_ob_type.c_tp_as_buffer.c_bf_getbuffer + assert py_obj.c_ob_type.c_tp_as_buffer.c_bf_getreadbuffer + assert py_obj.c_ob_type.c_tp_as_buffer.c_bf_getwritebuffer class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase): From pypy.commits at gmail.com Thu Feb 28 12:40:01 2019 From: pypy.commits at gmail.com (mattip) Date: Thu, 28 Feb 2019 09:40:01 -0800 (PST) Subject: [pypy-commit] pypy default: add test that passes -A (python2.7), fails on pypy Message-ID: <5c781cf1.1c69fb81.060d.b1e9@mx.google.com> Author: Matti Picus Branch: Changeset: r96194:275fd99e1c23 Date: 2019-02-28 19:39 +0200 http://bitbucket.org/pypy/pypy/changeset/275fd99e1c23/ Log: add test that passes -A (python2.7), fails on pypy diff --git a/pypy/module/cpyext/test/test_methodobject.py b/pypy/module/cpyext/test/test_methodobject.py --- a/pypy/module/cpyext/test/test_methodobject.py +++ b/pypy/module/cpyext/test/test_methodobject.py @@ -108,6 +108,7 @@ assert mod.getarg_KW(a=3, b=4) == ((), {'a': 3, 'b': 4}) assert mod.getarg_KW(1, 2, a=3, b=4) == ((1, 2), {'a': 3, 'b': 4}) assert mod.getarg_KW.__name__ == "getarg_KW" + assert mod.getarg_KW(*(), **{}) == ((), {}) def test_func_attributes(self): mod = self.import_extension('MyModule', [