[pypy-commit] pypy arm-backend-2: merge default

bivab noreply at buildbot.pypy.org
Tue Jun 12 15:54:54 CEST 2012


Author: David Schneider <david.schneider at picle.org>
Branch: arm-backend-2
Changeset: r55613:23d8bc08b002
Date: 2012-04-15 16:49 +0000
http://bitbucket.org/pypy/pypy/changeset/23d8bc08b002/

Log:	merge default

diff --git a/_pytest/assertion/oldinterpret.py b/_pytest/assertion/oldinterpret.py
--- a/_pytest/assertion/oldinterpret.py
+++ b/_pytest/assertion/oldinterpret.py
@@ -1,8 +1,7 @@
 import py
 import sys, inspect
 from compiler import parse, ast, pycodegen
-from _pytest.assertion.util import format_explanation
-from _pytest.assertion.reinterpret import BuiltinAssertionError
+from _pytest.assertion.util import format_explanation, BuiltinAssertionError
 
 passthroughex = py.builtin._sysex
 
diff --git a/_pytest/assertion/reinterpret.py b/_pytest/assertion/reinterpret.py
--- a/_pytest/assertion/reinterpret.py
+++ b/_pytest/assertion/reinterpret.py
@@ -1,7 +1,6 @@
 import sys
 import py
-
-BuiltinAssertionError = py.builtin.builtins.AssertionError
+from _pytest.assertion.util import BuiltinAssertionError
 
 class AssertionError(BuiltinAssertionError):
     def __init__(self, *args):
diff --git a/_pytest/assertion/util.py b/_pytest/assertion/util.py
--- a/_pytest/assertion/util.py
+++ b/_pytest/assertion/util.py
@@ -2,6 +2,7 @@
 
 import py
 
+BuiltinAssertionError = py.builtin.builtins.AssertionError
 
 # The _reprcompare attribute on the util module is used by the new assertion
 # interpretation code and assertion rewriter to detect this plugin was
diff --git a/lib_pypy/numpypy/core/fromnumeric.py b/lib_pypy/numpypy/core/fromnumeric.py
--- a/lib_pypy/numpypy/core/fromnumeric.py
+++ b/lib_pypy/numpypy/core/fromnumeric.py
@@ -411,7 +411,8 @@
             [3, 7]]])
 
     """
-    raise NotImplementedError('Waiting on interp level method')
+    swapaxes = a.swapaxes
+    return swapaxes(axis1, axis2)
 
 
 def transpose(a, axes=None):
diff --git a/py/bin/_findpy.py b/py/bin/_findpy.py
new file mode 100644
--- /dev/null
+++ b/py/bin/_findpy.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+#
+# find and import a version of 'py'
+#
+import sys
+import os
+from os.path import dirname as opd, exists, join, basename, abspath
+
+def searchpy(current):
+    while 1:
+        last = current
+        initpy = join(current, '__init__.py')
+        if not exists(initpy):
+            pydir = join(current, 'py')
+            # recognize py-package and ensure it is importable
+            if exists(pydir) and exists(join(pydir, '__init__.py')):
+                #for p in sys.path:
+                #    if p == current:
+                #        return True
+                if current != sys.path[0]:  # if we are already first, then ok
+                    sys.stderr.write("inserting into sys.path: %s\n" % current)
+                    sys.path.insert(0, current)
+                return True
+        current = opd(current)
+        if last == current:
+            return False
+
+if not searchpy(abspath(os.curdir)):
+    if not searchpy(opd(abspath(sys.argv[0]))):
+        if not searchpy(opd(__file__)):
+            pass # let's hope it is just on sys.path
+
+import py
+import pytest
+
+if __name__ == '__main__':
+    print ("py lib is at %s" % py.__file__)
diff --git a/py/bin/py.test b/py/bin/py.test
new file mode 100755
--- /dev/null
+++ b/py/bin/py.test
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+from _findpy import pytest
+raise SystemExit(pytest.main())
diff --git a/pypy/annotation/test/test_annrpython.py b/pypy/annotation/test/test_annrpython.py
--- a/pypy/annotation/test/test_annrpython.py
+++ b/pypy/annotation/test/test_annrpython.py
@@ -3746,9 +3746,9 @@
             return g(i)
         def main(i):
             if i == 2:
-                return f(i)
+                return f(2)
             elif i == 3:
-                return f(i)
+                return f(3)
             else:
                 raise NotImplementedError
 
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
@@ -158,6 +158,12 @@
 .. __: http://morepypy.blogspot.com/2008/02/python-finalizers-semantics-part-1.html
 .. __: http://morepypy.blogspot.com/2008/02/python-finalizers-semantics-part-2.html
 
+Note that this difference might show up indirectly in some cases.  For
+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.
+
 Using the default GC called ``minimark``, the built-in function ``id()``
 works like it does in CPython.  With other GCs it returns numbers that
 are not real addresses (because an object can move around several times)
diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -169,9 +169,11 @@
     def _combine_starstarargs_wrapped(self, w_starstararg):
         # unpack the ** arguments
         space = self.space
+        keywords, values_w = space.view_as_kwargs(w_starstararg)
+        if keywords is not None: # this path also taken for empty dicts
+            self._add_keywordargs_no_unwrapping(keywords, values_w)
+            return not jit.isconstant(len(self.keywords))
         if space.isinstance_w(w_starstararg, space.w_dict):
-            if not space.is_true(w_starstararg):
-                return False # don't call unpackiterable - it's jit-opaque
             keys_w = space.unpackiterable(w_starstararg)
         else:
             try:
@@ -186,11 +188,8 @@
                                    "a mapping, not %s" % (typename,)))
                 raise
             keys_w = space.unpackiterable(w_keys)
-        if keys_w:
-            self._do_combine_starstarargs_wrapped(keys_w, w_starstararg)
-            return True
-        else:
-            return False    # empty dict; don't disable the JIT
+        self._do_combine_starstarargs_wrapped(keys_w, w_starstararg)
+        return True
 
     def _do_combine_starstarargs_wrapped(self, keys_w, w_starstararg):
         space = self.space
@@ -227,6 +226,26 @@
             self.keywords_w = self.keywords_w + keywords_w
         self.keyword_names_w = keys_w
 
+    @jit.look_inside_iff(lambda self, keywords, keywords_w:
+            jit.isconstant(len(keywords) and
+            jit.isconstant(self.keywords)))
+    def _add_keywordargs_no_unwrapping(self, keywords, keywords_w):
+        if self.keywords is None:
+            self.keywords = keywords[:] # copy to make non-resizable
+            self.keywords_w = keywords_w[:]
+        else:
+            # looks quadratic, but the JIT should remove all of it nicely.
+            # Also, all the lists should be small
+            for key in keywords:
+                for otherkey in self.keywords:
+                    if otherkey == key:
+                        raise operationerrfmt(self.space.w_TypeError,
+                                              "got multiple values "
+                                              "for keyword argument "
+                                              "'%s'", key)
+            self.keywords = self.keywords + keywords
+            self.keywords_w = self.keywords_w + keywords_w
+
     def fixedunpack(self, argcount):
         """The simplest argument parsing: get the 'argcount' arguments,
         or raise a real ValueError if the length is wrong."""
@@ -385,7 +404,7 @@
 
         # collect extra keyword arguments into the **kwarg
         if has_kwarg:
-            w_kwds = self.space.newdict()
+            w_kwds = self.space.newdict(kwargs=True)
             if num_remainingkwds:
                 #
                 limit = len(keywords)
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -914,6 +914,12 @@
         """
         return None
 
+    def view_as_kwargs(self, w_dict):
+        """ if w_dict is a kwargs-dict, return two lists, one of unwrapped
+        strings and one of wrapped values. otherwise return (None, None)
+        """
+        return (None, None)
+
     def newlist_str(self, list_s):
         return self.newlist([self.wrap(s) for s in list_s])
 
diff --git a/pypy/interpreter/test/test_argument.py b/pypy/interpreter/test/test_argument.py
--- a/pypy/interpreter/test/test_argument.py
+++ b/pypy/interpreter/test/test_argument.py
@@ -75,7 +75,10 @@
     def unpackiterable(self, it):
         return list(it)
 
-    def newdict(self):
+    def view_as_kwargs(self, x):
+        return None, None
+
+    def newdict(self, kwargs=False):
         return {}
 
     def newlist(self, l=[]):
@@ -488,6 +491,57 @@
         assert len(l) == 1
         assert l[0] == space.wrap(5)
 
+    def test_starstarargs_special(self):
+        class kwargs(object):
+            def __init__(self, k, v):
+                self.k = k
+                self.v = v
+        class MyDummySpace(DummySpace):
+            def view_as_kwargs(self, kw):
+                if isinstance(kw, kwargs):
+                    return kw.k, kw.v
+                return None, None
+        space = MyDummySpace()
+        for i in range(3):
+            kwds = [("c", 3)]
+            kwds_w = dict(kwds[:i])
+            keywords = kwds_w.keys()
+            keywords_w = kwds_w.values()
+            rest = dict(kwds[i:])
+            w_kwds = kwargs(rest.keys(), rest.values())
+            if i == 2:
+                w_kwds = None
+            assert len(keywords) == len(keywords_w)
+            args = Arguments(space, [1, 2], keywords[:], keywords_w[:], w_starstararg=w_kwds)
+            l = [None, None, None]
+            args._match_signature(None, l, Signature(["a", "b", "c"]), defaults_w=[4])
+            assert l == [1, 2, 3]
+            args = Arguments(space, [1, 2], keywords[:], keywords_w[:], w_starstararg=w_kwds)
+            l = [None, None, None, None]
+            args._match_signature(None, l, Signature(["a", "b", "b1", "c"]), defaults_w=[4, 5])
+            assert l == [1, 2, 4, 3]
+            args = Arguments(space, [1, 2], keywords[:], keywords_w[:], w_starstararg=w_kwds)
+            l = [None, None, None, None]
+            args._match_signature(None, l, Signature(["a", "b", "c", "d"]), defaults_w=[4, 5])
+            assert l == [1, 2, 3, 5]
+            args = Arguments(space, [1, 2], keywords[:], keywords_w[:], w_starstararg=w_kwds)
+            l = [None, None, None, None]
+            py.test.raises(ArgErr, args._match_signature, None, l,
+                           Signature(["c", "b", "a", "d"]), defaults_w=[4, 5])
+            args = Arguments(space, [1, 2], keywords[:], keywords_w[:], w_starstararg=w_kwds)
+            l = [None, None, None, None]
+            py.test.raises(ArgErr, args._match_signature, None, l,
+                           Signature(["a", "b", "c1", "d"]), defaults_w=[4, 5])
+            args = Arguments(space, [1, 2], keywords[:], keywords_w[:], w_starstararg=w_kwds)
+            l = [None, None, None]
+            args._match_signature(None, l, Signature(["a", "b"], None, "**"))
+            assert l == [1, 2, {'c': 3}]
+        excinfo = py.test.raises(OperationError, Arguments, space, [], ["a"],
+                                 [1], w_starstararg=kwargs(["a"], [2]))
+        assert excinfo.value.w_type is TypeError
+
+
+
 class TestErrorHandling(object):
     def test_missing_args(self):
         # got_nargs, nkwds, expected_nargs, has_vararg, has_kwarg,
diff --git a/pypy/module/_lsprof/interp_lsprof.py b/pypy/module/_lsprof/interp_lsprof.py
--- a/pypy/module/_lsprof/interp_lsprof.py
+++ b/pypy/module/_lsprof/interp_lsprof.py
@@ -185,6 +185,7 @@
             if subentry is not None:
                 subentry._stop(tt, it)
 
+ at jit.elidable_promote()
 def create_spec(space, w_arg):
     if isinstance(w_arg, Method):
         w_function = w_arg.w_function
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -2253,24 +2253,6 @@
     """Concat two strings giving a new Unicode string."""
     raise NotImplementedError
 
- at cpython_api([PyObject, PyObject, Py_ssize_t], PyObject)
-def PyUnicode_Split(space, s, sep, maxsplit):
-    """Split a string giving a list of Unicode strings.  If sep is NULL, splitting
-    will be done at all whitespace substrings.  Otherwise, splits occur at the given
-    separator.  At most maxsplit splits will be done.  If negative, no limit is
-    set.  Separators are not included in the resulting list.
-
-    This function used an int type for maxsplit. This might require
-    changes in your code for properly supporting 64-bit systems."""
-    raise NotImplementedError
-
- at cpython_api([PyObject, rffi.INT_real], PyObject)
-def PyUnicode_Splitlines(space, s, keepend):
-    """Split a Unicode string at line breaks, returning a list of Unicode strings.
-    CRLF is considered to be one line break.  If keepend is 0, the Line break
-    characters are not included in the resulting strings."""
-    raise NotImplementedError
-
 @cpython_api([PyObject, PyObject, rffi.CCHARP], PyObject)
 def PyUnicode_Translate(space, str, table, errors):
     """Translate a string by applying a character mapping table to it and return the
@@ -2287,29 +2269,6 @@
     use the default error handling."""
     raise NotImplementedError
 
- at cpython_api([PyObject, PyObject, Py_ssize_t, Py_ssize_t, rffi.INT_real], Py_ssize_t, error=-2)
-def PyUnicode_Find(space, str, substr, start, end, direction):
-    """Return the first position of substr in str*[*start:end] using the given
-    direction (direction == 1 means to do a forward search, direction == -1 a
-    backward search).  The return value is the index of the first match; a value of
-    -1 indicates that no match was found, and -2 indicates that an error
-    occurred and an exception has been set.
-
-    This function used an int type for start and end. This
-    might require changes in your code for properly supporting 64-bit
-    systems."""
-    raise NotImplementedError
-
- at cpython_api([PyObject, PyObject, Py_ssize_t, Py_ssize_t], Py_ssize_t, error=-1)
-def PyUnicode_Count(space, str, substr, start, end):
-    """Return the number of non-overlapping occurrences of substr in
-    str[start:end].  Return -1 if an error occurred.
-
-    This function returned an int type and used an int
-    type for start and end. This might require changes in your code for
-    properly supporting 64-bit systems."""
-    raise NotImplementedError
-
 @cpython_api([PyObject, PyObject, rffi.INT_real], PyObject)
 def PyUnicode_RichCompare(space, left, right, op):
     """Rich compare two unicode strings and return one of the following:
diff --git a/pypy/module/cpyext/test/test_unicodeobject.py b/pypy/module/cpyext/test/test_unicodeobject.py
--- a/pypy/module/cpyext/test/test_unicodeobject.py
+++ b/pypy/module/cpyext/test/test_unicodeobject.py
@@ -457,3 +457,31 @@
         assert api.PyUnicode_Tailmatch(w_str, space.wrap("cde"), 1, 5, -1) == 1
         self.raises(space, api, TypeError,
                     api.PyUnicode_Tailmatch, w_str, space.wrap(3), 2, 10, 1)
+
+    def test_count(self, space, api):
+        w_str = space.wrap(u"abcabdab")
+        assert api.PyUnicode_Count(w_str, space.wrap(u"ab"), 0, -1) == 2
+        assert api.PyUnicode_Count(w_str, space.wrap(u"ab"), 0, 2) == 1
+        assert api.PyUnicode_Count(w_str, space.wrap(u"ab"), -5, 30) == 2
+
+    def test_find(self, space, api):
+        w_str = space.wrap(u"abcabcd")
+        assert api.PyUnicode_Find(w_str, space.wrap(u"c"), 0, 7, 1) == 2
+        assert api.PyUnicode_Find(w_str, space.wrap(u"c"), 3, 7, 1) == 5
+        assert api.PyUnicode_Find(w_str, space.wrap(u"c"), 0, 7, -1) == 5
+        assert api.PyUnicode_Find(w_str, space.wrap(u"c"), 3, 7, -1) == 5
+        assert api.PyUnicode_Find(w_str, space.wrap(u"c"), 0, 4, -1) == 2
+        assert api.PyUnicode_Find(w_str, space.wrap(u"z"), 0, 4, -1) == -1
+
+    def test_split(self, space, api):
+        w_str = space.wrap(u"a\nb\nc\nd")
+        assert "[u'a', u'b', u'c', u'd']" == space.unwrap(space.repr(
+                api.PyUnicode_Split(w_str, space.wrap('\n'), -1)))
+        assert r"[u'a', u'b', u'c\nd']" == space.unwrap(space.repr(
+                api.PyUnicode_Split(w_str, space.wrap('\n'), 2)))
+        assert r"[u'a', u'b', u'c d']" == space.unwrap(space.repr(
+                api.PyUnicode_Split(space.wrap(u'a\nb  c d'), None, 2)))
+        assert "[u'a', u'b', u'c', u'd']" == space.unwrap(space.repr(
+                api.PyUnicode_Splitlines(w_str, 0)))
+        assert r"[u'a\n', u'b\n', u'c\n', u'd']" == space.unwrap(space.repr(
+                api.PyUnicode_Splitlines(w_str, 1)))
diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py
--- a/pypy/module/cpyext/unicodeobject.py
+++ b/pypy/module/cpyext/unicodeobject.py
@@ -598,3 +598,46 @@
     else:
         return stringtype.stringendswith(str, substr, start, end)
 
+ at cpython_api([PyObject, PyObject, Py_ssize_t, Py_ssize_t], Py_ssize_t, error=-1)
+def PyUnicode_Count(space, w_str, w_substr, start, end):
+    """Return the number of non-overlapping occurrences of substr in
+    str[start:end].  Return -1 if an error occurred."""
+    w_count = space.call_method(w_str, "count", w_substr,
+                                space.wrap(start), space.wrap(end))
+    return space.int_w(w_count)
+
+ at cpython_api([PyObject, PyObject, Py_ssize_t, Py_ssize_t, rffi.INT_real],
+             Py_ssize_t, error=-2)
+def PyUnicode_Find(space, w_str, w_substr, start, end, direction):
+    """Return the first position of substr in str*[*start:end] using
+    the given direction (direction == 1 means to do a forward search,
+    direction == -1 a backward search).  The return value is the index
+    of the first match; a value of -1 indicates that no match was
+    found, and -2 indicates that an error occurred and an exception
+    has been set."""
+    if rffi.cast(lltype.Signed, direction) > 0:
+        w_pos = space.call_method(w_str, "find", w_substr,
+                                  space.wrap(start), space.wrap(end))
+    else:
+        w_pos = space.call_method(w_str, "rfind", w_substr,
+                                  space.wrap(start), space.wrap(end))
+    return space.int_w(w_pos)
+
+ at cpython_api([PyObject, PyObject, Py_ssize_t], PyObject)
+def PyUnicode_Split(space, w_str, w_sep, maxsplit):
+    """Split a string giving a list of Unicode strings.  If sep is
+    NULL, splitting will be done at all whitespace substrings.
+    Otherwise, splits occur at the given separator.  At most maxsplit
+    splits will be done.  If negative, no limit is set.  Separators
+    are not included in the resulting list."""
+    if w_sep is None:
+        w_sep = space.w_None
+    return space.call_method(w_str, "split", w_sep, space.wrap(maxsplit))
+
+ at cpython_api([PyObject, rffi.INT_real], PyObject)
+def PyUnicode_Splitlines(space, w_str, keepend):
+    """Split a Unicode string at line breaks, returning a list of
+    Unicode strings.  CRLF is considered to be one line break.  If
+    keepend is 0, the Line break characters are not included in the
+    resulting strings."""
+    return space.call_method(w_str, "splitlines", space.wrap(keepend))
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
@@ -513,7 +513,30 @@
             arr = concrete.copy(space)
             arr.setshape(space, new_shape)
         return arr
-
+       
+    @unwrap_spec(axis1=int, axis2=int)
+    def descr_swapaxes(self, space, axis1, axis2):
+        """a.swapaxes(axis1, axis2)
+    
+        Return a view of the array with `axis1` and `axis2` interchanged.
+    
+        Refer to `numpy.swapaxes` for full documentation.
+    
+        See Also
+        --------
+        numpy.swapaxes : equivalent function
+        """
+        concrete = self.get_concrete()
+        shape = concrete.shape[:]
+        strides = concrete.strides[:]
+        backstrides = concrete.backstrides[:]
+        shape[axis1], shape[axis2] = shape[axis2], shape[axis1]   
+        strides[axis1], strides[axis2] = strides[axis2], strides[axis1]
+        backstrides[axis1], backstrides[axis2] = backstrides[axis2], backstrides[axis1] 
+        arr = W_NDimSlice(concrete.start, strides, 
+                           backstrides, shape, concrete)
+        return space.wrap(arr)   
+                                      
     def descr_tolist(self, space):
         if len(self.shape) == 0:
             assert isinstance(self, Scalar)
@@ -1412,6 +1435,7 @@
     copy = interp2app(BaseArray.descr_copy),
     flatten = interp2app(BaseArray.descr_flatten),
     reshape = interp2app(BaseArray.descr_reshape),
+    swapaxes = interp2app(BaseArray.descr_swapaxes),
     tolist = interp2app(BaseArray.descr_tolist),
     take = interp2app(BaseArray.descr_take),
     compress = interp2app(BaseArray.descr_compress),
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
@@ -1410,6 +1410,35 @@
         assert (array([1, 2]).repeat(2) == array([1, 1, 2, 2])).all()
 
 
+    def test_swapaxes(self):
+        from _numpypy import array
+        # testcases from numpy docstring
+        x = array([[1, 2, 3]])
+        assert (x.swapaxes(0, 1) == array([[1], [2], [3]])).all() 
+        x = array([[[0,1],[2,3]],[[4,5],[6,7]]]) # shape = (2, 2, 2)
+        assert (x.swapaxes(0, 2) == array([[[0, 4], [2, 6]], 
+                                           [[1, 5], [3, 7]]])).all() 
+        assert (x.swapaxes(0, 1) == array([[[0, 1], [4, 5]], 
+                                           [[2, 3], [6, 7]]])).all()
+        assert (x.swapaxes(1, 2) == array([[[0, 2], [1, 3]], 
+                                           [[4, 6],[5, 7]]])).all()
+
+        # more complex shape i.e. (2, 2, 3)
+        x = array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) 
+        assert (x.swapaxes(0, 1) == array([[[1, 2, 3], [7, 8, 9]], 
+                                           [[4, 5, 6], [10, 11, 12]]])).all() 
+        assert (x.swapaxes(0, 2) == array([[[1, 7], [4, 10]], [[2, 8], [5, 11]], 
+                                           [[3, 9], [6, 12]]])).all() 
+        assert (x.swapaxes(1, 2) == array([[[1, 4], [2, 5], [3, 6]], 
+                                           [[7, 10], [8, 11],[9, 12]]])).all() 
+        
+        # test slice
+        assert (x[0:1,0:2].swapaxes(0,2) == array([[[1], [4]], [[2], [5]], 
+                                                   [[3], [6]]])).all()
+        # test virtual
+        assert ((x + x).swapaxes(0,1) == array([[[ 2,  4,  6], [14, 16, 18]], 
+                                         [[ 8, 10, 12], [20, 22, 24]]])).all()
+                        
 class AppTestMultiDim(BaseNumpyAppTest):
     def test_init(self):
         import _numpypy
diff --git a/pypy/module/pypyjit/test_pypy_c/test_call.py b/pypy/module/pypyjit/test_pypy_c/test_call.py
--- a/pypy/module/pypyjit/test_pypy_c/test_call.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_call.py
@@ -244,6 +244,7 @@
         print guards
         assert len(guards) <= 20
 
+
     def test_stararg_virtual(self):
         def main(x):
             def g(*args):
@@ -486,3 +487,38 @@
             --TICK--
             jump(..., descr=...)
         """)
+
+    def test_kwargs_virtual2(self):
+        log = self.run("""
+        def f(*args, **kwargs):
+            kwargs['a'] = kwargs['z'] * 0
+            return g(1, *args, **kwargs)
+
+        def g(x, y, z=2, a=1):
+            return x - y + z + a
+
+        def main(stop):
+            res = 0
+            i = 0
+            while i < stop:
+                res = f(res, z=i) # ID: call
+                i += 1
+            return res""", [1000])
+        assert log.result == 500
+        loop, = log.loops_by_id('call')
+        print loop.ops_by_id('call')
+        assert loop.match("""
+            i65 = int_lt(i58, i29)
+            guard_true(i65, descr=...)
+            guard_not_invalidated(..., descr=...)
+            i66 = force_token()
+            i67 = force_token()
+            i69 = int_sub_ovf(1, i56)
+            guard_no_overflow(..., descr=...)
+            i70 = int_add_ovf(i69, i58)
+            guard_no_overflow(..., descr=...)
+            i71 = int_add(i58, 1)
+            --TICK--
+            jump(..., descr=...)
+        """)
+
diff --git a/pypy/module/test_lib_pypy/numpypy/core/test_fromnumeric.py b/pypy/module/test_lib_pypy/numpypy/core/test_fromnumeric.py
--- a/pypy/module/test_lib_pypy/numpypy/core/test_fromnumeric.py
+++ b/pypy/module/test_lib_pypy/numpypy/core/test_fromnumeric.py
@@ -136,4 +136,11 @@
         raises(NotImplementedError, "transpose(x, axes=(1, 0, 2))")
         # x = ones((1, 2, 3))
         # assert transpose(x, (1, 0, 2)).shape == (2, 1, 3)
-        
+    
+    def test_fromnumeric(self):
+        from numpypy import array, swapaxes
+        x = array([[1,2,3]])
+        assert (swapaxes(x,0,1) == array([[1], [2], [3]])).all()
+        x = array([[[0,1],[2,3]],[[4,5],[6,7]]])
+        assert (swapaxes(x,0,2) == array([[[0, 4], [2, 6]], 
+                                          [[1, 5], [3, 7]]])).all()
diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py
--- a/pypy/objspace/fake/objspace.py
+++ b/pypy/objspace/fake/objspace.py
@@ -110,7 +110,7 @@
         "NOT_RPYTHON"
         raise NotImplementedError
 
-    def newdict(self, module=False, instance=False,
+    def newdict(self, module=False, instance=False, kwargs=False,
                 strdict=False):
         return w_some_obj()
 
diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py
--- a/pypy/objspace/std/dictmultiobject.py
+++ b/pypy/objspace/std/dictmultiobject.py
@@ -33,7 +33,7 @@
 
     @staticmethod
     def allocate_and_init_instance(space, w_type=None, module=False,
-                                   instance=False, strdict=False):
+                                   instance=False, strdict=False, kwargs=False):
 
         if space.config.objspace.std.withcelldict and module:
             from pypy.objspace.std.celldict import ModuleDictStrategy
@@ -46,11 +46,15 @@
             assert w_type is None
             strategy = space.fromcache(StringDictStrategy)
 
+        elif kwargs:
+            assert w_type is None
+            from pypy.objspace.std.kwargsdict import KwargsDictStrategy
+            strategy = space.fromcache(KwargsDictStrategy)
         else:
             strategy = space.fromcache(EmptyDictStrategy)
-
         if w_type is None:
             w_type = space.w_dict
+
         storage = strategy.get_empty_storage()
         w_self = space.allocate_instance(W_DictMultiObject, w_type)
         W_DictMultiObject.__init__(w_self, space, strategy, storage)
@@ -91,7 +95,8 @@
                     getitem_str delitem length \
                     clear w_keys values \
                     items iter setdefault \
-                    popitem listview_str listview_int".split()
+                    popitem listview_str listview_int \
+                    view_as_kwargs".split()
 
     def make_method(method):
         def f(self, *args):
@@ -165,6 +170,9 @@
     def listview_int(self, w_dict):
         return None
 
+    def view_as_kwargs(self, w_dict):
+        return (None, None)
+
 class EmptyDictStrategy(DictStrategy):
 
     erase, unerase = rerased.new_erasing_pair("empty")
@@ -254,6 +262,9 @@
     def popitem(self, w_dict):
         raise KeyError
 
+    def view_as_kwargs(self, w_dict):
+        return ([], [])
+
 registerimplementation(W_DictMultiObject)
 
 # DictImplementation lattice
diff --git a/pypy/objspace/std/kwargsdict.py b/pypy/objspace/std/kwargsdict.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/kwargsdict.py
@@ -0,0 +1,165 @@
+## ----------------------------------------------------------------------------
+## dict strategy (see dictmultiobject.py)
+
+from pypy.rlib import rerased, jit
+from pypy.objspace.std.dictmultiobject import (DictStrategy,
+                                               IteratorImplementation,
+                                               ObjectDictStrategy,
+                                               StringDictStrategy)
+
+
+class KwargsDictStrategy(DictStrategy):
+    erase, unerase = rerased.new_erasing_pair("kwargsdict")
+    erase = staticmethod(erase)
+    unerase = staticmethod(unerase)
+
+    def wrap(self, key):
+        return self.space.wrap(key)
+
+    def unwrap(self, wrapped):
+        return self.space.str_w(wrapped)
+
+    def get_empty_storage(self):
+        d = ([], [])
+        return self.erase(d)
+
+    def is_correct_type(self, w_obj):
+        space = self.space
+        return space.is_w(space.type(w_obj), space.w_str)
+
+    def _never_equal_to(self, w_lookup_type):
+        return False
+
+    def iter(self, w_dict):
+        return KwargsDictIterator(self.space, self, w_dict)
+
+    def w_keys(self, w_dict):
+        return self.space.newlist([self.space.wrap(key) for key in self.unerase(w_dict.dstorage)[0]])
+
+    def setitem(self, w_dict, w_key, w_value):
+        space = self.space
+        if self.is_correct_type(w_key):
+            self.setitem_str(w_dict, self.unwrap(w_key), w_value)
+            return
+        else:
+            self.switch_to_object_strategy(w_dict)
+            w_dict.setitem(w_key, w_value)
+
+    def setitem_str(self, w_dict, key, w_value):
+        self._setitem_str_indirection(w_dict, key, w_value)
+
+    @jit.look_inside_iff(lambda self, w_dict, key, w_value:
+            jit.isconstant(self.length(w_dict)) and jit.isconstant(key))
+    def _setitem_str_indirection(self, w_dict, key, w_value):
+        keys, values_w = self.unerase(w_dict.dstorage)
+        result = []
+        for i in range(len(keys)):
+            if keys[i] == key:
+                values_w[i] = w_value
+                break
+        else:
+            # limit the size so that the linear searches don't become too long
+            if len(keys) >= 16:
+                self.switch_to_string_strategy(w_dict)
+                w_dict.setitem_str(key, w_value)
+            else:
+                keys.append(key)
+                values_w.append(w_value)
+
+    def setdefault(self, w_dict, w_key, w_default):
+        # XXX could do better, but is it worth it?
+        self.switch_to_object_strategy(w_dict)
+        return w_dict.setdefault(w_key, w_default)
+
+    def delitem(self, w_dict, w_key):
+        # XXX could do better, but is it worth it?
+        self.switch_to_object_strategy(w_dict)
+        return w_dict.delitem(w_key)
+
+    def length(self, w_dict):
+        return len(self.unerase(w_dict.dstorage)[0])
+
+    def getitem_str(self, w_dict, key):
+        return self._getitem_str_indirection(w_dict, key)
+
+    @jit.look_inside_iff(lambda self, w_dict, key: jit.isconstant(self.length(w_dict)) and jit.isconstant(key))
+    def _getitem_str_indirection(self, w_dict, key):
+        keys, values_w = self.unerase(w_dict.dstorage)
+        result = []
+        for i in range(len(keys)):
+            if keys[i] == key:
+                return values_w[i]
+        return None
+
+    def getitem(self, w_dict, w_key):
+        space = self.space
+        if self.is_correct_type(w_key):
+            return self.getitem_str(w_dict, self.unwrap(w_key))
+        elif self._never_equal_to(space.type(w_key)):
+            return None
+        else:
+            self.switch_to_object_strategy(w_dict)
+            return w_dict.getitem(w_key)
+
+    def w_keys(self, w_dict):
+        l = self.unerase(w_dict.dstorage)[0]
+        return self.space.newlist_str(l[:])
+
+    def values(self, w_dict):
+        return self.unerase(w_dict.dstorage)[1][:] # to make non-resizable
+
+    def items(self, w_dict):
+        space = self.space
+        keys, values_w = self.unerase(w_dict.dstorage)
+        result = []
+        for i in range(len(keys)):
+            result.append(space.newtuple([self.wrap(keys[i]), values_w[i]]))
+        return result
+
+    def popitem(self, w_dict):
+        keys, values_w = self.unerase(w_dict.dstorage)
+        key = keys.pop()
+        w_value = values_w.pop()
+        return (self.wrap(key), w_value)
+
+    def clear(self, w_dict):
+        w_dict.dstorage = self.get_empty_storage()
+
+    def switch_to_object_strategy(self, w_dict):
+        strategy = self.space.fromcache(ObjectDictStrategy)
+        keys, values_w = self.unerase(w_dict.dstorage)
+        d_new = strategy.unerase(strategy.get_empty_storage())
+        for i in range(len(keys)):
+            d_new[self.wrap(keys[i])] = values_w[i]
+        w_dict.strategy = strategy
+        w_dict.dstorage = strategy.erase(d_new)
+
+    def switch_to_string_strategy(self, w_dict):
+        strategy = self.space.fromcache(StringDictStrategy)
+        keys, values_w = self.unerase(w_dict.dstorage)
+        storage = strategy.get_empty_storage()
+        d_new = strategy.unerase(storage)
+        for i in range(len(keys)):
+            d_new[keys[i]] = values_w[i]
+        w_dict.strategy = strategy
+        w_dict.dstorage = storage
+
+    def view_as_kwargs(self, w_dict):
+        return self.unerase(w_dict.dstorage)
+
+
+class KwargsDictIterator(IteratorImplementation):
+    def __init__(self, space, strategy, dictimplementation):
+        IteratorImplementation.__init__(self, space, strategy, dictimplementation)
+        keys, values_w = strategy.unerase(self.dictimplementation.dstorage)
+        self.iterator = iter(range(len(keys)))
+        # XXX this potentially leaks
+        self.keys = keys
+        self.values_w = values_w
+
+    def next_entry(self):
+        # note that this 'for' loop only runs once, at most
+        for i in self.iterator:
+            return self.space.wrap(self.keys[i]), self.values_w[i]
+        else:
+            return None, None
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -313,11 +313,11 @@
     def newlist_str(self, list_s):
         return W_ListObject.newlist_str(self, list_s)
 
-    def newdict(self, module=False, instance=False,
+    def newdict(self, module=False, instance=False, kwargs=False,
                 strdict=False):
         return W_DictMultiObject.allocate_and_init_instance(
                 self, module=module, instance=instance,
-                strdict=strdict)
+                strdict=strdict, kwargs=kwargs)
 
     def newset(self):
         from pypy.objspace.std.setobject import newset
@@ -472,6 +472,11 @@
             return w_obj.getitems_int()
         return None
 
+    def view_as_kwargs(self, w_dict):
+        if type(w_dict) is W_DictMultiObject:
+            return w_dict.view_as_kwargs()
+        return (None, None)
+
     def _uses_list_iter(self, w_obj):
         from pypy.objspace.descroperation import list_iter
         return self.lookup(w_obj, '__iter__') is list_iter(self)
diff --git a/pypy/objspace/std/stringobject.py b/pypy/objspace/std/stringobject.py
--- a/pypy/objspace/std/stringobject.py
+++ b/pypy/objspace/std/stringobject.py
@@ -14,6 +14,7 @@
 from pypy.objspace.std.tupleobject import W_TupleObject
 from pypy.rlib.rstring import StringBuilder, split
 from pypy.interpreter.buffer import StringBuffer
+from pypy.rlib import jit
 
 from pypy.objspace.std.stringtype import sliced, wrapstr, wrapchar, \
      stringendswith, stringstartswith, joined2
@@ -398,6 +399,8 @@
 
     return _str_join_many_items(space, w_self, list_w, size)
 
+ at jit.look_inside_iff(lambda space, w_self, list_w, size:
+                     jit.loop_unrolling_heuristic(list_w, size))
 def _str_join_many_items(space, w_self, list_w, size):
     self = w_self._value
     reslen = len(self) * (size - 1)
diff --git a/pypy/objspace/std/test/test_kwargsdict.py b/pypy/objspace/std/test/test_kwargsdict.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/test/test_kwargsdict.py
@@ -0,0 +1,120 @@
+import py
+from pypy.conftest import gettestobjspace, option
+from pypy.objspace.std.test.test_dictmultiobject import FakeSpace, W_DictMultiObject
+from pypy.objspace.std.kwargsdict import *
+
+space = FakeSpace()
+strategy = KwargsDictStrategy(space)
+
+def test_create():
+    keys = ["a", "b", "c"]
+    values = [1, 2, 3]
+    storage = strategy.erase((keys, values))
+    d = W_DictMultiObject(space, strategy, storage)
+    assert d.getitem_str("a") == 1
+    assert d.getitem_str("b") == 2
+    assert d.getitem_str("c") == 3
+    assert d.getitem(space.wrap("a")) == 1
+    assert d.getitem(space.wrap("b")) == 2
+    assert d.getitem(space.wrap("c")) == 3
+    assert d.w_keys() == keys
+    assert d.values() == values
+
+def test_set_existing():
+    keys = ["a", "b", "c"]
+    values = [1, 2, 3]
+    storage = strategy.erase((keys, values))
+    d = W_DictMultiObject(space, strategy, storage)
+    assert d.getitem_str("a") == 1
+    assert d.getitem_str("b") == 2
+    assert d.getitem_str("c") == 3
+    assert d.setitem_str("a", 4) is None
+    assert d.getitem_str("a") == 4
+    assert d.getitem_str("b") == 2
+    assert d.getitem_str("c") == 3
+    assert d.setitem_str("b", 5) is None
+    assert d.getitem_str("a") == 4
+    assert d.getitem_str("b") == 5
+    assert d.getitem_str("c") == 3
+    assert d.setitem_str("c", 6) is None
+    assert d.getitem_str("a") == 4
+    assert d.getitem_str("b") == 5
+    assert d.getitem_str("c") == 6
+    assert d.getitem(space.wrap("a")) == 4
+    assert d.getitem(space.wrap("b")) == 5
+    assert d.getitem(space.wrap("c")) == 6
+    assert d.w_keys() == keys
+    assert d.values() == values
+    assert keys == ["a", "b", "c"]
+    assert values == [4, 5, 6]
+
+
+def test_set_new():
+    keys = ["a", "b", "c"]
+    values = [1, 2, 3]
+    storage = strategy.erase((keys, values))
+    d = W_DictMultiObject(space, strategy, storage)
+    assert d.getitem_str("a") == 1
+    assert d.getitem_str("b") == 2
+    assert d.getitem_str("c") == 3
+    assert d.getitem_str("d") is None
+    assert d.setitem_str("d", 4) is None
+    assert d.getitem_str("a") == 1
+    assert d.getitem_str("b") == 2
+    assert d.getitem_str("c") == 3
+    assert d.getitem_str("d") == 4
+    assert d.w_keys() == keys
+    assert d.values() == values
+    assert keys == ["a", "b", "c", "d"]
+    assert values == [1, 2, 3, 4]
+
+def test_limit_size():
+    storage = strategy.get_empty_storage()
+    d = W_DictMultiObject(space, strategy, storage)
+    for i in range(100):
+        assert d.setitem_str("d%s" % i, 4) is None
+    assert d.strategy is not strategy
+    assert "StringDictStrategy" == d.strategy.__class__.__name__
+
+def test_keys_doesnt_wrap():
+    space = FakeSpace()
+    space.newlist = None
+    strategy = KwargsDictStrategy(space)
+    keys = ["a", "b", "c"]
+    values = [1, 2, 3]
+    storage = strategy.erase((keys, values))
+    d = W_DictMultiObject(space, strategy, storage)
+    w_l = d.w_keys() # does not crash
+
+
+from pypy.objspace.std.test.test_dictmultiobject import BaseTestRDictImplementation, BaseTestDevolvedDictImplementation
+def get_impl(self):
+    storage = strategy.erase(([], []))
+    return W_DictMultiObject(space, strategy, storage)
+class TestKwargsDictImplementation(BaseTestRDictImplementation):
+    StrategyClass = KwargsDictStrategy
+    get_impl = get_impl
+    def test_delitem(self):
+        pass # delitem devolves for now
+
+class TestDevolvedKwargsDictImplementation(BaseTestDevolvedDictImplementation):
+    get_impl = get_impl
+    StrategyClass = KwargsDictStrategy
+
+
+class AppTestKwargsDictStrategy(object):
+    def setup_class(cls):
+        if option.runappdirect:
+            py.test.skip("__repr__ doesn't work on appdirect")
+
+    def w_get_strategy(self, obj):
+        import __pypy__
+        r = __pypy__.internal_repr(obj)
+        return r[r.find("(") + 1: r.find(")")]
+
+    def test_create(self):
+        def f(**args):
+            return args
+        d = f(a=1)
+        assert "KwargsDictStrategy" in self.get_strategy(d)
+
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
@@ -16,6 +16,7 @@
 from pypy.rlib.runicode import unicode_encode_unicode_escape
 from pypy.module.unicodedata import unicodedb
 from pypy.tool.sourcetools import func_with_new_name
+from pypy.rlib import jit
 
 from pypy.objspace.std.formatting import mod_format
 from pypy.objspace.std.stringtype import stringstartswith, stringendswith
@@ -214,6 +215,8 @@
 
     return _unicode_join_many_items(space, w_self, list_w, size)
 
+ at jit.look_inside_iff(lambda space, w_self, list_w, size:
+                     jit.loop_unrolling_heuristic(list_w, size))
 def _unicode_join_many_items(space, w_self, list_w, size):
     self = w_self._value
     prealloc_size = len(self) * (size - 1)
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -204,6 +204,14 @@
     return NonConstant(False)
 isvirtual._annspecialcase_ = "specialize:call_location"
 
+LIST_CUTOFF = 2
+
+ at specialize.call_location()
+def loop_unrolling_heuristic(lst, size):
+    """ In which cases iterating over items of lst can be unrolled
+    """
+    return isvirtual(lst) or (isconstant(size) and size <= LIST_CUTOFF)
+
 class Entry(ExtRegistryEntry):
     _about_ = hint
 
diff --git a/pypy/rpython/lltypesystem/rstr.py b/pypy/rpython/lltypesystem/rstr.py
--- a/pypy/rpython/lltypesystem/rstr.py
+++ b/pypy/rpython/lltypesystem/rstr.py
@@ -709,7 +709,8 @@
         return count
 
     @enforceargs(int, None)
-    @jit.look_inside_iff(lambda length, items: jit.isconstant(length) and length <= 2)
+    @jit.look_inside_iff(lambda length, items: jit.loop_unrolling_heuristic(
+        items, length))
     def ll_join_strs(length, items):
         # Special case for length 1 items, helps both the JIT and other code
         if length == 1:
diff --git a/pypy/tool/clean_old_branches.py b/pypy/tool/clean_old_branches.py
--- a/pypy/tool/clean_old_branches.py
+++ b/pypy/tool/clean_old_branches.py
@@ -4,30 +4,28 @@
 called 'closed-branch'.  It reduces the number of heads.
 """
 
-import os, sys
+import os
+import sys
+import commands
 
-if not os.listdir('.hg'):
+if not os.path.isdir('.hg'):
     print 'Must run this script from the top-level directory.'
     sys.exit(1)
 
-def heads(args):
-    g = os.popen(r"hg heads --topo %s --template '{node|short}:{branches}\n'"
-                 % args, 'r')
-    result = g.read()
-    g.close()
+def heads():
+    result = commands.getoutput(
+        "hg heads --topo --closed --template '{node|short}:{branches}:{extras}\n'")
     result = result.splitlines(False)
+    result = [s.split(':', 2) for s in result]
     for line in result:
-        if len(line.split(':', 1)) != 2:
+        if len(line) != 3:
             raise ValueError("'result' contains: %r" % line)
-    result = [s.split(':', 1) for s in result]
-    result = [(head, branch) for (head, branch) in result
-                if branch not in ['', 'closed-branches']]
+    result = [(head, branch) for (head, branch, extra) in result
+                if branch not in ['', 'closed-branches'] and 'close' in extra]
     return result
 
-all_heads = heads("--closed")
-opened_heads = heads("")
 
-closed_heads = [s for s in all_heads if s not in opened_heads]
+closed_heads = heads()
 
 if not closed_heads:
     print >> sys.stderr, 'no dangling closed heads.'
@@ -56,16 +54,14 @@
         print '*** error %r' % (err,)
         sys.exit(1)
 
+print '*** switching to closed branches *** '
+do("hg up --clean closed-branches")
+do("hg --config extensions.purge= purge --all")
+
 for head, branch in closed_heads:
     print
     print '***** %s ***** %s *****' % (branch, head)
-    do("hg up --clean closed-branches")
-    do("hg --config extensions.purge= purge --all")
-    do("hg merge -y %s" % head)
-    for fn in os.listdir('.'):
-        if fn.lower() != '.hg':
-            do("rm -fr -- '%s'" % fn)
-            do("hg rm --after -- '%s' || true" % fn)
+    do("hg debugsetparents closed-branches %s" % head)
     do("hg ci -m'Merge closed head %s on branch %s'" % (head, branch))
 
 print
diff --git a/pypy/tool/test/test_udir.py b/pypy/tool/test/test_udir.py
--- a/pypy/tool/test/test_udir.py
+++ b/pypy/tool/test/test_udir.py
@@ -13,6 +13,8 @@
 def test_make_udir_with_basename():
     root = str(udir.udir.ensure('make_udir2', dir=1))
     p1 = udir.make_udir(dir=root, basename='foobar')
+    def assert_relto(path, root, expected):
+        assert path.relto(root) == expected, path.relto(root)
     assert p1.relto(root) == 'usession-foobar-0'
     p1 = udir.make_udir(dir=root, basename='-foobar')
     assert p1.relto(root) == 'usession-foobar-1'
@@ -24,3 +26,5 @@
     assert p1.relto(root) == 'usession-0'
     p1 = udir.make_udir(dir=root, basename='-')
     assert p1.relto(root) == 'usession-1'
+    p1 = udir.make_udir(dir=root, basename='fun/bar')
+    assert p1.relto(root) == 'usession-fun--bar-0'
diff --git a/pypy/tool/udir.py b/pypy/tool/udir.py
--- a/pypy/tool/udir.py
+++ b/pypy/tool/udir.py
@@ -41,6 +41,7 @@
                 basename = basename.encode(sys.getdefaultencoding())
         else:
             basename = ''
+    basename = basename.replace('/', '--')
     if not basename.startswith('-'):
         basename = '-' + basename
     if not basename.endswith('-'):
diff --git a/pypy/translator/driver.py b/pypy/translator/driver.py
--- a/pypy/translator/driver.py
+++ b/pypy/translator/driver.py
@@ -115,12 +115,10 @@
         backend, ts = self.get_backend_and_type_system()
         for task in self.tasks:
             explicit_task = task
-            parts = task.split('_')
-            if len(parts) == 1:
-                if task in ('annotate',):
-                    expose_task(task)
+            if task == 'annotate':
+                expose_task(task)
             else:
-                task, postfix = parts
+                task, postfix = task.split('_')
                 if task in ('rtype', 'backendopt', 'llinterpret',
                             'pyjitpl'):
                     if ts:


More information about the pypy-commit mailing list