Author: Armin Rigo <ar...@tunes.org> Branch: py3.6 Changeset: r97941:b2dc8d7edf61 Date: 2019-11-02 17:31 +0100 http://bitbucket.org/pypy/pypy/changeset/b2dc8d7edf61/
Log: hg merge default diff --git a/lib_pypy/_curses_build.py b/lib_pypy/_curses_build.py --- a/lib_pypy/_curses_build.py +++ b/lib_pypy/_curses_build.py @@ -1,12 +1,18 @@ from cffi import FFI import os +version_str = ''' + static const int NCURSES_VERSION_MAJOR; + static const int NCURSES_VERSION_MINOR; +''' + def find_library(options): for library in options: ffi = FFI() - ffi.set_source("_curses_cffi_check", "", libraries=[library]) + ffi.cdef(version_str) + ffi.set_source("_curses_cffi_check", version_str, libraries=[library]) try: - ffi.compile() + lib = ffi.compile() except VerificationError as e: e_last = e continue @@ -21,12 +27,11 @@ if os.path.exists('/usr/include/ncurses'): return ['/usr/include/ncurses'] if os.path.exists('/usr/include/ncursesw'): - return ['/usr/include/ncursesw'] + return ['/usr/include/ncursesw'] return [] ffi = FFI() - ffi.set_source("_curses_cffi", """ #ifdef __APPLE__ /* the following define is necessary for OS X 10.6+; without it, the @@ -77,6 +82,9 @@ find_library(['panel', 'panelw'])], include_dirs=find_curses_include_dirs()) +import _curses_cffi_check +lib = _curses_cffi_check.lib +version = (lib.NCURSES_VERSION_MAJOR, lib.NCURSES_VERSION_MINOR) ffi.cdef(""" typedef ... WINDOW; @@ -186,8 +194,6 @@ void filter(void); int flash(void); int flushinp(void); -int wget_wch(WINDOW *, wint_t *); -int mvwget_wch(WINDOW *, int, int, wint_t *); chtype getbkgd(WINDOW *); WINDOW * getwin(FILE *); int halfdelay(int); @@ -263,7 +269,6 @@ int touchwin(WINDOW *); int typeahead(int); int ungetch(int); -int unget_wch(const wchar_t); int untouchwin(WINDOW *); void use_env(bool); int waddch(WINDOW *, const chtype); @@ -367,6 +372,13 @@ void _m_getsyx(int *yx); """) +if version > (5, 7): + ffi.cdef(""" +int wget_wch(WINDOW *, wint_t *); +int mvwget_wch(WINDOW *, int, int, wint_t *); +int unget_wch(const wchar_t); +""") + if __name__ == "__main__": ffi.compile() diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -348,6 +348,9 @@ return space.w_None return tb + def got_any_traceback(self): + return self._application_traceback is not None + def set_traceback(self, traceback): """Set the current traceback.""" self._application_traceback = traceback diff --git a/pypy/interpreter/test/test_class.py b/pypy/interpreter/test/test_class.py --- a/pypy/interpreter/test/test_class.py +++ b/pypy/interpreter/test/test_class.py @@ -123,3 +123,20 @@ assert C.__qualname__ == 'test_qualname.<locals>.C' assert C.D.__qualname__ == 'test_qualname.<locals>.C.D' assert not hasattr(C(), '__qualname__') + + def test_nonsensical_base_error_message(self): + with raises(TypeError) as exc: + class Foo('base'): + pass + assert str(exc.value).startswith( + "metaclass found to be 'str', but calling <class 'str'> " + "with args ('Foo', ('base',), ...) raised ") + # + def foo_func(): pass + with raises(TypeError) as exc: + class Foo(foo_func): + pass + assert str(exc.value).startswith( + "metaclass found to be 'function', but calling <class 'function'> " + "with args ('Foo', (<function test_nonsensical_base_error_message" + ".<locals>.foo_func at ") diff --git a/pypy/module/__builtin__/compiling.py b/pypy/module/__builtin__/compiling.py --- a/pypy/module/__builtin__/compiling.py +++ b/pypy/module/__builtin__/compiling.py @@ -149,7 +149,19 @@ args_w=[w_name, w_bases, w_namespace], keywords=keywords, keywords_w=kwds_w.values()) - w_class = space.call_args(w_meta, args) + try: + w_class = space.call_args(w_meta, args) + except OperationError as e: + # give a more comprehensible error message for TypeErrors + if e.got_any_traceback(): + raise + if not e.match(space, space.w_TypeError): + raise + raise oefmt(space.w_TypeError, + "metaclass found to be '%N', but calling %R " + "with args (%R, %R, ...) raised %R", + w_meta, w_meta, w_name, w_bases, + e.get_w_value(space)) if isinstance(w_cell, Cell) and isinstance(w_class, W_TypeObject): if w_cell.empty(): # will become an error in Python 3.7 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 @@ -343,7 +343,14 @@ currmap = self.startmap while True: # parse a key: value - currmap = self.decode_key_map(i, currmap) + newmap = self.decode_key_map(i, currmap) + if newmap is None: + # We've seen a repeated value, switch to dict-based storage. + dict_w = self._switch_to_dict(currmap, values_w, nextindex) + # We re-parse the last key, to get the correct overwriting + # effect. Pointless to care for performance here. + return self.decode_object_dict(i, start, dict_w) + currmap = newmap i = self.skip_whitespace(self.pos) ch = self.ll_chars[i] if ch != ':': @@ -611,6 +618,8 @@ """ Given the current map currmap of an object, decode the next key at position i. This returns the new map of the object. """ newmap = self._decode_key_map(i, currmap) + if newmap is None: + return None currmap.observe_transition(newmap, self.startmap) return newmap @@ -790,6 +799,11 @@ self.nextmap_first._check_invariants() def get_next(self, w_key, string, start, stop, terminator): + """ Returns the next map, given a wrapped key w_key, the json input + string with positions start and stop, as well as a terminator. + + Returns None if the key already appears somewhere in the map chain. + """ from pypy.objspace.std.dictmultiobject import unicode_hash, unicode_eq if isinstance(self, JSONMap): assert not self.state == MapBase.BLOCKED @@ -804,6 +818,8 @@ if nextmap_first is None: # first transition ever seen, don't initialize nextmap_all next = self._make_next_map(w_key, string[start:stop]) + if next is None: + return None self.nextmap_first = next else: if self.nextmap_all is None: @@ -818,6 +834,8 @@ # if we are at this point we didn't find the transition yet, so # create a new one next = self._make_next_map(w_key, string[start:stop]) + if next is None: + return None self.nextmap_all[w_key] = next # one new leaf has been created @@ -860,6 +878,14 @@ self.mark_useful(terminator) def _make_next_map(self, w_key, key_repr): + # Check whether w_key is already part of the self.prev chain + # to prevent strangeness in the json dict implementation. + # This is slow, but it should be rare to call this function. + check = self + while isinstance(check, JSONMap): + if check.w_key._utf8 == w_key._utf8: + return None + check = check.prev return JSONMap(self.space, self, w_key, key_repr) def fill_dict(self, dict_w, values_w): diff --git a/pypy/module/_pypyjson/test/test__pypyjson.py b/pypy/module/_pypyjson/test/test__pypyjson.py --- a/pypy/module/_pypyjson/test/test__pypyjson.py +++ b/pypy/module/_pypyjson/test/test__pypyjson.py @@ -74,6 +74,17 @@ m3.fill_dict(d, [space.w_None, space.w_None, space.w_None]) assert list(d) == [w_a, w_b, w_c] + def test_repeated_key_get_next(self): + m = Terminator(self.space) + w_a = self.space.newutf8("a", 1) + w_b = self.space.newutf8("b", 1) + w_c = self.space.newutf8("c", 1) + m1 = m.get_next(w_a, '"a"', 0, 3, m) + m1 = m1.get_next(w_b, '"b"', 0, 3, m) + m1 = m1.get_next(w_c, '"c"', 0, 3, m) + m2 = m1.get_next(w_a, '"a"', 0, 3, m) + assert m2 is None + def test_decode_key_map(self): m = Terminator(self.space) @@ -531,3 +542,12 @@ pass exc = raises(MyError, _pypyjson.loads, 'nul', MyError) assert exc.value.args == ('Error when decoding null', 'nul', 1) + + def test_repeated_key(self): + import _pypyjson + a = '{"abc": "4", "k": 1, "k": 2}' + d = _pypyjson.loads(a) + assert d == {u"abc": u"4", u"k": 2} + a = '{"abc": "4", "k": 1, "k": 1.5, "c": null, "k": 2}' + d = _pypyjson.loads(a) + assert d == {u"abc": u"4", u"c": None, u"k": 2} 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 @@ -1251,7 +1251,8 @@ except TypeError as e: import sys if '__pypy__' in sys.builtin_module_names: - assert str(e) == 'instance layout conflicts in multiple inheritance' + print(str(e)) + assert 'instance layout conflicts in multiple inheritance' in str(e) else: assert 'instance lay-out conflict' in str(e) diff --git a/pypy/objspace/std/test/test_jsondict.py b/pypy/objspace/std/test/test_jsondict.py --- a/pypy/objspace/std/test/test_jsondict.py +++ b/pypy/objspace/std/test/test_jsondict.py @@ -87,3 +87,19 @@ assert not 1 in d assert __pypy__.strategy(d) == "UnicodeDictStrategy" assert list(d) == [u"a", u"b"] + + def test_bug(self): + import _pypyjson + a = """ + { + "top": { + "k": "8", + "k": "8", + "boom": 1 + } + } + """ + d = _pypyjson.loads(a) + str(d) + repr(d) + diff --git a/pypy/tool/release/make_portable.py b/pypy/tool/release/make_portable.py new file mode 100644 --- /dev/null +++ b/pypy/tool/release/make_portable.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python + +bundle = ['sqlite3', 'ssl', 'crypto', 'ffi', 'expat', 'tcl', 'tk', 'gdbm', + 'lzma', 'tinfo', 'tinfow', 'ncursesw', 'panelw', 'ncurses', 'panel', 'panelw'] + +from os import chdir, mkdir, symlink +from os.path import dirname, relpath, join, exists, basename, realpath +from shutil import copy2 +import sys +from glob import glob +from subprocess import check_output, check_call + + +def get_deps(binary): + deps = {} + output = check_output(['ldd', binary]) + for line in output.splitlines(): + if '=>' not in line: + continue + line = line.strip() + needed, path = line.split(' => ') + if path == 'not found': + print('Broken dependency in ' + binary) + path = path.split(' ')[0] + path = realpath(path) + if not path: + continue + + if needed[3:].split('.', 1)[0] not in bundle: + continue + + deps[needed] = path + deps.update(get_deps(path)) + + return deps + + +def gather_deps(binaries): + deps = {} + for binary in binaries: + deps.update(get_deps(binary)) + + return deps + + +def copy_deps(deps): + copied = {} + + for needed, path in deps.items(): + bname = basename(path) + + copy2(path, 'lib/' + bname) + copied[path] = 'lib/' + bname + + if not exists('lib/' + needed): + symlink(bname, 'lib/' + needed) + + return copied + + +def rpath_binaries(binaries): + rpaths = {} + + for binary in binaries: + rpath = join('$ORIGIN', relpath('lib', dirname(binary))) + check_call(['patchelf', '--set-rpath', rpath, binary]) + + rpaths[binary] = rpath + + return rpaths + + +def make_portable(): + binaries = glob('bin/libpypy*.so') + if not binaries: + raise ValueError('Could not find bin/libpypy*.so') + binaries.extend(glob('lib_pypy/*_cffi.pypy*.so')) + binaries.extend(glob('lib_pypy/_pypy_openssl*.so')) + binaries.extend(glob('lib_pypy/_tkinter/*_cffi.pypy*.so')) + + deps = gather_deps(binaries) + + copied = copy_deps(deps) + + for path, item in copied.items(): + print('Copied {0} to {1}'.format(path, item)) + + binaries.extend(copied.values()) + + rpaths = rpath_binaries(binaries) + for binary, rpath in rpaths.items(): + print('Set RPATH of {0} to {1}'.format(binary, rpath)) + + return deps + + +if __name__ == '__main__': + try: + chdir(sys.argv[1]) + except: + print('Call as %s <path/to/pypy/topdir' % sys.argv[0]) + exit(-1) + + try: + mkdir('lib') + except OSError: + pass + + make_portable() + 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 @@ -1,4 +1,5 @@ #!/usr/bin/env python +from __future__ import print_function """ packages PyPy, provided that it's already built. It uses 'pypy/goal/pypy3-c' and parts of the rest of the working copy. Usage: @@ -20,7 +21,9 @@ import py import fnmatch import subprocess +import platform from pypy.tool.release.smartstrip import smartstrip +from pypy.tool.release.make_portable import make_portable USE_ZIPFILE_MODULE = sys.platform == 'win32' @@ -90,10 +93,11 @@ ) for key, module in failures: - print >>sys.stderr, """!!!!!!!!!!\nBuilding {0} bindings failed. + print("""!!!!!!!!!!\nBuilding {0} bindings failed. You can either install development headers package, add the --without-{0} option to skip packaging this - binary CFFI extension, or say --without-cffi.""".format(key) + binary CFFI extension, or say --without-cffi.""".format(key), + file=sys.stderr) if len(failures) > 0: return 1, None @@ -130,7 +134,7 @@ if pypyw.exists(): tgt = py.path.local(tgt) binaries.append((pypyw, tgt.new(purebasename=tgt.purebasename + 'w').basename, None)) - print "Picking %s" % str(pypyw) + print("Picking %s" % str(pypyw)) # Can't rename a DLL: it is always called 'libpypy3-c.dll' win_extras = [('libpypy3-c.dll', None), ('sqlite3.dll', lib_pypy)] if not options.no_tk: @@ -142,18 +146,19 @@ if not p.check(): p = py.path.local.sysfind(extra) if not p: - print "%s not found, expect trouble if this is a shared build" % (extra,) + print("%s not found, expect trouble if this " + "is a shared build" % (extra,)) continue - print "Picking %s" % p + print("Picking %s" % p) binaries.append((p, p.basename, target_dir)) libsdir = basedir.join('libs') if libsdir.exists(): - print 'Picking %s (and contents)' % libsdir + print('Picking %s (and contents)' % libsdir) shutil.copytree(str(libsdir), str(pypydir.join('libs'))) else: - print '"libs" dir with import library not found.' - print 'You have to create %r' % (str(libsdir),) - print 'and copy libpypy3-c.lib in there, renamed to python32.lib' + print('"libs" dir with import library not found.') + print('You have to create %r' % (str(libsdir),)) + print('and copy libpypy3-c.lib in there, renamed to python32.lib') # XXX users will complain that they cannot compile capi (cpyext) # modules for windows, also embedding pypy (i.e. in cffi) # will fail. @@ -170,14 +175,15 @@ tktcldir = p.dirpath().join('..').join('lib') shutil.copytree(str(tktcldir), str(pypydir.join('tcl'))) except WindowsError: - print >>sys.stderr, r"""Packaging Tk runtime failed. -tk85.dll and tcl85.dll found in %s, expecting to find runtime in %s -directory next to the dlls, as per build instructions.""" %(p, tktcldir) + print("Packaging Tk runtime failed. tk85.dll and tcl85.dll " + "found in %s, expecting to find runtime in %s directory " + "next to the dlls, as per build " + "instructions." %(p, tktcldir), file=sys.stderr) import traceback;traceback.print_exc() raise MissingDependenciesError('Tk runtime') - print '* Binaries:', [source.relto(str(basedir)) - for source, target, target_dir in binaries] + print('* Binaries:', [source.relto(str(basedir)) + for source, target, target_dir in binaries]) # Careful: to copy lib_pypy, copying just the hg-tracked files # would not be enough: there are also ctypes_config_cache/_*_cache.py. @@ -238,7 +244,11 @@ else: archive = bindir.join(target) smartstrip(archive, keep_debug=options.keep_debug) - # + + # make the package portable by adding rpath=$ORIGIN/..lib, + # bundling dependencies + if options.make_portable: + make_portable() if USE_ZIPFILE_MODULE: import zipfile archive = str(builddir.join(name + '.zip')) @@ -252,10 +262,11 @@ else: archive = str(builddir.join(name + '.tar.bz2')) if sys.platform == 'darwin': - print >>sys.stderr, """Warning: tar on current platform does not suport overriding the uid and gid -for its contents. The tarball will contain your uid and gid. If you are -building the actual release for the PyPy website, you may want to be -using another platform...""" + print("Warning: tar on current platform does not suport " + "overriding the uid and gid for its contents. The tarball " + "will contain your uid and gid. If you are building the " + "actual release for the PyPy website, you may want to be " + "using another platform...", file=sys.stderr) e = os.system('tar --numeric-owner -cvjf ' + archive + " " + name) elif sys.platform.startswith('freebsd'): e = os.system('tar --uname=root --gname=wheel -cvjf ' + archive + " " + name) @@ -268,10 +279,10 @@ finally: os.chdir(old_dir) if options.targetdir: - print "Copying %s to %s" % (archive, options.targetdir) + print("Copying %s to %s" % (archive, options.targetdir)) shutil.copy(archive, options.targetdir) else: - print "Ready in %s" % (builddir,) + print("Ready in %s" % (builddir,)) return retval, builddir # for tests def package(*args, **kwds): @@ -319,8 +330,14 @@ dest='embed_dependencies', action=NegateAction, default=(sys.platform == 'darwin'), - help='whether to embed dependencies for distribution ' + help='whether to embed dependencies in CFFI modules ' '(default on OS X)') + parser.add_argument('--make-portable', '--no-make-portable', + dest='make_portable', + action=NegateAction, + default=(platform.linux_distribution() in ('CentOS',)), + help='whether to make the package portable by shipping ' + 'dependent shared objects and mangling RPATH') options = parser.parse_args(args) if os.environ.has_key("PYPY_PACKAGE_NOKEEPDEBUG"): @@ -331,6 +348,10 @@ options.embed_dependencies = True elif os.environ.has_key("PYPY_NO_EMBED_DEPENDENCIES"): options.embed_dependencies = False + if os.environ.has_key("PYPY_MAKE_PORTABLE"): + options.embed_dependencies = True + elif os.environ.has_key("PYPY_NO_MAKE_PORTABLE"): + options.embed_dependencies = False if not options.builddir: # The import actually creates the udir directory from rpython.tool.udir import udir _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit