Author: Maciej Fijalkowski <fij...@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit