Author: Ronan Lamy <ronan.l...@gmail.com> Branch: PyBuffer Changeset: r91094:165dc3ba1754 Date: 2017-04-19 17:05 +0100 http://bitbucket.org/pypy/pypy/changeset/165dc3ba1754/
Log: hg merge py3.5 diff too long, truncating to 2000 out of 20552 lines diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -53,9 +53,11 @@ ^rpython/translator/goal/.+\.dll$ ^pypy/goal/pypy-translation-snapshot$ ^pypy/goal/pypy-c +^pypy/goal/pypy3-c ^pypy/goal/.+\.exe$ ^pypy/goal/.+\.dll$ ^pypy/goal/.+\.lib$ +^pypy/goal/.+\.dylib$ ^pypy/_cache$ ^lib-python/2.7/lib2to3/.+\.pickle$ ^lib_pypy/__pycache__$ @@ -86,3 +88,5 @@ ^\.cache$ pypy/module/cppyy/.+/*\.pcm + + diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -36,3 +36,5 @@ aff251e543859ce4508159dd9f1a82a2f553de00 release-pypy2.7-v5.6.0 fa3249d55d15b9829e1be69cdf45b5a44cec902d release-pypy2.7-v5.7.0 b16a4363e930f6401bceb499b9520955504c6cb0 release-pypy3.5-v5.7.0 +1aa2d8e03cdfab54b7121e93fda7e98ea88a30bf release-pypy2.7-v5.7.1 +2875f328eae2216a87f3d6f335092832eb031f56 release-pypy3.5-v5.7.1 diff --git a/lib-python/2.7/distutils/sysconfig_pypy.py b/lib-python/2.7/distutils/sysconfig_pypy.py --- a/lib-python/2.7/distutils/sysconfig_pypy.py +++ b/lib-python/2.7/distutils/sysconfig_pypy.py @@ -61,12 +61,12 @@ def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" g = {} - g['CC'] = "gcc -pthread" - g['CXX'] = "g++ -pthread" + g['CC'] = "cc -pthread" + g['CXX'] = "c++ -pthread" g['OPT'] = "-DNDEBUG -O2" g['CFLAGS'] = "-DNDEBUG -O2" g['CCSHARED'] = "-fPIC" - g['LDSHARED'] = "gcc -pthread -shared" + g['LDSHARED'] = "cc -pthread -shared" g['SO'] = [s[0] for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION][0] g['AR'] = "ar" g['ARFLAGS'] = "rc" diff --git a/lib-python/3/site.py b/lib-python/3/site.py --- a/lib-python/3/site.py +++ b/lib-python/3/site.py @@ -305,9 +305,7 @@ seen.add(prefix) if is_pypy: - from distutils.sysconfig import get_python_lib - sitepackages.append(get_python_lib(standard_lib=False, - prefix=prefix)) + sitepackages.append(os.path.join(prefix, "site-packages")) elif os.sep == '/': sitepackages.append(os.path.join(prefix, "lib", "python" + sys.version[:3], diff --git a/lib_pypy/_cffi_ssl/README.md b/lib_pypy/_cffi_ssl/README.md --- a/lib_pypy/_cffi_ssl/README.md +++ b/lib_pypy/_cffi_ssl/README.md @@ -5,16 +5,22 @@ it renames the compiled shared object to _pypy_openssl.so (which means that cryptography can ship their own cffi backend) +NOTE: currently, we have changed ``_cffi_src/openssl/callbacks.py`` to +not rely on the CPython C API. + # Tests? Currently this module is tested using CPython's standard library test suite. # Install it into PyPy's source tree -Copy over all the sources into the folder `lib_pypy/_cffi_ssl/*`. Updating the cffi backend can be simply done by the following command: +Copy over all the sources into the folder `lib_pypy/_cffi_ssl/*`. Updating the cffi backend can be simply done by the following command:: $ cp -r <cloned cryptography folder>/src/_cffi_src/* . +NOTE: you need to keep our version of ``_cffi_src/openssl/callbacks.py`` +for now! + # Crpytography version Copied over release version `1.7.2` diff --git a/lib_pypy/_cffi_ssl/_cffi_src/openssl/callbacks.py b/lib_pypy/_cffi_ssl/_cffi_src/openssl/callbacks.py --- a/lib_pypy/_cffi_ssl/_cffi_src/openssl/callbacks.py +++ b/lib_pypy/_cffi_ssl/_cffi_src/openssl/callbacks.py @@ -13,8 +13,6 @@ #include <openssl/x509.h> #include <openssl/x509_vfy.h> #include <openssl/crypto.h> - -#include <pythread.h> """ TYPES = """ @@ -64,8 +62,44 @@ Copyright 2001-2016 Python Software Foundation; All Rights Reserved. */ +#ifdef _WIN32 +#include <Windows.h> +typedef CRITICAL_SECTION mutex1_t; +static inline void mutex1_init(mutex1_t *mutex) { + InitializeCriticalSection(mutex); +} +static inline void mutex1_lock(mutex1_t *mutex) { + EnterCriticalSection(mutex); +} +static inline void mutex1_unlock(mutex1_t *mutex) { + LeaveCriticalSection(mutex); +} +#else +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +typedef pthread_mutex_t mutex1_t; +#define ASSERT_STATUS(call) \ + if (call != 0) { \ + perror("Fatal error in _cffi_ssl: " #call); \ + abort(); \ + } +static inline void mutex1_init(mutex1_t *mutex) { +#if !defined(pthread_mutexattr_default) +# define pthread_mutexattr_default ((pthread_mutexattr_t *)NULL) +#endif + ASSERT_STATUS(pthread_mutex_init(mutex, pthread_mutexattr_default)); +} +static inline void mutex1_lock(mutex1_t *mutex) { + ASSERT_STATUS(pthread_mutex_lock(mutex)); +} +static inline void mutex1_unlock(mutex1_t *mutex) { + ASSERT_STATUS(pthread_mutex_unlock(mutex)); +} +#endif + static unsigned int _ssl_locks_count = 0; -static PyThread_type_lock *_ssl_locks = NULL; +static mutex1_t *_ssl_locks = NULL; static void _ssl_thread_locking_function(int mode, int n, const char *file, int line) { @@ -89,35 +123,32 @@ } if (mode & CRYPTO_LOCK) { - PyThread_acquire_lock(_ssl_locks[n], 1); + mutex1_lock(_ssl_locks + n); } else { - PyThread_release_lock(_ssl_locks[n]); + mutex1_unlock(_ssl_locks + n); + } +} + +static void init_mutexes(void) +{ + int i; + for (i = 0; i < _ssl_locks_count; i++) { + mutex1_init(_ssl_locks + i); } } int _setup_ssl_threads(void) { - unsigned int i; - if (_ssl_locks == NULL) { _ssl_locks_count = CRYPTO_num_locks(); - _ssl_locks = PyMem_New(PyThread_type_lock, _ssl_locks_count); + _ssl_locks = malloc(sizeof(mutex1_t) * _ssl_locks_count); if (_ssl_locks == NULL) { - PyErr_NoMemory(); return 0; } - memset(_ssl_locks, 0, sizeof(PyThread_type_lock) * _ssl_locks_count); - for (i = 0; i < _ssl_locks_count; i++) { - _ssl_locks[i] = PyThread_allocate_lock(); - if (_ssl_locks[i] == NULL) { - unsigned int j; - for (j = 0; j < i; j++) { - PyThread_free_lock(_ssl_locks[j]); - } - PyMem_Free(_ssl_locks); - return 0; - } - } + init_mutexes(); CRYPTO_set_locking_callback(_ssl_thread_locking_function); +#ifndef _WIN32 + pthread_atfork(NULL, NULL, &init_mutexes); +#endif } return 1; } 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 @@ -83,8 +83,9 @@ res = self.__new__(self) ffiarray = self._ffiarray.fromaddress(resarray.buffer, self._length_) res._buffer = ffiarray - res._base = base - res._index = index + if base is not None: + res._base = base + res._index = index return res def _CData_retval(self, resbuffer): 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 @@ -64,8 +64,9 @@ res = object.__new__(self) res.__class__ = self res.__dict__['_buffer'] = resbuffer - res.__dict__['_base'] = base - res.__dict__['_index'] = index + if base is not None: + res.__dict__['_base'] = base + res.__dict__['_index'] = index return res def _CData_retval(self, resbuffer): diff --git a/lib_pypy/_pypy_collections.py b/lib_pypy/_pypy_collections.py --- a/lib_pypy/_pypy_collections.py +++ b/lib_pypy/_pypy_collections.py @@ -1,6 +1,5 @@ -from __pypy__ import reversed_dict, move_to_end +from __pypy__ import reversed_dict, move_to_end, objects_in_repr from _operator import eq as _eq -from reprlib import recursive_repr as _recursive_repr import _collections_abc @@ -44,12 +43,21 @@ ''' return move_to_end(self, key, last) - @_recursive_repr() def __repr__(self): 'od.__repr__() <==> repr(od)' if not self: return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self.items())) + currently_in_repr = objects_in_repr() + if self in currently_in_repr: + return '...' + currently_in_repr[self] = 1 + try: + return '%s(%r)' % (self.__class__.__name__, list(self.items())) + finally: + try: + del currently_in_repr[self] + except: + pass def __reduce__(self): 'Return state information for pickling' diff --git a/lib_pypy/_structseq.py b/lib_pypy/_structseq.py --- a/lib_pypy/_structseq.py +++ b/lib_pypy/_structseq.py @@ -129,3 +129,38 @@ parts = ["%s=%r" % (fields[index].__name__, value) for index, value in enumerate(self[:visible_count])] return "%s(%s)" % (self._name, ", ".join(parts)) + + +class SimpleNamespace: + """A simple attribute-based namespace. + +SimpleNamespace(**kwargs)""" + + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __repr__(self): + ident = id(self) + if ident in sns_recurse: + return "namespace(...)" + sns_recurse.add(ident) + try: + pairs = ('%s=%r' % item for item in sorted(self.__dict__.items())) + return "namespace(%s)" % ', '.join(pairs) + finally: + sns_recurse.discard(ident) + + def __eq__(self, other): + if issubclass(type(other), SimpleNamespace): + return self.__dict__ == other.__dict__ + return NotImplemented + + def __ne__(self, other): + if issubclass(type(other), SimpleNamespace): + return self.__dict__ != other.__dict__ + return NotImplemented + +sns_recurse = set() + +# This class is not exposed in sys, but by the types module. +SimpleNamespace.__module__ = 'types' diff --git a/lib_pypy/_sysconfigdata.py b/lib_pypy/_sysconfigdata.py --- a/lib_pypy/_sysconfigdata.py +++ b/lib_pypy/_sysconfigdata.py @@ -1,6 +1,6 @@ -import imp +import _imp -so_ext = [s[0] for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION][0] +so_ext = _imp.extension_suffixes()[0] build_time_vars = { "EXT_SUFFIX": so_ext, diff --git a/pypy/doc/index-of-release-notes.rst b/pypy/doc/index-of-release-notes.rst --- a/pypy/doc/index-of-release-notes.rst +++ b/pypy/doc/index-of-release-notes.rst @@ -6,6 +6,7 @@ .. toctree:: + release-v5.7.1.rst release-v5.7.0.rst release-pypy2.7-v5.6.0.rst release-pypy2.7-v5.4.1.rst @@ -59,6 +60,7 @@ .. toctree:: + release-v5.7.1.rst release-v5.7.0.rst CPython 3.3 compatible versions diff --git a/pypy/doc/install.rst b/pypy/doc/install.rst --- a/pypy/doc/install.rst +++ b/pypy/doc/install.rst @@ -32,10 +32,10 @@ .. code-block:: console - $ tar xf pypy-2.1.tar.bz2 - $ ./pypy-2.1/bin/pypy - Python 2.7.3 (480845e6b1dd, Jul 31 2013, 11:05:31) - [PyPy 2.1.0 with GCC 4.4.3] on linux2 + $ tar xf pypy-x.y.z.tar.bz2 + $ ./pypy-x.y.z/bin/pypy + Python 2.7.x (xxxxxxxxxxxx, Date, Time) + [PyPy x.y.z with GCC x.y.z] on linux2 Type "help", "copyright", "credits" or "license" for more information. And now for something completely different: ``PyPy is an exciting technology that lets you to write fast, portable, multi-platform interpreters with less diff --git a/pypy/doc/release-v5.7.0.rst b/pypy/doc/release-v5.7.0.rst --- a/pypy/doc/release-v5.7.0.rst +++ b/pypy/doc/release-v5.7.0.rst @@ -99,7 +99,7 @@ tp_dealloc * refactor and clean up poor handling of unicode exposed in work on py3.5 * builtin module cppyy_ supports C++ 11, 14, etc. via cling (reflex has been removed) - * adapt ``weakref`` according to CPython issue #19542_, will be in CPython 2.7.14 + * adapt ``weakref`` according to CPython issue 19542_, will be in CPython 2.7.14 * support translations with cpyext and the Boehm GC (for special cases like RevDB_ * implement ``StringBuffer.get_raw_address`` for the buffer protocol, it is @@ -125,18 +125,18 @@ * disable ``clock_gettime()`` on OS/X, since we support 10.11 and it was only added in 10.12 * support ``HAVE_FSTATVFS`` which was unintentionally always false - * fix user-created C-API heaptype, issue #2434_ + * fix user-created C-API heaptype, issue 2434_ * fix ``PyDict_Update`` is not actually the same as ``dict.update`` * assign ``tp_doc`` on ``PyTypeObject`` and tie it to the app-level ``__doc__`` attribute - issue #2446_ + issue 2446_ * clean up memory leaks around ``PyObject_GetBuffer``, ``PyMemoryView_GET_BUFFER``, ``PyMemoryView_FromBuffer``, and ``PyBuffer_Release`` * improve support for creating C-extension objects from app-level classes, filling more slots, especially ``tp_new`` and ``tp_dealloc`` - * fix for ``ctypes.c_bool`` returning ``bool`` restype, issue #2475_ + * fix for ``ctypes.c_bool`` returning ``bool`` restype, issue 2475_ * fix in corner cases with the GIL and C-API functions - * allow overriding thread.local.__init__ in a subclass, issue #2501_ - * allow ``PyClass_New`` to be called with NULL as the first arguemnt, issue #2504_ + * allow overriding thread.local.__init__ in a subclass, issue 2501_ + * allow ``PyClass_New`` to be called with NULL as the first arguemnt, issue 2504_ * Performance improvements: diff --git a/pypy/doc/release-v5.7.1.rst b/pypy/doc/release-v5.7.1.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-v5.7.1.rst @@ -0,0 +1,50 @@ +========== +PyPy 5.7.1 +========== + +We have released a bugfix PyPy2.7-v5.7.1 and PyPy3.5-v5.7.1 beta (Linux 64bit), +due to the following issues: + + * correctly handle an edge case in dict.pop (issue 2508_) + + * fix a regression to correctly handle multiple inheritance in a C-API type + where the second base is an app-level class with a ``__new__`` function + + * fix a regression to fill a C-API type's ``tp_getattr`` slot from a + ``__getattr__`` method (issue 2523_) + +Thanks to those who reported the issues. + +.. _2508: https://bitbucket.org/pypy/pypy/issues/2508 +.. _2523: https://bitbucket.org/pypy/pypy/issues/2523 + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7 and CPython 3.5. It's fast (`PyPy and CPython 2.7.x`_ performance comparison) +due to its integrated tracing JIT compiler. + +We also welcome developers of other `dynamic languages`_ to see what RPython +can do for them. + +The PyPy 2.7 release supports: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS X 64 bits, Windows 32 bits, OpenBSD, FreeBSD) + + * newer **ARM** hardware (ARMv6 or ARMv7, with VFPv3) running Linux, + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://rpython.readthedocs.io/en/latest/examples.html + +Please update, and continue to help us make PyPy better. + +Cheers + +The PyPy Team + 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 @@ -21,3 +21,10 @@ .. branch: vmprof-native PyPy support to profile native frames in vmprof. + +.. branch: reusing-r11 +.. branch: branch-prediction + +Performance tweaks in the x86 JIT-generated machine code: rarely taken +blocks are moved off-line. Also, the temporary register used to contain +large constants is reused across instructions. 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 @@ -18,3 +18,6 @@ Implement posix.posix_fallocate() and posix.posix_fadvise() +.. branch: py3.5-mac-translate + +Fix for different posix primitives on MacOS diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py --- a/pypy/interpreter/astcompiler/assemble.py +++ b/pypy/interpreter/astcompiler/assemble.py @@ -7,6 +7,7 @@ from pypy.interpreter.astcompiler import ast, consts, misc, symtable from pypy.interpreter.error import OperationError from pypy.interpreter.pycode import PyCode +from pypy.interpreter.miscutils import string_sort from pypy.tool import stdlib_opcode as ops @@ -138,9 +139,12 @@ def _make_index_dict_filter(syms, flag1, flag2): + names = syms.keys() + string_sort(names) # return cell vars in alphabetical order i = 0 result = {} - for name, scope in syms.iteritems(): + for name in names: + scope = syms[name] if scope in (flag1, flag2): result[name] = i i += 1 @@ -172,6 +176,7 @@ self.cell_vars = _make_index_dict_filter(scope.symbols, symtable.SCOPE_CELL, symtable.SCOPE_CELL_CLASS) + string_sort(scope.free_vars) # return free vars in alphabetical order self.free_vars = _iter_to_dict(scope.free_vars, len(self.cell_vars)) self.w_consts = space.newdict() self.argcount = 0 diff --git a/pypy/interpreter/astcompiler/symtable.py b/pypy/interpreter/astcompiler/symtable.py --- a/pypy/interpreter/astcompiler/symtable.py +++ b/pypy/interpreter/astcompiler/symtable.py @@ -38,7 +38,7 @@ self.roles = {} self.varnames = [] self.children = [] - self.free_vars = {} + self.free_vars = [] # a bag of names: the order doesn't matter here self.temp_name_counter = 1 self.has_free = False self.child_has_free = False @@ -135,7 +135,8 @@ err = "no binding for nonlocal '%s' found" % (name,) raise SyntaxError(err, self.lineno, self.col_offset) self.symbols[name] = SCOPE_FREE - self.free_vars[name] = None + if not self._hide_bound_from_nested_scopes: + self.free_vars.append(name) free[name] = None self.has_free = True elif flags & SYM_BOUND: @@ -147,7 +148,7 @@ pass elif bound and name in bound: self.symbols[name] = SCOPE_FREE - self.free_vars[name] = None + self.free_vars.append(name) free[name] = None self.has_free = True elif name in globs: @@ -204,7 +205,7 @@ except KeyError: if bound and name in bound: self.symbols[name] = SCOPE_FREE - self.free_vars[name] = None + self.free_vars.append(name) else: if role_here & (SYM_BOUND | SYM_GLOBAL) and \ self._hide_bound_from_nested_scopes: @@ -213,7 +214,7 @@ # scope. We add the name to the class scope's list of free # vars, so it will be passed through by the interpreter, but # we leave the scope alone, so it can be local on its own. - self.free_vars[name] = None + self.free_vars.append(name) self._check_optimization() free.update(new_free) diff --git a/pypy/interpreter/miscutils.py b/pypy/interpreter/miscutils.py --- a/pypy/interpreter/miscutils.py +++ b/pypy/interpreter/miscutils.py @@ -2,6 +2,9 @@ Miscellaneous utilities. """ +from rpython.rlib.listsort import make_timsort_class + + class ThreadLocals: """Pseudo thread-local storage, for 'space.threadlocals'. This is not really thread-local at all; the intention is that the PyPy @@ -53,3 +56,15 @@ def set(self, key, value): self._dict[key] = value return FakeWeakValueDict() + + +_StringBaseTimSort = make_timsort_class() + +class StringSort(_StringBaseTimSort): + def lt(self, a, b): + return a < b + +def string_sort(lst): + """Sort a (resizable) list of strings.""" + sorter = StringSort(lst, len(lst)) + sorter.sort() 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 @@ -1035,6 +1035,32 @@ else: assert l1 == l2 == l3 == l4 == [1, 3, 2, 4] + def test_freevars_order(self): + # co_cellvars and co_freevars are guaranteed to appear in + # alphabetical order. See CPython Issue #15368 (which does + # not come with tests). + source = """if 1: + def f1(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15): + def g1(): + return (x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15) + return g1 + def f2(x15,x14,x13,x12,x11,x10,x9,x8,x7,x6,x5,x4,x3,x2,x1): + def g2(): + return (x15,x14,x13,x12,x11,x10,x9,x8,x7,x6,x5,x4,x3,x2,x1) + return g2 + c1 = f1(*range(15)).__code__.co_freevars + c2 = f2(*range(15)).__code__.co_freevars + r1 = f1.__code__.co_cellvars + r2 = f2.__code__.co_cellvars + """ + d = {} + exec(source, d) + assert d['c1'] == d['c2'] + # the test above is important for a few bytecode hacks, + # but actually we get them in alphabetical order, so check that: + assert d['c1'] == tuple(sorted(d['c1'])) + assert d['r1'] == d['r2'] == d['c1'] + def test_ast_equality(self): import _ast sample_code = [ 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 @@ -67,6 +67,7 @@ interpleveldefs = { 'attach_gdb' : 'interp_magic.attach_gdb', 'internal_repr' : 'interp_magic.internal_repr', + 'objects_in_repr' : 'interp_magic.objects_in_repr', 'bytebuffer' : 'bytebuffer.bytebuffer', 'identity_dict' : 'interp_identitydict.W_IdentityDict', 'debug_start' : 'interp_debug.debug_start', diff --git a/pypy/module/__pypy__/interp_magic.py b/pypy/module/__pypy__/interp_magic.py --- a/pypy/module/__pypy__/interp_magic.py +++ b/pypy/module/__pypy__/interp_magic.py @@ -15,6 +15,14 @@ def internal_repr(space, w_object): return space.newtext('%r' % (w_object,)) +def objects_in_repr(space): + """The identitydict of objects currently being repr(). + + This object is thread-local and can be used in a __repr__ method + to avoid recursion. + """ + return space.get_objects_in_repr() + def attach_gdb(space): """Run an interp-level gdb (or pdb when untranslated)""" diff --git a/pypy/module/_collections/interp_deque.py b/pypy/module/_collections/interp_deque.py --- a/pypy/module/_collections/interp_deque.py +++ b/pypy/module/_collections/interp_deque.py @@ -406,11 +406,7 @@ def repr(self): space = self.space - ec = space.getexecutioncontext() - w_currently_in_repr = ec._py_repr - if w_currently_in_repr is None: - w_currently_in_repr = ec._py_repr = space.newdict() - return dequerepr(space, w_currently_in_repr, self) + return dequerepr(space, space.get_objects_in_repr(), self) @specialize.arg(2) def compare(self, w_other, op): @@ -523,18 +519,17 @@ app = gateway.applevel(""" def dequerepr(currently_in_repr, d): 'The app-level part of repr().' - deque_id = id(d) - if deque_id in currently_in_repr: + if d in currently_in_repr: return '[...]' # strange because it's a deque and this # strongly suggests it's a list instead, # but confirmed behavior from python-dev else: - currently_in_repr[deque_id] = 1 + currently_in_repr[d] = 1 try: listrepr = "[" + ", ".join([repr(x) for x in d]) + ']' finally: try: - del currently_in_repr[deque_id] + del currently_in_repr[d] except: pass if d.maxlen is None: 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 @@ -6,3 +6,9 @@ from _collections import OrderedDict assert issubclass(OrderedDict, dict) assert hasattr(OrderedDict, 'move_to_end') + + def test_recursive_repr(self): + from _collections import OrderedDict + d = OrderedDict() + d[1] = d + assert repr(d) == 'OrderedDict([(1, ...)])' diff --git a/pypy/module/_io/interp_bufferedio.py b/pypy/module/_io/interp_bufferedio.py --- a/pypy/module/_io/interp_bufferedio.py +++ b/pypy/module/_io/interp_bufferedio.py @@ -1035,7 +1035,12 @@ raise oefmt(space.w_ValueError, "I/O operation on uninitialized object") w_meth = space.getattr(self.w_reader, space.newtext("close")) - space.call_args(w_meth, __args__) + try: + space.call_args(w_meth, __args__) + except OperationError as e2: + if e: + e2.chain_exceptions(space, e) + e = e2 if e: raise e diff --git a/pypy/module/_io/interp_io.py b/pypy/module/_io/interp_io.py --- a/pypy/module/_io/interp_io.py +++ b/pypy/module/_io/interp_io.py @@ -86,46 +86,61 @@ if binary and newline is not None: raise oefmt(space.w_ValueError, "binary mode doesn't take a newline argument") - w_raw = space.call_function( - space.gettypefor(W_FileIO), w_file, space.newtext(rawmode), - space.newbool(bool(closefd)), w_opener) - isatty = space.is_true(space.call_method(w_raw, "isatty")) - line_buffering = buffering == 1 or (buffering < 0 and isatty) - if line_buffering: - buffering = -1 + w_result = None + try: + w_raw = space.call_function( + space.gettypefor(W_FileIO), w_file, space.newtext(rawmode), + space.newbool(bool(closefd)), w_opener) + w_result = w_raw - if buffering < 0: - buffering = space.c_int_w(space.getattr(w_raw, space.newtext("_blksize"))) + isatty = space.is_true(space.call_method(w_raw, "isatty")) + line_buffering = buffering == 1 or (buffering < 0 and isatty) + if line_buffering: + buffering = -1 - if buffering < 0: - raise oefmt(space.w_ValueError, "invalid buffering size") + if buffering < 0: + buffering = space.c_int_w(space.getattr( + w_raw, space.newtext("_blksize"))) - if buffering == 0: - if not binary: - raise oefmt(space.w_ValueError, "can't have unbuffered text I/O") - return w_raw + if buffering < 0: + raise oefmt(space.w_ValueError, "invalid buffering size") - if updating: - buffer_cls = W_BufferedRandom - elif writing or creating or appending: - buffer_cls = W_BufferedWriter - elif reading: - buffer_cls = W_BufferedReader - else: - raise oefmt(space.w_ValueError, "unknown mode: '%s'", mode) - w_buffer = space.call_function( - space.gettypefor(buffer_cls), w_raw, space.newint(buffering) - ) - if binary: - return w_buffer + if buffering == 0: + if not binary: + raise oefmt(space.w_ValueError, + "can't have unbuffered text I/O") + return w_result - w_wrapper = space.call_function(space.gettypefor(W_TextIOWrapper), - w_buffer, - space.newtext_or_none(encoding), - space.newtext_or_none(errors), - space.newtext_or_none(newline), - space.newbool(line_buffering) - ) - space.setattr(w_wrapper, space.newtext("mode"), space.newtext(mode)) - return w_wrapper + if updating: + buffer_cls = W_BufferedRandom + elif writing or creating or appending: + buffer_cls = W_BufferedWriter + elif reading: + buffer_cls = W_BufferedReader + else: + raise oefmt(space.w_ValueError, "unknown mode: '%s'", mode) + w_buffer = space.call_function( + space.gettypefor(buffer_cls), w_raw, space.newint(buffering) + ) + w_result = w_buffer + if binary: + return w_result + + w_wrapper = space.call_function(space.gettypefor(W_TextIOWrapper), + w_buffer, + space.newtext_or_none(encoding), + space.newtext_or_none(errors), + space.newtext_or_none(newline), + space.newbool(line_buffering) + ) + w_result = w_wrapper + space.setattr(w_wrapper, space.newtext("mode"), space.newtext(mode)) + return w_result + except OperationError as e: + if w_result: + try: + space.call_method(w_result, "close") + except OperationError as e2: + e.chain_exceptions(space, e2) + raise diff --git a/pypy/module/_io/interp_textio.py b/pypy/module/_io/interp_textio.py --- a/pypy/module/_io/interp_textio.py +++ b/pypy/module/_io/interp_textio.py @@ -1,6 +1,6 @@ import sys -from pypy.interpreter.baseobjspace import W_Root +from pypy.interpreter.baseobjspace import W_Root, BufferInterfaceNotFound from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import WrappedDefault, interp2app, unwrap_spec from pypy.interpreter.typedef import ( @@ -290,7 +290,7 @@ try: w_locale = space.call_method(space.builtin, '__import__', - space.newtext('locale')) + space.newtext('_bootlocale')) w_encoding = space.call_method(w_locale, 'getpreferredencoding', space.w_False) except OperationError as e: @@ -528,13 +528,20 @@ def close_w(self, space): self._check_attached(space) - if not space.is_true(space.getattr(self.w_buffer, - space.newtext("closed"))): + if space.is_true(space.getattr(self.w_buffer, + space.newtext("closed"))): + return + try: + space.call_method(self, "flush") + except OperationError as e: try: - space.call_method(self, "flush") - finally: ret = space.call_method(self.w_buffer, "close") - return ret + except OperationError as e2: + e2.chain_exceptions(space, e) + raise + else: + ret = space.call_method(self.w_buffer, "close") + return ret def _dealloc_warn_w(self, space, w_source): space.call_method(self.w_buffer, "_dealloc_warn", w_source) @@ -584,6 +591,10 @@ # Given this, we know there was a valid snapshot point # len(dec_buffer) bytes ago with decoder state (b'', dec_flags). w_dec_buffer, w_dec_flags = space.unpackiterable(w_state, 2) + if not space.isinstance_w(w_dec_buffer, space.w_bytes): + msg = "decoder getstate() should have returned a bytes " \ + "object not '%T'" + raise oefmt(space.w_TypeError, msg, w_dec_buffer) dec_buffer = space.bytes_w(w_dec_buffer) dec_flags = space.int_w(w_dec_flags) else: @@ -591,16 +602,18 @@ dec_flags = 0 # Read a chunk, decode it, and put the result in self._decoded_chars - w_input = space.call_method(self.w_buffer, - "read1" if self.has_read1 else "read", + func_name = "read1" if self.has_read1 else "read" + w_input = space.call_method(self.w_buffer, func_name, space.newint(self.chunk_size)) - if not space.isinstance_w(w_input, space.w_bytes): - msg = "decoder getstate() should have returned a bytes " \ - "object not '%T'" - raise oefmt(space.w_TypeError, msg, w_input) + try: + input_buf = w_input.buffer_w(space, space.BUF_SIMPLE) + except BufferInterfaceNotFound: + msg = ("underlying %s() should have returned a bytes-like " + "object, not '%T'") + raise oefmt(space.w_TypeError, msg, func_name, w_input) - eof = space.len_w(w_input) == 0 + eof = input_buf.getlength() == 0 w_decoded = space.call_method(self.w_decoder, "decode", w_input, space.newbool(eof)) check_decoded(space, w_decoded) @@ -611,7 +624,7 @@ if self.telling: # At the snapshot point, len(dec_buffer) bytes before the read, # the next input to be decoded is dec_buffer + input_chunk. - next_input = dec_buffer + space.bytes_w(w_input) + next_input = dec_buffer + input_buf.as_str() self.snapshot = PositionSnapshot(dec_flags, next_input) return not eof @@ -788,9 +801,10 @@ text = space.unicode_w(w_text) needflush = False + text_needflush = False if self.write_through: - needflush = True - elif self.line_buffering and (haslf or text.find(u'\r') >= 0): + text_needflush = True + if self.line_buffering and (haslf or text.find(u'\r') >= 0): needflush = True # XXX What if we were just reading? @@ -807,7 +821,8 @@ self.pending_bytes.append(b) self.pending_bytes_count += len(b) - if self.pending_bytes_count > self.chunk_size or needflush: + if (self.pending_bytes_count > self.chunk_size or + needflush or text_needflush): self._writeflush(space) if needflush: @@ -863,14 +878,18 @@ space.newtuple([space.newbytes(""), space.newint(cookie.dec_flags)])) - def _encoder_setstate(self, space, cookie): - if cookie.start_pos == 0 and cookie.dec_flags == 0: + def _encoder_reset(self, space, start_of_stream): + if start_of_stream: space.call_method(self.w_encoder, "reset") self.encoding_start_of_stream = True else: space.call_method(self.w_encoder, "setstate", space.newint(0)) self.encoding_start_of_stream = False + def _encoder_setstate(self, space, cookie): + self._encoder_reset(space, + cookie.start_pos == 0 and cookie.dec_flags == 0) + @unwrap_spec(whence=int) def seek_w(self, space, w_pos, whence=0): self._check_attached(space) @@ -898,8 +917,13 @@ self.snapshot = None if self.w_decoder: space.call_method(self.w_decoder, "reset") - return space.call_method(self.w_buffer, "seek", - w_pos, space.newint(whence)) + w_res = space.call_method(self.w_buffer, "seek", + w_pos, space.newint(whence)) + if self.w_encoder: + # If seek() == 0, we are at the start of stream + start_of_stream = space.eq_w(w_res, space.newint(0)) + self._encoder_reset(space, start_of_stream) + return w_res elif whence != 0: raise oefmt(space.w_ValueError, diff --git a/pypy/module/_io/test/test_bufferedio.py b/pypy/module/_io/test/test_bufferedio.py --- a/pypy/module/_io/test/test_bufferedio.py +++ b/pypy/module/_io/test/test_bufferedio.py @@ -291,6 +291,39 @@ raises(_io.UnsupportedOperation, bufio.seek, 0) raises(_io.UnsupportedOperation, bufio.tell) + def test_bufio_write_through(self): + import _io as io + # Issue #21396: write_through=True doesn't force a flush() + # on the underlying binary buffered object. + flush_called, write_called = [], [] + class BufferedWriter(io.BufferedWriter): + def flush(self, *args, **kwargs): + flush_called.append(True) + return super().flush(*args, **kwargs) + def write(self, *args, **kwargs): + write_called.append(True) + return super().write(*args, **kwargs) + + rawio = io.BytesIO() + data = b"a" + bufio = BufferedWriter(rawio, len(data)*2) + textio = io.TextIOWrapper(bufio, encoding='ascii', + write_through=True) + # write to the buffered io but don't overflow the buffer + text = data.decode('ascii') + textio.write(text) + + # buffer.flush is not called with write_through=True + assert not flush_called + # buffer.write *is* called with write_through=True + assert write_called + assert rawio.getvalue() == b"" # no flush + + write_called = [] # reset + textio.write(text * 10) # total content is larger than bufio buffer + assert write_called + assert rawio.getvalue() == data * 11 # all flushed + class AppTestBufferedReaderWithThreads(AppTestBufferedReader): spaceconfig = dict(usemodules=['_io', 'thread', 'time']) @@ -707,6 +740,7 @@ pair = _io.BufferedRWPair(reader, writer) err = raises(NameError, pair.close) assert 'reader_non_existing' in str(err.value) + assert 'writer_non_existing' in str(err.value.__context__) assert not pair.closed assert not reader.closed assert not writer.closed diff --git a/pypy/module/_io/test/test_fileio.py b/pypy/module/_io/test/test_fileio.py --- a/pypy/module/_io/test/test_fileio.py +++ b/pypy/module/_io/test/test_fileio.py @@ -302,6 +302,36 @@ return -1 raises(ValueError, _io.FileIO, "foo", 'r', opener=opener) + def test_seek_bom(self): + # The BOM is not written again when seeking manually + import _io + filename = self.tmpfile + '_x3' + for charset in ('utf-8-sig', 'utf-16', 'utf-32'): + with _io.open(filename, 'w', encoding=charset) as f: + f.write('aaa') + pos = f.tell() + with _io.open(filename, 'r+', encoding=charset) as f: + f.seek(pos) + f.write('zzz') + f.seek(0) + f.write('bbb') + with _io.open(filename, 'rb') as f: + assert f.read() == 'bbbzzz'.encode(charset) + + def test_seek_append_bom(self): + # Same test, but first seek to the start and then to the end + import _io, os + filename = self.tmpfile + '_x3' + for charset in ('utf-8-sig', 'utf-16', 'utf-32'): + with _io.open(filename, 'w', encoding=charset) as f: + f.write('aaa') + with _io.open(filename, 'a', encoding=charset) as f: + f.seek(0) + f.seek(0, os.SEEK_END) + f.write('xxx') + with _io.open(filename, 'rb') as f: + assert f.read() == 'aaaxxx'.encode(charset) + def test_flush_at_exit(): from pypy import conftest diff --git a/pypy/module/_io/test/test_io.py b/pypy/module/_io/test/test_io.py --- a/pypy/module/_io/test/test_io.py +++ b/pypy/module/_io/test/test_io.py @@ -474,6 +474,22 @@ assert f.mode == 'x' raises(FileExistsError, _io.open, filename, 'x') + def test_nonbuffered_textio(self): + import warnings, _io as io + filename = self.tmpfile + '_x2' + warnings.simplefilter("always", category=ResourceWarning) + with warnings.catch_warnings(record=True) as recorded: + raises(ValueError, io.open, filename, 'w', buffering=0) + assert recorded == [] + + def test_invalid_newline(self): + import warnings, _io as io + filename = self.tmpfile + '_x2' + warnings.simplefilter("always", category=ResourceWarning) + with warnings.catch_warnings(record=True) as recorded: + raises(ValueError, io.open, filename, 'w', newline='invalid') + assert recorded == [] + class AppTestIoAferClose: spaceconfig = dict(usemodules=['_io']) diff --git a/pypy/module/_io/test/test_textio.py b/pypy/module/_io/test/test_textio.py --- a/pypy/module/_io/test/test_textio.py +++ b/pypy/module/_io/test/test_textio.py @@ -1,5 +1,5 @@ class AppTestTextIO: - spaceconfig = dict(usemodules=['_io', '_locale']) + spaceconfig = dict(usemodules=['_io', '_locale', 'array']) def setup_class(cls): from rpython.rlib.rarithmetic import INT_MAX, UINT_MAX @@ -365,6 +365,22 @@ raises(IOError, txt.close) # exception not swallowed assert txt.closed + def test_close_error_on_close(self): + import _io as io + buffer = io.BytesIO(b'testdata') + def bad_flush(): + raise OSError('flush') + def bad_close(): + raise OSError('close') + buffer.close = bad_close + txt = io.TextIOWrapper(buffer, encoding="ascii") + txt.flush = bad_flush + err = raises(OSError, txt.close) + assert err.value.args == ('close',) + assert isinstance(err.value.__context__, OSError) + assert err.value.__context__.args == ('flush',) + assert not txt.closed + def test_illegal_decoder(self): import _io raises(LookupError, _io.TextIOWrapper, _io.BytesIO(), @@ -381,6 +397,38 @@ t = _io.TextIOWrapper(NonbytesStream(u'a')) raises(TypeError, t.read) + def test_read_byteslike(self): + import _io as io + import array + + class MemviewBytesIO(io.BytesIO): + '''A BytesIO object whose read method returns memoryviews + rather than bytes''' + + def read1(self, len_): + return _to_memoryview(super().read1(len_)) + + def read(self, len_): + return _to_memoryview(super().read(len_)) + + def _to_memoryview(buf): + '''Convert bytes-object *buf* to a non-trivial memoryview''' + + arr = array.array('i') + idx = len(buf) - len(buf) % arr.itemsize + arr.frombytes(buf[:idx]) + return memoryview(arr) + + r = MemviewBytesIO(b'Just some random string\n') + t = io.TextIOWrapper(r, 'utf-8') + + # TextIOwrapper will not read the full string, because + # we truncate it to a multiple of the native int size + # so that we can construct a more complex memoryview. + bytes_val = _to_memoryview(r.getvalue()).tobytes() + + assert t.read(200) == bytes_val.decode('utf-8') + def test_device_encoding(self): import os import sys diff --git a/pypy/module/_posixsubprocess/_posixsubprocess.c b/pypy/module/_posixsubprocess/_posixsubprocess.c --- a/pypy/module/_posixsubprocess/_posixsubprocess.c +++ b/pypy/module/_posixsubprocess/_posixsubprocess.c @@ -18,7 +18,19 @@ #ifdef HAVE_SYS_SYSCALL_H #include <sys/syscall.h> #endif +#if defined(HAVE_SYS_RESOURCE_H) +#include <sys/resource.h> +#endif +#ifdef HAVE_DIRENT_H #include <dirent.h> +#endif + +#if defined(__ANDROID__) && !defined(SYS_getdents64) +/* Android doesn't expose syscalls, add the definition manually. */ +# include <sys/linux-syscalls.h> +# define SYS_getdents64 __NR_getdents64 +#endif + #include "_posixsubprocess.h" #if defined(sun) @@ -38,11 +50,7 @@ # define FD_DIR "/proc/self/fd" #endif -#define POSIX_CALL(call) if ((call) == -1) goto error - - -/* Maximum file descriptor, initialized on module load. */ -static long max_fd; +#define POSIX_CALL(call) do { if ((call) == -1) goto error; } while (0) /* Convert ASCII to a positive int, no libc call. no overflow. -1 on error. */ @@ -75,7 +83,7 @@ if (stat("/dev", &dev_stat) != 0) return 0; if (stat(FD_DIR, &dev_fd_stat) != 0) - return 0; + return 0; if (dev_stat.st_dev == dev_fd_stat.st_dev) return 0; /* / == /dev == /dev/fd means it is static. #fail */ return 1; @@ -130,15 +138,47 @@ } -/* Close all file descriptors in the range start_fd inclusive to - * end_fd exclusive except for those in py_fds_to_keep. If the - * range defined by [start_fd, end_fd) is large this will take a - * long time as it calls close() on EVERY possible fd. +/* Get the maximum file descriptor that could be opened by this process. + * This function is async signal safe for use between fork() and exec(). + */ +static long +safe_get_max_fd(void) +{ + long local_max_fd; +#if defined(__NetBSD__) + local_max_fd = fcntl(0, F_MAXFD); + if (local_max_fd >= 0) + return local_max_fd; +#endif +#if defined(HAVE_SYS_RESOURCE_H) && defined(__OpenBSD__) + struct rlimit rl; + /* Not on the POSIX async signal safe functions list but likely + * safe. TODO - Someone should audit OpenBSD to make sure. */ + if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) + return (long) rl.rlim_max; +#endif +#ifdef _SC_OPEN_MAX + local_max_fd = sysconf(_SC_OPEN_MAX); + if (local_max_fd == -1) +#endif + local_max_fd = 256; /* Matches legacy Lib/subprocess.py behavior. */ + return local_max_fd; +} + + +/* Close all file descriptors in the range from start_fd and higher + * except for those in py_fds_to_keep. If the range defined by + * [start_fd, safe_get_max_fd()) is large this will take a long + * time as it calls close() on EVERY possible fd. + * + * It isn't possible to know for sure what the max fd to go up to + * is for processes with the capability of raising their maximum. */ static void -_close_fds_by_brute_force(int start_fd, int end_fd, long *py_fds_to_keep, +_close_fds_by_brute_force(long start_fd, long *py_fds_to_keep, ssize_t num_fds_to_keep) { + long end_fd = safe_get_max_fd(); ssize_t keep_seq_idx; int fd_num; /* As py_fds_to_keep is sorted we can loop through the list closing @@ -148,13 +188,13 @@ if (keep_fd < start_fd) continue; for (fd_num = start_fd; fd_num < keep_fd; ++fd_num) { - while (close(fd_num) < 0 && errno == EINTR); + close(fd_num); } start_fd = keep_fd + 1; } if (start_fd <= end_fd) { for (fd_num = start_fd; fd_num < end_fd; ++fd_num) { - while (close(fd_num) < 0 && errno == EINTR); + close(fd_num); } } } @@ -175,8 +215,8 @@ char d_name[256]; /* Filename (null-terminated) */ }; -/* Close all open file descriptors in the range start_fd inclusive to end_fd - * exclusive. Do not close any in the sorted py_fds_to_keep list. +/* Close all open file descriptors in the range from start_fd and higher + * Do not close any in the sorted py_fds_to_keep list. * * This version is async signal safe as it does not make any unsafe C library * calls, malloc calls or handle any locks. It is _unfortunate_ to be forced @@ -191,12 +231,10 @@ * it with some cpp #define magic to work on other OSes as well if you want. */ static void -_close_open_fd_range_safe(int start_fd, int end_fd, long *py_fds_to_keep, +_close_open_fds_safe(int start_fd, long *py_fds_to_keep, ssize_t num_fds_to_keep) { int fd_dir_fd; - if (start_fd >= end_fd) - return; #ifdef O_CLOEXEC fd_dir_fd = open(FD_DIR, O_RDONLY | O_CLOEXEC, 0); #else @@ -211,8 +249,7 @@ #endif if (fd_dir_fd == -1) { /* No way to get a list of open fds. */ - _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep, - num_fds_to_keep); + _close_fds_by_brute_force(start_fd, py_fds_to_keep, num_fds_to_keep); return; } else { char buffer[sizeof(struct linux_dirent64)]; @@ -227,10 +264,10 @@ entry = (struct linux_dirent64 *)(buffer + offset); if ((fd = _pos_int_from_ascii(entry->d_name)) < 0) continue; /* Not a number. */ - if (fd != fd_dir_fd && fd >= start_fd && fd < end_fd && + if (fd != fd_dir_fd && fd >= start_fd && !_is_fd_in_sorted_fd_sequence( fd, py_fds_to_keep, num_fds_to_keep)) { - while (close(fd) < 0 && errno == EINTR); + close(fd); } } } @@ -238,13 +275,13 @@ } } -#define _close_open_fd_range _close_open_fd_range_safe +#define _close_open_fds _close_open_fds_safe #else /* NOT (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)) */ -/* Close all open file descriptors in the range start_fd inclusive to end_fd - * exclusive. Do not close any in the sorted py_fds_to_keep list. +/* Close all open file descriptors from start_fd and higher. + * Do not close any in the sorted py_fds_to_keep list. * * This function violates the strict use of async signal safe functions. :( * It calls opendir(), readdir() and closedir(). Of these, the one most @@ -257,26 +294,21 @@ * http://womble.decadent.org.uk/readdir_r-advisory.html */ static void -_close_open_fd_range_maybe_unsafe(int start_fd, int end_fd, - long *py_fds_to_keep, ssize_t num_fds_to_keep) +_close_open_fds_maybe_unsafe(long start_fd, + long *py_fds_to_keep, ssize_t num_fds_to_keep) { DIR *proc_fd_dir; #ifndef HAVE_DIRFD - while (_is_fd_in_sorted_fd_sequence(start_fd, py_fds_to_keep, num_fds_to_keep) && - (start_fd < end_fd)) { + while (_is_fd_in_sorted_fd_sequence(start_fd, py_fds_to_keep, num_fds_to_keep)) { ++start_fd; } - if (start_fd >= end_fd) - return; /* Close our lowest fd before we call opendir so that it is likely to * reuse that fd otherwise we might close opendir's file descriptor in * our loop. This trick assumes that fd's are allocated on a lowest * available basis. */ - while (close(start_fd) < 0 && errno == EINTR); + close(start_fd); ++start_fd; #endif - if (start_fd >= end_fd) - return; #if defined(__FreeBSD__) if (!_is_fdescfs_mounted_on_dev_fd()) @@ -286,7 +318,7 @@ proc_fd_dir = opendir(FD_DIR); if (!proc_fd_dir) { /* No way to get a list of open fds. */ - _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep, num_fds_to_keep); + _close_fds_by_brute_force(start_fd, py_fds_to_keep, num_fds_to_keep); } else { struct dirent *dir_entry; #ifdef HAVE_DIRFD @@ -299,22 +331,22 @@ int fd; if ((fd = _pos_int_from_ascii(dir_entry->d_name)) < 0) continue; /* Not a number. */ - if (fd != fd_used_by_opendir && fd >= start_fd && fd < end_fd && + if (fd != fd_used_by_opendir && fd >= start_fd && !_is_fd_in_sorted_fd_sequence(fd, py_fds_to_keep, num_fds_to_keep)) { - while (close(fd) < 0 && errno == EINTR); + close(fd); } errno = 0; } if (errno) { /* readdir error, revert behavior. Highly Unlikely. */ _close_fds_by_brute_force( - start_fd, end_fd, py_fds_to_keep, num_fds_to_keep); + start_fd, py_fds_to_keep, num_fds_to_keep); } closedir(proc_fd_dir); } } -#define _close_open_fd_range _close_open_fd_range_maybe_unsafe +#define _close_open_fds _close_open_fds_maybe_unsafe #endif /* else NOT (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)) */ @@ -357,15 +389,12 @@ goto error; /* Close parent's pipe ends. */ - if (p2cwrite != -1) { + if (p2cwrite != -1) POSIX_CALL(close(p2cwrite)); - } - if (c2pread != -1) { + if (c2pread != -1) POSIX_CALL(close(c2pread)); - } - if (errread != -1) { + if (errread != -1) POSIX_CALL(close(errread)); - } POSIX_CALL(close(errpipe_read)); /* When duping fds, if there arises a situation where one of the fds is @@ -401,15 +430,12 @@ /* Close pipe fds. Make sure we don't close the same fd more than */ /* once, or standard fds. */ - if (p2cread > 2) { + if (p2cread > 2) POSIX_CALL(close(p2cread)); - } - if (c2pwrite > 2 && c2pwrite != p2cread) { + if (c2pwrite > 2 && c2pwrite != p2cread) POSIX_CALL(close(c2pwrite)); - } - if (errwrite != c2pwrite && errwrite != p2cread && errwrite > 2) { + if (errwrite != c2pwrite && errwrite != p2cread && errwrite > 2) POSIX_CALL(close(errwrite)); - } if (cwd) POSIX_CALL(chdir(cwd)); @@ -441,14 +467,8 @@ /* close FDs after executing preexec_fn, which might open FDs */ if (close_fds) { - int local_max_fd = max_fd; -#if defined(__NetBSD__) - local_max_fd = fcntl(0, F_MAXFD); - if (local_max_fd < 0) - local_max_fd = max_fd; -#endif /* TODO HP-UX could use pstat_getproc() if anyone cares about it. */ - _close_open_fd_range(3, local_max_fd, py_fds_to_keep, num_fds_to_keep); + _close_open_fds(3, py_fds_to_keep, num_fds_to_keep); } /* This loop matches the Lib/os.py _execvpe()'s PATH search when */ @@ -491,7 +511,7 @@ /* We can't call strerror(saved_errno). It is not async signal safe. * The parent process will look the error message up. */ } else { - unused = write(errpipe_write, "RuntimeError:0:", 15); + unused = write(errpipe_write, "SubprocessError:0:", 18); unused = write(errpipe_write, err_msg, strlen(err_msg)); } if (unused) return; /* silly? yes! avoids gcc compiler warning. */ @@ -579,9 +599,4 @@ void pypy_subprocess_init(void) { -#ifdef _SC_OPEN_MAX - max_fd = sysconf(_SC_OPEN_MAX); - if (max_fd == -1) -#endif - max_fd = 256; /* Matches Lib/subprocess.py */ } diff --git a/pypy/module/_posixsubprocess/interp_subprocess.py b/pypy/module/_posixsubprocess/interp_subprocess.py --- a/pypy/module/_posixsubprocess/interp_subprocess.py +++ b/pypy/module/_posixsubprocess/interp_subprocess.py @@ -36,6 +36,15 @@ if config['HAVE_SETSID']: compile_extra.append("-DHAVE_SETSID") +class CConfig: + _compilation_info_ = ExternalCompilationInfo(includes=['dirent.h']) + HAVE_DIRENT_H = platform.Has("opendir") + +config = platform.configure(CConfig) + +if config['HAVE_DIRENT_H']: + compile_extra.append("-DHAVE_DIRENT_H") + eci = eci.merge( rposix.eci_inheritable, ExternalCompilationInfo( diff --git a/pypy/module/_vmprof/test/test_direct.py b/pypy/module/_vmprof/test/test_direct.py --- a/pypy/module/_vmprof/test/test_direct.py +++ b/pypy/module/_vmprof/test/test_direct.py @@ -1,4 +1,4 @@ - +import sys import py try: import cffi @@ -7,6 +7,7 @@ from rpython.rlib import rvmprof srcdir = py.path.local(rvmprof.__file__).join("..", "src") +shareddir = srcdir.join('shared') ffi = cffi.FFI() ffi.cdef(""" @@ -43,7 +44,7 @@ } -""" + open(str(srcdir.join("shared/vmprof_get_custom_offset.h"))).read(), include_dirs=[str(srcdir)]) +""" + open(str(srcdir.join("shared/vmprof_get_custom_offset.h"))).read(), include_dirs=[str(srcdir), str(shareddir)]) class TestDirect(object): def test_infrastructure(self): 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 @@ -419,11 +419,11 @@ """ while True: try: - res = rposix.posix_fadvise(fd, offset, length, advice) + rposix.posix_fadvise(fd, offset, length, advice) except OSError as e: wrap_oserror(space, e, eintr_retry=True) else: - return space.newint(res) + return # ____________________________________________________________ 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 @@ -862,23 +862,19 @@ if hasattr(rposix, 'posix_fadvise'): def test_os_posix_fadvise(self): - posix, os = self.posix, self.os - localdir = os.getcwd() - os.mkdir(self.path2 + 'test_os_posix_fadvise') + posix = self.posix + fd = posix.open(self.path2 + 'test_os_posix_fadvise', posix.O_CREAT | posix.O_RDWR) try: - fd = os.open(self.path2 + 'test_os_posix_fadvise', os.O_RDONLY) - try: - mypath = os.getcwd() - assert posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_WILLNEED) == 0 - assert posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_NORMAL) == 0 - assert posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_SEQUENTIAL) == 0 - assert posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_RANDOM) == 0 - assert posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_NOREUSE) == 0 - assert posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_DONTNEED) == 0 - finally: - os.close(fd) + posix.write(fd, b"foobar") + assert posix.posix_fadvise(fd, 0, 1, posix.POSIX_FADV_WILLNEED) is None + assert posix.posix_fadvise(fd, 1, 1, posix.POSIX_FADV_NORMAL) is None + assert posix.posix_fadvise(fd, 2, 1, posix.POSIX_FADV_SEQUENTIAL) is None + assert posix.posix_fadvise(fd, 3, 1, posix.POSIX_FADV_RANDOM) is None + assert posix.posix_fadvise(fd, 4, 1, posix.POSIX_FADV_NOREUSE) is None + assert posix.posix_fadvise(fd, 5, 1, posix.POSIX_FADV_DONTNEED) is None + raises(OSError, posix.posix_fadvise, fd, 6, 1, 1234567) finally: - os.chdir(localdir) + posix.close(fd) if hasattr(rposix, 'posix_fallocate'): def test_os_posix_posix_fallocate(self): diff --git a/pypy/module/sys/app.py b/pypy/module/sys/app.py --- a/pypy/module/sys/app.py +++ b/pypy/module/sys/app.py @@ -3,7 +3,7 @@ The 'sys' module. """ -from _structseq import structseqtype, structseqfield +from _structseq import structseqtype, structseqfield, SimpleNamespace import sys import _imp @@ -111,41 +111,6 @@ null__xoptions = {} -class SimpleNamespace: - """A simple attribute-based namespace. - -SimpleNamespace(**kwargs)""" - - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - def __repr__(self): - ident = id(self) - if ident in sns_recurse: - return "namespace(...)" - sns_recurse.add(ident) - try: - pairs = ('%s=%r' % item for item in sorted(self.__dict__.items())) - return "namespace(%s)" % ', '.join(pairs) - finally: - sns_recurse.discard(ident) - - def __eq__(self, other): - if issubclass(type(other), SimpleNamespace): - return self.__dict__ == other.__dict__ - return NotImplemented - - def __ne__(self, other): - if issubclass(type(other), SimpleNamespace): - return self.__dict__ != other.__dict__ - return NotImplemented - -sns_recurse = set() - -# This class is not exposed in sys, but by the types module. -SimpleNamespace.__module__ = 'types' - - implementation = SimpleNamespace( name='pypy', version=sys.version_info, diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py b/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py +++ b/pypy/module/test_lib_pypy/ctypes_tests/test_functions.py @@ -596,3 +596,37 @@ get_data.errcheck = ret_list_p(1) assert get_data('testing!') == [-1, -2, -3, -4] + + def test_issue2533(self): + import cffi + ffi = cffi.FFI() + ffi.cdef("int **fetchme(void);") + ffi.set_source("_x_cffi", """ + int **fetchme(void) + { + static int a = 42; + static int *pa = &a; + return &pa; + } + """) + from rpython.tool.udir import udir + ffi.compile(verbose=True, tmpdir=str(udir)) + + import sys + sys.path.insert(0, str(udir)) + try: + from _x_cffi import ffi, lib + finally: + sys.path.pop(0) + fetchme = ffi.addressof(lib, 'fetchme') + fetchme = int(ffi.cast("intptr_t", fetchme)) + + FN = CFUNCTYPE(POINTER(POINTER(c_int))) + ff = cast(fetchme, FN) + + g = ff() + assert g.contents.contents.value == 42 + + h = c_int(43) + g[0] = pointer(h) # used to crash here + assert g.contents.contents.value == 43 diff --git a/pypy/module/time/app_time.py b/pypy/module/time/app_time.py --- a/pypy/module/time/app_time.py +++ b/pypy/module/time/app_time.py @@ -1,7 +1,6 @@ # NOT_RPYTHON -from _structseq import structseqtype, structseqfield -from types import SimpleNamespace +from _structseq import structseqtype, structseqfield, SimpleNamespace import time class struct_time(metaclass=structseqtype): 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 @@ -143,11 +143,7 @@ init_or_update(space, self, __args__, 'dict') def descr_repr(self, space): - ec = space.getexecutioncontext() - w_currently_in_repr = ec._py_repr - if w_currently_in_repr is None: - w_currently_in_repr = ec._py_repr = space.newdict() - return dictrepr(space, w_currently_in_repr, self) + return dictrepr(space, space.get_objects_in_repr(), self) def descr_eq(self, space, w_other): if space.is_w(self, w_other): @@ -404,10 +400,9 @@ def dictrepr(currently_in_repr, d): if len(d) == 0: return "{}" - dict_id = id(d) - if dict_id in currently_in_repr: + if d in currently_in_repr: return '{...}' - currently_in_repr[dict_id] = 1 + currently_in_repr[d] = 1 try: items = [] # XXX for now, we cannot use items() without list at @@ -419,7 +414,7 @@ return "{" + ', '.join(items) + "}" finally: try: - del currently_in_repr[dict_id] + del currently_in_repr[d] except: pass ''', filename=__file__) 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 @@ -23,6 +23,7 @@ WrappedDefault, applevel, interp2app, unwrap_spec) from pypy.interpreter.signature import Signature from pypy.interpreter.typedef import TypeDef +from pypy.interpreter.miscutils import StringSort from pypy.objspace.std.bytesobject import W_BytesObject from pypy.objspace.std.floatobject import W_FloatObject from pypy.objspace.std.intobject import W_IntObject @@ -438,11 +439,7 @@ def descr_repr(self, space): if self.length() == 0: return space.newtext('[]') - ec = space.getexecutioncontext() - w_currently_in_repr = ec._py_repr - if w_currently_in_repr is None: - w_currently_in_repr = ec._py_repr = space.newdict() - return listrepr(space, w_currently_in_repr, self) + return listrepr(space, space.get_objects_in_repr(), self) def descr_eq(self, space, w_other): if not isinstance(w_other, W_ListObject): @@ -2014,15 +2011,14 @@ app = applevel(""" def listrepr(currently_in_repr, l): 'The app-level part of repr().' - list_id = id(l) - if list_id in currently_in_repr: + if l in currently_in_repr: return '[...]' - currently_in_repr[list_id] = 1 + currently_in_repr[l] = 1 try: return "[" + ", ".join([repr(x) for x in l]) + ']' finally: try: - del currently_in_repr[list_id] + del currently_in_repr[l] except: pass """, filename=__file__) @@ -2039,7 +2035,6 @@ IntBaseTimSort = make_timsort_class() FloatBaseTimSort = make_timsort_class() IntOrFloatBaseTimSort = make_timsort_class() -StringBaseTimSort = make_timsort_class() UnicodeBaseTimSort = make_timsort_class() @@ -2076,11 +2071,6 @@ return fa < fb -class StringSort(StringBaseTimSort): - def lt(self, a, b): - return a < b - - class UnicodeSort(UnicodeBaseTimSort): def lt(self, a, b): return a < b 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 @@ -126,6 +126,14 @@ ec._py_repr = None return ec + def get_objects_in_repr(self): + from pypy.module.__pypy__.interp_identitydict import W_IdentityDict + ec = self.getexecutioncontext() + w_currently_in_repr = ec._py_repr + if w_currently_in_repr is None: + w_currently_in_repr = ec._py_repr = W_IdentityDict(self) + return w_currently_in_repr + def gettypefor(self, cls): return self.gettypeobject(cls.typedef) 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 @@ -165,11 +165,7 @@ _initialize_set(space, self, w_iterable) def descr_repr(self, space): - ec = space.getexecutioncontext() - w_currently_in_repr = ec._py_repr - if w_currently_in_repr is None: - w_currently_in_repr = ec._py_repr = space.newdict() - return setrepr(space, w_currently_in_repr, self) + return setrepr(space, space.get_objects_in_repr(), self) def descr_eq(self, space, w_other): if isinstance(w_other, W_BaseSetObject): @@ -1700,10 +1696,9 @@ app = gateway.applevel(""" def setrepr(currently_in_repr, s): 'The app-level part of repr().' - set_id = id(s) - if set_id in currently_in_repr: + if s in currently_in_repr: return '%s(...)' % (s.__class__.__name__,) - currently_in_repr[set_id] = 1 + currently_in_repr[s] = 1 try: if not s: return '%s()' % (s.__class__.__name__,) @@ -1714,7 +1709,7 @@ return '%s({%s})' % (s.__class__.__name__, listrepr[1:-1]) finally: try: - del currently_in_repr[set_id] + del currently_in_repr[s] except: pass """, filename=__file__) diff --git a/pypy/objspace/std/sliceobject.py b/pypy/objspace/std/sliceobject.py --- a/pypy/objspace/std/sliceobject.py +++ b/pypy/objspace/std/sliceobject.py @@ -253,7 +253,7 @@ app = gateway.applevel(""" - from operator import index + from _operator import index def evaluate_slice_index(x): try: 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 @@ -1087,7 +1087,7 @@ return w_name.text_w(space) def create_all_slots(w_self, hasoldstylebase, w_bestbase, force_new_layout): - from pypy.objspace.std.listobject import StringSort + from pypy.interpreter.miscutils import string_sort base_layout = w_bestbase.layout index_next_extra_slot = base_layout.nslots @@ -1120,8 +1120,7 @@ else: newslotnames.append(slot_name) # Sort the list of names collected so far - sorter = StringSort(newslotnames, len(newslotnames)) - sorter.sort() + string_sort(newslotnames) # Try to create all slots in order. The creation of some of # them might silently fail; then we delete the name from the # list. At the end, 'index_next_extra_slot' has been advanced 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 @@ -241,12 +241,14 @@ zf.close() else: archive = str(builddir.join(name + '.tar.bz2')) - if sys.platform == 'darwin' or sys.platform.startswith('freebsd'): + 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...""" 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) elif sys.platform == 'cygwin': e = os.system('tar --owner=Administrator --group=Administrators --numeric-owner -cvjf ' + archive + " " + name) else: diff --git a/rpython/jit/backend/llsupport/asmmemmgr.py b/rpython/jit/backend/llsupport/asmmemmgr.py --- a/rpython/jit/backend/llsupport/asmmemmgr.py +++ b/rpython/jit/backend/llsupport/asmmemmgr.py @@ -250,7 +250,7 @@ return self.rawstart def overwrite(self, index, char): - assert 0 <= index < self.get_relative_pos() + assert 0 <= index < self.get_relative_pos(break_basic_block=False) block = self._cursubblock index -= self._baserelpos while index < 0: @@ -264,7 +264,8 @@ self.overwrite(index + 2, chr((val >> 16) & 0xff)) self.overwrite(index + 3, chr((val >> 24) & 0xff)) - def get_relative_pos(self): + def get_relative_pos(self, break_basic_block=True): + # 'break_basic_block' is only used in x86 return self._baserelpos + self._cursubindex def copy_to_raw_memory(self, addr): @@ -288,7 +289,7 @@ HEX = '0123456789ABCDEF' dump = [] src = rffi.cast(rffi.CCHARP, addr) - end = self.get_relative_pos() + end = self.get_relative_pos(break_basic_block=False) if count != -1: end = offset + count for p in range(offset, end): @@ -336,17 +337,20 @@ def _become_a_plain_block_builder(self): # hack purely for speed of tests - self._data = [] - self.writechar = self._data.append - self.overwrite = self._data.__setitem__ - self.get_relative_pos = self._data.__len__ + self._data = _data = [] + self.writechar = _data.append + self.overwrite = _data.__setitem__ + def get_relative_pos(break_basic_block=True): + return len(_data) + self.get_relative_pos = get_relative_pos def plain_copy_to_raw_memory(addr): dst = rffi.cast(rffi.CCHARP, addr) - for i, c in enumerate(self._data): + for i, c in enumerate(_data): dst[i] = c self._copy_to_raw_memory = plain_copy_to_raw_memory def insert_gcroot_marker(self, mark): if self.gcroot_markers is None: self.gcroot_markers = [] - self.gcroot_markers.append((self.get_relative_pos(), mark)) + self.gcroot_markers.append( + (self.get_relative_pos(break_basic_block=False), mark)) 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 @@ -265,14 +265,16 @@ def enter_portal_frame(self, op): if self.cpu.HAS_CODEMAP: + pos = self.mc.get_relative_pos(break_basic_block=False) self.codemap_builder.enter_portal_frame(op.getarg(0).getint(), op.getarg(1).getint(), - self.mc.get_relative_pos()) + pos) def leave_portal_frame(self, op): if self.cpu.HAS_CODEMAP: + pos = self.mc.get_relative_pos(break_basic_block=False) self.codemap_builder.leave_portal_frame(op.getarg(0).getint(), - self.mc.get_relative_pos()) + pos) def call_assembler(self, op, argloc, vloc, result_loc, tmploc): """ 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 @@ -76,6 +76,7 @@ BaseAssembler.setup(self, looptoken) assert self.memcpy_addr != 0, "setup_once() not called?" self.current_clt = looptoken.compiled_loop_token + self.pending_slowpaths = [] self.pending_guard_tokens = [] if WORD == 8: self.pending_memoryerror_trampoline_from = [] @@ -95,6 +96,7 @@ self.pending_memoryerror_trampoline_from = None self.mc = None self.current_clt = None + self.frame_depth_to_patch = None def _build_float_constants(self): # 0x80000000000000008000000000000000 @@ -181,6 +183,7 @@ """ This builds a general call slowpath, for whatever call happens to come. """ + self.pending_slowpaths = [] mc = codebuf.MachineCodeBlockWrapper() # copy registers to the frame, with the exception of the # 'cond_call_register_arguments' and eax, because these have already _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit