Author: Philip Jenvey <pjen...@underboss.org> Branch: Changeset: r73480:a552e9ed86d6 Date: 2014-09-11 12:43 -0700 http://bitbucket.org/pypy/pypy/changeset/a552e9ed86d6/
Log: merge upstream diff too long, truncating to 2000 out of 2511 lines diff --git a/_pytest/resultlog.py b/_pytest/resultlog.py --- a/_pytest/resultlog.py +++ b/_pytest/resultlog.py @@ -54,15 +54,16 @@ self.logfile = logfile # preferably line buffered def write_log_entry(self, testpath, lettercode, longrepr, sections=None): - py.builtin.print_("%s %s" % (lettercode, testpath), file=self.logfile) + _safeprint("%s %s" % (lettercode, testpath), file=self.logfile) for line in longrepr.splitlines(): - py.builtin.print_(" %s" % line, file=self.logfile) - if sections is not None: + _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: - py.builtin.print_(" ---------- %s ----------" % (title,), - file=self.logfile) + _safeprint(" ---------- %s ----------" % (title,), + file=self.logfile) for line in content.splitlines(): - py.builtin.print_(" %s" % line, file=self.logfile) + _safeprint(" %s" % line, file=self.logfile) def log_outcome(self, report, lettercode, longrepr): testpath = getattr(report, 'nodeid', None) @@ -105,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/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/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,119 @@ +================================================= +PyPy 2.4 - Snow White +================================================= + +We're pleased to announce PyPy 2.4, which contains significant performance +enhancements and bug fixes. + +You can already download the PyPy 2.4-beta1 pre-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 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.3.1.html +.. _resolved: https://bitbucket.org/pypy/pypy/issues?status=resolved + +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,8 @@ + ======================= -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 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/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 @@ -985,6 +985,11 @@ assert sys.path == old_sys_path + [self.goal_dir] app_main.setup_bootstrap_path(self.fake_exe) + 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/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/_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 SelectError, e: + message = e.get_msg() + raise ssl_error(space, message, e.errno) if ready: return SOCKET_OPERATION_OK else: 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/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): @@ -28,8 +28,18 @@ print 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 - if sys.maxint != 2147483647: - assert ' call(' not in repr(loop.ops_by_id(method)) + # 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/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): return space.fromcache(State) + class IOState: def __init__(self, space): from pypy.module._file.interp_file import W_File @@ -36,17 +36,17 @@ stdin = W_File(space) stdin.file_fdopen(0, "r", 1) - stdin.name = '<stdin>' + stdin.w_name = space.wrap('<stdin>') self.w_stdin = space.wrap(stdin) stdout = W_File(space) stdout.file_fdopen(1, "w", 1) - stdout.name = '<stdout>' + stdout.w_name = space.wrap('<stdout>') self.w_stdout = space.wrap(stdout) stderr = W_File(space) stderr.file_fdopen(2, "w", 0) - stderr.name = '<stderr>' + stderr.w_name = space.wrap('<stderr>') self.w_stderr = space.wrap(stderr) stdin._when_reading_first_flush(stdout) @@ -54,9 +54,9 @@ def getio(space): return space.fromcache(IOState) + def pypy_getudir(space): """NOT_RPYTHON (should be removed from interpleveldefs before translation)""" from rpython.tool.udir import udir return space.wrap(str(udir)) - diff --git a/pypy/module/sys/test/test_sysmodule.py b/pypy/module/sys/test/test_sysmodule.py --- a/pypy/module/sys/test/test_sysmodule.py +++ b/pypy/module/sys/test/test_sysmodule.py @@ -91,6 +91,10 @@ assert isinstance(sys.__stderr__, file) assert isinstance(sys.__stdin__, file) + assert sys.__stdin__.name == "<stdin>" + assert sys.__stdout__.name == "<stdout>" + assert sys.__stderr__.name == "<stderr>" + if self.appdirect and not isinstance(sys.stdin, file): return diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py --- a/pypy/module/sys/version.py +++ b/pypy/module/sys/version.py @@ -10,7 +10,7 @@ #XXX # sync CPYTHON_VERSION with patchlevel.h, package.py CPYTHON_API_VERSION = 1013 #XXX # sync with include/modsupport.h -PYPY_VERSION = (2, 4, 0, "alpha", 0) #XXX # sync patchlevel.h +PYPY_VERSION = (2, 5, 0, "alpha", 0) #XXX # sync patchlevel.h if platform.name == 'msvc': COMPILER_INFO = 'MSC v.%d 32 bit' % (platform.version * 10 + 600) diff --git a/pypy/module/thread/test/test_thread.py b/pypy/module/thread/test/test_thread.py --- a/pypy/module/thread/test/test_thread.py +++ b/pypy/module/thread/test/test_thread.py @@ -13,18 +13,26 @@ def f(): lock.acquire() lock.release() + start = thread._count() try: try: for i in range(1000): thread.start_new_thread(f, ()) finally: lock.release() - # wait a bit to allow most threads to finish now - time.sleep(0.5) except (thread.error, MemoryError): cls.w_can_start_many_threads = space.wrap(False) else: cls.w_can_start_many_threads = space.wrap(True) + # wait a bit to allow all threads to finish now + remaining = thread._count() + retries = 0 + while remaining > start: + retries += 1 + if retries == 200: + raise Exception("the test's threads don't stop!") + time.sleep(0.2) + remaining = thread._count() def test_start_new_thread(self): import thread @@ -227,7 +235,7 @@ import signal def f(): - for x in range(5): + for x in range(40): if waiting: thread.interrupt_main() return @@ -236,7 +244,7 @@ def busy_wait(): waiting.append(None) - for x in range(10): + for x in range(50): print 'tick...', x # <-force the GIL to be released, as time.sleep(0.1) # time.sleep doesn't do non-translated waiting.pop() @@ -245,6 +253,8 @@ signal.signal(signal.SIGINT, signal.default_int_handler) for i in range(100): + print + print "loop", i waiting = [] thread.start_new_thread(f, ()) raises(KeyboardInterrupt, busy_wait) diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py --- a/pypy/objspace/descroperation.py +++ b/pypy/objspace/descroperation.py @@ -671,6 +671,7 @@ left, right = specialnames errormsg = "unsupported operand type(s) for %s: '%%N' and '%%N'" % ( symbol.replace('%', '%%'),) + seq_bug_compat = (symbol == '+' or symbol == '*') def binop_impl(space, w_obj1, w_obj2): w_typ1 = space.type(w_obj1) @@ -686,20 +687,16 @@ # __xxx__ and __rxxx__ methods where found by identity. # Note that space.is_w() is potentially not happy if one of them # is None... - if w_left_src is not w_right_src: # XXX - # -- cpython bug compatibility: see objspace/std/test/ - # -- test_unicodeobject.test_str_unicode_concat_overrides. - # -- The following handles "unicode + string subclass" by - # -- pretending that the unicode is a superclass of the - # -- string, thus giving priority to the string subclass' - # -- __radd__() method. The case "string + unicode subclass" - # -- is handled directly by add__String_Unicode(). - if symbol == '+' and space.is_w(w_typ1, space.w_unicode): - w_typ1 = space.w_basestring - # -- end of bug compatibility - if space.is_true(space.issubtype(w_typ2, w_typ1)): - if (w_left_src and w_right_src and - not space.abstract_issubclass_w(w_left_src, w_right_src) and + if w_right_src and (w_left_src is not w_right_src) and w_left_src: + # 'seq_bug_compat' is for cpython bug-to-bug compatibility: + # see objspace/std/test/test_unicodeobject.*concat_overrides + # and objspace/test/test_descrobject.*rmul_overrides. + # For cases like "unicode + string subclass". + if ((seq_bug_compat and w_typ1.flag_sequence_bug_compat + and not w_typ2.flag_sequence_bug_compat) + # the non-bug-compat part is the following check: + or space.is_true(space.issubtype(w_typ2, w_typ1))): + if (not space.abstract_issubclass_w(w_left_src, w_right_src) and not space.abstract_issubclass_w(w_typ1, w_right_src)): w_obj1, w_obj2 = w_obj2, w_obj1 w_left_impl, w_right_impl = w_right_impl, w_left_impl diff --git a/pypy/objspace/std/bytearrayobject.py b/pypy/objspace/std/bytearrayobject.py --- a/pypy/objspace/std/bytearrayobject.py +++ b/pypy/objspace/std/bytearrayobject.py @@ -534,7 +534,7 @@ if not e.match(space, space.w_TypeError): raise else: - return [c for c in buf.as_str()] + return list(buf.as_str()) # sequence of bytes w_iter = space.iter(w_source) @@ -1131,6 +1131,7 @@ reverse = interp2app(W_BytearrayObject.descr_reverse, doc=BytearrayDocstrings.reverse.__doc__), ) +W_BytearrayObject.typedef.flag_sequence_bug_compat = True init_signature = Signature(['source', 'encoding', 'errors'], None, None) init_defaults = [None, None, None] diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py --- a/pypy/objspace/std/bytesobject.py +++ b/pypy/objspace/std/bytesobject.py @@ -951,6 +951,7 @@ _formatter_field_name_split = interp2app(W_BytesObject.descr_formatter_field_name_split), ) +W_BytesObject.typedef.flag_sequence_bug_compat = True def string_escape_encode(s, quote): 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 @@ -1874,3 +1874,4 @@ insert = interp2app(W_ListObject.descr_insert), remove = interp2app(W_ListObject.descr_remove), ) +W_ListObject.typedef.flag_sequence_bug_compat = True diff --git a/pypy/objspace/std/stdtypedef.py b/pypy/objspace/std/stdtypedef.py --- a/pypy/objspace/std/stdtypedef.py +++ b/pypy/objspace/std/stdtypedef.py @@ -93,6 +93,8 @@ overridetypedef=overridetypedef) if typedef is not overridetypedef: w_type.w_doc = space.wrap(typedef.doc) + if hasattr(typedef, 'flag_sequence_bug_compat'): + w_type.flag_sequence_bug_compat = typedef.flag_sequence_bug_compat w_type.lazyloaders = lazyloaders return w_type diff --git a/pypy/objspace/std/tupleobject.py b/pypy/objspace/std/tupleobject.py --- a/pypy/objspace/std/tupleobject.py +++ b/pypy/objspace/std/tupleobject.py @@ -244,6 +244,7 @@ count = interp2app(W_AbstractTupleObject.descr_count), index = interp2app(W_AbstractTupleObject.descr_index) ) +W_AbstractTupleObject.typedef.flag_sequence_bug_compat = True class W_TupleObject(W_AbstractTupleObject): 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 @@ -67,6 +67,7 @@ _immutable_fields_ = ["flag_heaptype", "flag_cpytype", "flag_abstract?", + "flag_sequence_bug_compat", 'needsdel', 'weakrefable', 'hasdict', @@ -104,6 +105,7 @@ w_self.flag_heaptype = False w_self.flag_cpytype = False w_self.flag_abstract = False + w_self.flag_sequence_bug_compat = False w_self.instancetypedef = overridetypedef if overridetypedef is not None: diff --git a/pypy/objspace/std/unicodeobject.py b/pypy/objspace/std/unicodeobject.py --- a/pypy/objspace/std/unicodeobject.py +++ b/pypy/objspace/std/unicodeobject.py @@ -1068,6 +1068,7 @@ _formatter_field_name_split = interp2app(W_UnicodeObject.descr_formatter_field_name_split), ) +W_UnicodeObject.typedef.flag_sequence_bug_compat = True def _create_list_from_unicode(value): diff --git a/pypy/objspace/test/test_descroperation.py b/pypy/objspace/test/test_descroperation.py --- a/pypy/objspace/test/test_descroperation.py +++ b/pypy/objspace/test/test_descroperation.py @@ -734,6 +734,44 @@ assert X() == 'hello' + def test_sequence_rmul_overrides(self): + class oops(object): + def __rmul__(self, other): + return 42 + def __index__(self): + return 3 + assert '2' * oops() == 42 + assert [2] * oops() == 42 + assert (2,) * oops() == 42 + assert u'2' * oops() == 42 + assert bytearray('2') * oops() == 42 + assert 1000 * oops() == 42 + assert '2'.__mul__(oops()) == '222' + + def test_sequence_rmul_overrides_oldstyle(self): + class oops: + def __rmul__(self, other): + return 42 + def __index__(self): + return 3 + assert '2' * oops() == 42 + assert [2] * oops() == 42 + assert (2,) * oops() == 42 + assert u'2' * oops() == 42 + assert bytearray('2') * oops() == 42 + assert 1000 * oops() == 42 + assert '2'.__mul__(oops()) == '222' + + def test_sequence_radd_overrides(self): + class A1(list): + pass + class A2(list): + def __radd__(self, other): + return 42 + assert [2] + A1([3]) == [2, 3] + assert type([2] + A1([3])) is list + assert [2] + A2([3]) == 42 + class AppTestWithBuiltinShortcut(AppTest_Descroperation): spaceconfig = {'objspace.std.builtinshortcut': True} diff --git a/rpython/annotator/builtin.py b/rpython/annotator/builtin.py --- a/rpython/annotator/builtin.py +++ b/rpython/annotator/builtin.py @@ -255,8 +255,13 @@ BUILTIN_ANALYZERS[original] = value -@analyzer_for(getattr(OSError.__init__, 'im_func', OSError.__init__)) -def OSError_init(s_self, *args): +@analyzer_for(getattr(object.__init__, 'im_func', object.__init__)) +def object_init(s_self, *args): + # ignore - mostly used for abstract classes initialization + pass + +@analyzer_for(getattr(EnvironmentError.__init__, 'im_func', EnvironmentError.__init__)) +def EnvironmentError_init(s_self, *args): pass try: @@ -268,11 +273,6 @@ def WindowsError_init(s_self, *args): pass -@analyzer_for(getattr(object.__init__, 'im_func', object.__init__)) -def object_init(s_self, *args): - # ignore - mostly used for abstract classes initialization - pass - @analyzer_for(sys.getdefaultencoding) def conf(): diff --git a/rpython/annotator/classdef.py b/rpython/annotator/classdef.py --- a/rpython/annotator/classdef.py +++ b/rpython/annotator/classdef.py @@ -438,8 +438,10 @@ # ____________________________________________________________ FORCE_ATTRIBUTES_INTO_CLASSES = { - OSError: {'errno': SomeInteger()}, - } + EnvironmentError: {'errno': SomeInteger(), + 'strerror': SomeString(can_be_None=True), + 'filename': SomeString(can_be_None=True)}, +} try: WindowsError @@ -455,4 +457,3 @@ else: FORCE_ATTRIBUTES_INTO_CLASSES[termios.error] = \ {'args': SomeTuple([SomeInteger(), SomeString()])} - diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py --- a/rpython/annotator/unaryop.py +++ b/rpython/annotator/unaryop.py @@ -304,10 +304,10 @@ def hint(self, *args_s): hints = args_s[-1].const if 'maxlength' in hints: - # only for iteration over lists or dicts at the moment, + # only for iteration over lists or dicts or strs at the moment, # not over an iterator object (because it has no known length) s_iterable = args_s[0] - if isinstance(s_iterable, (SomeList, SomeDict)): + if isinstance(s_iterable, (SomeList, SomeDict, SomeString)): self = SomeList(self.listdef) # create a fresh copy self.listdef.resize() self.listdef.listitem.hint_maxlength = True diff --git a/rpython/jit/backend/arm/codebuilder.py b/rpython/jit/backend/arm/codebuilder.py --- a/rpython/jit/backend/arm/codebuilder.py +++ b/rpython/jit/backend/arm/codebuilder.py @@ -333,8 +333,11 @@ | (rn & 0xF) << 16) def DMB(self): - # note: 'cond' is only permitted on Thumb here - self.write32(0xf57ff05f) + # note: 'cond' is only permitted on Thumb here, but don't + # write literally 0xf57ff05f, because it's larger than 31 bits + c = cond.AL + self.write32(c << 28 + | 0x157ff05f) DIV = binary_helper_call('int_div') MOD = binary_helper_call('int_mod') diff --git a/rpython/jit/backend/arm/detect.py b/rpython/jit/backend/arm/detect.py --- a/rpython/jit/backend/arm/detect.py +++ b/rpython/jit/backend/arm/detect.py @@ -38,9 +38,9 @@ try: buf = os.read(fd, 2048) if not buf: + n = 6 # we assume ARMv6 as base case debug_print("Could not detect ARM architecture " "version, assuming", "ARMv%d" % n) - n = 6 # we asume ARMv6 as base case finally: os.close(fd) # "Processor : ARMv%d-compatible processor rev 7 (v6l)" diff --git a/rpython/jit/backend/arm/opassembler.py b/rpython/jit/backend/arm/opassembler.py --- a/rpython/jit/backend/arm/opassembler.py +++ b/rpython/jit/backend/arm/opassembler.py @@ -1128,6 +1128,8 @@ self.mc.VCVT_int_to_float(res.value, r.svfp_ip.value) return fcond + # the following five instructions are only ARMv7; + # regalloc.py won't call them at all on ARMv6 emit_op_llong_add = gen_emit_float_op('llong_add', 'VADD_i64') emit_op_llong_sub = gen_emit_float_op('llong_sub', 'VSUB_i64') emit_op_llong_and = gen_emit_float_op('llong_and', 'VAND_i64') diff --git a/rpython/jit/backend/arm/regalloc.py b/rpython/jit/backend/arm/regalloc.py --- a/rpython/jit/backend/arm/regalloc.py +++ b/rpython/jit/backend/arm/regalloc.py @@ -184,7 +184,7 @@ class Regalloc(BaseRegalloc): - def __init__(self, assembler=None): + def __init__(self, assembler): self.cpu = assembler.cpu self.assembler = assembler self.frame_manager = None @@ -290,7 +290,7 @@ return self.vfprm.convert_to_imm(value) def _prepare(self, inputargs, operations, allgcrefs): - cpu = self.assembler.cpu + cpu = self.cpu self.fm = ARMFrameManager(cpu.get_baseofs_of_frame_field()) self.frame_manager = self.fm operations = cpu.gc_ll_descr.rewrite_assembler(cpu, operations, @@ -550,18 +550,19 @@ EffectInfo.OS_LLONG_AND, EffectInfo.OS_LLONG_OR, EffectInfo.OS_LLONG_XOR): - args = self._prepare_llong_binop_xx(op, fcond) - self.perform_llong(op, args, fcond) - return - if oopspecindex == EffectInfo.OS_LLONG_TO_INT: + if self.cpu.cpuinfo.arch_version >= 7: + args = self._prepare_llong_binop_xx(op, fcond) + self.perform_llong(op, args, fcond) + return + elif oopspecindex == EffectInfo.OS_LLONG_TO_INT: args = self._prepare_llong_to_int(op, fcond) self.perform_llong(op, args, fcond) return - if oopspecindex == EffectInfo.OS_MATH_SQRT: + elif oopspecindex == EffectInfo.OS_MATH_SQRT: args = self.prepare_op_math_sqrt(op, fcond) self.perform_math(op, args, fcond) return - #if oopspecindex == EffectInfo.OS_MATH_READ_TIMESTAMP: + #elif oopspecindex == EffectInfo.OS_MATH_READ_TIMESTAMP: # ... return self._prepare_call(op) @@ -590,7 +591,7 @@ # spill variables that need to be saved around calls self.vfprm.before_call(save_all_regs=save_all_regs) if not save_all_regs: - gcrootmap = self.assembler.cpu.gc_ll_descr.gcrootmap + gcrootmap = self.cpu.gc_ll_descr.gcrootmap if gcrootmap and gcrootmap.is_shadow_stack: save_all_regs = 2 self.rm.before_call(save_all_regs=save_all_regs) @@ -1082,7 +1083,7 @@ gcmap = self.get_gcmap([r.r0, r.r1]) self.possibly_free_var(t) # - gc_ll_descr = self.assembler.cpu.gc_ll_descr + gc_ll_descr = self.cpu.gc_ll_descr self.assembler.malloc_cond_varsize_frame( gc_ll_descr.get_nursery_free_addr(), gc_ll_descr.get_nursery_top_addr(), @@ -1092,7 +1093,7 @@ self.assembler._alignment_check() def prepare_op_call_malloc_nursery_varsize(self, op, fcond): - gc_ll_descr = self.assembler.cpu.gc_ll_descr + gc_ll_descr = self.cpu.gc_ll_descr if not hasattr(gc_ll_descr, 'max_size_of_young_obj'): raise Exception("unreachable code") # for boehm, this function should never be called diff --git a/rpython/jit/metainterp/warmstate.py b/rpython/jit/metainterp/warmstate.py --- a/rpython/jit/metainterp/warmstate.py +++ b/rpython/jit/metainterp/warmstate.py @@ -127,6 +127,7 @@ JC_TRACING = 0x01 JC_DONT_TRACE_HERE = 0x02 JC_TEMPORARY = 0x04 +JC_TRACING_OCCURRED= 0x08 class BaseJitCell(object): """Subclasses of BaseJitCell are used in tandem with the single @@ -160,6 +161,8 @@ JC_TRACING: we are now tracing the loop from this greenkey. We'll likely end up with a wref_procedure_token, soonish. + JC_TRACING_OCCURRED: set if JC_TRACING was set at least once. + JC_TEMPORARY: a "temporary" wref_procedure_token. It's the procedure_token of a dummy loop that simply calls back the interpreter. Used for a CALL_ASSEMBLER where the @@ -206,7 +209,7 @@ # if we have this flag, and we *had* a procedure_token but # we no longer have one, then remove me. this prevents this # JitCell from being immortal. - return self.has_seen_a_procedure_token() + return self.has_seen_a_procedure_token() # i.e. dead weakref return True # Other JitCells can be removed. # ____________________________________________________________ @@ -374,7 +377,7 @@ if cell is None: cell = JitCell(*greenargs) jitcounter.install_new_cell(hash, cell) - cell.flags |= JC_TRACING + cell.flags |= JC_TRACING | JC_TRACING_OCCURRED try: metainterp.compile_and_run_once(jitdriver_sd, *args) finally: @@ -418,9 +421,15 @@ if procedure_token is None: if cell.flags & JC_DONT_TRACE_HERE: if not cell.has_seen_a_procedure_token(): - # we're seeing a fresh JC_DONT_TRACE_HERE with no - # procedure_token. Compile now. - bound_reached(hash, cell, *args) + # A JC_DONT_TRACE_HERE, i.e. a non-inlinable function. + # If we never tried to trace it, try it now immediately. + # Otherwise, count normally. + if cell.flags & JC_TRACING_OCCURRED: + tick = jitcounter.tick(hash, increment_threshold) + else: + tick = True + if tick: + bound_reached(hash, cell, *args) return # it was an aborted compilation, or maybe a weakref that # has been freed diff --git a/rpython/memory/gctransform/asmgcroot.py b/rpython/memory/gctransform/asmgcroot.py --- a/rpython/memory/gctransform/asmgcroot.py +++ b/rpython/memory/gctransform/asmgcroot.py @@ -517,6 +517,8 @@ "anywhere I know, bug in asmgcc") # fish the depth extra_stack_depth = (ebp_in_caller + STACK_DEPTH_OFS).signed[0] + ll_assert((extra_stack_depth & (rffi.sizeof(lltype.Signed) - 1)) + == 0, "asmgcc: misaligned extra_stack_depth") extra_stack_depth //= rffi.sizeof(lltype.Signed) self._shape_decompressor.setjitframe(extra_stack_depth) return diff --git a/rpython/rlib/rfile.py b/rpython/rlib/rfile.py --- a/rpython/rlib/rfile.py +++ b/rpython/rlib/rfile.py @@ -38,6 +38,7 @@ FILEP = rffi.COpaquePtr("FILE") OFF_T = config['off_t'] + _IONBF = config['_IONBF'] _IOLBF = config['_IOLBF'] _IOFBF = config['_IOFBF'] @@ -47,6 +48,11 @@ BASE_BUF_SIZE = 4096 BASE_LINE_SIZE = 100 +NEWLINE_UNKNOWN = 0 +NEWLINE_CR = 1 +NEWLINE_LF = 2 +NEWLINE_CRLF = 4 + def llexternal(*args, **kwargs): return rffi.llexternal(*args, compilation_info=eci, **kwargs) @@ -63,7 +69,16 @@ c_fclose = llexternal('fclose', [FILEP], rffi.INT) c_pclose = llexternal('pclose', [FILEP], rffi.INT) +# Note: the following two functions are called from __del__ methods, +# so must be 'releasegil=False'. Otherwise, a program using both +# threads and the RFile class cannot translate. See c684bf704d1f +c_fclose_in_del = llexternal('fclose', [FILEP], rffi.INT, releasegil=False) +c_pclose_in_del = llexternal('pclose', [FILEP], rffi.INT, releasegil=False) +_fclose2 = (c_fclose, c_fclose_in_del) +_pclose2 = (c_pclose, c_pclose_in_del) + c_getc = llexternal('getc', [FILEP], rffi.INT, macro=True) +c_ungetc = llexternal('ungetc', [rffi.INT, FILEP], rffi.INT) c_fgets = llexternal('fgets', [rffi.CCHARP, rffi.INT, FILEP], rffi.CCHARP) c_fread = llexternal('fread', [rffi.CCHARP, rffi.SIZE_T, rffi.SIZE_T, FILEP], rffi.SIZE_T) @@ -81,11 +96,15 @@ c_ferror = llexternal('ferror', [FILEP], rffi.INT) c_clearerr = llexternal('clearerr', [FILEP], lltype.Void) +c_stdin = rffi.CExternVariable(FILEP, 'stdin', eci, c_type='FILE*')[0] +c_stdout = rffi.CExternVariable(FILEP, 'stdout', eci, c_type='FILE*')[0] +c_stderr = rffi.CExternVariable(FILEP, 'stderr', eci, c_type='FILE*')[0] + def _error(ll_file): err = c_ferror(ll_file) c_clearerr(ll_file) - raise OSError(err, os.strerror(err)) + raise IOError(err, os.strerror(err)) def _dircheck(ll_file): @@ -96,7 +115,7 @@ else: if stat.S_ISDIR(st[0]): err = errno.EISDIR - raise OSError(err, os.strerror(err)) + raise IOError(err, os.strerror(err)) def _sanitize_mode(mode): @@ -120,43 +139,41 @@ def create_file(filename, mode="r", buffering=-1): - mode = _sanitize_mode(mode) + newmode = _sanitize_mode(mode) ll_name = rffi.str2charp(filename) try: - ll_mode = rffi.str2charp(mode) + ll_mode = rffi.str2charp(newmode) try: ll_file = c_fopen(ll_name, ll_mode) if not ll_file: errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) + raise IOError(errno, os.strerror(errno)) finally: lltype.free(ll_mode, flavor='raw') finally: lltype.free(ll_name, flavor='raw') _dircheck(ll_file) - if buffering >= 0: - buf = lltype.nullptr(rffi.CCHARP.TO) - if buffering == 0: - c_setvbuf(ll_file, buf, _IONBF, 0) - elif buffering == 1: - c_setvbuf(ll_file, buf, _IOLBF, BUFSIZ) - else: - c_setvbuf(ll_file, buf, _IOFBF, buffering) - return RFile(ll_file) + f = RFile(ll_file, mode) + f._setbufsize(buffering) + return f -def create_fdopen_rfile(fd, mode="r"): - mode = _sanitize_mode(mode) - ll_mode = rffi.str2charp(mode) +def create_fdopen_rfile(fd, mode="r", buffering=-1): + newmode = _sanitize_mode(mode) + fd = rffi.cast(rffi.INT, fd) + rposix.validate_fd(fd) + ll_mode = rffi.str2charp(newmode) try: - ll_file = c_fdopen(rffi.cast(rffi.INT, fd), ll_mode) + ll_file = c_fdopen(fd, ll_mode) if not ll_file: errno = rposix.get_errno() raise OSError(errno, os.strerror(errno)) finally: lltype.free(ll_mode, flavor='raw') _dircheck(ll_file) - return RFile(ll_file) + f = RFile(ll_file, mode) + f._setbufsize(buffering) + return f def create_temp_rfile(): @@ -180,16 +197,61 @@ lltype.free(ll_type, flavor='raw') finally: lltype.free(ll_command, flavor='raw') - return RFile(ll_file, c_pclose) + return RFile(ll_file, close2=_pclose2) + + +def create_stdio(): + close2 = (None, None) + stdin = RFile(c_stdin(), close2=close2) + stdout = RFile(c_stdout(), close2=close2) + stderr = RFile(c_stderr(), close2=close2) + return stdin, stdout, stderr class RFile(object): - def __init__(self, ll_file, do_close=c_fclose): + _setbuf = lltype.nullptr(rffi.CCHARP.TO) + _univ_newline = False + _newlinetypes = NEWLINE_UNKNOWN + _skipnextlf = False + + def __init__(self, ll_file, mode=None, close2=_fclose2): self._ll_file = ll_file - self._do_close = do_close + if mode is not None: + self._univ_newline = 'U' in mode + self._close2 = close2 + + def _setbufsize(self, bufsize): + if bufsize >= 0: + if bufsize == 0: + mode = _IONBF + elif bufsize == 1: + mode = _IOLBF + bufsize = BUFSIZ + else: + mode = _IOFBF + if self._setbuf: + lltype.free(self._setbuf, flavor='raw') + if mode == _IONBF: + self._setbuf = lltype.nullptr(rffi.CCHARP.TO) + else: + self._setbuf = lltype.malloc(rffi.CCHARP.TO, bufsize, flavor='raw') + c_setvbuf(self._ll_file, self._setbuf, mode, bufsize) def __del__(self): - self.close() + """Closes the described file when the object's last reference + goes away. Unlike an explicit call to close(), this is meant + as a last-resort solution and cannot release the GIL or return + an error code.""" + ll_file = self._ll_file + if ll_file: + do_close = self._close2[1] + if do_close: + do_close(ll_file) # return value ignored + if self._setbuf: + lltype.free(self._setbuf, flavor='raw') + + def _cleanup_(self): + self._ll_file = lltype.nullptr(FILEP.TO) def close(self): """Closes the described file. @@ -204,16 +266,69 @@ if ll_file: # double close is allowed self._ll_file = lltype.nullptr(FILEP.TO) - res = self._do_close(ll_file) - if res == -1: - errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) + do_close = self._close2[0] + try: + if do_close: + res = do_close(ll_file) + if res == -1: + errno = rposix.get_errno() + raise IOError(errno, os.strerror(errno)) + finally: + if self._setbuf: + lltype.free(self._setbuf, flavor='raw') + self._setbuf = lltype.nullptr(rffi.CCHARP.TO) return res def _check_closed(self): if not self._ll_file: raise ValueError("I/O operation on closed file") + def _fread(self, buf, n, stream): + if not self._univ_newline: + return c_fread(buf, 1, n, stream) + + i = 0 + dst = buf + newlinetypes = self._newlinetypes + skipnextlf = self._skipnextlf + while n: + nread = c_fread(dst, 1, n, stream) + if nread == 0: + break + + src = dst + n -= nread + shortread = n != 0 + while nread: + nread -= 1 + c = src[0] + src = rffi.ptradd(src, 1) + if c == '\r': + dst[0] = '\n' + dst = rffi.ptradd(dst, 1) + i += 1 + skipnextlf = True + elif skipnextlf and c == '\n': + skipnextlf = False + newlinetypes |= NEWLINE_CRLF + n += 1 + else: + if c == '\n': + newlinetypes |= NEWLINE_LF + elif skipnextlf: + newlinetypes |= NEWLINE_CR + dst[0] = c + dst = rffi.ptradd(dst, 1) + i += 1 + skipnextlf = False + if shortread: + if skipnextlf and c_feof(stream): + newlinetypes |= NEWLINE_CR + break + self._newlinetypes = newlinetypes + self._skipnextlf = skipnextlf + return i + def read(self, size=-1): # XXX CPython uses a more delicate logic here self._check_closed() @@ -226,7 +341,7 @@ try: s = StringBuilder() while True: - returned_size = c_fread(buf, 1, BASE_BUF_SIZE, ll_file) + returned_size = self._fread(buf, BASE_BUF_SIZE, ll_file) returned_size = intmask(returned_size) # is between 0 and BASE_BUF_SIZE if returned_size == 0: if c_feof(ll_file): @@ -238,12 +353,13 @@ lltype.free(buf, flavor='raw') else: # size > 0 with rffi.scoped_alloc_buffer(size) as buf: - returned_size = c_fread(buf.raw, 1, size, ll_file) + returned_size = self._fread(buf.raw, size, ll_file) returned_size = intmask(returned_size) # is between 0 and size if returned_size == 0: if not c_feof(ll_file): raise _error(ll_file) s = buf.str(returned_size) + assert s is not None return s def _readline1(self, raw_buf): @@ -284,7 +400,7 @@ self._check_closed() if size == 0: return "" - elif size < 0: + elif size < 0 and not self._univ_newline: with rffi.scoped_alloc_buffer(BASE_LINE_SIZE) as buf: c = self._readline1(buf.raw) if c >= 0: @@ -299,19 +415,50 @@ break s.append_charpsize(buf.raw, c) return s.build() - else: # size > 0 + else: # size > 0 or self._univ_newline ll_file = self._ll_file + c = 0 s = StringBuilder() - while s.getlength() < size: - c = c_getc(ll_file) + if self._univ_newline: + newlinetypes = self._newlinetypes + skipnextlf = self._skipnextlf + while size < 0 or s.getlength() < size: + c = c_getc(ll_file) + if c == EOF: + break + if skipnextlf: + skipnextlf = False + if c == ord('\n'): + newlinetypes |= NEWLINE_CRLF + c = c_getc(ll_file) + if c == EOF: + break + else: + newlinetypes |= NEWLINE_CR + if c == ord('\r'): + skipnextlf = True + c = ord('\n') + elif c == ord('\n'): + newlinetypes |= NEWLINE_LF + s.append(chr(c)) + if c == ord('\n'): + break if c == EOF: - if c_ferror(ll_file): - raise _error(ll_file) - break - c = chr(c) - s.append(c) - if c == '\n': - break + if skipnextlf: + newlinetypes |= NEWLINE_CR + self._newlinetypes = newlinetypes + self._skipnextlf = skipnextlf + else: + while s.getlength() < size: + c = c_getc(ll_file) + if c == EOF: + break + s.append(chr(c)) + if c == ord('\n'): + break + if c == EOF: + if c_ferror(ll_file): + raise _error(ll_file) return s.build() @enforceargs(None, str) @@ -325,7 +472,8 @@ bytes = c_fwrite(ll_value, 1, length, self._ll_file) if bytes != length: errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) + c_clearerr(self._ll_file) + raise IOError(errno, os.strerror(errno)) finally: rffi.free_nonmovingbuffer(value, ll_value) @@ -334,7 +482,7 @@ res = c_fflush(self._ll_file) if res != 0: errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) + raise IOError(errno, os.strerror(errno)) def truncate(self, arg=-1): self._check_closed() @@ -344,23 +492,36 @@ res = c_ftruncate(self.fileno(), arg) if res == -1: errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) + raise IOError(errno, os.strerror(errno)) def seek(self, pos, whence=0): self._check_closed() res = c_fseek(self._ll_file, pos, whence) if res == -1: errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) + raise IOError(errno, os.strerror(errno)) + self._skipnextlf = False def tell(self): self._check_closed() res = intmask(c_ftell(self._ll_file)) if res == -1: errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) + raise IOError(errno, os.strerror(errno)) + if self._skipnextlf: + c = c_getc(self._ll_file) + if c == ord('\n'): + self._newlinetypes |= NEWLINE_CRLF + res += 1 + self._skipnextlf = False + elif c != EOF: + c_ungetc(c, self._ll_file) return res def fileno(self): self._check_closed() return intmask(c_fileno(self._ll_file)) + + def isatty(self): + self._check_closed() + return os.isatty(c_fileno(self._ll_file)) diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -119,7 +119,7 @@ return 1 def validate_fd(fd): - return 1 + pass def closerange(fd_low, fd_high): # this behaves like os.closerange() from Python 2.6. diff --git a/rpython/rlib/streamio.py b/rpython/rlib/streamio.py --- a/rpython/rlib/streamio.py +++ b/rpython/rlib/streamio.py @@ -900,6 +900,13 @@ return '\n'.join(result) + def readline(self): + line = self.base.readline() + limit = len(line) - 2 + if limit >= 0 and line[limit] == '\r' and line[limit + 1] == '\n': + line = line[:limit] + '\n' + return line + def tell(self): pos = self.base.tell() return pos - len(self.lfbuffer) diff --git a/rpython/rlib/test/test_rfile.py b/rpython/rlib/test/test_rfile.py --- a/rpython/rlib/test/test_rfile.py +++ b/rpython/rlib/test/test_rfile.py @@ -1,4 +1,4 @@ -import os, sys, py, errno +import os, sys, py, errno, gc from rpython.rtyper.test.tool import BaseRtypingTest from rpython.tool.udir import udir from rpython.rlib import rfile @@ -25,61 +25,67 @@ f.close() f() + assert open(fname, "r").read() == "dupa" self.interpret(f, []) assert open(fname, "r").read() == "dupa" def test_open_errors(self): - def f(exc): - def g(run): - try: - open('zzz', 'badmode') - except ValueError: - pass + def f(run): + try: + open('zzz', 'badmode') + except ValueError: + pass + else: + assert False + + try: + open('zzz') + except IOError as e: + assert e.errno == errno.ENOENT + else: + assert False + + try: + open('.') + except IOError as e: + if os.name == 'posix': + assert e.errno == errno.EISDIR else: - assert False + assert e.errno == errno.EACCES + else: + assert False - try: - open('zzz') - except exc as e: - assert e.errno == errno.ENOENT - else: - assert False + try: + os.fdopen(42, "badmode") + except ValueError: + pass + else: + assert False - try: - open('.') - except exc as e: - if os.name == 'posix': + try: + fd = os.open('.', os.O_RDONLY, 0777) + except OSError as e: + assert os.name == 'nt' and e.errno == errno.EACCES + else: + assert os.name != 'nt' + if run: + try: + os.fdopen(fd) + except IOError as e: assert e.errno == errno.EISDIR else: - assert e.errno == errno.EACCES - else: - assert False + assert False + os.close(fd) - try: - os.fdopen(42, "badmode") - except ValueError: - pass - else: - assert False + try: + os.fdopen(12345) + except OSError as e: + assert e.errno == errno.EBADF + else: + assert False - try: - fd = os.open('.', os.O_RDONLY, 0777) - except OSError as e: - assert os.name == 'nt' and e.errno == errno.EACCES - else: - assert os.name != 'nt' - if run: - try: - os.fdopen(fd) - except exc as e: - assert e.errno == errno.EISDIR - else: - assert False - os.close(fd) - return g - - f(IOError)(sys.version_info >= (2, 7, 9)) - self.interpret(f(OSError), [True]) + f(sys.version_info >= (2, 7, 9)) + self.interpret(f, [True]) @py.test.mark.skipif("sys.platform == 'win32'") # http://msdn.microsoft.com/en-us/library/86cebhfs.aspx @@ -98,15 +104,53 @@ f() self.interpret(f, []) + @py.test.mark.skipif("sys.platform == 'win32'") + # http://msdn.microsoft.com/en-us/library/86cebhfs.aspx + def test_fdopen_buffering_line(self): + fname = str(self.tmpdir.join('file_1a')) + + def f(): + g = open(fname, 'w') + f = os.fdopen(os.dup(g.fileno()), 'w', 1) + g.close() + f.write('dupa\ndupb') + f2 = open(fname, 'r') + assert f2.read() == 'dupa\n' + f.close() + assert f2.read() == 'dupb' + f2.close() + + f() + self.interpret(f, []) + def test_open_buffering_full(self): fname = str(self.tmpdir.join('file_1b')) def f(): f = open(fname, 'w', 128) - f.write('dupa') + f.write('dupa\ndupb') f2 = open(fname, 'r') assert f2.read() == '' - f.write('z' * 5000) + f.write('z' * 120) + assert f2.read() != '' + f.close() + assert f2.read() != '' + f2.close() + + f() + self.interpret(f, []) + + def test_fdopen_buffering_full(self): + fname = str(self.tmpdir.join('file_1b')) + + def f(): + g = open(fname, 'w') + f = os.fdopen(os.dup(g.fileno()), 'w', 128) + g.close() + f.write('dupa\ndupb') + f2 = open(fname, 'r') + assert f2.read() == '' + f.write('z' * 120) assert f2.read() != '' f.close() assert f2.read() != '' @@ -120,10 +164,28 @@ def f(): f = open(fname, "w") + try: + f.read() + except IOError as e: + pass + else: + assert False + try: + f.readline() + except IOError as e: + pass + else: + assert False f.write("dupa\x00dupb") f.close() for mode in ['r', 'U']: f2 = open(fname, mode) + try: + f2.write('z') + except IOError as e: + pass + else: + assert False dupa = f2.read(0) assert dupa == "" dupa = f2.read() @@ -162,6 +224,40 @@ assert d == "a" assert e == "" + f() + self.interpret(f, []) + + def test_read_universal(self): + fname = str(self.tmpdir.join('read_univ')) + with open(fname, 'wb') as f: + f.write("dupa\ndupb\r\ndupc\rdupd") + + def f(): + f = open(fname, 'U') + assert f.read() == "dupa\ndupb\ndupc\ndupd" + assert f.read() == "" + f.seek(0) + assert f.read(10) == "dupa\ndupb\n" + assert f.read(42) == "dupc\ndupd" + assert f.read(1) == "" + f.seek(0) + assert f.readline() == "dupa\n" + assert f.tell() == 5 + assert f.readline() == "dupb\n" + assert f.tell() == 11 + assert f.readline() == "dupc\n" + assert f.tell() == 16 + assert f.readline() == "dupd" + assert f.tell() == 20 + assert f.readline() == "" + f.seek(0) + assert f.readline() == "dupa\n" + assert f.readline() == "dupb\n" + f.seek(4) + assert f.read(1) == "\n" + f.close() + + f() self.interpret(f, []) def test_seek(self): @@ -172,6 +268,12 @@ f.write("xxx") f.seek(0) assert f.read() == "xxx" + try: + f.seek(0, 42) + except IOError as e: + assert e.errno == errno.EINVAL + else: + assert False f.close() f() @@ -196,6 +298,12 @@ new_fno = os.dup(f.fileno()) f2 = os.fdopen(new_fno, "w") f.close() + try: + f2.read() + except IOError as e: + pass + else: + assert False f2.write("xxx") f2.close() @@ -209,11 +317,14 @@ def f(): f = open(fname, "w") + assert not f.isatty() try: return f.fileno() finally: f.close() + res = f() + assert res > 2 res = self.interpret(f, []) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit