Author: Ronny Pfannschmidt <opensou...@ronnypfannschmidt.de> Branch: refine-testrunner Changeset: r73784:27f5610c0e09 Date: 2014-10-05 17:51 +0200 http://bitbucket.org/pypy/pypy/changeset/27f5610c0e09/
Log: merge from default diff too long, truncating to 2000 out of 11590 lines diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -367,3 +367,43 @@ Detailed license information is contained in the NOTICE file in the directory. + +Licenses and Acknowledgements for Incorporated Software +======================================================= + +This section is an incomplete, but growing list of licenses and +acknowledgements for third-party software incorporated in the PyPy +distribution. + +License for 'Tcl/Tk' +-------------------- + +This copy of PyPy contains library code that may, when used, result in +the Tcl/Tk library to be loaded. PyPy also includes code that may be +regarded as being a copy of some parts of the Tcl/Tk header files. +You may see a copy of the License for Tcl/Tk in the file +`lib_pypy/_tkinter/license.terms` included here. + +License for 'bzip2' +------------------- + +This copy of PyPy may be linked (dynamically or statically) with the +bzip2 library. You may see a copy of the License for bzip2/libbzip2 at + + http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html + +License for 'openssl' +--------------------- + +This copy of PyPy may be linked (dynamically or statically) with the +openssl library. You may see a copy of the License for OpenSSL at + + https://www.openssl.org/source/license.html + +License for 'gdbm' +------------------ + +The gdbm module includes code from gdbm.h, which is distributed under +the terms of the GPL license version 2 or any later version. Thus the +gdbm module, provided in the file lib_pypy/gdbm.py, is redistributed +under the terms of the GPL license as well. diff --git a/_pytest/README-BEFORE-UPDATING b/_pytest/README-BEFORE-UPDATING new file mode 100644 --- /dev/null +++ b/_pytest/README-BEFORE-UPDATING @@ -0,0 +1,17 @@ +This is PyPy's code of the pytest lib. We don't expect to upgrade it +very often, but once we do: + + WARNING! + + WE HAVE MADE A FEW TWEAKS HERE! + +Please be sure that you don't just copy the newer version from +upstream without checking the few changes that we did. This +can be done like this: + + cd <this directory> + hg log . -v | less + +then search for all " _pytest/" in that list to know which are the +relevant checkins. (Look for the checkins that only edit one +or two files in this directory.) diff --git a/_pytest/resultlog.py b/_pytest/resultlog.py --- a/_pytest/resultlog.py +++ b/_pytest/resultlog.py @@ -53,16 +53,24 @@ self.config = config self.logfile = logfile # preferably line buffered - def write_log_entry(self, testpath, lettercode, longrepr): - py.builtin.print_("%s %s" % (lettercode, testpath), file=self.logfile) + def write_log_entry(self, testpath, lettercode, longrepr, sections=None): + _safeprint("%s %s" % (lettercode, testpath), file=self.logfile) for line in longrepr.splitlines(): - py.builtin.print_(" %s" % line, file=self.logfile) + _safeprint(" %s" % line, file=self.logfile) + if sections is not None and ( + lettercode in ('E', 'F')): # to limit the size of logs + for title, content in sections: + _safeprint(" ---------- %s ----------" % (title,), + file=self.logfile) + for line in content.splitlines(): + _safeprint(" %s" % line, file=self.logfile) def log_outcome(self, report, lettercode, longrepr): testpath = getattr(report, 'nodeid', None) if testpath is None: testpath = report.fspath - self.write_log_entry(testpath, lettercode, longrepr) + self.write_log_entry(testpath, lettercode, longrepr, + getattr(report, 'sections', None)) def pytest_runtest_logreport(self, report): if report.when != "call" and report.passed: @@ -98,3 +106,8 @@ if path is None: path = "cwd:%s" % py.path.local() self.write_log_entry(path, '!', str(excrepr)) + +def _safeprint(s, file): + if isinstance(s, unicode): + s = s.encode('utf-8') + py.builtin.print_(s, file=file) diff --git a/lib-python/2.7/test/test_mmap.py b/lib-python/2.7/test/test_mmap.py --- a/lib-python/2.7/test/test_mmap.py +++ b/lib-python/2.7/test/test_mmap.py @@ -179,25 +179,27 @@ import sys f = open(TESTFN, "r+b") try: - m = mmap.mmap(f.fileno(), mapsize+1) - except ValueError: - # we do not expect a ValueError on Windows - # CAUTION: This also changes the size of the file on disk, and - # later tests assume that the length hasn't changed. We need to - # repair that. + try: + m = mmap.mmap(f.fileno(), mapsize+1) + except ValueError: + # we do not expect a ValueError on Windows + # CAUTION: This also changes the size of the file on disk, and + # later tests assume that the length hasn't changed. We need to + # repair that. + if sys.platform.startswith('win'): + self.fail("Opening mmap with size+1 should work on Windows.") + else: + # we expect a ValueError on Unix, but not on Windows + if not sys.platform.startswith('win'): + self.fail("Opening mmap with size+1 should raise ValueError.") + m.close() + finally: + f.close() if sys.platform.startswith('win'): - self.fail("Opening mmap with size+1 should work on Windows.") - else: - # we expect a ValueError on Unix, but not on Windows - if not sys.platform.startswith('win'): - self.fail("Opening mmap with size+1 should raise ValueError.") - m.close() - f.close() - if sys.platform.startswith('win'): - # Repair damage from the resizing test. - f = open(TESTFN, 'r+b') - f.truncate(mapsize) - f.close() + # Repair damage from the resizing test. + f = open(TESTFN, 'r+b') + f.truncate(mapsize) + f.close() # Opening mmap with access=ACCESS_WRITE f = open(TESTFN, "r+b") diff --git a/lib-python/2.7/test/test_select.py b/lib-python/2.7/test/test_select.py --- a/lib-python/2.7/test/test_select.py +++ b/lib-python/2.7/test/test_select.py @@ -57,7 +57,17 @@ del a[-1] return sys.__stdout__.fileno() a[:] = [F()] * 10 - self.assertEqual(select.select([], a, []), ([], a[:5], [])) + result = select.select([], a, []) + # CPython: 'a' ends up with 5 items, because each fileno() + # removes an item and at the middle the iteration stops. + # PyPy: 'a' ends up empty, because the iteration is done on + # a copy of the original list: fileno() is called 10 times. + if test_support.check_impl_detail(cpython=True): + self.assertEqual(len(result[1]), 5) + self.assertEqual(len(a), 5) + if test_support.check_impl_detail(pypy=True): + self.assertEqual(len(result[1]), 10) + self.assertEqual(len(a), 0) def test_main(): test_support.run_unittest(SelectTestCase) diff --git a/lib_pypy/_curses.py b/lib_pypy/_curses.py --- a/lib_pypy/_curses.py +++ b/lib_pypy/_curses.py @@ -286,6 +286,13 @@ lib = ffi.verify(""" +#ifdef __APPLE__ +/* the following define is necessary for OS X 10.6+; without it, the + Apple-supplied ncurses.h sets NCURSES_OPAQUE to 1, and then Python + can't get at the WINDOW flags field. */ +#define NCURSES_OPAQUE 0 +#endif + #include <ncurses.h> #include <panel.h> #include <term.h> diff --git a/lib_pypy/datetime.py b/lib_pypy/datetime.py --- a/lib_pypy/datetime.py +++ b/lib_pypy/datetime.py @@ -1242,7 +1242,7 @@ (other._hour, other._minute, other._second, other._microsecond)) if myoff is None or otoff is None: - raise TypeError("cannot compare naive and aware times") + raise TypeError("can't compare offset-naive and offset-aware times") myhhmm = self._hour * 60 + self._minute - myoff othhmm = other._hour * 60 + other._minute - otoff return _cmp((myhhmm, self._second, self._microsecond), @@ -1838,7 +1838,7 @@ other._hour, other._minute, other._second, other._microsecond)) if myoff is None or otoff is None: - raise TypeError("cannot compare naive and aware datetimes") + raise TypeError("can't compare offset-naive and offset-aware datetimes") # XXX What follows could be done more efficiently... diff = self - other # this will take offsets into account if diff.days < 0: @@ -1885,7 +1885,7 @@ if myoff == otoff: return base if myoff is None or otoff is None: - raise TypeError("cannot mix naive and timezone-aware time") + raise TypeError("can't subtract offset-naive and offset-aware datetimes") return base + timedelta(minutes = otoff-myoff) def __hash__(self): diff --git a/py/README-BEFORE-UPDATING b/py/README-BEFORE-UPDATING new file mode 100644 --- /dev/null +++ b/py/README-BEFORE-UPDATING @@ -0,0 +1,17 @@ +This is PyPy's code of the py lib. We don't expect to upgrade it +very often, but once we do: + + WARNING! + + WE HAVE MADE A FEW TWEAKS HERE! + +Please be sure that you don't just copy the newer version from +upstream without checking the few changes that we did. This +can be done like this: + + cd <this directory> + hg log . -v | less + +then search for all " py/" in that list to know which are the +relevant checkins. (Look for the checkins that only edit one +or two files in this directory.) diff --git a/py/_path/local.py b/py/_path/local.py --- a/py/_path/local.py +++ b/py/_path/local.py @@ -750,7 +750,8 @@ mkdtemp = classmethod(mkdtemp) def make_numbered_dir(cls, prefix='session-', rootdir=None, keep=3, - lock_timeout = 172800): # two days + lock_timeout = 172800, # two days + min_timeout = 300): # five minutes """ return unique directory with a number greater than the current maximum one. The number is assumed to start directly after prefix. if keep is true directories with a number less than (maxnum-keep) @@ -818,6 +819,20 @@ for path in rootdir.listdir(): num = parse_num(path) if num is not None and num <= (maxnum - keep): + if min_timeout: + # NB: doing this is needed to prevent (or reduce + # a lot the chance of) the following situation: + # 'keep+1' processes call make_numbered_dir() at + # the same time, they create dirs, but then the + # last process notices the first dir doesn't have + # (yet) a .lock in it and kills it. + try: + t1 = path.lstat().mtime + t2 = lockfile.lstat().mtime + if abs(t2-t1) < min_timeout: + continue # skip directories too recent + except py.error.Error: + continue # failure to get a time, better skip lf = path.join('.lock') try: t1 = lf.lstat().mtime diff --git a/pypy/doc/conf.py b/pypy/doc/conf.py --- a/pypy/doc/conf.py +++ b/pypy/doc/conf.py @@ -65,9 +65,9 @@ # built documents. # # The short X.Y version. -version = '2.3' +version = '2.4' # The full version, including alpha/beta/rc tags. -release = '2.3.0' +release = '2.4.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/pypy/doc/getting-started-python.rst b/pypy/doc/getting-started-python.rst --- a/pypy/doc/getting-started-python.rst +++ b/pypy/doc/getting-started-python.rst @@ -111,6 +111,10 @@ of your choice. Typical example: ``--opt=2`` gives a good (but of course slower) Python interpreter without the JIT. + Consider using PyPy instead of CPython in the above command line, + as it is much faster. (Note that ``rpython`` is a Python 2 program, + not Python 3; you need to run either PyPy 2 or CPython 2.) + .. _`optimization level`: config/opt.html If everything works correctly this will create an executable diff --git a/pypy/doc/how-to-release.rst b/pypy/doc/how-to-release.rst --- a/pypy/doc/how-to-release.rst +++ b/pypy/doc/how-to-release.rst @@ -38,14 +38,16 @@ no JIT: windows, linux, os/x sandbox: linux, os/x +* repackage and upload source tar.bz2 to bitbucket and to cobra, as some packagers + prefer a clearly labeled source package * write release announcement pypy/doc/release-x.y(.z).txt the release announcement should contain a direct link to the download page * update pypy.org (under extradoc/pypy.org), rebuild and commit * post announcement on morepypy.blogspot.com -* send announcements to pypy-dev, python-list, +* send announcements to twitter.com, pypy-dev, python-list, python-announce, python-dev ... * add a tag on the pypy/jitviewer repo that corresponds to pypy release * add a tag on the codespeed web site that corresponds to pypy release - +* revise versioning at https://readthedocs.org/projects/pypy 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-2.4.0.rst release-2.3.1.rst release-2.3.0.rst release-2.2.1.rst diff --git a/pypy/doc/index.rst b/pypy/doc/index.rst --- a/pypy/doc/index.rst +++ b/pypy/doc/index.rst @@ -40,7 +40,7 @@ * `FAQ`_: some frequently asked questions. -* `Release 2.3.1`_: the latest official release +* `Release 2.4.0`_: the latest official release * `PyPy Blog`_: news and status info about PyPy @@ -110,7 +110,7 @@ .. _`Getting Started`: getting-started.html .. _`Papers`: extradoc.html .. _`Videos`: video-index.html -.. _`Release 2.3.1`: http://pypy.org/download.html +.. _`Release 2.4.0`: http://pypy.org/download.html .. _`speed.pypy.org`: http://speed.pypy.org .. _`RPython toolchain`: translation.html .. _`potential project ideas`: project-ideas.html diff --git a/pypy/doc/release-2.4.0.rst b/pypy/doc/release-2.4.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-2.4.0.rst @@ -0,0 +1,122 @@ +================================================= +PyPy 2.4 - Snow White +================================================= + +We're pleased to announce PyPy 2.4, which contains significant performance +enhancements and bug fixes. + +You can download the PyPy 2.4.0 release here: + + http://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project, and for those who donate to our three sub-projects. +We've shown quite a bit of progress, but we're slowly running out of funds. +Please consider donating more, or even better convince your employer to donate, +so we can finish those projects! We would like to also point out that in +September, `the Python Software Foundation`_ will `match funds`_ for +any donations up to $10k! The three sub-projects are: + +* `Py3k`_ (supporting Python 3.x): We have released a Python 3.2.5 compatible version + we call PyPy3 2.3.1, and are working toward a Python 3.3 compatible version + +* `STM`_ (software transactional memory): We have released a first working version, + and continue to try out new promising paths of achieving a fast multithreaded Python + +* `NumPy`_ which requires installation of our fork of upstream numpy, + available `on bitbucket`_ + +.. _`Py3k`: http://pypy.org/py3donate.html +.. _`STM`: http://pypy.org/tmdonate2.html +.. _`NumPy`: http://pypy.org/numpydonate.html +.. _`on bitbucket`: https://www.bitbucket.org/pypy/numpy +.. _`the Python Software Foundation`: https://www.python.org/psf/ +.. _`match funds`: http://morepypy.blogspot.com/2014/09/python-software-foundation-matching.html + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7. It's fast (`pypy 2.4 and cpython 2.7.x`_ performance comparison) +due to its integrated tracing JIT compiler. + +This release supports **x86** machines on most common operating systems +(Linux 32/64, Mac OS X 64, Windows, and OpenBSD), +as well as newer **ARM** hardware (ARMv6 or ARMv7, with VFPv3) running Linux. + +While we support 32 bit python on Windows, work on the native Windows 64 +bit python is still stalling, we would welcome a volunteer +to `handle that`_. + +.. _`pypy 2.4 and cpython 2.7.x`: http://speed.pypy.org +.. _`handle that`: http://doc.pypy.org/en/latest/windows.html#what-is-missing-for-a-full-64-bit-translation + +Highlights +========== + +Benchmarks improved after internal enhancements in string and +bytearray handling, and a major rewrite of the GIL handling. This means +that external calls are now a lot faster, especially the CFFI ones. It also +means better performance in a lot of corner cases with handling strings or +bytearrays. The main bugfix is handling of many socket objects in your +program which in the long run used to "leak" memory. + +PyPy now uses Python 2.7.8 standard library. + +We fixed a memory leak in IO in the sandbox_ code + +We welcomed more than 12 new contributors, and conducted two Google +Summer of Code projects, as well as other student projects not +directly related to Summer of Code. + + +Issues reported with our previous release were fixed after reports from users on +our new issue tracker at https://bitbucket.org/pypy/pypy/issues or on IRC at +#pypy. Here is a summary of the user-facing changes; +for more information see `whats-new`_: + +* Reduced internal copying of bytearray operations + +* Tweak the internal structure of StringBuilder to speed up large string + handling, which becomes advantageous on large programs at the cost of slightly + slower small *benchmark* type programs. + +* Boost performance of thread-local variables in both unjitted and jitted code, + this mostly affects errno handling on linux, which makes external calls + faster. + +* Move to a mixed polling and mutex GIL model that make mutlithreaded jitted + code run *much* faster + +* Optimize errno handling in linux (x86 and x86-64 only) + +* Remove ctypes pythonapi and ctypes.PyDLL, which never worked on PyPy + +* Fix performance regression on ufunc(<scalar>, <scalar>) in numpy + +* Classes in the ast module are now distinct from structures used by + the compiler, which simplifies and speeds up translation of our + source code to the PyPy binary interpreter + +* Upgrade stdlib from 2.7.5 to 2.7.8 + +* Win32 now links statically to zlib, expat, bzip, and openssl-1.0.1i. + No more missing DLLs + +* Many issues were resolved_ since the 2.3.1 release on June 8 + +.. _`whats-new`: http://doc.pypy.org/en/latest/whatsnew-2.4.0.html +.. _resolved: https://bitbucket.org/pypy/pypy/issues?status=resolved +.. _sandbox: http://doc.pypy.org/en/latest/sandbox.html + +We have further improvements on the way: rpython file handling, +numpy linalg compatibility, as well +as improved GC and many smaller improvements. + +Please try it out and let us know what you think. We especially welcome +success stories, we know you are using PyPy, please tell us about it! + +Cheers + +The PyPy Team + diff --git a/pypy/doc/release-2.4.rst b/pypy/doc/release-2.4.rst deleted file mode 100644 --- a/pypy/doc/release-2.4.rst +++ /dev/null @@ -1,107 +0,0 @@ -================================================= -PyPy 2.4 - ???????? -================================================= - -We're pleased to announce PyPy 2.4, a significant milestone on it's own right -and the proud parent of our recent PyPy3 and STM releases. - -This release contains several improvements and bugfixes. - -You can download the PyPy 2.4 release here: - - http://pypy.org/download.html - -We would like to thank our donors for the continued support of the PyPy -project, and for those who donate to our three sub-projects. -We've shown quite a bit of progress -but we're slowly running out of funds. -Please consider donating more, or even better convince your employer to donate, -so we can finish those projects! The three sub-projects are: - -* `Py3k`_ (supporting Python 3.x): We have released a Python 3.2.5 compatable version - we call PyPy3 2.3.1, and are working toward a Python 3.3 compatable version - -* `STM`_ (software transactional memory): We have release a first working version, and -continue to try out new promising paths of acheiving a fast multithreaded python - -* `NumPy`_ which requires installation of our fork of upstream numpy, available `on bitbucket`_ - -.. _`Py3k`: http://pypy.org/py3donate.html -.. _`STM`: http://pypy.org/tmdonate2.html -.. _`NumPy`: http://pypy.org/numpydonate.html -.. _`on bitbucket`: https://www.bitbucket.org/pypy/numpy - -What is PyPy? -============= - -PyPy is a very compliant Python interpreter, almost a drop-in replacement for -CPython 2.7. It's fast (`pypy 2.3 and cpython 2.7.x`_ performance comparison; -note that cpython's speed has not changed since 2.7.2) -due to its integrated tracing JIT compiler. - -This release supports x86 machines running Linux 32/64, Mac OS X 64, Windows, -and OpenBSD, -as well as newer ARM hardware (ARMv6 or ARMv7, with VFPv3) running Linux. - -While we support 32 bit python on Windows, work on the native Windows 64 -bit python is still stalling, we would welcome a volunteer -to `handle that`_. - -.. _`pypy 2.3 and cpython 2.7.x`: http://speed.pypy.org -.. _`handle that`: http://doc.pypy.org/en/latest/windows.html#what-is-missing-for-a-full-64-bit-translation - -Highlights -========== - -Benchmarks improved after internal improvements in string and bytearray handling, -and a major rewrite of the GIL handling. Many of these improvements are offshoots -of the STM work. - -We merged in Python's 2.7.8 stdlib in a record time of one week, proving the -maturity of our underlying RPython code base and PyPy interpreter. - -We welcomed more than 12 new contributors, and conducted two Google Summer of Code -projects XXX details? - -Issues reported with our previous release were fixed after reports from users on -our new issue tracker at https://bitbucket.org/pypy/pypy/issues or on IRC at -#pypy. Here is a summary of the user-facing changes; -for more information see `whats-new`_: - -* Reduced internal copying of bytearray operations - -* Tweak the internal structure of StringBuilder to speed up large string -handling, which becomes advantageous on large programs at the cost of slightly -slower small *benchmark* type programs. - -* Boost performance of thread-local variables in both unjitted and jitted code - -* Move to a mixed polling and mutex GIL model that make mutli-threaded jitted - code run *much* faster - -* Optimize errno handling in linux - -* Remove ctypes pythonapi and ctypes.PyDLL, which never worked on PyPy - -* Fix performance regression on ufunc(<scalar>, <scalar>) in numpy - -* Classes in the ast module are now distinct from structures used by the compiler, - which simplifies and speeds up translation of our source code to the PyPy binary - interpreter - -* Upgrade stdlib from 2.7.5 to 2.7.8 - -* - -* Many issues were resolved_ since the 2.3.1 release on June 8 - -.. _`whats-new`: http://doc.pypy.org/en/latest/whatsnew-2.3.1.html -.. _resolved: https://bitbucket.org/pypy/pypy/issues?status=resolved - -Please try it out and let us know what you think. We especially welcome -success stories, we know you are using PyPy, please tell us about it! - -Cheers - -The PyPy Team - diff --git a/pypy/doc/whatsnew-2.4.0.rst b/pypy/doc/whatsnew-2.4.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/whatsnew-2.4.0.rst @@ -0,0 +1,66 @@ +======================= +What's new in PyPy 2.4+ +======================= + +.. this is a revision shortly after release-2.3.x +.. startrev: ca9b7cf02cf4 + +.. branch: fix-bytearray-complexity +Bytearray operations no longer copy the bytearray unnecessarily + +Added support for ``__getitem__``, ``__setitem__``, ``__getslice__``, +``__setslice__``, and ``__len__`` to RPython + +.. branch: stringbuilder2-perf +Give the StringBuilder a more flexible internal structure, with a +chained list of strings instead of just one string. This make it +more efficient when building large strings, e.g. with cStringIO(). + +Also, use systematically jit.conditional_call() instead of regular +branches. This lets the JIT make more linear code, at the cost of +forcing a bit more data (to be passed as arguments to +conditional_calls). I would expect the net result to be a slight +slow-down on some simple benchmarks and a speed-up on bigger +programs. + +.. branch: ec-threadlocal +Change the executioncontext's lookup to be done by reading a thread- +local variable (which is implemented in C using '__thread' if +possible, and pthread_getspecific() otherwise). On Linux x86 and +x86-64, the JIT backend has a special optimization that lets it emit +directly a single MOV from a %gs- or %fs-based address. It seems +actually to give a good boost in performance. + +.. branch: fast-gil +A faster way to handle the GIL, particularly in JIT code. The GIL is +now a composite of two concepts: a global number (it's just set from +1 to 0 and back around CALL_RELEASE_GIL), and a real mutex. If there +are threads waiting to acquire the GIL, one of them is actively +checking the global number every 0.1 ms to 1 ms. Overall, JIT loops +full of external function calls now run a bit faster (if no thread was +started yet), or a *lot* faster (if threads were started already). + +.. branch: jit-get-errno +Optimize the errno handling in the JIT, notably around external +function calls. Linux-only. + +.. branch: disable_pythonapi +Remove non-functioning ctypes.pyhonapi and ctypes.PyDLL, document this +incompatibility with cpython. Recast sys.dllhandle to an int. + +.. branch: scalar-operations +Fix performance regression on ufunc(<scalar>, <scalar>) in numpy. + +.. branch: pytest-25 +Update our copies of py.test and pylib to versions 2.5.2 and 1.4.20, +respectively. + +.. branch: split-ast-classes +Classes in the ast module are now distinct from structures used by the compiler. + +.. branch: stdlib-2.7.8 +Upgrades from 2.7.6 to 2.7.8 + +.. branch: cpybug-seq-radd-rmul +Fix issue #1861 - cpython compatability madness + diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -1,62 +1,14 @@ + ======================= -What's new in PyPy 2.4+ +What's new in PyPy 2.5+ ======================= -.. this is a revision shortly after release-2.3.x -.. startrev: ca9b7cf02cf4 +.. this is a revision shortly after release-2.4.x +.. startrev: 7026746cbb1b -.. branch: fix-bytearray-complexity -Bytearray operations no longer copy the bytearray unnecessarily - -Added support for ``__getitem__``, ``__setitem__``, ``__getslice__``, -``__setslice__``, and ``__len__`` to RPython - -.. branch: stringbuilder2-perf -Give the StringBuilder a more flexible internal structure, with a -chained list of strings instead of just one string. This make it -more efficient when building large strings, e.g. with cStringIO(). - -Also, use systematically jit.conditional_call() instead of regular -branches. This lets the JIT make more linear code, at the cost of -forcing a bit more data (to be passed as arguments to -conditional_calls). I would expect the net result to be a slight -slow-down on some simple benchmarks and a speed-up on bigger -programs. - -.. branch: ec-threadlocal -Change the executioncontext's lookup to be done by reading a thread- -local variable (which is implemented in C using '__thread' if -possible, and pthread_getspecific() otherwise). On Linux x86 and -x86-64, the JIT backend has a special optimization that lets it emit -directly a single MOV from a %gs- or %fs-based address. It seems -actually to give a good boost in performance. - -.. branch: fast-gil -A faster way to handle the GIL, particularly in JIT code. The GIL is -now a composite of two concepts: a global number (it's just set from -1 to 0 and back around CALL_RELEASE_GIL), and a real mutex. If there -are threads waiting to acquire the GIL, one of them is actively -checking the global number every 0.1 ms to 1 ms. Overall, JIT loops -full of external function calls now run a bit faster (if no thread was -started yet), or a *lot* faster (if threads were started already). - -.. branch: jit-get-errno -Optimize the errno handling in the JIT, notably around external -function calls. Linux-only. - -.. branch: disable_pythonapi -Remove non-functioning ctypes.pyhonapi and ctypes.PyDLL, document this -incompatibility with cpython. Recast sys.dllhandle to an int. - -.. branch: scalar-operations -Fix performance regression on ufunc(<scalar>, <scalar>) in numpy. - -.. branch: pytest-25 -Update our copies of py.test and pylib to versions 2.5.2 and 1.4.20, -respectively. - -.. branch: split-ast-classes -Classes in the ast module are now distinct from structures used by the compiler. - -.. branch: stdlib-2.7.8 -Upgrades from 2.7.6 to 2.7.8 +.. branch: win32-fixes5 +Fix c code generation for msvc so empty "{ }" are avoided in unions, +Avoid re-opening files created with NamedTemporaryFile, +Allocate by 4-byte chunks in rffi_platform, +Skip testing objdump if it does not exist, +and other small adjustments in own tests diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst --- a/pypy/doc/windows.rst +++ b/pypy/doc/windows.rst @@ -85,10 +85,13 @@ Abridged method (for -Ojit builds using Visual Studio 2008) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Download the versions of all the external packages -from +Download the versions of all the external packages from +https://bitbucket.org/pypy/pypy/downloads/local_2.4.zip +(for 2.4 release and later) or https://bitbucket.org/pypy/pypy/downloads/local.zip -Then expand it into the base directory (base_dir) and modify your environment to reflect this:: +(for pre-2.4 versions) +Then expand it into the base directory (base_dir) and modify your environment +to reflect this:: set PATH=<base_dir>\bin;<base_dir>\tcltk\bin;%PATH% set INCLUDE=<base_dir>\include;<base_dir>\tcltk\include;%INCLUDE% diff --git a/pypy/interpreter/module.py b/pypy/interpreter/module.py --- a/pypy/interpreter/module.py +++ b/pypy/interpreter/module.py @@ -29,6 +29,17 @@ space.w_None) self.startup_called = False + def _cleanup_(self): + """Called by the annotator on prebuilt Module instances. + We don't have many such modules, but for the ones that + show up, remove their __file__ rather than translate it + statically inside the executable.""" + try: + space = self.space + space.delitem(self.w_dict, space.wrap('__file__')) + except OperationError: + pass + def install(self): """NOT_RPYTHON: installs this module into space.builtin_modules""" w_mod = self.space.wrap(self) diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py --- a/pypy/interpreter/pycode.py +++ b/pypy/interpreter/pycode.py @@ -38,18 +38,15 @@ def cpython_code_signature(code): "([list-of-arg-names], vararg-name-or-None, kwarg-name-or-None)." argcount = code.co_argcount + varnames = code.co_varnames assert argcount >= 0 # annotator hint - argnames = list(code.co_varnames[:argcount]) + argnames = list(varnames[:argcount]) if code.co_flags & CO_VARARGS: - varargname = code.co_varnames[argcount] + varargname = varnames[argcount] argcount += 1 else: varargname = None - if code.co_flags & CO_VARKEYWORDS: - kwargname = code.co_varnames[argcount] - argcount += 1 - else: - kwargname = None + kwargname = varnames[argcount] if code.co_flags & CO_VARKEYWORDS else None return Signature(argnames, varargname, kwargname) diff --git a/pypy/interpreter/pyparser/parsestring.py b/pypy/interpreter/pyparser/parsestring.py --- a/pypy/interpreter/pyparser/parsestring.py +++ b/pypy/interpreter/pyparser/parsestring.py @@ -83,12 +83,6 @@ v = PyString_DecodeEscape(space, substr, 'strict', enc) return space.wrap(v) -def hexbyte(val): - result = "%x" % val - if len(result) == 1: - result = "0" + result - return result - def decode_unicode_utf8(space, s, ps, q): # ****The Python 2.7 version, producing UTF-32 escapes**** # String is utf8-encoded, but 'unicode_escape' expects @@ -108,15 +102,14 @@ # instead. lis.append("u005c") if ord(s[ps]) & 0x80: # XXX inefficient - w, ps = decode_utf8(space, s, ps, end, "utf-32-be") - rn = len(w) - assert rn % 4 == 0 - for i in range(0, rn, 4): - lis.append('\\U') - lis.append(hexbyte(ord(w[i]))) - lis.append(hexbyte(ord(w[i+1]))) - lis.append(hexbyte(ord(w[i+2]))) - lis.append(hexbyte(ord(w[i+3]))) + w, ps = decode_utf8(space, s, ps, end) + for c in w: + # The equivalent of %08x, which is not supported by RPython. + # 7 zeroes are enough for the unicode range, and the + # result still fits in 32-bit. + hexa = hex(ord(c) + 0x10000000) + lis.append('\\U0') + lis.append(hexa[3:]) # Skip 0x and the leading 1 else: lis.append(s[ps]) ps += 1 @@ -136,7 +129,7 @@ # note that the C code has a label here. # the logic is the same. if recode_encoding and ord(s[ps]) & 0x80: - w, ps = decode_utf8(space, s, ps, end, recode_encoding) + w, ps = decode_utf8_recode(space, s, ps, end, recode_encoding) # Append bytes to output buffer. builder.append(w) else: @@ -222,14 +215,18 @@ ch >= 'A' and ch <= 'F') -def decode_utf8(space, s, ps, end, encoding): +def decode_utf8(space, s, ps, end): assert ps >= 0 pt = ps # while (s < end && *s != '\\') s++; */ /* inefficient for u".." while ps < end and ord(s[ps]) & 0x80: ps += 1 - w_u = space.wrap(unicodehelper.decode_utf8(space, s[pt:ps])) - w_v = unicodehelper.encode(space, w_u, encoding) + u = unicodehelper.decode_utf8(space, s[pt:ps]) + return u, ps + +def decode_utf8_recode(space, s, ps, end, recode_encoding): + u, ps = decode_utf8(space, s, ps, end) + w_v = unicodehelper.encode(space, space.wrap(u), recode_encoding) v = space.str_w(w_v) return v, ps diff --git a/pypy/interpreter/pyparser/test/test_parsestring.py b/pypy/interpreter/pyparser/test/test_parsestring.py --- a/pypy/interpreter/pyparser/test/test_parsestring.py +++ b/pypy/interpreter/pyparser/test/test_parsestring.py @@ -73,11 +73,11 @@ def test_simple_enc_roundtrip(self): space = self.space - s = "'\x81'" + s = "'\x81\\t'" s = s.decode("koi8-u").encode("utf8") w_ret = parsestring.parsestr(self.space, 'koi8-u', s) ret = space.unwrap(w_ret) - assert ret == eval("# -*- coding: koi8-u -*-\n'\x81'") + assert ret == eval("# -*- coding: koi8-u -*-\n'\x81\\t'") def test_multiline_unicode_strings_with_backslash(self): space = self.space diff --git a/pypy/interpreter/test/test_app_main.py b/pypy/interpreter/test/test_app_main.py --- a/pypy/interpreter/test/test_app_main.py +++ b/pypy/interpreter/test/test_app_main.py @@ -945,7 +945,7 @@ prefix = udir.join('pathtest').ensure(dir=1) fake_exe = 'bin/pypy-c' if sys.platform == 'win32': - fake_exe += '.exe' + fake_exe = 'pypy-c.exe' fake_exe = prefix.join(fake_exe).ensure(file=1) expected_path = [str(prefix.join(subdir).ensure(dir=1)) for subdir in ('lib_pypy', @@ -985,6 +985,13 @@ assert sys.path == old_sys_path + [self.goal_dir] app_main.setup_bootstrap_path(self.fake_exe) + if not sys.platform == 'win32': + # an existing file is always 'executable' on windows + assert sys.executable == '' # not executable! + assert sys.path == old_sys_path + [self.goal_dir] + + os.chmod(self.fake_exe, 0755) + app_main.setup_bootstrap_path(self.fake_exe) assert sys.executable == self.fake_exe assert self.goal_dir not in sys.path diff --git a/pypy/interpreter/test/test_module.py b/pypy/interpreter/test/test_module.py --- a/pypy/interpreter/test/test_module.py +++ b/pypy/interpreter/test/test_module.py @@ -1,4 +1,5 @@ - +import py +from pypy.interpreter.error import OperationError from pypy.interpreter.module import Module class TestModule: @@ -17,6 +18,18 @@ space.raises_w(space.w_AttributeError, space.delattr, w_m, w('x')) + def test___file__(self, space): + w = space.wrap + m = Module(space, space.wrap('m')) + py.test.raises(OperationError, space.getattr, w(m), w('__file__')) + m._cleanup_() + py.test.raises(OperationError, space.getattr, w(m), w('__file__')) + space.setattr(w(m), w('__file__'), w('m.py')) + space.getattr(w(m), w('__file__')) # does not raise + m._cleanup_() + py.test.raises(OperationError, space.getattr, w(m), w('__file__')) + + class AppTest_ModuleObject: def test_attr(self): m = __import__('__builtin__') diff --git a/pypy/interpreter/unicodehelper.py b/pypy/interpreter/unicodehelper.py --- a/pypy/interpreter/unicodehelper.py +++ b/pypy/interpreter/unicodehelper.py @@ -5,6 +5,7 @@ @specialize.memo() def decode_error_handler(space): + # Fast version of the "strict" errors handler. def raise_unicode_exception_decode(errors, encoding, msg, s, startingpos, endingpos): raise OperationError(space.w_UnicodeDecodeError, @@ -17,6 +18,7 @@ @specialize.memo() def encode_error_handler(space): + # Fast version of the "strict" errors handler. def raise_unicode_exception_encode(errors, encoding, msg, u, startingpos, endingpos): raise OperationError(space.w_UnicodeEncodeError, diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py --- a/pypy/module/_cffi_backend/ctypefunc.py +++ b/pypy/module/_cffi_backend/ctypefunc.py @@ -8,6 +8,7 @@ from rpython.rlib.jit_libffi import (CIF_DESCRIPTION, CIF_DESCRIPTION_P, FFI_TYPE, FFI_TYPE_P, FFI_TYPE_PP, SIZE_OF_FFI_ARG) from rpython.rlib.objectmodel import we_are_translated, instantiate +from rpython.rlib.objectmodel import keepalive_until_here from rpython.rtyper.lltypesystem import lltype, llmemory, rffi from pypy.interpreter.error import OperationError, oefmt @@ -160,6 +161,7 @@ raw_cdata = rffi.cast(rffi.CCHARPP, data)[0] lltype.free(raw_cdata, flavor='raw') lltype.free(buffer, flavor='raw') + keepalive_until_here(args_w) return w_res def get_mustfree_flag(data): diff --git a/pypy/module/_io/interp_stringio.py b/pypy/module/_io/interp_stringio.py --- a/pypy/module/_io/interp_stringio.py +++ b/pypy/module/_io/interp_stringio.py @@ -86,7 +86,7 @@ initval = space.unicode_w(w_initval) size = len(initval) self.resize_buffer(size) - self.buf = [c for c in initval] + self.buf = list(initval) pos = space.getindex_w(w_pos, space.w_TypeError) if pos < 0: raise OperationError(space.w_ValueError, diff --git a/pypy/module/_pypyjson/interp_encoder.py b/pypy/module/_pypyjson/interp_encoder.py --- a/pypy/module/_pypyjson/interp_encoder.py +++ b/pypy/module/_pypyjson/interp_encoder.py @@ -37,16 +37,14 @@ sb = StringBuilder(len(u)) sb.append_slice(s, 0, first) else: + # We used to check if 'u' contains only safe characters, and return + # 'w_string' directly. But this requires an extra pass over all + # characters, and the expected use case of this function, from + # json.encoder, will anyway re-encode a unicode result back to + # a string (with the ascii encoding). This requires two passes + # over the characters. So we may as well directly turn it into a + # string here --- only one pass. u = space.unicode_w(w_string) - for i in range(len(u)): - c = u[i] - if c >= u' ' and c <= u'~' and c != u'"' and c != u'\\': - pass - else: - break - else: - # the input is a unicode with only non-special ascii chars - return w_string sb = StringBuilder(len(u)) first = 0 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 @@ -192,14 +192,14 @@ def test_raw_encode_basestring_ascii(self): import _pypyjson - def check(s, expected_type=str): + def check(s): s = _pypyjson.raw_encode_basestring_ascii(s) - assert type(s) is expected_type + assert type(s) is str return s assert check("") == "" - assert check(u"", expected_type=unicode) == u"" + assert check(u"") == "" assert check("abc ") == "abc " - assert check(u"abc ", expected_type=unicode) == u"abc " + assert check(u"abc ") == "abc " raises(UnicodeDecodeError, check, "\xc0") assert check("\xc2\x84") == "\\u0084" assert check("\xf0\x92\x8d\x85") == "\\ud808\\udf45" diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py --- a/pypy/module/_ssl/interp_ssl.py +++ b/pypy/module/_ssl/interp_ssl.py @@ -759,17 +759,25 @@ # socket's timeout is in seconds, poll's timeout in ms timeout = int(sock_timeout * 1000 + 0.5) - ready = rpoll.poll(fddict, timeout) + try: + ready = rpoll.poll(fddict, timeout) + except rpoll.PollError, e: + message = e.get_msg() + raise ssl_error(space, message, e.errno) else: if MAX_FD_SIZE is not None and sock_fd >= MAX_FD_SIZE: return SOCKET_TOO_LARGE_FOR_SELECT - if writing: - r, w, e = rpoll.select([], [sock_fd], [], sock_timeout) - ready = w - else: - r, w, e = rpoll.select([sock_fd], [], [], sock_timeout) - ready = r + try: + if writing: + r, w, e = rpoll.select([], [sock_fd], [], sock_timeout) + ready = w + else: + r, w, e = rpoll.select([sock_fd], [], [], sock_timeout) + ready = r + except rpoll.SelectError as e: + message = e.get_msg() + raise ssl_error(space, message, e.errno) if ready: return SOCKET_OPERATION_OK else: diff --git a/pypy/module/_winreg/interp_winreg.py b/pypy/module/_winreg/interp_winreg.py --- a/pypy/module/_winreg/interp_winreg.py +++ b/pypy/module/_winreg/interp_winreg.py @@ -266,10 +266,16 @@ buf = None if typ == rwinreg.REG_DWORD: - if space.isinstance_w(w_value, space.w_int): + if space.is_none(w_value) or ( + space.isinstance_w(w_value, space.w_int) or + space.isinstance_w(w_value, space.w_long)): + if space.is_none(w_value): + value = r_uint(0) + else: + value = space.c_uint_w(w_value) buflen = rffi.sizeof(rwin32.DWORD) buf1 = lltype.malloc(rffi.CArray(rwin32.DWORD), 1, flavor='raw') - buf1[0] = space.uint_w(w_value) + buf1[0] = value buf = rffi.cast(rffi.CCHARP, buf1) elif typ == rwinreg.REG_SZ or typ == rwinreg.REG_EXPAND_SZ: diff --git a/pypy/module/_winreg/test/test_winreg.py b/pypy/module/_winreg/test/test_winreg.py --- a/pypy/module/_winreg/test/test_winreg.py +++ b/pypy/module/_winreg/test/test_winreg.py @@ -40,7 +40,7 @@ cls.w_tmpfilename = space.wrap(str(udir.join('winreg-temp'))) test_data = [ - ("Int Value", 45, _winreg.REG_DWORD), + ("Int Value", 0xFEDCBA98, _winreg.REG_DWORD), ("Str Value", "A string Value", _winreg.REG_SZ), ("Unicode Value", u"A unicode Value", _winreg.REG_SZ), ("Str Expand", "The path is %path%", _winreg.REG_EXPAND_SZ), @@ -137,9 +137,11 @@ assert 0, "Did not raise" def test_SetValueEx(self): - from _winreg import CreateKey, SetValueEx, REG_BINARY + from _winreg import CreateKey, SetValueEx, REG_BINARY, REG_DWORD key = CreateKey(self.root_key, self.test_key_name) sub_key = CreateKey(key, "sub_key") + SetValueEx(sub_key, 'Int Value', 0, REG_DWORD, None) + SetValueEx(sub_key, 'Int Value', 0, REG_DWORD, 45) for name, value, type in self.test_data: SetValueEx(sub_key, name, 0, type, value) exc = raises(TypeError, SetValueEx, sub_key, 'test_name', None, diff --git a/pypy/module/cpyext/include/patchlevel.h b/pypy/module/cpyext/include/patchlevel.h --- a/pypy/module/cpyext/include/patchlevel.h +++ b/pypy/module/cpyext/include/patchlevel.h @@ -29,7 +29,7 @@ #define PY_VERSION "2.7.8" /* PyPy version as a string */ -#define PYPY_VERSION "2.4.0-alpha0" +#define PYPY_VERSION "2.5.0-alpha0" /* Subversion Revision number of this file (not of the repository). * Empty since Mercurial migration. */ diff --git a/pypy/module/micronumpy/ndarray.py b/pypy/module/micronumpy/ndarray.py --- a/pypy/module/micronumpy/ndarray.py +++ b/pypy/module/micronumpy/ndarray.py @@ -407,8 +407,19 @@ -------- numpy.swapaxes : equivalent function """ - if self.is_scalar(): + if axis1 == axis2: return self + n = len(self.get_shape()) + if n <= 1: + return self + if axis1 < 0: + axis1 += n + if axis2 < 0: + axis2 += n + if axis1 < 0 or axis1 >= n: + raise oefmt(space.w_ValueError, "bad axis1 argument to swapaxes") + if axis2 < 0 or axis2 >= n: + raise oefmt(space.w_ValueError, "bad axis2 argument to swapaxes") return self.implementation.swapaxes(space, self, axis1, axis2) def descr_nonzero(self, space): diff --git a/pypy/module/micronumpy/test/test_ndarray.py b/pypy/module/micronumpy/test/test_ndarray.py --- a/pypy/module/micronumpy/test/test_ndarray.py +++ b/pypy/module/micronumpy/test/test_ndarray.py @@ -2020,6 +2020,14 @@ def test_swapaxes(self): from numpypy import array + x = array([]) + assert x.swapaxes(0, 2) is x + x = array([[1, 2]]) + assert x.swapaxes(0, 0) is x + exc = raises(ValueError, x.swapaxes, -3, 0) + assert exc.value.message == "bad axis1 argument to swapaxes" + exc = raises(ValueError, x.swapaxes, 0, 3) + assert exc.value.message == "bad axis2 argument to swapaxes" # testcases from numpy docstring x = array([[1, 2, 3]]) assert (x.swapaxes(0, 1) == array([[1], [2], [3]])).all() diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py --- a/pypy/module/micronumpy/test/test_zjit.py +++ b/pypy/module/micronumpy/test/test_zjit.py @@ -617,7 +617,7 @@ 'raw_store': 1, 'same_as': 2, 'setarrayitem_gc': 8, - 'setfield_gc': 21, + 'setfield_gc': 22, }) def define_argsort(): diff --git a/pypy/module/operator/__init__.py b/pypy/module/operator/__init__.py --- a/pypy/module/operator/__init__.py +++ b/pypy/module/operator/__init__.py @@ -39,7 +39,7 @@ 'irshift', 'isub', 'itruediv', 'ixor', '_length_hint'] interpleveldefs = { - '_compare_digest': 'interp_operator.compare_digest', + '_compare_digest': 'tscmp.compare_digest', } for name in interp_names: diff --git a/pypy/module/operator/app_operator.py b/pypy/module/operator/app_operator.py --- a/pypy/module/operator/app_operator.py +++ b/pypy/module/operator/app_operator.py @@ -4,7 +4,7 @@ This module exports a set of operators as functions. E.g. operator.add(x,y) is equivalent to x+y. ''' -from __pypy__ import builtinify + import types @@ -27,7 +27,7 @@ 'getslice(a, b, c) -- Same as a[b:c].' if not isinstance(start, int) or not isinstance(end, int): raise TypeError("an integer is expected") - return a[start:end] + return a[start:end] __getslice__ = getslice def indexOf(a, b): @@ -37,7 +37,7 @@ if x == b: return index index += 1 - raise ValueError, 'sequence.index(x): x not in sequence' + raise ValueError('sequence.index(x): x not in sequence') def isMappingType(obj,): 'isMappingType(a) -- Return True if a has a mapping type, False otherwise.' @@ -58,9 +58,9 @@ def repeat(obj, num): 'repeat(a, b) -- Return a * b, where a is a sequence, and b is an integer.' if not isinstance(num, (int, long)): - raise TypeError, 'an integer is required' + raise TypeError('an integer is required') if not isSequenceType(obj): - raise TypeError, "non-sequence object can't be repeated" + raise TypeError("non-sequence object can't be repeated") return obj * num @@ -68,59 +68,85 @@ def setslice(a, b, c, d): 'setslice(a, b, c, d) -- Same as a[b:c] = d.' - a[b:c] = d + a[b:c] = d __setslice__ = setslice +def _resolve_attr_chain(chain, obj, idx=0): + obj = getattr(obj, chain[idx]) + if idx + 1 == len(chain): + return obj + else: + return _resolve_attr_chain(chain, obj, idx + 1) + + +class _simple_attrgetter(object): + def __init__(self, attr): + self._attr = attr + + def __call__(self, obj): + return getattr(obj, self._attr) + + +class _single_attrgetter(object): + def __init__(self, attrs): + self._attrs = attrs + + def __call__(self, obj): + return _resolve_attr_chain(self._attrs, obj) + + +class _multi_attrgetter(object): + def __init__(self, attrs): + self._attrs = attrs + + def __call__(self, obj): + return tuple([ + _resolve_attr_chain(attrs, obj) + for attrs in self._attrs + ]) + + def attrgetter(attr, *attrs): + if ( + not isinstance(attr, basestring) or + not all(isinstance(a, basestring) for a in attrs) + ): + def _raise_typeerror(obj): + raise TypeError( + "argument must be a string, not %r" % type(attr).__name__ + ) + return _raise_typeerror if attrs: - getters = [single_attr_getter(a) for a in (attr,) + attrs] - def getter(obj): - return tuple([getter(obj) for getter in getters]) + return _multi_attrgetter([ + a.split(".") for a in [attr] + list(attrs) + ]) + elif "." not in attr: + return _simple_attrgetter(attr) else: - getter = single_attr_getter(attr) - return builtinify(getter) + return _single_attrgetter(attr.split(".")) -def single_attr_getter(attr): - if not isinstance(attr, str): - if not isinstance(attr, unicode): - def _raise_typeerror(obj): - raise TypeError("argument must be a string, not %r" % - (type(attr).__name__,)) - return _raise_typeerror - attr = attr.encode('ascii') - # - def make_getter(name, prevfn=None): - if prevfn is None: - def getter(obj): - return getattr(obj, name) + +class itemgetter(object): + def __init__(self, item, *items): + self._single = not bool(items) + if self._single: + self._idx = item else: - def getter(obj): - return getattr(prevfn(obj), name) - return getter - # - last = 0 - getter = None - while True: - dot = attr.find(".", last) - if dot < 0: break - getter = make_getter(attr[last:dot], getter) - last = dot + 1 - return make_getter(attr[last:], getter) + self._idx = [item] + list(items) + def __call__(self, obj): + if self._single: + return obj[self._idx] + else: + return tuple([obj[i] for i in self._idx]) -def itemgetter(item, *items): - if items: - list_of_indices = [item] + list(items) - def getter(obj): - return tuple([obj[i] for i in list_of_indices]) - else: - def getter(obj): - return obj[item] - return builtinify(getter) +class methodcaller(object): + def __init__(self, method_name, *args, **kwargs): + self._method_name = method_name + self._args = args + self._kwargs = kwargs -def methodcaller(method_name, *args, **kwargs): - def call(obj): - return getattr(obj, method_name)(*args, **kwargs) - return builtinify(call) + def __call__(self, obj): + return getattr(obj, self._method_name)(*self._args, **self._kwargs) diff --git a/pypy/module/operator/interp_operator.py b/pypy/module/operator/interp_operator.py --- a/pypy/module/operator/interp_operator.py +++ b/pypy/module/operator/interp_operator.py @@ -1,6 +1,4 @@ -from rpython.rlib.objectmodel import specialize - -from pypy.interpreter.error import OperationError, oefmt +from pypy.interpreter.error import OperationError from pypy.interpreter.gateway import unwrap_spec @@ -249,33 +247,3 @@ @unwrap_spec(default=int) def _length_hint(space, w_iterable, default): return space.wrap(space.length_hint(w_iterable, default)) - -def compare_digest(space, w_a, w_b): - if ( - space.isinstance_w(w_a, space.w_unicode) and - space.isinstance_w(w_b, space.w_unicode) - ): - return space.wrap(tscmp(space.unicode_w(w_a), space.unicode_w(w_b))) - if ( - space.isinstance_w(w_a, space.w_unicode) or - space.isinstance_w(w_b, space.w_unicode) - ): - raise oefmt( - space.w_TypeError, - "unsupported operand types(s) or combination of types: '%N' and '%N'", - w_a, - w_b, - ) - else: - return space.wrap(tscmp(space.bufferstr_w(w_a), space.bufferstr_w(w_b))) - - -@specialize.argtype(0, 1) -def tscmp(a, b): - len_a = len(a) - len_b = len(b) - length = min(len(a), len(b)) - res = len_a ^ len_b - for i in xrange(length): - res |= ord(a[i]) ^ ord(b[i]) - return res == 0 diff --git a/pypy/module/operator/test/test_operator.py b/pypy/module/operator/test/test_operator.py --- a/pypy/module/operator/test/test_operator.py +++ b/pypy/module/operator/test/test_operator.py @@ -334,3 +334,9 @@ assert operator._compare_digest(a, b) a, b = mybytes(b"foobar"), mybytes(b"foobaz") assert not operator._compare_digest(a, b) + + def test_compare_digest_unicode(self): + import operator + assert operator._compare_digest(u'asd', u'asd') + assert not operator._compare_digest(u'asd', u'qwe') + raises(TypeError, operator._compare_digest, u'asd', b'qwe') diff --git a/pypy/module/operator/test/test_tscmp.py b/pypy/module/operator/test/test_tscmp.py new file mode 100644 --- /dev/null +++ b/pypy/module/operator/test/test_tscmp.py @@ -0,0 +1,28 @@ +from pypy.module.operator.tscmp import pypy_tscmp, pypy_tscmp_wide + +class TestTimingSafeCompare: + tostr = str + tscmp = staticmethod(pypy_tscmp) + + def test_tscmp_neq(self): + assert not self.tscmp(self.tostr('asd'), self.tostr('qwe'), 3, 3) + + def test_tscmp_eq(self): + assert self.tscmp(self.tostr('asd'), self.tostr('asd'), 3, 3) + + def test_tscmp_len(self): + assert self.tscmp(self.tostr('asdp'), self.tostr('asdq'), 3, 3) + + def test_tscmp_nlen(self): + assert not self.tscmp(self.tostr('asd'), self.tostr('asd'), 2, 3) + + +class TestTimingSafeCompareWide(TestTimingSafeCompare): + tostr = unicode + tscmp = staticmethod(pypy_tscmp_wide) + + def test_tscmp_wide_nonascii(self): + a, b = u"\ud808\udf45", u"\ud808\udf45" + assert self.tscmp(a, b, len(a), len(b)) + a, b = u"\ud808\udf45", u"\ud808\udf45 " + assert not self.tscmp(a, b, len(a), len(b)) diff --git a/pypy/module/operator/tscmp.c b/pypy/module/operator/tscmp.c new file mode 100644 --- /dev/null +++ b/pypy/module/operator/tscmp.c @@ -0,0 +1,80 @@ +/* Derived from CPython 3.3.5's operator.c::_tscmp + */ + +#include <stdlib.h> +#include <wchar.h> +#include "tscmp.h" + +int +pypy_tscmp(const char *a, const char *b, long len_a, long len_b) +{ + /* The volatile type declarations make sure that the compiler has no + * chance to optimize and fold the code in any way that may change + * the timing. + */ + volatile long length; + volatile const char *left; + volatile const char *right; + long i; + char result; + + /* loop count depends on length of b */ + length = len_b; + left = NULL; + right = b; + + /* don't use else here to keep the amount of CPU instructions constant, + * volatile forces re-evaluation + * */ + if (len_a == length) { + left = *((volatile const char**)&a); + result = 0; + } + if (len_a != length) { + left = b; + result = 1; + } + + for (i=0; i < length; i++) { + result |= *left++ ^ *right++; + } + + return (result == 0); +} + +int +pypy_tscmp_wide(const wchar_t *a, const wchar_t *b, long len_a, long len_b) +{ + /* The volatile type declarations make sure that the compiler has no + * chance to optimize and fold the code in any way that may change + * the timing. + */ + volatile long length; + volatile const wchar_t *left; + volatile const wchar_t *right; + long i; + wchar_t result; + + /* loop count depends on length of b */ + length = len_b; + left = NULL; + right = b; + + /* don't use else here to keep the amount of CPU instructions constant, + * volatile forces re-evaluation + * */ + if (len_a == length) { + left = *((volatile const wchar_t**)&a); + result = 0; + } + if (len_a != length) { + left = b; + result = 1; + } + + for (i=0; i < length; i++) { + result |= *left++ ^ *right++; + } + + return (result == 0); +} diff --git a/pypy/module/operator/tscmp.h b/pypy/module/operator/tscmp.h new file mode 100644 --- /dev/null +++ b/pypy/module/operator/tscmp.h @@ -0,0 +1,2 @@ +int pypy_tscmp(const char *, const char *, long, long); +int pypy_tscmp_wide(const wchar_t *, const wchar_t *, long, long); diff --git a/pypy/module/operator/tscmp.py b/pypy/module/operator/tscmp.py new file mode 100644 --- /dev/null +++ b/pypy/module/operator/tscmp.py @@ -0,0 +1,73 @@ +""" +Provides _compare_digest method, which is a safe comparing to prevent timing +attacks for the hmac module. +""" +import py + +from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.translator.tool.cbuild import ExternalCompilationInfo + +from pypy.interpreter.error import oefmt + +cwd = py.path.local(__file__).dirpath() +eci = ExternalCompilationInfo( + includes=[cwd.join('tscmp.h')], + include_dirs=[str(cwd)], + separate_module_files=[cwd.join('tscmp.c')], + export_symbols=['pypy_tscmp', 'pypy_tscmp_wide']) + + +def llexternal(*args, **kwargs): + kwargs.setdefault('compilation_info', eci) + kwargs.setdefault('sandboxsafe', True) + return rffi.llexternal(*args, **kwargs) + + +pypy_tscmp = llexternal( + 'pypy_tscmp', + [rffi.CCHARP, rffi.CCHARP, rffi.LONG, rffi.LONG], + rffi.INT) +pypy_tscmp_wide = llexternal( + 'pypy_tscmp_wide', + [rffi.CWCHARP, rffi.CWCHARP, rffi.LONG, rffi.LONG], + rffi.INT) + + +def compare_digest(space, w_a, w_b): + """compare_digest(a, b) -> bool + + Return 'a == b'. This function uses an approach designed to prevent + timing analysis, making it appropriate for cryptography. a and b + must both be of the same type: either str (ASCII only), or any type + that supports the buffer protocol (e.g. bytes). + + Note: If a and b are of different lengths, or if an error occurs, a + timing attack could theoretically reveal information about the types + and lengths of a and b--but not their values. + """ + if (space.isinstance_w(w_a, space.w_unicode) and + space.isinstance_w(w_b, space.w_unicode)): + a = space.unicode_w(w_a) + b = space.unicode_w(w_b) + with rffi.scoped_nonmoving_unicodebuffer(a) as a_buf: + with rffi.scoped_nonmoving_unicodebuffer(b) as b_buf: + result = pypy_tscmp_wide(a_buf, b_buf, len(a), len(b)) + return space.wrap(rffi.cast(lltype.Bool, result)) + return compare_digest_buffer(space, w_a, w_b) + + +def compare_digest_buffer(space, w_a, w_b): + try: + a_buf = w_a.buffer_w(space, space.BUF_SIMPLE) + b_buf = w_b.buffer_w(space, space.BUF_SIMPLE) + except TypeError: + raise oefmt(space.w_TypeError, + "unsupported operand types(s) or combination of types: " + "'%T' and '%T'", w_a, w_b) + + a = a_buf.as_str() + b = b_buf.as_str() + with rffi.scoped_nonmovingbuffer(a) as a_buf: + with rffi.scoped_nonmovingbuffer(b) as b_buf: + result = pypy_tscmp(a_buf, b_buf, len(a), len(b)) + return space.wrap(rffi.cast(lltype.Bool, result)) diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py --- a/pypy/module/pypyjit/test_pypy_c/test_call.py +++ b/pypy/module/pypyjit/test_pypy_c/test_call.py @@ -17,13 +17,18 @@ # now we can inline it as call assembler i = 0 j = 0 - while i < 20: + while i < 25: i += 1 j += rec(100) # ID: call_rec return j # - log = self.run(fn, [], threshold=18) - loop, = log.loops_by_filename(self.filepath) + # NB. the parameters below are a bit ad-hoc. After 16 iterations, + # the we trace from the "while" and reach a "trace too long". Then + # in the next execution, we trace the "rec" function from start; + # that's "functrace" below. Then after one or two extra iterations + # we try again from "while", and this time we succeed. + log = self.run(fn, [], threshold=20) + functrace, loop = log.loops_by_filename(self.filepath) assert loop.match_by_id('call_rec', """ ... p53 = call_assembler(..., descr=...) @@ -377,12 +382,16 @@ ... p20 = force_token() p22 = new_with_vtable(...) - p24 = new_array(1, descr=<ArrayP .>) + p24 = new_array_clear(1, descr=<ArrayP .>) p26 = new_with_vtable(ConstClass(W_ListObject)) {{{ setfield_gc(p0, p20, descr=<FieldP .*PyFrame.vable_token .*>) + setfield_gc(p22, ConstPtr(null), descr=<FieldP pypy.interpreter.argument.Arguments.inst_keywords_w .*>) + setfield_gc(p22, ConstPtr(null), descr=<FieldP pypy.interpreter.argument.Arguments.inst_keywords .*>) setfield_gc(p22, 1, descr=<FieldU pypy.interpreter.argument.Arguments.inst__jit_few_keywords .*>) + setfield_gc(p22, ConstPtr(null), descr=<FieldP pypy.interpreter.argument.Arguments.inst_keyword_names_w .*>) setfield_gc(p26, ConstPtr(ptr22), descr=<FieldP pypy.objspace.std.listobject.W_ListObject.inst_strategy .*>) + setfield_gc(p26, ConstPtr(null), descr=<FieldP pypy.objspace.std.listobject.W_ListObject.inst_lstorage .*>) setarrayitem_gc(p24, 0, p26, descr=<ArrayP .>) setfield_gc(p22, p24, descr=<FieldP .*Arguments.inst_arguments_w .*>) }}} diff --git a/pypy/module/pypyjit/test_pypy_c/test_containers.py b/pypy/module/pypyjit/test_pypy_c/test_containers.py --- a/pypy/module/pypyjit/test_pypy_c/test_containers.py +++ b/pypy/module/pypyjit/test_pypy_c/test_containers.py @@ -68,10 +68,13 @@ guard_no_exception(descr=...) i12 = call(ConstClass(ll_strhash), p10, descr=<Calli . r EF=0>) p13 = new(descr=...) - p15 = new_array(8, descr=<ArrayX .*>) + p15 = new_array_clear(8, descr=<ArrayX .*>) setfield_gc(p13, p15, descr=<FieldP dicttable.entries .*>) i17 = call(ConstClass(ll_dict_lookup_trampoline), p13, p10, i12, descr=<Calli . rri EF=4 OS=4>) + {{{ setfield_gc(p13, 16, descr=<FieldS dicttable.resize_counter .*>) + setfield_gc(p13, 0, descr=<FieldS dicttable.num_items .+>) + }}} guard_no_exception(descr=...) p20 = new_with_vtable(ConstClass(W_IntObject)) call(ConstClass(_ll_dict_setitem_lookup_done_trampoline), p13, p10, p20, i12, i17, descr=<Callv 0 rrrii EF=4>) diff --git a/pypy/module/pypyjit/test_pypy_c/test_cprofile.py b/pypy/module/pypyjit/test_pypy_c/test_cprofile.py --- a/pypy/module/pypyjit/test_pypy_c/test_cprofile.py +++ b/pypy/module/pypyjit/test_pypy_c/test_cprofile.py @@ -1,4 +1,4 @@ -import py, sys +import py, sys, re from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC class TestCProfile(BaseTestPyPyC): @@ -26,10 +26,20 @@ for method in ['append', 'pop']: loop, = log.loops_by_id(method) print loop.ops_by_id(method) - # on 32-bit, there is f1=read_timestamp(); ...; - # f2=read_timestamp(); f3=call(llong_sub,f1,f2) - # which should turn into a single PADDQ/PSUBQ - if sys.maxint != 2147483647: - assert ' call(' not in repr(loop.ops_by_id(method)) + # on 32-bit, there is f1=call(read_timestamp); ...; + # f2=call(read_timestamp); f3=call(llong_sub,f1,f2) + # but all calls can be special-cased by the backend if + # supported. On 64-bit there is only the two calls to + # read_timestamp. + r = re.compile(r" call[(]ConstClass[(](.+?)[)]") + calls = r.findall(repr(loop.ops_by_id(method))) + if sys.maxint == 2147483647: + assert len(calls) == 6 + else: + assert len(calls) == 2 + for x in calls: + assert ('ll_read_timestamp' in x or 'llong_sub' in x + or 'llong_add' in x) + # assert ' call_may_force(' not in repr(loop.ops_by_id(method)) assert ' cond_call(' in repr(loop.ops_by_id(method)) diff --git a/pypy/module/pypyjit/test_pypy_c/test_ffi.py b/pypy/module/pypyjit/test_pypy_c/test_ffi.py --- a/pypy/module/pypyjit/test_pypy_c/test_ffi.py +++ b/pypy/module/pypyjit/test_pypy_c/test_ffi.py @@ -340,30 +340,19 @@ guard_value(p166, ConstPtr(ptr72), descr=...) p167 = call(ConstClass(_ll_0_alloc_with_del___), descr=<Callr . EF=4>) guard_no_exception(descr=...) - i168 = call(ConstClass(_ll_1_raw_malloc_varsize__Signed), 6, descr=<Calli . i EF=4 OS=110>) - i169 = int_add(i168, i97) - i170 = int_sub(i160, i106) - setfield_gc(p167, i168, descr=<FieldU pypy.module._cffi_backend.cdataobj.W_CData.inst__cdata .>) + i112 = int_sub(i160, -32768) setfield_gc(p167, ConstPtr(null), descr=<FieldP pypy.module._cffi_backend.cdataobj.W_CData.inst__lifeline_ .+>) - setfield_gc(p167, ConstPtr(ptr89), descr=<FieldP pypy.module._cffi_backend.cdataobj.W_CData.inst_ctype .+>) - i171 = uint_gt(i170, i108) - guard_false(i171, descr=...) - i172 = int_sub(i160, -32768) - i173 = int_and(i172, 65535) - i174 = int_add(i173, -32768) - setarrayitem_raw(i169, 0, i174, descr=<ArrayS 2>) - i175 = int_add(i168, i121) - i176 = int_sub(i160, i130) - i177 = uint_gt(i176, i132) - guard_false(i177, descr=...) - setarrayitem_raw(i175, 0, i174, descr=<ArrayS 2>) - i178 = int_add(i168, i140) - i179 = int_sub(i160, i149) - i180 = uint_gt(i179, i151) - guard_false(i180, descr=...) - setarrayitem_raw(i178, 0, i174, descr=<ArrayS 2>) + setfield_gc(p167, ConstPtr(ptr85), descr=<FieldP pypy.module._cffi_backend.cdataobj.W_CData.inst_ctype .+>) + i114 = uint_gt(i112, 65535) + guard_false(i114, descr=...) + i115 = int_and(i112, 65535) + i116 = int_add(i115, -32768) --TICK-- - i183 = arraylen_gc(p67, descr=<ArrayP .>) - i184 = arraylen_gc(p92, descr=<ArrayP .>) + i119 = call(ConstClass(_ll_1_raw_malloc_varsize__Signed), 6, descr=<Calli . i EF=4 OS=110>) + raw_store(i119, 0, i116, descr=<ArrayS 2>) + raw_store(i119, 2, i116, descr=<ArrayS 2>) + raw_store(i119, 4, i116, descr=<ArrayS 2>) + setfield_gc(p167, i119, descr=<FieldU pypy.module._cffi_backend.cdataobj.W_CData.inst__cdata .+>) + i123 = arraylen_gc(p67, descr=<ArrayP .>) jump(..., descr=...) """) diff --git a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py --- a/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py +++ b/pypy/module/pypyjit/test_pypy_c/test_micronumpy.py @@ -1,4 +1,5 @@ from pypy.module.pypyjit.test_pypy_c.test_00_model import BaseTestPyPyC +from rpython.rlib.rawstorage import misaligned_is_fine class TestMicroNumPy(BaseTestPyPyC): @@ -15,6 +16,14 @@ log = self.run(main, []) assert log.result == 0 loop, = log.loops_by_filename(self.filepath) + if misaligned_is_fine: + alignment_check = "" + else: + alignment_check = """ + i93 = int_and(i79, 7) + i94 = int_is_zero(i93) + guard_true(i94, descr=...) + """ assert loop.match(""" i76 = int_lt(i71, 300) guard_true(i76, descr=...) @@ -22,6 +31,7 @@ guard_false(i77, descr=...) i78 = int_mul(i71, i61) i79 = int_add(i55, i78) + """ + alignment_check + """ f80 = raw_load(i67, i79, descr=<ArrayF 8>) i81 = int_add(i71, 1) guard_not_invalidated(descr=...) @@ -44,6 +54,14 @@ log = self.run(main, []) assert log.result == 0 loop, = log.loops_by_filename(self.filepath) + if misaligned_is_fine: + alignment_check = "" + else: + alignment_check = """ + i97 = int_and(i84, 7) + i98 = int_is_zero(i97) + guard_true(i98, descr=...) + """ assert loop.match(""" i81 = int_lt(i76, 300) guard_true(i81, descr=...) @@ -51,6 +69,7 @@ guard_false(i82, descr=...) i83 = int_mul(i76, i64) i84 = int_add(i58, i83) + """ + alignment_check + """ f85 = raw_load(i70, i84, descr=<ArrayF 8>) guard_not_invalidated(descr=...) f86 = float_add(f74, f85) diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py --- a/pypy/module/pypyjit/test_pypy_c/test_string.py +++ b/pypy/module/pypyjit/test_pypy_c/test_string.py @@ -110,9 +110,12 @@ i85 = strlen(p80) p86 = new(descr=<SizeDescr .+>) p88 = newstr(23) - setfield_gc(..., descr=<Field. stringbuilder.+>) - setfield_gc(..., descr=<Field. stringbuilder.+>) - setfield_gc(..., descr=<Field. stringbuilder.+>) + {{{ + setfield_gc(p86, 0, descr=<FieldS stringbuilder.current_pos .+>) + setfield_gc(p86, p88, descr=<FieldP stringbuilder.current_buf .+>) + setfield_gc(p86, 23, descr=<FieldS stringbuilder.current_end .+>) + setfield_gc(p86, 23, descr=<FieldS stringbuilder.total_size .+>) + }}} call(ConstClass(ll_append_res0__stringbuilderPtr_rpy_stringPtr), p86, p80, descr=<Callv 0 rr EF=4>) guard_no_exception(descr=...) i89 = getfield_gc(p86, descr=<FieldS stringbuilder.current_pos .+>) diff --git a/pypy/module/select/interp_select.py b/pypy/module/select/interp_select.py --- a/pypy/module/select/interp_select.py +++ b/pypy/module/select/interp_select.py @@ -173,9 +173,9 @@ On Windows, only sockets are supported; on Unix, all file descriptors. """ - iwtd_w = space.listview(w_iwtd) - owtd_w = space.listview(w_owtd) - ewtd_w = space.listview(w_ewtd) + iwtd_w = space.unpackiterable(w_iwtd) + owtd_w = space.unpackiterable(w_owtd) + ewtd_w = space.unpackiterable(w_ewtd) if space.is_w(w_timeout, space.w_None): timeout = -1.0 diff --git a/pypy/module/select/test/test_select.py b/pypy/module/select/test/test_select.py --- a/pypy/module/select/test/test_select.py +++ b/pypy/module/select/test/test_select.py @@ -85,17 +85,18 @@ assert owtd == [writeend] total_out += writeend.send(b'x' * 512) total_in = 0 - while True: - iwtd, owtd, ewtd = select.select([readend], [], [], 0) + while total_in < total_out: + iwtd, owtd, ewtd = select.select([readend], [], [], 5) assert owtd == ewtd == [] - if iwtd == []: - break - assert iwtd == [readend] + assert iwtd == [readend] # there is more expected data = readend.recv(4096) assert len(data) > 0 assert data == b'x' * len(data) total_in += len(data) assert total_in == total_out + iwtd, owtd, ewtd = select.select([readend], [], [], 0) + assert owtd == ewtd == [] + assert iwtd == [] # there is not more expected finally: writeend.close() readend.close() @@ -304,6 +305,20 @@ for fd in rfds: os.close(fd) + def test_resize_list_in_select(self): + import select + class Foo(object): + def fileno(self): + print len(l) + if len(l) < 100: + l.append(Foo()) + return 0 + l = [Foo()] + select.select(l, (), (), 0) + assert 1 <= len(l) <= 100 + # ^^^ CPython gives 100, PyPy gives 1. I think both are OK as + # long as there is no crash. + class AppTestSelectWithSockets(_AppTestSelect): """Same tests with connected sockets. diff --git a/pypy/module/sys/initpath.py b/pypy/module/sys/initpath.py --- a/pypy/module/sys/initpath.py +++ b/pypy/module/sys/initpath.py @@ -18,6 +18,13 @@ _WIN32 = sys.platform == 'win32' +def _exists_and_is_executable(fn): + # os.access checks using the user's real uid and gid. + # Since pypy should not be run setuid/setgid, this + # should be sufficient. + return os.path.isfile(fn) and os.access(fn, os.X_OK) + + def find_executable(executable): """ Return the absolute path of the executable, by looking into PATH and @@ -34,14 +41,14 @@ if path: for dir in path.split(os.pathsep): fn = os.path.join(dir, executable) - if os.path.isfile(fn): + if _exists_and_is_executable(fn): executable = fn break executable = rpath.rabspath(executable) # 'sys.executable' should not end up being an non-existing file; # just use '' in this case. (CPython issue #7774) - return executable if os.path.isfile(executable) else '' + return executable if _exists_and_is_executable(executable) else '' def _readlink_maybe(filename): diff --git a/pypy/module/sys/state.py b/pypy/module/sys/state.py --- a/pypy/module/sys/state.py +++ b/pypy/module/sys/state.py @@ -7,15 +7,15 @@ # ____________________________________________________________ # -class State: - def __init__(self, space): - self.space = space +class State: + def __init__(self, space): + self.space = space self.w_modules = space.newdict(module=True) - self.w_warnoptions = space.newlist([]) self.w_argv = space.newlist([]) - self.setinitialpath(space) + + self.setinitialpath(space) def setinitialpath(self, space): from pypy.module.sys.initpath import compute_stdlib_path @@ -25,10 +25,10 @@ path = compute_stdlib_path(self, srcdir) self.w_path = space.newlist([space.wrap(p) for p in path]) - def get(space): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit