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

Reply via email to