[pypy-commit] pypy default: merge

fijal noreply at buildbot.pypy.org
Fri Jan 31 13:59:29 CET 2014


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: 
Changeset: r69036:22ee335c3d51
Date: 2014-01-31 13:58 +0100
http://bitbucket.org/pypy/pypy/changeset/22ee335c3d51/

Log:	merge

diff --git a/pypy/module/_cffi_backend/handle.py b/pypy/module/_cffi_backend/handle.py
--- a/pypy/module/_cffi_backend/handle.py
+++ b/pypy/module/_cffi_backend/handle.py
@@ -2,58 +2,13 @@
 from pypy.interpreter.error import OperationError, operationerrfmt
 from pypy.interpreter.gateway import unwrap_spec
 from pypy.module._cffi_backend import ctypeobj, ctypeptr, cdataobj
-from pypy.module._weakref.interp__weakref import dead_ref
 from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rlib import rweaklist
 
 
-def reduced_value(s):
-    while True:
-        divide = s & 1
-        s >>= 1
-        if not divide:
-            return s
-
-# ____________________________________________________________
-
-
-class CffiHandles:
+class CffiHandles(rweaklist.RWeakListMixin):
     def __init__(self, space):
-        self.handles = []
-        self.look_distance = 0
-
-    def reserve_next_handle_index(self):
-        # The reservation ordering done here is tweaked for pypy's
-        # memory allocator.  We look from index 'look_distance'.
-        # Look_distance increases from 0.  But we also look at
-        # "look_distance/2" or "/4" or "/8", etc.  If we find that one
-        # of these secondary locations is free, we assume it's because
-        # there was recently a minor collection; so we reset
-        # look_distance to 0 and start again from the lowest locations.
-        length = len(self.handles)
-        for d in range(self.look_distance, length):
-            if self.handles[d]() is None:
-                self.look_distance = d + 1
-                return d
-            s = reduced_value(d)
-            if self.handles[s]() is None:
-                break
-        # restart from the beginning
-        for d in range(0, length):
-            if self.handles[d]() is None:
-                self.look_distance = d + 1
-                return d
-        # full! extend, but don't use '+=' here
-        self.handles = self.handles + [dead_ref] * (length // 3 + 5)
-        self.look_distance = length + 1
-        return length
-
-    def store_handle(self, index, content):
-        self.handles[index] = weakref.ref(content)
-
-    def fetch_handle(self, index):
-        if 0 <= index < len(self.handles):
-            return self.handles[index]()
-        return None
+        self.initialize()
 
 def get(space):
     return space.fromcache(CffiHandles)
diff --git a/pypy/module/_io/interp_iobase.py b/pypy/module/_io/interp_iobase.py
--- a/pypy/module/_io/interp_iobase.py
+++ b/pypy/module/_io/interp_iobase.py
@@ -5,7 +5,7 @@
 from pypy.interpreter.gateway import interp2app
 from pypy.interpreter.error import OperationError, operationerrfmt
 from rpython.rlib.rstring import StringBuilder
-from rpython.rlib import rweakref
+from rpython.rlib import rweakref, rweaklist
 
 
 DEFAULT_BUFFER_SIZE = 8192
@@ -52,7 +52,6 @@
         self.w_dict = space.newdict()
         self.__IOBase_closed = False
         if add_to_autoflusher:
-            self.streamholder = None # needed by AutoFlusher
             get_autoflusher(space).add(self)
 
     def getdict(self, space):
@@ -115,7 +114,6 @@
             space.call_method(self, "flush")
         finally:
             self.__IOBase_closed = True
-            get_autoflusher(space).remove(self)
 
     def flush_w(self, space):
         if self._CLOSED():
@@ -339,55 +337,35 @@
 # functions to make sure that all streams are flushed on exit
 # ------------------------------------------------------------
 
-class StreamHolder(object):
-    def __init__(self, w_iobase):
-        self.w_iobase_ref = rweakref.ref(w_iobase)
-        w_iobase.autoflusher = self
 
-    def autoflush(self, space):
-        w_iobase = self.w_iobase_ref()
-        if w_iobase is not None:
-            try:
-                space.call_method(w_iobase, 'flush')
-            except OperationError:
-                # Silencing all errors is bad, but getting randomly
-                # interrupted here is equally as bad, and potentially
-                # more frequent (because of shutdown issues).
-                pass 
-
-
-class AutoFlusher(object):
+class AutoFlusher(rweaklist.RWeakListMixin):
     def __init__(self, space):
-        self.streams = {}
+        self.initialize()
 
     def add(self, w_iobase):
-        assert w_iobase.streamholder is None
         if rweakref.has_weakref_support():
-            holder = StreamHolder(w_iobase)
-            w_iobase.streamholder = holder
-            self.streams[holder] = None
+            self.add_handle(w_iobase)
         #else:
         #   no support for weakrefs, so ignore and we
         #   will not get autoflushing
 
-    def remove(self, w_iobase):
-        holder = w_iobase.streamholder
-        if holder is not None:
-            try:
-                del self.streams[holder]
-            except KeyError:
-                # this can happen in daemon threads
-                pass
-
     def flush_all(self, space):
-        while self.streams:
-            for streamholder in self.streams.keys():
+        while True:
+            handles = self.get_all_handles()
+            if len(handles) == 0:
+                break
+            self.initialize()  # reset the state here
+            for wr in handles:
+                w_iobase = wr()
+                if w_iobase is None:
+                    continue
                 try:
-                    del self.streams[streamholder]
-                except KeyError:
-                    pass    # key was removed in the meantime
-                else:
-                    streamholder.autoflush(space)
+                    space.call_method(w_iobase, 'flush')
+                except OperationError:
+                    # Silencing all errors is bad, but getting randomly
+                    # interrupted here is equally as bad, and potentially
+                    # more frequent (because of shutdown issues).
+                    pass 
 
 def get_autoflusher(space):
     return space.fromcache(AutoFlusher)
diff --git a/pypy/module/_weakref/interp__weakref.py b/pypy/module/_weakref/interp__weakref.py
--- a/pypy/module/_weakref/interp__weakref.py
+++ b/pypy/module/_weakref/interp__weakref.py
@@ -6,6 +6,7 @@
 from rpython.rlib import jit
 from rpython.rlib.rshrinklist import AbstractShrinkList
 from rpython.rlib.objectmodel import specialize
+from rpython.rlib.rweakref import dead_ref
 import weakref
 
 
@@ -144,14 +145,6 @@
 
 # ____________________________________________________________
 
-class Dummy:
-    pass
-dead_ref = weakref.ref(Dummy())
-for i in range(5):
-    if dead_ref() is not None:
-        import gc; gc.collect()
-assert dead_ref() is None
-
 
 class W_WeakrefBase(W_Root):
     def __init__(w_self, space, w_obj, w_callable):
diff --git a/pypy/module/exceptions/interp_exceptions.py b/pypy/module/exceptions/interp_exceptions.py
--- a/pypy/module/exceptions/interp_exceptions.py
+++ b/pypy/module/exceptions/interp_exceptions.py
@@ -446,6 +446,9 @@
 
     if hasattr(rwin32, 'build_winerror_to_errno'):
         _winerror_to_errno, _default_errno = rwin32.build_winerror_to_errno()
+        # Python 2 doesn't map ERROR_DIRECTORY (267) to ENOTDIR but
+        # Python 3 (CPython issue #12802) and build_winerror_to_errno do
+        del _winerror_to_errno[267]
     else:
         _winerror_to_errno, _default_errno = {}, 22 # EINVAL
 
diff --git a/pypy/module/micronumpy/interp_boxes.py b/pypy/module/micronumpy/interp_boxes.py
--- a/pypy/module/micronumpy/interp_boxes.py
+++ b/pypy/module/micronumpy/interp_boxes.py
@@ -256,6 +256,10 @@
         value = space.is_true(self)
         return get_dtype_cache(space).w_booldtype.box(value)
 
+    def descr_zero(self, space):
+        from pypy.module.micronumpy.interp_dtype import get_dtype_cache
+        return get_dtype_cache(space).w_longdtype.box(0)
+
     def descr_ravel(self, space):
         from pypy.module.micronumpy.base import convert_to_array
         w_values = space.newtuple([self])
@@ -327,6 +331,9 @@
     def descr_buffer(self, space):
         return self.descr_ravel(space).descr_get_data(space)
 
+    def descr_byteswap(self, space):
+        return self.get_dtype(space).itemtype.byteswap(self)
+
     w_flags = None
     def descr_get_flags(self, space):
         if self.w_flags is None:
@@ -583,6 +590,12 @@
     __hash__ = interp2app(W_GenericBox.descr_hash),
 
     tolist = interp2app(W_GenericBox.item),
+    min = interp2app(W_GenericBox.descr_self),
+    max = interp2app(W_GenericBox.descr_self),
+    argmin = interp2app(W_GenericBox.descr_zero),
+    argmax = interp2app(W_GenericBox.descr_zero),
+    sum = interp2app(W_GenericBox.descr_self),
+    prod = interp2app(W_GenericBox.descr_self),
     any = interp2app(W_GenericBox.descr_any),
     all = interp2app(W_GenericBox.descr_all),
     ravel = interp2app(W_GenericBox.descr_ravel),
@@ -592,6 +605,7 @@
     view = interp2app(W_GenericBox.descr_view),
     squeeze = interp2app(W_GenericBox.descr_self),
     copy = interp2app(W_GenericBox.descr_copy),
+    byteswap = interp2app(W_GenericBox.descr_byteswap),
 
     dtype = GetSetProperty(W_GenericBox.descr_get_dtype),
     size = GetSetProperty(W_GenericBox.descr_get_size),
diff --git a/pypy/module/micronumpy/interp_dtype.py b/pypy/module/micronumpy/interp_dtype.py
--- a/pypy/module/micronumpy/interp_dtype.py
+++ b/pypy/module/micronumpy/interp_dtype.py
@@ -803,29 +803,19 @@
         for dtype in reversed(self.builtin_dtypes):
             self.dtypes_by_num[dtype.num] = dtype
             self.dtypes_by_name[dtype.name] = dtype
-            can_name = dtype.kind + str(dtype.get_size())
-            self.dtypes_by_name[can_name] = dtype
-            self.dtypes_by_name[NPY_NATBYTE + can_name] = dtype
-            self.dtypes_by_name[NPY_NATIVE + can_name] = dtype
-            new_name = NPY_OPPBYTE + can_name
-            itemtype = type(dtype.itemtype)(False)
-            self.dtypes_by_name[new_name] = W_Dtype(
-                itemtype, dtype.num, dtype.kind, new_name, dtype.char,
-                dtype.w_box_type, byteorder=NPY_OPPBYTE,
-                float_type=dtype.float_type)
-            if dtype.kind != dtype.char:
-                can_name = dtype.char
+            for can_name in [dtype.kind + str(dtype.get_size()),
+                             dtype.char]:
+                self.dtypes_by_name[can_name] = dtype
                 self.dtypes_by_name[NPY_NATBYTE + can_name] = dtype
                 self.dtypes_by_name[NPY_NATIVE + can_name] = dtype
                 new_name = NPY_OPPBYTE + can_name
+                itemtype = type(dtype.itemtype)(False)
                 self.dtypes_by_name[new_name] = W_Dtype(
                     itemtype, dtype.num, dtype.kind, new_name, dtype.char,
                     dtype.w_box_type, byteorder=NPY_OPPBYTE,
                     float_type=dtype.float_type)
-
             for alias in dtype.aliases:
                 self.dtypes_by_name[alias] = dtype
-            self.dtypes_by_name[dtype.char] = dtype
 
         typeinfo_full = {
             'LONGLONG': self.w_int64dtype,
diff --git a/pypy/module/micronumpy/test/test_dtypes.py b/pypy/module/micronumpy/test/test_dtypes.py
--- a/pypy/module/micronumpy/test/test_dtypes.py
+++ b/pypy/module/micronumpy/test/test_dtypes.py
@@ -788,6 +788,14 @@
         assert dtype('>i8').str == '>i8'
         assert dtype('int8').str == '|i1'
         assert dtype('float').str == byteorder + 'f8'
+        assert dtype('f').str == byteorder + 'f4'
+        assert dtype('=f').str == byteorder + 'f4'
+        assert dtype('>f').str == '>f4'
+        assert dtype('<f').str == '<f4'
+        assert dtype('d').str == byteorder + 'f8'
+        assert dtype('=d').str == byteorder + 'f8'
+        assert dtype('>d').str == '>f8'
+        assert dtype('<d').str == '<f8'
         # strange
         assert dtype('string').str == '|S0'
         assert dtype('unicode').str == byteorder + 'U0'
diff --git a/pypy/module/micronumpy/test/test_scalar.py b/pypy/module/micronumpy/test/test_scalar.py
--- a/pypy/module/micronumpy/test/test_scalar.py
+++ b/pypy/module/micronumpy/test/test_scalar.py
@@ -102,6 +102,16 @@
         assert b == a
         assert b is not a
 
+    def test_methods(self):
+        import numpy as np
+        for a in [np.int32(2), np.float64(2.0), np.complex64(42)]:
+            for op in ['min', 'max', 'sum', 'prod']:
+                assert getattr(a, op)() == a
+            for op in ['argmin', 'argmax']:
+                b = getattr(a, op)()
+                assert type(b) is np.int_
+                assert b == 0
+
     def test_buffer(self):
         import numpy as np
         a = np.int32(123)
@@ -111,6 +121,13 @@
         b = buffer(a)
         assert str(b) == a
 
+    def test_byteswap(self):
+        import numpy as np
+        assert np.int64(123).byteswap() == 8863084066665136128
+        a = np.complex64(1+2j).byteswap()
+        assert repr(a.real).startswith('4.60060')
+        assert repr(a.imag).startswith('8.96831')
+
     def test_squeeze(self):
         import numpy as np
         assert np.True_.squeeze() is np.True_
diff --git a/rpython/rlib/rbigint.py b/rpython/rlib/rbigint.py
--- a/rpython/rlib/rbigint.py
+++ b/rpython/rlib/rbigint.py
@@ -254,16 +254,18 @@
 
     @staticmethod
     @jit.elidable
-    def fromstr(s, base=0):
-        """As string_to_int(), but ignores an optional 'l' or 'L' suffix
-        and returns an rbigint."""
+    def fromstr(s, base=0, ignore_l_suffix=False, fname='long'):
+        """As string_to_int(), but optionally ignores an optional 'l' or
+        'L' suffix and returns an rbigint.
+        """
         from rpython.rlib.rstring import NumberStringParser, \
             strip_spaces
         s = literal = strip_spaces(s)
-        if (s.endswith('l') or s.endswith('L')) and base < 22:
+        if (not ignore_l_suffix and (s.endswith('l') or s.endswith('L')) and
+            base < 22):
             # in base 22 and above, 'L' is a valid digit!  try: long('L',22)
             s = s[:-1]
-        parser = NumberStringParser(s, literal, base, 'long')
+        parser = NumberStringParser(s, literal, base, fname)
         return rbigint._from_numberstring_parser(parser)
 
     @staticmethod
diff --git a/rpython/rlib/rweaklist.py b/rpython/rlib/rweaklist.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/rweaklist.py
@@ -0,0 +1,60 @@
+import weakref
+from rpython.rlib.rweakref import dead_ref
+
+
+def _reduced_value(s):
+    while True:
+        divide = s & 1
+        s >>= 1
+        if not divide:
+            return s
+
+
+class RWeakListMixin(object):
+    _mixin_ = True
+
+    def initialize(self):
+        self.handles = []
+        self.look_distance = 0
+
+    def get_all_handles(self):
+        return self.handles
+
+    def reserve_next_handle_index(self):
+        # The reservation ordering done here is tweaked for pypy's
+        # memory allocator.  We look from index 'look_distance'.
+        # Look_distance increases from 0.  But we also look at
+        # "look_distance/2" or "/4" or "/8", etc.  If we find that one
+        # of these secondary locations is free, we assume it's because
+        # there was recently a minor collection; so we reset
+        # look_distance to 0 and start again from the lowest locations.
+        length = len(self.handles)
+        for d in range(self.look_distance, length):
+            if self.handles[d]() is None:
+                self.look_distance = d + 1
+                return d
+            s = _reduced_value(d)
+            if self.handles[s]() is None:
+                break
+        # restart from the beginning
+        for d in range(0, length):
+            if self.handles[d]() is None:
+                self.look_distance = d + 1
+                return d
+        # full! extend, but don't use '+=' here
+        self.handles = self.handles + [dead_ref] * (length // 3 + 5)
+        self.look_distance = length + 1
+        return length
+
+    def add_handle(self, content):
+        index = self.reserve_next_handle_index()
+        self.store_handle(index, content)
+        return index
+
+    def store_handle(self, index, content):
+        self.handles[index] = weakref.ref(content)
+
+    def fetch_handle(self, index):
+        if 0 <= index < len(self.handles):
+            return self.handles[index]()
+        return None
diff --git a/rpython/rlib/rweakref.py b/rpython/rlib/rweakref.py
--- a/rpython/rlib/rweakref.py
+++ b/rpython/rlib/rweakref.py
@@ -12,6 +12,14 @@
 def has_weakref_support():
     return True      # returns False if --no-translation-rweakref
 
+class Dummy:
+    pass
+dead_ref = weakref.ref(Dummy())
+for i in range(5):
+    if dead_ref() is not None:
+        import gc; gc.collect()
+assert dead_ref() is None      # a known-to-be-dead weakref object
+
 
 class RWeakValueDictionary(object):
     """A dictionary containing weak values."""
diff --git a/rpython/rlib/rwin32.py b/rpython/rlib/rwin32.py
--- a/rpython/rlib/rwin32.py
+++ b/rpython/rlib/rwin32.py
@@ -178,8 +178,13 @@
                     int i;
                     for(i=1; i < 65000; i++) {
                         _dosmaperr(i);
-                        if (errno == EINVAL)
-                            continue;
+                        if (errno == EINVAL) {
+                            /* CPython issue #12802 */
+                            if (i == ERROR_DIRECTORY)
+                                errno = ENOTDIR;
+                            else
+                                continue;
+                        }
                         printf("%d\t%d\n", i, errno);
                     }
                     return 0;
@@ -201,7 +206,7 @@
                 132: 13, 145: 41, 158: 13, 161: 2, 164: 11, 167: 13, 183: 17,
                 188: 8, 189: 8, 190: 8, 191: 8, 192: 8, 193: 8, 194: 8,
                 195: 8, 196: 8, 197: 8, 198: 8, 199: 8, 200: 8, 201: 8,
-                202: 8, 206: 2, 215: 11, 1816: 12,
+                202: 8, 206: 2, 215: 11, 267: 20, 1816: 12,
                 }
         else:
             output = os.popen(str(exename))
diff --git a/rpython/rlib/test/test_rbigint.py b/rpython/rlib/test/test_rbigint.py
--- a/rpython/rlib/test/test_rbigint.py
+++ b/rpython/rlib/test/test_rbigint.py
@@ -214,8 +214,13 @@
         from rpython.rlib.rstring import ParseStringError
         assert rbigint.fromstr('123L').tolong() == 123
         assert rbigint.fromstr('123L  ').tolong() == 123
+        py.test.raises(ParseStringError, rbigint.fromstr, '123L  ',
+                       ignore_l_suffix=True)
         py.test.raises(ParseStringError, rbigint.fromstr, 'L')
         py.test.raises(ParseStringError, rbigint.fromstr, 'L  ')
+        e = py.test.raises(ParseStringError, rbigint.fromstr, 'L  ',
+                           fname='int')
+        assert 'int()' in e.value.msg
         assert rbigint.fromstr('123L', 4).tolong() == 27
         assert rbigint.fromstr('123L', 30).tolong() == 27000 + 1800 + 90 + 21
         assert rbigint.fromstr('123L', 22).tolong() == 10648 + 968 + 66 + 21
diff --git a/rpython/rlib/test/test_rweaklist.py b/rpython/rlib/test/test_rweaklist.py
new file mode 100644
--- /dev/null
+++ b/rpython/rlib/test/test_rweaklist.py
@@ -0,0 +1,57 @@
+import gc
+from rpython.rlib.rweaklist import RWeakListMixin
+
+
+class A(object):
+    pass
+
+
+def test_simple():
+    a1 = A(); a2 = A()
+    wlist = RWeakListMixin(); wlist.initialize()
+    i = wlist.add_handle(a1)
+    assert i == 0
+    i = wlist.reserve_next_handle_index()
+    assert i == 1
+    wlist.store_handle(i, a2)
+    assert wlist.fetch_handle(0) is a1
+    assert wlist.fetch_handle(1) is a2
+    #
+    del a2
+    for i in range(5):
+        gc.collect()
+        if wlist.fetch_handle(1) is None:
+            break
+    else:
+        raise AssertionError("handle(1) did not disappear")
+    assert wlist.fetch_handle(0) is a1
+
+def test_reuse():
+    alist = [A() for i in range(200)]
+    wlist = RWeakListMixin(); wlist.initialize()
+    for i in range(200):
+        j = wlist.reserve_next_handle_index()
+        assert j == i
+        wlist.store_handle(i, alist[i])
+    #
+    del alist[1::2]
+    del alist[1::2]
+    del alist[1::2]
+    del alist[1::2]
+    del alist[1::2]
+    for i in range(5):
+        gc.collect()
+    #
+    for i in range(200):
+        a = wlist.fetch_handle(i)
+        if i % 32 == 0:
+            assert a is alist[i // 32]
+        else:
+            assert a is None
+    #
+    maximum = -1
+    for i in range(200):
+        j = wlist.reserve_next_handle_index()
+        maximum = max(maximum, j)
+        wlist.store_handle(j, A())
+    assert maximum <= 240


More information about the pypy-commit mailing list