[pypy-commit] pypy release-2.4.x: merge default into branch

mattip noreply at buildbot.pypy.org
Sun Sep 7 15:16:37 CEST 2014


Author: mattip <matti.picus at gmail.com>
Branch: release-2.4.x
Changeset: r73359:9f425c60afdf
Date: 2014-09-07 14:38 +0300
http://bitbucket.org/pypy/pypy/changeset/9f425c60afdf/

Log:	merge default into branch

diff --git a/pypy/doc/release-2.4.0.rst b/pypy/doc/release-2.4.0.rst
--- a/pypy/doc/release-2.4.0.rst
+++ b/pypy/doc/release-2.4.0.rst
@@ -2,12 +2,8 @@
 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.
-
-XXX this sentence is confusing, refactor
-
-This release contains several improvements and bugfixes.
+We're pleased to announce PyPy 2.4, which contains significant performance
+enhancements and bug fixes. 
 
 You can download the PyPy 2.4 release here:
 
@@ -15,20 +11,20 @@
 
 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.
+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 compatable version
-   we call PyPy3 2.3.1, and are working toward a Python 3.3 compatable version
+* `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 release a first working version, and
-continue to try out new promising paths of acheiving a fast multithreaded python
+* `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`_
+* `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
@@ -41,15 +37,12 @@
 =============
 
 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;
-note that cpython's speed has not changed since 2.7.2)
+CPython 2.7. It's fast (`pypy 2.4 and cpython 2.7.x`_ performance comparison)
 due to its integrated tracing JIT compiler.
 
-XXX confusing sentence, rewrite
-
-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. 
+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
@@ -61,19 +54,18 @@
 Highlights
 ==========
 
-Benchmarks improved after internal improvements in string and
+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 that a lot of corner cases when handling strings or bytearrays have
-better performance.
+means better performance in a lot of corner cases with handling strings or
+bytearrays.
 
 PyPy now uses Python 2.7.8 standard library.
 
 We welcomed more than 12 new contributors, and conducted two Google
-Summer of Code as well as other student projects not directly related
-to Summer of Code.
+Summer of Code projects, as well as other student projects not
+directly related to Summer of Code.
 
-XXX mention the work is ongoing and did not make it to 2.4?
 
 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
@@ -83,8 +75,8 @@
 * 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.
+  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
@@ -93,7 +85,7 @@
 * Move to a mixed polling and mutex GIL model that make mutli-threaded jitted
   code run *much* faster
 
-* Optimize errno handling in linux
+* Optimize errno handling in linux (x86 and x86-64 only)
 
 * Remove ctypes pythonapi and ctypes.PyDLL, which never worked on PyPy
 
@@ -110,6 +102,9 @@
 .. _`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 and
+usable numpy linalg compatabiity should be merged soon.
+
 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!
 
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_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/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
@@ -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,8 @@
         BUILTIN_ANALYZERS[original] = value
 
 
- at analyzer_for(getattr(OSError.__init__, 'im_func', OSError.__init__))
-def OSError_init(s_self, *args):
+ at analyzer_for(getattr(EnvironmentError.__init__, 'im_func', EnvironmentError.__init__))
+def EnvironmentError_init(s_self, *args):
     pass
 
 try:
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,8 @@
 # ____________________________________________________________
 
 FORCE_ATTRIBUTES_INTO_CLASSES = {
-    OSError: {'errno': SomeInteger()},
-    }
+    EnvironmentError: {'errno': SomeInteger(), 'strerror': SomeString(can_be_None=True), 'filename': SomeString(can_be_None=True)},
+}
 
 try:
     WindowsError
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
@@ -93,7 +93,7 @@
 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):
@@ -104,7 +104,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):
@@ -136,7 +136,7 @@
             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:
@@ -223,7 +223,7 @@
             res = do_close(ll_file)
             if res == -1:
                 errno = rposix.get_errno()
-                raise OSError(errno, os.strerror(errno))
+                raise IOError(errno, os.strerror(errno))
         return res
 
     def _check_closed(self):
@@ -341,7 +341,7 @@
             bytes = c_fwrite(ll_value, 1, length, self._ll_file)
             if bytes != length:
                 errno = rposix.get_errno()
-                raise OSError(errno, os.strerror(errno))
+                raise IOError(errno, os.strerror(errno))
         finally:
             rffi.free_nonmovingbuffer(value, ll_value)
 
@@ -350,7 +350,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()
@@ -360,21 +360,21 @@
         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))
 
     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))
         return res
 
     def fileno(self):
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
@@ -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
@@ -120,6 +126,12 @@
 
         def f():
             f = open(fname, "w")
+            try:
+                f.read()
+            except IOError as e:
+                pass
+            else:
+                assert False
             f.write("dupa\x00dupb")
             f.close()
             for mode in ['r', 'U']:
@@ -162,6 +174,7 @@
             assert d == "a"
             assert e == ""
 
+        f()
         self.interpret(f, [])
 
     def test_seek(self):
@@ -172,6 +185,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()
@@ -214,6 +233,8 @@
             finally:
                 f.close()
 
+        res = f()
+        assert res > 2
         res = self.interpret(f, [])
         assert res > 2
 
@@ -228,6 +249,8 @@
             finally:
                 f.close()
 
+        res = f()
+        assert res == 3
         res = self.interpret(f, [])
         assert res == 3
 
@@ -243,6 +266,7 @@
             f2.close()
             f.close()
 
+        f()
         self.interpret(f, [])
 
     def test_truncate(self):
diff --git a/rpython/rtyper/exceptiondata.py b/rpython/rtyper/exceptiondata.py
--- a/rpython/rtyper/exceptiondata.py
+++ b/rpython/rtyper/exceptiondata.py
@@ -44,13 +44,6 @@
             classdef = bk.getuniqueclassdef(cls)
             rclass.getclassrepr(rtyper, classdef).setup()
 
-    def make_raise_OSError(self, rtyper):
-        # ll_raise_OSError(errno)
-        def ll_raise_OSError(errno):
-            raise OSError(errno, None)
-        helper_fn = rtyper.annotate_helper_fn(ll_raise_OSError, [annmodel.SomeInteger()])
-        return helper_fn
-
     def get_standard_ll_exc_instance(self, rtyper, clsdef):
         from rpython.rtyper.lltypesystem.rclass import getinstancerepr
         r_inst = getinstancerepr(rtyper, clsdef)
@@ -69,7 +62,6 @@
         # create helper functionptrs
         self.fn_exception_match  = self.make_exception_matcher(rtyper)
         self.fn_type_of_exc_inst = self.make_type_of_exc_inst(rtyper)
-        self.fn_raise_OSError    = self.make_raise_OSError(rtyper)
 
     def make_exception_matcher(self, rtyper):
         # ll_exception_matcher(real_exception_vtable, match_exception_vtable)
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -441,12 +441,8 @@
             extraargs = ()
         typer = self.llinterpreter.typer
         exdata = typer.exceptiondata
-        if isinstance(exc, OSError):
-            self.op_direct_call(exdata.fn_raise_OSError, exc.errno)
-            assert False, "op_direct_call above should have raised"
-        else:
-            evalue = exdata.get_standard_ll_exc_instance_by_class(exc.__class__)
-            etype = self.op_direct_call(exdata.fn_type_of_exc_inst, evalue)
+        evalue = exdata.get_standard_ll_exc_instance_by_class(exc.__class__)
+        etype = self.op_direct_call(exdata.fn_type_of_exc_inst, evalue)
         raise LLException(etype, evalue, *extraargs)
 
     def invoke_callable_with_pyexceptions(self, fptr, *args):
diff --git a/rpython/rtyper/rbuiltin.py b/rpython/rtyper/rbuiltin.py
--- a/rpython/rtyper/rbuiltin.py
+++ b/rpython/rtyper/rbuiltin.py
@@ -3,9 +3,10 @@
 from rpython.rlib import rarithmetic, objectmodel
 from rpython.rtyper import raddress, rptr, extregistry, rrange
 from rpython.rtyper.error import TyperError
-from rpython.rtyper.lltypesystem import lltype, llmemory, rclass
+from rpython.rtyper.lltypesystem import lltype, llmemory, rclass, rstr
 from rpython.rtyper.lltypesystem.rdict import rtype_r_dict
 from rpython.rtyper.rmodel import Repr
+from rpython.rtyper.rstr import AbstractStringRepr
 from rpython.tool.pairtype import pairtype
 
 
@@ -264,16 +265,21 @@
 def rtype_object__init__(hop):
     hop.exception_cannot_occur()
 
-def rtype_OSError__init__(hop):
+def rtype_EnvironmentError__init__(hop):
     hop.exception_cannot_occur()
-    if hop.nb_args == 2:
-        raise TyperError("OSError() should not be called with "
-                         "a single argument")
+    v_self = hop.args_v[0]
+    r_self = hop.args_r[0]
+    if hop.nb_args >= 2:
+        v_errno = hop.inputarg(lltype.Signed, arg=1)
+    else:
+        v_errno = hop.inputconst(lltype.Signed, 0)
+    r_self.setfield(v_self, 'errno', v_errno, hop.llops)
     if hop.nb_args >= 3:
-        v_self = hop.args_v[0]
-        r_self = hop.args_r[0]
-        v_errno = hop.inputarg(lltype.Signed, arg=1)
-        r_self.setfield(v_self, 'errno', v_errno, hop.llops)
+        v_strerror = hop.inputarg(rstr.string_repr, arg=2)
+        r_self.setfield(v_self, 'strerror', v_strerror, hop.llops)
+        if hop.nb_args >= 4:
+            v_filename = hop.inputarg(rstr.string_repr, arg=3)
+            r_self.setfield(v_self, 'filename', v_filename, hop.llops)
 
 def rtype_WindowsError__init__(hop):
     hop.exception_cannot_occur()
@@ -333,8 +339,8 @@
         original = getattr(__builtin__, name[14:])
         BUILTIN_TYPER[original] = value
 
-BUILTIN_TYPER[getattr(OSError.__init__, 'im_func', OSError.__init__)] = (
-    rtype_OSError__init__)
+BUILTIN_TYPER[getattr(EnvironmentError.__init__, 'im_func', EnvironmentError.__init__)] = (
+    rtype_EnvironmentError__init__)
 
 try:
     WindowsError
diff --git a/rpython/rtyper/test/test_exception.py b/rpython/rtyper/test/test_exception.py
--- a/rpython/rtyper/test/test_exception.py
+++ b/rpython/rtyper/test/test_exception.py
@@ -36,14 +36,53 @@
 class TestException(BaseRtypingTest):
     def test_exception_with_arg(self):
         def g(n):
-            raise OSError(n, "?")
+            raise IOError(n)
+        def h(n):
+            raise OSError(n, "?", None)
+        def i(n):
+            raise EnvironmentError(n, "?", "test")
+        def j(n):
+            raise IOError(0, "test")
+        def k(n):
+            raise OSError
         def f(n):
             try:
                 g(n)
+            except IOError, e:
+                assert e.errno == 42
+                assert e.strerror is None
+                assert e.filename is None
+            else:
+                assert False
+            try:
+                h(n)
             except OSError, e:
-                return e.errno
-        res = self.interpret(f, [42])
-        assert res == 42
+                assert e.errno == 42
+                assert e.strerror == "?"
+                assert e.filename is None
+            else:
+                assert False
+            try:
+                i(n)
+            except EnvironmentError as e:
+                assert e.errno == 42
+                assert e.strerror == "?"
+                assert e.filename == "test"
+            else:
+                assert False
+            try:
+                j(n)
+            except (IOError, OSError) as e:
+                assert e.errno == 0
+                assert e.strerror == "test"
+                assert e.filename is None
+            try:
+                k(n)
+            except EnvironmentError as e:
+                assert e.errno == 0
+                assert e.strerror is None
+                assert e.filename is None
+        self.interpret(f, [42])
 
     def test_catch_incompatible_class(self):
         class MyError(Exception):
diff --git a/rpython/translator/c/extfunc.py b/rpython/translator/c/extfunc.py
--- a/rpython/translator/c/extfunc.py
+++ b/rpython/translator/c/extfunc.py
@@ -90,7 +90,6 @@
 
     yield ('RPYTHON_EXCEPTION_MATCH',  exceptiondata.fn_exception_match)
     yield ('RPYTHON_TYPE_OF_EXC_INST', exceptiondata.fn_type_of_exc_inst)
-    yield ('RPYTHON_RAISE_OSERROR',    exceptiondata.fn_raise_OSError)
 
     yield ('RPyExceptionOccurred1',    exctransformer.rpyexc_occured_ptr.value)
     yield ('RPyFetchExceptionType',    exctransformer.rpyexc_fetch_type_ptr.value)


More information about the pypy-commit mailing list