Author: David Schneider <david.schnei...@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) +@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 -@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 - -@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 -@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 - -@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) +@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) + +@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) + +@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)) + +@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) +@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) +@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 + +@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: _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit