Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit