Author: Ronan Lamy <ronan.l...@gmail.com> Branch: reflowing Changeset: r88645:9b3564c820ef Date: 2016-11-24 06:07 +0000 http://bitbucket.org/pypy/pypy/changeset/9b3564c820ef/
Log: hg merge default diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py --- a/pypy/interpreter/argument.py +++ b/pypy/interpreter/argument.py @@ -111,7 +111,9 @@ self.keywords = self.keywords + keywords self.keywords_w = self.keywords_w + values_w return + is_dict = False if space.isinstance_w(w_starstararg, space.w_dict): + is_dict = True keys_w = space.unpackiterable(w_starstararg) else: try: @@ -125,7 +127,9 @@ keys_w = space.unpackiterable(w_keys) keywords_w = [None] * len(keys_w) keywords = [None] * len(keys_w) - _do_combine_starstarargs_wrapped(space, keys_w, w_starstararg, keywords, keywords_w, self.keywords) + _do_combine_starstarargs_wrapped( + space, keys_w, w_starstararg, keywords, keywords_w, self.keywords, + is_dict) self.keyword_names_w = keys_w if self.keywords is None: self.keywords = keywords @@ -355,7 +359,7 @@ key) def _do_combine_starstarargs_wrapped(space, keys_w, w_starstararg, keywords, - keywords_w, existingkeywords): + keywords_w, existingkeywords, is_dict): i = 0 for w_key in keys_w: try: @@ -374,7 +378,16 @@ "got multiple values for keyword argument '%s'", key) keywords[i] = key - keywords_w[i] = space.getitem(w_starstararg, w_key) + if is_dict: + # issue 2435: bug-to-bug compatibility with cpython. for a subclass of + # dict, just ignore the __getitem__ and access the underlying dict + # directly + from pypy.objspace.descroperation import dict_getitem + w_descr = dict_getitem(space) + w_value = space.get_and_call_function(w_descr, w_starstararg, w_key) + else: + w_value = space.getitem(w_starstararg, w_key) + keywords_w[i] = w_value i += 1 @jit.look_inside_iff( 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 @@ -120,6 +120,12 @@ raise OperationError(AttributeError, name) return method(*args) + def lookup_in_type_where(self, cls, name): + return 'hopefully not needed', getattr(cls, name) + + def get_and_call_function(self, w_descr, w_obj, *args): + return w_descr.__get__(w_obj)(*args) + def type(self, obj): class Type: def getname(self, space): @@ -805,3 +811,19 @@ assert str(e) == "myerror" else: assert False, "Expected TypeError" + + def test_dict_subclass_with_weird_getitem(self): + # issue 2435: bug-to-bug compatibility with cpython. for a subclass of + # dict, just ignore the __getitem__ and behave like ext_do_call in ceval.c + # which just uses the underlying dict + class d(dict): + def __getitem__(self, key): + return key + + for key in ["foo", u"foo"]: + q = d() + q[key] = "bar" + + def test(**kwargs): + return kwargs + assert test(**q) == {"foo": "bar"} diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py --- a/pypy/module/_file/interp_file.py +++ b/pypy/module/_file/interp_file.py @@ -495,11 +495,10 @@ if space.isinstance_w(w_data, space.w_unicode): # note: "encode" is called before we acquire the lock # for this file, which is done in file_write_str(). - # Direct access to unicodeobject because we don't want + # Use a specific space method because we don't want # to call user-defined "encode" methods here. - from pypy.objspace.std.unicodeobject import encode_object - w_data = encode_object(space, w_data, self.encoding, - self.errors) + w_data = space.encode_unicode_object(w_data, + self.encoding, self.errors) data = space.charbuf_w(w_data) self.file_write_str(data) diff --git a/pypy/module/bz2/interp_bz2.py b/pypy/module/bz2/interp_bz2.py --- a/pypy/module/bz2/interp_bz2.py +++ b/pypy/module/bz2/interp_bz2.py @@ -293,7 +293,7 @@ same_attributes_as_in_file.remove('__init__') same_attributes_as_in_file.extend([ 'name', 'mode', 'encoding', 'closed', 'newlines', 'softspace', - 'writelines', '__exit__', '__weakref__']) + 'writelines', '__exit__', '__weakref__', 'write']) W_BZ2File.typedef = TypeDef( "BZ2File", diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py --- a/pypy/module/cpyext/dictobject.py +++ b/pypy/module/cpyext/dictobject.py @@ -137,8 +137,7 @@ """This is the same as PyDict_Merge(a, b, 1) in C, or a.update(b) in Python. Return 0 on success or -1 if an exception was raised. """ - space.call_method(space.w_dict, "update", w_obj, w_other) - return 0 + return PyDict_Merge(space, w_obj, w_other, 1) @cpython_api([PyObject], PyObject) def PyDict_Keys(space, w_obj): diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py --- a/pypy/module/cpyext/object.py +++ b/pypy/module/cpyext/object.py @@ -252,7 +252,10 @@ def PyObject_Format(space, w_obj, w_format_spec): if w_format_spec is None: w_format_spec = space.wrap('') - return space.call_method(w_obj, '__format__', w_format_spec) + w_ret = space.call_method(w_obj, '__format__', w_format_spec) + if space.isinstance_w(w_format_spec, space.w_unicode): + return space.unicode_from_object(w_ret) + return w_ret @cpython_api([PyObject], PyObject) def PyObject_Unicode(space, w_obj): diff --git a/pypy/module/cpyext/test/test_dictobject.py b/pypy/module/cpyext/test/test_dictobject.py --- a/pypy/module/cpyext/test/test_dictobject.py +++ b/pypy/module/cpyext/test/test_dictobject.py @@ -103,6 +103,17 @@ api.PyDict_Update(w_d, w_d2) assert space.unwrap(w_d) == dict(a='b', c='d', e='f') + def test_update_doesnt_accept_list_of_tuples(self, space, api): + w_d = space.newdict() + space.setitem(w_d, space.wrap("a"), space.wrap("b")) + + w_d2 = space.wrap([("c", "d"), ("e", "f")]) + + api.PyDict_Update(w_d, w_d2) + assert api.PyErr_Occurred() is space.w_AttributeError + api.PyErr_Clear() + assert space.unwrap(w_d) == dict(a='b') # unchanged + def test_iter(self, space, api): w_dict = space.sys.getdict(space) py_dict = make_ref(space, w_dict) @@ -199,3 +210,18 @@ """), ]) assert module.dict_proxy({'a': 1, 'b': 2}) == 2 + + def test_update(self): + module = self.import_extension('foo', [ + ("update", "METH_VARARGS", + ''' + if (PyDict_Update(PyTuple_GetItem(args, 0), PyTuple_GetItem(args, 1))) + return NULL; + Py_RETURN_NONE; + ''')]) + d = {"a": 1} + module.update(d, {"c": 2}) + assert d == dict(a=1, c=2) + d = {"a": 1} + raises(AttributeError, module.update, d, [("c", 2)]) + diff --git a/pypy/module/cpyext/test/test_object.py b/pypy/module/cpyext/test/test_object.py --- a/pypy/module/cpyext/test/test_object.py +++ b/pypy/module/cpyext/test/test_object.py @@ -312,6 +312,16 @@ assert isinstance(dict(), collections.Mapping) assert module.ismapping(dict()) + def test_format_returns_unicode(self): + module = self.import_extension('foo', [ + ("empty_format", "METH_O", + """ + PyObject* empty_unicode = PyUnicode_FromStringAndSize("", 0); + PyObject* obj = PyObject_Format(args, empty_unicode); + return obj; + """)]) + a = module.empty_format('hello') + assert isinstance(a, unicode) class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase): """ 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 @@ -331,12 +331,34 @@ PyHeapTypeObject *heaptype = (PyHeapTypeObject *)args; Py_INCREF(heaptype->ht_name); return heaptype->ht_name; + '''), + ("setattr", "METH_O", ''' - ) + int ret; + PyObject* name = PyString_FromString("mymodule"); + PyObject *obj = PyType_Type.tp_alloc(&PyType_Type, 0); + PyHeapTypeObject *type = (PyHeapTypeObject*)obj; + if ((type->ht_type.tp_flags & Py_TPFLAGS_HEAPTYPE) == 0) + { + PyErr_SetString(PyExc_ValueError, + "Py_TPFLAGS_HEAPTYPE not set"); + return NULL; + } + type->ht_type.tp_name = ((PyTypeObject*)args)->tp_name; + PyType_Ready(&type->ht_type); + ret = PyObject_SetAttrString((PyObject*)&type->ht_type, + "__module__", name); + Py_DECREF(name); + if (ret < 0) + return NULL; + return PyLong_FromLong(ret); + '''), ]) class C(object): pass assert module.name_by_heaptype(C) == "C" + assert module.setattr(C) == 0 + def test_type_dict(self): foo = self.import_module("foo") diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -469,7 +469,7 @@ W_TypeObject.__init__(self, space, name, bases_w or [space.w_object], dict_w, force_new_layout=new_layout) self.flag_cpytype = True - self.flag_heaptype = False + self.flag_heaptype = pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE # if a sequence or a mapping, then set the flag to force it if pto.c_tp_as_sequence and pto.c_tp_as_sequence.c_sq_item: self.flag_map_or_seq = 'S' @@ -852,14 +852,14 @@ w_obj = space.allocate_instance(W_PyCTypeObject, w_metatype) track_reference(space, py_obj, w_obj) # __init__ wraps all slotdefs functions from py_type via add_operators - w_obj.__init__(space, py_type) + w_obj.__init__(space, py_type) w_obj.ready() finish_type_2(space, py_type, w_obj) base = py_type.c_tp_base if base: # XXX refactor - parts of this are done in finish_type_2 -> inherit_slots - if not py_type.c_tp_as_number: + if not py_type.c_tp_as_number: py_type.c_tp_as_number = base.c_tp_as_number py_type.c_tp_flags |= base.c_tp_flags & Py_TPFLAGS_CHECKTYPES py_type.c_tp_flags |= base.c_tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py --- a/pypy/objspace/descroperation.py +++ b/pypy/objspace/descroperation.py @@ -61,16 +61,24 @@ @specialize.memo() def str_getitem(space): "Utility that returns the app-level descriptor str.__getitem__." - w_src, w_iter = space.lookup_in_type_where(space.w_str, - '__getitem__') - return w_iter + w_src, w_getitem = space.lookup_in_type_where(space.w_str, + '__getitem__') + return w_getitem @specialize.memo() def unicode_getitem(space): "Utility that returns the app-level descriptor unicode.__getitem__." - w_src, w_iter = space.lookup_in_type_where(space.w_unicode, - '__getitem__') - return w_iter + w_src, w_getitem = space.lookup_in_type_where(space.w_unicode, + '__getitem__') + return w_getitem + +@specialize.memo() +def dict_getitem(space): + "Utility that returns the app-level descriptor dict.__getitem__." + w_src, w_getitem = space.lookup_in_type_where(space.w_dict, + '__getitem__') + return w_getitem + def raiseattrerror(space, w_obj, name, w_descr=None): if w_descr is None: 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 @@ -330,6 +330,9 @@ def unicode_from_object(self, w_obj): return w_some_obj() + def encode_unicode_object(self, w_unicode, encoding, errors): + return w_some_obj() + def _try_fetch_pycode(self, w_func): return 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 @@ -626,6 +626,10 @@ from pypy.objspace.std.unicodeobject import unicode_from_object return unicode_from_object(self, w_obj) + def encode_unicode_object(self, w_unicode, encoding, errors): + from pypy.objspace.std.unicodeobject import encode_object + return encode_object(self, w_unicode, encoding, errors) + def call_method(self, w_obj, methname, *arg_w): return callmethod.call_method_opt(self, w_obj, methname, *arg_w) diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py --- a/rpython/annotator/annrpython.py +++ b/rpython/annotator/annrpython.py @@ -22,7 +22,8 @@ """Block annotator for RPython. See description in doc/translation.txt.""" - def __init__(self, translator=None, policy=None, bookkeeper=None): + def __init__(self, translator=None, policy=None, bookkeeper=None, + keepgoing=False): import rpython.rtyper.extfuncregistry # has side effects if translator is None: @@ -51,6 +52,9 @@ if bookkeeper is None: bookkeeper = Bookkeeper(self) self.bookkeeper = bookkeeper + self.keepgoing = keepgoing + self.failed_blocks = set() + self.errors = [] def __getstate__(self): attrs = """translator pendingblocks annotated links_followed @@ -203,6 +207,12 @@ else: newgraphs = self.translator.graphs #all of them got_blocked_blocks = False in self.annotated.values() + if self.failed_blocks: + text = ('Annotation failed, %s errors were recorded:' % + len(self.errors)) + text += '\n-----'.join(str(e) for e in self.errors) + raise annmodel.AnnotatorError(text) + if got_blocked_blocks: for graph in self.blocked_graphs.values(): self.blocked_graphs[graph] = True @@ -358,6 +368,8 @@ #print '* processblock', block, cells self.annotated[block] = graph + if block in self.failed_blocks: + return if block in self.blocked_blocks: del self.blocked_blocks[block] try: @@ -402,6 +414,10 @@ except annmodel.UnionError as e: # Add source code to the UnionError e.source = '\n'.join(source_lines(graph, block, None, long=True)) + if self.keepgoing: + self.errors.append(e) + self.failed_blocks.add(block) + return raise # if the merged cells changed, we must redo the analysis if unions != oldcells: @@ -492,6 +508,10 @@ except annmodel.AnnotatorError as e: # note that UnionError is a subclass e.source = gather_error(self, graph, block, i) + if self.keepgoing: + self.errors.append(e) + self.failed_blocks.add(block) + return raise else: diff --git a/rpython/annotator/test/test_model.py b/rpython/annotator/test/test_model.py --- a/rpython/annotator/test/test_model.py +++ b/rpython/annotator/test/test_model.py @@ -192,6 +192,20 @@ assert union(union(s1, s2), s3) == union(s1, union(s2, s3)) +@pytest.mark.xfail +@given(st_annotation, st_annotation) +def test_generalize_isinstance(annotator, s1, s2): + try: + s_12 = union(s1, s2) + except UnionError: + assume(False) + assume(s1 != s_ImpossibleValue) + from rpython.annotator.unaryop import s_isinstance + s_int = annotator.bookkeeper.immutablevalue(int) + s_res_12 = s_isinstance(annotator, s_12, s_int, []) + s_res_1 = s_isinstance(annotator, s1, s_int, []) + assert s_res_12.contains(s_res_1) + def compile_function(function, annotation=[]): t = TranslationContext() t.buildannotator().build_types(function, annotation) diff --git a/rpython/config/translationoption.py b/rpython/config/translationoption.py --- a/rpython/config/translationoption.py +++ b/rpython/config/translationoption.py @@ -188,6 +188,10 @@ "When true, enable the use of tagged pointers. " "If false, use normal boxing", default=False), + BoolOption("keepgoing", + "Continue annotating when errors are encountered, and report " + "them all at the end of the annotation phase", + default=False, cmdline="--keepgoing"), BoolOption("lldebug", "If true, makes an lldebug build", default=False, cmdline="--lldebug"), diff --git a/rpython/jit/metainterp/optimizeopt/intbounds.py b/rpython/jit/metainterp/optimizeopt/intbounds.py --- a/rpython/jit/metainterp/optimizeopt/intbounds.py +++ b/rpython/jit/metainterp/optimizeopt/intbounds.py @@ -399,9 +399,9 @@ def optimize_INT_EQ(self, op): arg0 = self.get_box_replacement(op.getarg(0)) + b1 = self.getintbound(arg0) arg1 = self.get_box_replacement(op.getarg(1)) - b1 = self.getintbound(op.getarg(0)) - b2 = self.getintbound(op.getarg(1)) + b2 = self.getintbound(arg1) if b1.known_gt(b2): self.make_constant_int(op, 0) elif b1.known_lt(b2): diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py --- a/rpython/rlib/objectmodel.py +++ b/rpython/rlib/objectmodel.py @@ -323,6 +323,8 @@ def register_replacement_for(replaced_function, sandboxed_name=None): def wrap(func): from rpython.rtyper.extregistry import ExtRegistryEntry + # to support calling func directly + func._sandbox_external_name = sandboxed_name class ExtRegistry(ExtRegistryEntry): _about_ = replaced_function def compute_annotation(self): diff --git a/rpython/rlib/rfloat.py b/rpython/rlib/rfloat.py --- a/rpython/rlib/rfloat.py +++ b/rpython/rlib/rfloat.py @@ -1,6 +1,7 @@ """Float constants""" import math, struct +from math import isinf, isnan, copysign, acosh, asinh, atanh, log1p, expm1 from rpython.annotator.model import SomeString, SomeChar from rpython.rlib import objectmodel, unroll @@ -184,104 +185,6 @@ INFINITY = 1e200 * 1e200 NAN = abs(INFINITY / INFINITY) # bah, INF/INF gives us -NAN? -try: - # Try to get math functions added in 2.6. - from math import isinf, isnan, copysign, acosh, asinh, atanh, log1p -except ImportError: - @not_rpython - def isinf(x): - return x == INFINITY or x == -INFINITY - - @not_rpython - def isnan(v): - return v != v - - @not_rpython - def copysign(x, y): - """Return x with the sign of y""" - if x < 0.: - x = -x - if y > 0. or (y == 0. and math.atan2(y, -1.) > 0.): - return x - else: - return -x - - _2_to_m28 = 3.7252902984619141E-09; # 2**-28 - _2_to_p28 = 268435456.0; # 2**28 - _ln2 = 6.93147180559945286227E-01 - - @not_rpython - def acosh(x): - if isnan(x): - return NAN - if x < 1.: - raise ValueError("math domain error") - if x >= _2_to_p28: - if isinf(x): - return x - else: - return math.log(x) + _ln2 - if x == 1.: - return 0. - if x >= 2.: - t = x * x - return math.log(2. * x - 1. / (x + math.sqrt(t - 1.0))) - t = x - 1.0 - return log1p(t + math.sqrt(2. * t + t * t)) - - @not_rpython - def asinh(x): - absx = abs(x) - if not isfinite(x): - return x - if absx < _2_to_m28: - return x - if absx > _2_to_p28: - w = math.log(absx) + _ln2 - elif absx > 2.: - w = math.log(2. * absx + 1. / (math.sqrt(x * x + 1.) + absx)) - else: - t = x * x - w = log1p(absx + t / (1. + math.sqrt(1. + t))) - return copysign(w, x) - - @not_rpython - def atanh(x): - if isnan(x): - return x - absx = abs(x) - if absx >= 1.: - raise ValueError("math domain error") - if absx < _2_to_m28: - return x - if absx < .5: - t = absx + absx - t = .5 * log1p(t + t * absx / (1. - absx)) - else: - t = .5 * log1p((absx + absx) / (1. - absx)) - return copysign(t, x) - - @not_rpython - def log1p(x): - if abs(x) < DBL_EPSILON // 2.: - return x - elif -.5 <= x <= 1.: - y = 1. + x - return math.log(y) - ((y - 1.) - x) / y - else: - return math.log(1. + x) - -try: - from math import expm1 # Added in Python 2.7. -except ImportError: - @not_rpython - def expm1(x): - if abs(x) < .7: - u = math.exp(x) - if u == 1.: - return x - return (u - 1.) * x / math.log(u) - return math.exp(x) - 1. def log2(x): # Uses an algorithm that should: diff --git a/rpython/translator/sandbox/test/test_sandbox.py b/rpython/translator/sandbox/test/test_sandbox.py --- a/rpython/translator/sandbox/test/test_sandbox.py +++ b/rpython/translator/sandbox/test/test_sandbox.py @@ -65,6 +65,24 @@ f.close() assert tail == "" +def test_open_dup_rposix(): + from rpython.rlib import rposix + def entry_point(argv): + fd = rposix.open("/tmp/foobar", os.O_RDONLY, 0777) + assert fd == 77 + fd2 = rposix.dup(fd) + assert fd2 == 78 + return 0 + + exe = compile(entry_point) + g, f = run_in_subprocess(exe) + expect(f, g, "ll_os.ll_os_open", ("/tmp/foobar", os.O_RDONLY, 0777), 77) + expect(f, g, "ll_os.ll_os_dup", (77, True), 78) + g.close() + tail = f.read() + f.close() + assert tail == "" + def test_read_write(): def entry_point(argv): fd = os.open("/tmp/foobar", os.O_RDONLY, 0777) diff --git a/rpython/translator/translator.py b/rpython/translator/translator.py --- a/rpython/translator/translator.py +++ b/rpython/translator/translator.py @@ -67,7 +67,8 @@ if self.annotator is not None: raise ValueError("we already have an annotator") from rpython.annotator.annrpython import RPythonAnnotator - self.annotator = RPythonAnnotator(self, policy=policy) + self.annotator = RPythonAnnotator( + self, policy=policy, keepgoing=self.config.translation.keepgoing) return self.annotator def buildrtyper(self): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit