[pypy-commit] pypy py3k: hg merge default

antocuni noreply at buildbot.pypy.org
Tue May 8 21:07:43 CEST 2012


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: py3k
Changeset: r54972:cf14a5197515
Date: 2012-05-08 14:46 +0200
http://bitbucket.org/pypy/pypy/changeset/cf14a5197515/

Log:	hg merge default

diff --git a/pypy/doc/cppyy.rst b/pypy/doc/cppyy.rst
--- a/pypy/doc/cppyy.rst
+++ b/pypy/doc/cppyy.rst
@@ -51,8 +51,15 @@
 `Download`_ a binary or install from `source`_.
 Some Linux and Mac systems may have ROOT provided in the list of scientific
 software of their packager.
-A current, standalone version of Reflex should be provided at some point,
-once the dependencies and general packaging have been thought out.
+If, however, you prefer a standalone version of Reflex, the best is to get
+this `recent snapshot`_, and install like so::
+
+    $ tar jxf reflex-2012-05-02.tar.bz2
+    $ cd reflex-2012-05-02
+    $ build/autogen
+    $ ./configure <usual set of options such as --prefix>
+    $ make && make install
+
 Also, make sure you have a version of `gccxml`_ installed, which is most
 easily provided by the packager of your system.
 If you read up on gccxml, you'll probably notice that it is no longer being
@@ -61,12 +68,13 @@
 
 .. _`Download`: http://root.cern.ch/drupal/content/downloading-root
 .. _`source`: http://root.cern.ch/drupal/content/installing-root-source
+.. _`recent snapshot`: http://cern.ch/wlav/reflex-2012-05-02.tar.bz2
 .. _`gccxml`: http://www.gccxml.org
 
 Next, get the `PyPy sources`_, select the reflex-support branch, and build
 pypy-c.
 For the build to succeed, the ``$ROOTSYS`` environment variable must point to
-the location of your ROOT installation::
+the location of your ROOT (or standalone Reflex) installation::
 
     $ hg clone https://bitbucket.org/pypy/pypy
     $ cd pypy
diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst
--- a/pypy/doc/cpython_differences.rst
+++ b/pypy/doc/cpython_differences.rst
@@ -138,12 +138,8 @@
 useful: they will appear to stay alive for a bit longer in PyPy, and
 suddenly they will really be dead, raising a ``ReferenceError`` on the
 next access.  Any code that uses weak proxies must carefully catch such
-``ReferenceError`` at any place that uses them.
-
-As a side effect, the ``finally`` clause inside a generator will be executed
-only when the generator object is garbage collected (see `issue 736`__).
-
-.. __: http://bugs.pypy.org/issue736
+``ReferenceError`` at any place that uses them.  (Or, better yet, don't use
+``weakref.proxy()`` at all; use ``weakref.ref()``.)
 
 There are a few extra implications for the difference in the GC.  Most
 notably, if an object has a ``__del__``, the ``__del__`` is never called more
@@ -162,7 +158,10 @@
 example, a generator left pending in the middle is --- again ---
 garbage-collected later in PyPy than in CPython.  You can see the
 difference if the ``yield`` keyword it is suspended at is itself
-enclosed in a ``try:`` or a ``with:`` block.
+enclosed in a ``try:`` or a ``with:`` block.  This shows up for example
+as `issue 736`__.
+
+.. __: http://bugs.pypy.org/issue736
 
 Using the default GC called ``minimark``, the built-in function ``id()``
 works like it does in CPython.  With other GCs it returns numbers that
@@ -186,7 +185,8 @@
 Even more obscure: the same is true, for old-style classes, if you attach
 the ``__del__`` to an instance (even in CPython this does not work with
 new-style classes).  You get a RuntimeWarning in PyPy.  To fix these cases
-just make sure there is a ``__del__`` method in the class to start with.
+just make sure there is a ``__del__`` method in the class to start with
+(even containing only ``pass``; replacing or overriding it later works fine).
 
 
 Subclasses of built-in types
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1633,8 +1633,6 @@
     'UnicodeTranslateError',
     'ValueError',
     'ZeroDivisionError',
-    'UnicodeEncodeError',
-    'UnicodeDecodeError',
     ]
     
 if sys.platform.startswith("win"):
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -1272,10 +1272,6 @@
                                w(self.valuestackdepth)])
 
     def handle(self, frame, unroller):
-        next_instr = self.really_handle(frame, unroller)   # JIT hack
-        return r_uint(next_instr)
-
-    def really_handle(self, frame, unroller):
         """ Purely abstract method
         """
         raise NotImplementedError
@@ -1287,17 +1283,17 @@
     _opname = 'SETUP_LOOP'
     handling_mask = SBreakLoop.kind | SContinueLoop.kind
 
-    def really_handle(self, frame, unroller):
+    def handle(self, frame, unroller):
         if isinstance(unroller, SContinueLoop):
             # re-push the loop block without cleaning up the value stack,
             # and jump to the beginning of the loop, stored in the
             # exception's argument
             frame.append_block(self)
-            return unroller.jump_to
+            return r_uint(unroller.jump_to)
         else:
             # jump to the end of the loop
             self.cleanupstack(frame)
-            return self.handlerposition
+            return r_uint(self.handlerposition)
 
 
 class ExceptBlock(FrameBlock):
@@ -1307,7 +1303,7 @@
     _opname = 'SETUP_EXCEPT'
     handling_mask = SApplicationException.kind
 
-    def really_handle(self, frame, unroller):
+    def handle(self, frame, unroller):
         # push the exception to the value stack for inspection by the
         # exception handler (the code after the except:)
         self.cleanupstack(frame)
@@ -1327,7 +1323,7 @@
         frame.pushvalue(operationerr.get_w_value(frame.space))
         frame.pushvalue(operationerr.w_type)
         frame.last_exception = operationerr
-        return self.handlerposition   # jump to the handler
+        return r_uint(self.handlerposition)   # jump to the handler
 
 
 class FinallyBlock(FrameBlock):
@@ -1349,7 +1345,7 @@
         frame.pushvalue(frame.space.w_None)
         frame.pushvalue(frame.space.w_None)
 
-    def really_handle(self, frame, unroller):
+    def handle(self, frame, unroller):
         # any abnormal reason for unrolling a finally: triggers the end of
         # the block unrolling and the entering the finally: handler.
         # see comments in cleanup().
@@ -1364,9 +1360,8 @@
         frame.pushvalue(frame.space.w_None)
         if operationerr and self.restore_last_exception:
             frame.last_exception = operationerr
-        return self.handlerposition   # jump to the handler
+        return r_uint(self.handlerposition)   # jump to the handler
 
-        
 
 
 class WithBlock(FinallyBlock):
@@ -1374,11 +1369,11 @@
     _immutable_ = True
     restore_last_exception = False
 
-    def really_handle(self, frame, unroller):
+    def handle(self, frame, unroller):
         if (frame.space.full_exceptions and
             isinstance(unroller, SApplicationException)):
             unroller.operr.normalize_exception(frame.space)
-        return FinallyBlock.really_handle(self, frame, unroller)
+        return FinallyBlock.handle(self, frame, unroller)
 
 block_classes = {'SETUP_LOOP': LoopBlock,
                  'SETUP_EXCEPT': ExceptBlock,
diff --git a/pypy/jit/metainterp/optimizeopt/earlyforce.py b/pypy/jit/metainterp/optimizeopt/earlyforce.py
--- a/pypy/jit/metainterp/optimizeopt/earlyforce.py
+++ b/pypy/jit/metainterp/optimizeopt/earlyforce.py
@@ -7,7 +7,8 @@
         opnum = op.getopnum()
         if (opnum != rop.SETFIELD_GC and 
             opnum != rop.SETARRAYITEM_GC and
-            opnum != rop.QUASIIMMUT_FIELD):
+            opnum != rop.QUASIIMMUT_FIELD and
+            opnum != rop.SAME_AS):
                
             for arg in op.getarglist():
                 if arg in self.optimizer.values:
diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py
--- a/pypy/jit/metainterp/optimizeopt/optimizer.py
+++ b/pypy/jit/metainterp/optimizeopt/optimizer.py
@@ -652,8 +652,15 @@
             arrayvalue.make_len_gt(MODE_UNICODE, op.getdescr(), indexvalue.box.getint())
         self.optimize_default(op)
 
+    # These are typically removed already by OptRewrite, but it can be
+    # dissabled and unrolling emits some SAME_AS ops to setup the
+    # optimizier state. These needs to always be optimized out.
+    def optimize_SAME_AS(self, op):
+        self.make_equal_to(op.result, self.getvalue(op.getarg(0)))
 
-
+    def optimize_MARK_OPAQUE_PTR(self, op):
+        value = self.getvalue(op.getarg(0))
+        self.optimizer.opaque_pointers[value] = True
 
 dispatch_opt = make_dispatcher_method(Optimizer, 'optimize_',
         default=Optimizer.optimize_default)
diff --git a/pypy/jit/metainterp/optimizeopt/unroll.py b/pypy/jit/metainterp/optimizeopt/unroll.py
--- a/pypy/jit/metainterp/optimizeopt/unroll.py
+++ b/pypy/jit/metainterp/optimizeopt/unroll.py
@@ -335,9 +335,13 @@
                                       
             args[short_inputargs[i]] = jmp_to_short_args[i]
         self.short_inliner = Inliner(short_inputargs, jmp_to_short_args)
-        for op in self.short[1:]:
+        i = 1
+        while i < len(self.short):
+            # Note that self.short might be extended during this loop
+            op = self.short[i]
             newop = self.short_inliner.inline_op(op)
             self.optimizer.send_extra_operation(newop)
+            i += 1
 
         # Import boxes produced in the preamble but used in the loop
         newoperations = self.optimizer.get_newoperations()
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -107,7 +107,7 @@
         obj.double_member = 9.25;      assert obj.double_member == 9.25
         obj.longlong_member = -2**59;  assert obj.longlong_member == -2**59
         obj.ulonglong_member = 2**63;  assert obj.ulonglong_member == 2**63
-        obj.ssizet_member = 2**31;     assert obj.ssizet_member == 2**31
+        obj.ssizet_member = sys.maxint;assert obj.ssizet_member == sys.maxint
         #
 
     def test_staticmethod(self):
diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py
--- a/pypy/module/micronumpy/compile.py
+++ b/pypy/module/micronumpy/compile.py
@@ -6,6 +6,7 @@
 import re
 
 from pypy.interpreter.baseobjspace import InternalSpaceCache, W_Root
+from pypy.interpreter.error import OperationError
 from pypy.module.micronumpy import interp_boxes
 from pypy.module.micronumpy.interp_dtype import get_dtype_cache
 from pypy.module.micronumpy.interp_numarray import (Scalar, BaseArray,
@@ -39,11 +40,11 @@
 THREE_ARG_FUNCTIONS = ['where']
 
 class FakeSpace(object):
-    w_ValueError = None
-    w_TypeError = None
-    w_IndexError = None
-    w_OverflowError = None
-    w_NotImplementedError = None
+    w_ValueError = "ValueError"
+    w_TypeError = "TypeError"
+    w_IndexError = "IndexError"
+    w_OverflowError = "OverflowError"
+    w_NotImplementedError = "NotImplementedError"
     w_None = None
 
     w_bool = "bool"
@@ -126,8 +127,13 @@
             return w_obj.intval
         elif isinstance(w_obj, FloatObject):
             return int(w_obj.floatval)
+        elif isinstance(w_obj, SliceObject):
+            raise OperationError(self.w_TypeError, self.wrap("slice."))
         raise NotImplementedError
 
+    def index(self, w_obj):
+        return self.wrap(self.int_w(w_obj))
+
     def str_w(self, w_obj):
         if isinstance(w_obj, StringObject):
             return w_obj.v
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -350,12 +350,31 @@
         if shape_len == 1:
             if space.isinstance_w(w_idx, space.w_int):
                 return True
+
+            try:
+                value = space.int_w(space.index(w_idx))
+                return True
+            except OperationError:
+                pass
+
+            try:
+                value = space.int_w(w_idx)
+                return True
+            except OperationError:
+                pass
+
             if space.isinstance_w(w_idx, space.w_slice):
                 return False
         elif (space.isinstance_w(w_idx, space.w_slice) or
               space.isinstance_w(w_idx, space.w_int)):
             return False
-        lgt = space.len_w(w_idx)
+
+        try:
+            lgt = space.len_w(w_idx)
+        except OperationError:
+            raise OperationError(space.w_IndexError,
+                                 space.wrap("index must be either an int or a sequence."))
+
         if lgt > shape_len:
             raise OperationError(space.w_IndexError,
                                  space.wrap("invalid index"))
@@ -1030,8 +1049,21 @@
 
     @jit.unroll_safe
     def _index_of_single_item(self, space, w_idx):
-        if space.isinstance_w(w_idx, space.w_int):
-            idx = space.int_w(w_idx)
+        is_valid = False
+        try:
+            idx = space.int_w(space.index(w_idx))
+            is_valid = True
+        except OperationError:
+            pass
+
+        if not is_valid:
+            try:
+                idx = space.int_w(w_idx)
+                is_valid = True
+            except OperationError:
+                pass
+
+        if is_valid:
             if idx < 0:
                 idx = self.shape[0] + idx
             if idx < 0 or idx >= self.shape[0]:
diff --git a/pypy/module/micronumpy/test/test_base.py b/pypy/module/micronumpy/test/test_base.py
--- a/pypy/module/micronumpy/test/test_base.py
+++ b/pypy/module/micronumpy/test/test_base.py
@@ -11,6 +11,7 @@
 import sys
 
 class BaseNumpyAppTest(object):
+    @classmethod
     def setup_class(cls):
         py.test.py3k_skip("micronumpy not supported on py3k")
         if option.runappdirect:
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -8,7 +8,6 @@
 from pypy.module.micronumpy.interp_numarray import W_NDimArray, shape_agreement
 from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
 
-
 class MockDtype(object):
     class itemtype(object):
         @staticmethod
@@ -195,6 +194,36 @@
         assert _to_coords(13, 'F') == [1, 0, 2]
 
 class AppTestNumArray(BaseNumpyAppTest):
+    def w_CustomIndexObject(self, index):
+        class CustomIndexObject(object):
+            def __init__(self, index):
+                self.index = index
+            def __index__(self):
+                return self.index
+
+        return CustomIndexObject(index)
+
+    def w_CustomIndexIntObject(self, index, value):
+        class CustomIndexIntObject(object):
+            def __init__(self, index, value):
+                self.index = index
+                self.value = value
+            def __index__(self):
+                return self.index
+            def __int__(self):
+                return self.value
+
+        return CustomIndexIntObject(index, value)
+
+    def w_CustomIntObject(self, value):
+        class CustomIntObject(object):
+            def __init__(self, value):
+                self.value = value
+            def __index__(self):
+                return self.value
+
+        return CustomIntObject(value)
+
     def test_ndarray(self):
         from _numpypy import ndarray, array, dtype
 
@@ -329,6 +358,28 @@
         assert a[1, 3] == 8
         assert a.T[1, 2] == 11
 
+    def test_getitem_obj_index(self):
+        from _numpypy import arange
+
+        a = arange(10)
+
+        assert a[self.CustomIndexObject(1)] == 1
+
+    def test_getitem_obj_prefer_index_to_int(self):
+        from _numpypy import arange
+
+        a = arange(10)
+
+
+        assert a[self.CustomIndexIntObject(0, 1)] == 0
+
+    def test_getitem_obj_int(self):
+        from _numpypy import arange
+
+        a = arange(10)
+
+        assert a[self.CustomIntObject(1)] == 1
+
     def test_setitem(self):
         from _numpypy import array
         a = array(range(5))
@@ -348,6 +399,48 @@
         for i in xrange(5):
             assert a[i] == i
 
+    def test_setitem_obj_index(self):
+        from _numpypy import arange
+
+        a = arange(10)
+
+        a[self.CustomIndexObject(1)] = 100
+        assert a[1] == 100
+
+    def test_setitem_obj_prefer_index_to_int(self):
+        from _numpypy import arange
+
+        a = arange(10)
+
+        a[self.CustomIndexIntObject(0, 1)] = 100
+        assert a[0] == 100
+
+    def test_setitem_obj_int(self):
+        from _numpypy import arange
+
+        a = arange(10)
+
+        a[self.CustomIntObject(1)] = 100
+
+        assert a[1] == 100
+
+    def test_access_swallow_exception(self):
+        class ErrorIndex(object):
+            def __index__(self):
+                return 1 / 0
+
+        class ErrorInt(object):
+            def __int__(self):
+                return 1 / 0
+
+        # numpy will swallow errors in __int__ and __index__ and
+        # just raise IndexError.
+
+        from _numpypy import arange
+        a = arange(10)
+        raises(IndexError, "a[ErrorIndex()] == 0")
+        raises(IndexError, "a[ErrorInt()] == 0")
+
     def test_setslice_array(self):
         from _numpypy import array
         a = array(range(5))
diff --git a/pypy/module/thread/__init__.py b/pypy/module/thread/__init__.py
--- a/pypy/module/thread/__init__.py
+++ b/pypy/module/thread/__init__.py
@@ -21,7 +21,7 @@
         'allocate':               'os_lock.allocate_lock',  # obsolete synonym
         'LockType':               'os_lock.Lock',
         'RLock':                  'os_lock.W_RLock',
-        '_local':                 'os_local.Local',
+        #'_local':                 'os_local.Local',
         'TIMEOUT_MAX':            'space.wrap(float(os_lock.TIMEOUT_MAX) / 1000000.0)',
         'error':                  'space.fromcache(error.Cache).w_error',
     }
@@ -39,3 +39,7 @@
         from pypy.module.thread.os_thread import reinit_threads
         add_fork_hook('child', reinit_threads)
 
+    def setup_after_space_initialization(self):
+        """NOT_RPYTHON"""
+        if self.space.config.translation.rweakref:
+            self.extra_interpdef('_local', 'os_local.Local')
diff --git a/pypy/module/thread/gil.py b/pypy/module/thread/gil.py
--- a/pypy/module/thread/gil.py
+++ b/pypy/module/thread/gil.py
@@ -5,7 +5,7 @@
 # This module adds a global lock to an object space.
 # If multiple threads try to execute simultaneously in this space,
 # all but one will be blocked.  The other threads get a chance to run
-# from time to time, using the hook yield_thread().
+# from time to time, using the periodic action GILReleaseAction.
 
 from pypy.module.thread import ll_thread as thread
 from pypy.module.thread.error import wrap_thread_error
@@ -30,7 +30,6 @@
             if not thread.gil_allocate():
                 raise wrap_thread_error(space, "can't allocate GIL")
             self.gil_ready = True
-            self.enter_thread(space)   # setup the main thread
             result = True
         else:
             result = False      # already set up
@@ -51,8 +50,6 @@
             self.gil_ready = False
             self.setup_threads(space)
 
-    def yield_thread(self):
-        do_yield_thread()
 
 class GILReleaseAction(PeriodicAsyncAction):
     """An action called every sys.checkinterval bytecodes.  It releases
diff --git a/pypy/module/thread/os_local.py b/pypy/module/thread/os_local.py
--- a/pypy/module/thread/os_local.py
+++ b/pypy/module/thread/os_local.py
@@ -1,5 +1,6 @@
-from pypy.module.thread import ll_thread as thread
-from pypy.interpreter.baseobjspace import Wrappable
+from pypy.rlib.rweakref import RWeakKeyDictionary
+from pypy.interpreter.baseobjspace import Wrappable, W_Root
+from pypy.interpreter.executioncontext import ExecutionContext
 from pypy.interpreter.typedef import (TypeDef, interp2app, GetSetProperty,
     descr_get_dict)
 
@@ -9,16 +10,23 @@
 
     def __init__(self, space, initargs):
         self.initargs = initargs
-        ident = thread.get_ident()
-        self.dicts = {ident: space.newdict(instance=True)}
+        self.dicts = RWeakKeyDictionary(ExecutionContext, W_Root)
+        # The app-level __init__() will be called by the general
+        # instance-creation logic.  It causes getdict() to be
+        # immediately called.  If we don't prepare and set a w_dict
+        # for the current thread, then this would in cause getdict()
+        # to call __init__() a second time.
+        ec = space.getexecutioncontext()
+        w_dict = space.newdict(instance=True)
+        self.dicts.set(ec, w_dict)
 
     def getdict(self, space):
-        ident = thread.get_ident()
-        try:
-            w_dict = self.dicts[ident]
-        except KeyError:
+        ec = space.getexecutioncontext()
+        w_dict = self.dicts.get(ec)
+        if w_dict is None:
             # create a new dict for this thread
-            w_dict = self.dicts[ident] = space.newdict(instance=True)
+            w_dict = space.newdict(instance=True)
+            self.dicts.set(ec, w_dict)
             # call __init__
             try:
                 w_self = space.wrap(self)
@@ -27,10 +35,9 @@
                 space.call_obj_args(w_init, w_self, self.initargs)
             except:
                 # failed, forget w_dict and propagate the exception
-                del self.dicts[ident]
+                self.dicts.set(ec, None)
                 raise
             # ready
-            space.threadlocals.atthreadexit(space, finish_thread, self)
         return w_dict
 
     def descr_local__new__(space, w_subtype, __args__):
@@ -48,8 +55,3 @@
                         __init__ = interp2app(Local.descr_local__init__),
                         __dict__ = GetSetProperty(descr_get_dict, cls=Local),
                         )
-
-def finish_thread(w_obj):
-    assert isinstance(w_obj, Local)
-    ident = thread.get_ident()
-    del w_obj.dicts[ident]
diff --git a/pypy/module/thread/os_thread.py b/pypy/module/thread/os_thread.py
--- a/pypy/module/thread/os_thread.py
+++ b/pypy/module/thread/os_thread.py
@@ -90,7 +90,6 @@
         bootstrapper.nbthreads += 1
         bootstrapper.release()
         # run!
-        space.threadlocals.enter_thread(space)
         try:
             bootstrapper.run(space, w_callable, args)
         finally:
diff --git a/pypy/module/thread/test/test_gil.py b/pypy/module/thread/test/test_gil.py
--- a/pypy/module/thread/test/test_gil.py
+++ b/pypy/module/thread/test/test_gil.py
@@ -55,7 +55,7 @@
                 assert state.datalen3 == len(state.data)
                 assert state.datalen4 == len(state.data)
                 debug_print(main, i, state.datalen4)
-                state.threadlocals.yield_thread()
+                gil.do_yield_thread()
                 assert i == j
                 j += 1
         def bootstrap():
diff --git a/pypy/module/thread/test/test_local.py b/pypy/module/thread/test/test_local.py
--- a/pypy/module/thread/test/test_local.py
+++ b/pypy/module/thread/test/test_local.py
@@ -48,7 +48,7 @@
 
     def test_local_init(self):
         import _thread
-        tags = [1, 2, 3, 4, 5, 54321]
+        tags = ['???', 1, 2, 3, 4, 5, 54321]
         seen = []
 
         raises(TypeError, _thread._local, a=1)
@@ -61,6 +61,7 @@
 
         x = X(42)
         assert x.tag == 54321
+        assert x.tag == 54321
         def f():
             seen.append(x.tag)
         for i in range(5):
@@ -69,7 +70,7 @@
         seen1 = seen[:]
         seen1.sort()
         assert seen1 == [1, 2, 3, 4, 5]
-        assert tags == []
+        assert tags == ['???']
 
     def test_local_setdict(self):
         import _thread
@@ -87,3 +88,23 @@
             _thread.start_new_thread(f, (i,))
         self.waitfor(lambda: len(done) == 5, delay=2)
         assert len(done) == 5
+
+    def test_local_is_not_immortal(self):
+        import thread, gc, time
+        class Local(thread._local):
+            def __del__(self):
+                done.append('del')
+        done = []
+        def f():
+            assert not hasattr(l, 'foo')
+            l.bar = 42
+            done.append('ok')
+            self.waitfor(lambda: len(done) == 3, delay=8)
+        l = Local()
+        l.foo = 42
+        thread.start_new_thread(f, ())
+        self.waitfor(lambda: len(done) == 1, delay=2)
+        l = None
+        gc.collect()
+        assert done == ['ok', 'del']
+        done.append('shutdown')
diff --git a/pypy/module/thread/threadlocals.py b/pypy/module/thread/threadlocals.py
--- a/pypy/module/thread/threadlocals.py
+++ b/pypy/module/thread/threadlocals.py
@@ -1,5 +1,6 @@
 from pypy.module.thread import ll_thread as thread
 
+
 class OSThreadLocals:
     """Thread-local storage for OS-level threads.
     For memory management, this version depends on explicit notification when
@@ -51,21 +52,6 @@
     def getallvalues(self):
         return self._valuedict
 
-    def enter_thread(self, space):
-        "Notification that the current thread is just starting."
-        ec = space.getexecutioncontext()
-        ec.thread_exit_funcs = []
-
     def leave_thread(self, space):
         "Notification that the current thread is about to stop."
-        try:
-            ec = space.getexecutioncontext()
-            while ec.thread_exit_funcs:
-                exit_func, w_obj = ec.thread_exit_funcs.pop()
-                exit_func(w_obj)
-        finally:
-            self.setvalue(None)
-
-    def atthreadexit(self, space, exit_func, w_obj):
-        ec = space.getexecutioncontext()
-        ec.thread_exit_funcs.append((exit_func, w_obj))
+        self.setvalue(None)
diff --git a/pypy/rlib/runicode.py b/pypy/rlib/runicode.py
--- a/pypy/rlib/runicode.py
+++ b/pypy/rlib/runicode.py
@@ -510,8 +510,9 @@
     pos = 0
     if byteorder == 'native':
         if size >= 4:
-            bom = ((ord(s[iorder[3]]) << 24) | (ord(s[iorder[2]]) << 16) |
-                   (ord(s[iorder[1]]) << 8)  | ord(s[iorder[0]]))
+            bom = intmask(
+                (ord(s[iorder[3]]) << 24) | (ord(s[iorder[2]]) << 16) |
+                (ord(s[iorder[1]]) << 8)  | ord(s[iorder[0]]))
             if BYTEORDER == 'little':
                 if bom == BOM32_DIRECT:
                     pos += 4


More information about the pypy-commit mailing list