Author: Philip Jenvey <pjen...@underboss.org> Branch: py3.3 Changeset: r72103:7c07509a944c Date: 2014-06-19 22:41 -0700 http://bitbucket.org/pypy/pypy/changeset/7c07509a944c/
Log: merge py3k diff --git a/lib_pypy/gdbm.py b/lib_pypy/_gdbm.py rename from lib_pypy/gdbm.py rename to lib_pypy/_gdbm.py --- a/lib_pypy/gdbm.py +++ b/lib_pypy/_gdbm.py @@ -1,4 +1,5 @@ import cffi, os +import sys ffi = cffi.FFI() ffi.cdef(''' @@ -46,12 +47,15 @@ # failure must be due to missing gdbm dev libs raise ImportError('%s: %s' %(e.__class__.__name__, e)) -class error(Exception): +class error(IOError): pass def _fromstr(key): - if not isinstance(key, str): - raise TypeError("gdbm mappings have string indices only") + if isinstance(key, str): + key = key.encode(sys.getdefaultencoding()) + elif not isinstance(key, bytes): + msg = "gdbm mappings have bytes or string indices only, not {!r}" + raise TypeError(msg.format(type(key).__name__)) return {'dptr': ffi.new("char[]", key), 'dsize': len(key)} class gdbm(object): @@ -98,21 +102,27 @@ return lib.gdbm_exists(self.ll_dbm, _fromstr(key)) has_key = __contains__ - def __getitem__(self, key): + def get(self, key, default=None): self._check_closed() drec = lib.gdbm_fetch(self.ll_dbm, _fromstr(key)) if not drec.dptr: - raise KeyError(key) - res = str(ffi.buffer(drec.dptr, drec.dsize)) + return default + res = bytes(ffi.buffer(drec.dptr, drec.dsize)) lib.free(drec.dptr) return res + def __getitem__(self, key): + value = self.get(key) + if value is None: + raise KeyError(key) + return value + def keys(self): self._check_closed() l = [] key = lib.gdbm_firstkey(self.ll_dbm) while key.dptr: - l.append(str(ffi.buffer(key.dptr, key.dsize))) + l.append(bytes(ffi.buffer(key.dptr, key.dsize))) nextkey = lib.gdbm_nextkey(self.ll_dbm, key) lib.free(key.dptr) key = nextkey @@ -122,7 +132,7 @@ self._check_closed() key = lib.gdbm_firstkey(self.ll_dbm) if key.dptr: - res = str(ffi.buffer(key.dptr, key.dsize)) + res = bytes(ffi.buffer(key.dptr, key.dsize)) lib.free(key.dptr) return res @@ -130,7 +140,7 @@ self._check_closed() key = lib.gdbm_nextkey(self.ll_dbm, _fromstr(key)) if key.dptr: - res = str(ffi.buffer(key.dptr, key.dsize)) + res = bytes(ffi.buffer(key.dptr, key.dsize)) lib.free(key.dptr) return res @@ -149,7 +159,18 @@ self._check_closed() lib.gdbm_sync(self.ll_dbm) + def setdefault(self, key, default=None): + value = self.get(key) + if value is not None: + return value + self[key] = default + return default + def open(filename, flags='r', mode=0o666): + if not isinstance(filename, str): + raise TypeError("must be str, not %s" % type(filename).__name__) + filename = filename.encode(sys.getdefaultencoding()) + if flags[0] == 'r': iflags = lib.GDBM_READER elif flags[0] == 'w': diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py --- a/pypy/interpreter/argument.py +++ b/pypy/interpreter/argument.py @@ -283,8 +283,7 @@ missing += 1 continue name = signature.kwonlyargnames[i - co_argcount] - w_name = self.space.wrap(name) - w_def = self.space.finditem(w_kw_defs, w_name) + w_def = self.space.finditem_str(w_kw_defs, name) if w_def is not None: scope_w[i] = w_def else: diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py --- a/pypy/interpreter/error.py +++ b/pypy/interpreter/error.py @@ -36,44 +36,11 @@ def setup(self, w_type, w_value=None): assert w_type is not None - from pypy.objspace.std.typeobject import W_TypeObject self.w_type = w_type self._w_value = w_value - # HACK: isinstance(w_type, W_TypeObject) won't translate under - # the fake objspace, but w_type.__class__ is W_TypeObject does - # and short circuits to a False constant there, causing the - # isinstance to be ignored =[ - if (w_type is not None and w_type.__class__ is W_TypeObject and - isinstance(w_type, W_TypeObject)): - self.setup_context(w_type.space) if not we_are_translated(): self.debug_excs = [] - def setup_context(self, space): - # Implicit exception chaining - last_operror = space.getexecutioncontext().sys_exc_info() - if (last_operror is None or - last_operror is get_cleared_operation_error(space)): - return - - # We must normalize the value right now to check for cycles - self.normalize_exception(space) - w_value = self.get_w_value(space) - w_last_value = last_operror.get_w_value(space) - if not space.is_w(w_value, w_last_value): - # Avoid reference cycles through the context chain. This is - # O(chain length) but context chains are usually very short. - w_obj = w_last_value - while True: - w_context = space.getattr(w_obj, space.wrap('__context__')) - if space.is_w(w_context, space.w_None): - break - if space.is_w(w_context, w_value): - space.setattr(w_obj, space.wrap('__context__'), space.w_None) - break - w_obj = w_context - space.setattr(w_value, space.wrap('__context__'), w_last_value) - def clear(self, space): # XXX remove this method. The point is that we cannot always # hack at 'self' to clear w_type and _w_value, because in some @@ -353,6 +320,53 @@ """ self._application_traceback = traceback + def record_context(self, space, frame): + """Record a __context__ for this exception from the current + frame if one exists. + + __context__ is otherwise lazily determined from the + traceback. However the current frame.last_exception must be + checked for a __context__ before this OperationError overwrites + it (making the previous last_exception unavailable later on). + """ + last_exception = frame.last_exception + if (last_exception is not None and not frame.hide() or + last_exception is get_cleared_operation_error(space)): + # normalize w_value so setup_context can check for cycles + self.normalize_exception(space) + w_value = self.get_w_value(space) + w_last = last_exception.get_w_value(space) + w_context = setup_context(space, w_value, w_last, lazy=True) + space.setattr(w_value, space.wrap('__context__'), w_context) + + +def setup_context(space, w_exc, w_last, lazy=False): + """Determine the __context__ for w_exc from w_last and break + reference cycles in the __context__ chain. + """ + from pypy.module.exceptions.interp_exceptions import W_BaseException + if space.is_w(w_exc, w_last): + w_last = space.w_None + # w_last may also be space.w_None if from ClearedOpErr + if not space.is_w(w_last, space.w_None): + # Avoid reference cycles through the context chain. This is + # O(chain length) but context chains are usually very short. + w_obj = w_last + while True: + assert isinstance(w_obj, W_BaseException) + if lazy: + w_context = w_obj.w_context + else: + # triggers W_BaseException._setup_context + w_context = space.getattr(w_obj, space.wrap('__context__')) + if space.is_none(w_context): + break + if space.is_w(w_context, w_exc): + w_obj.w_context = space.w_None + break + w_obj = w_context + return w_last + class ClearedOpErr: def __init__(self, space): diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py --- a/pypy/interpreter/executioncontext.py +++ b/pypy/interpreter/executioncontext.py @@ -202,18 +202,21 @@ self._trace(frame, 'exception', None, operationerr) #operationerr.print_detailed_traceback(self.space) + @staticmethod + def last_operr(space, frame): + while frame: + last = frame.last_exception + if (last is not None and + (not frame.hide() or + last is get_cleared_operation_error(space))): + return last + frame = frame.f_backref() + return None + def sys_exc_info(self): # attn: the result is not the wrapped sys.exc_info() !!! """Implements sys.exc_info(). Return an OperationError instance or None.""" - frame = self.gettopframe() - while frame: - if frame.last_exception is not None: - if (not frame.hide() or - frame.last_exception is - get_cleared_operation_error(self.space)): - return frame.last_exception - frame = frame.f_backref() - return None + return self.last_operr(self.space, self.gettopframe()) def set_sys_exc_info(self, operror): frame = self.gettopframe_nohidden() diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py --- a/pypy/interpreter/function.py +++ b/pypy/interpreter/function.py @@ -32,7 +32,8 @@ 'w_func_globals?', 'closure?[*]', 'defs_w?[*]', - 'name?'] + 'name?', + 'w_kw_defs?'] def __init__(self, space, code, w_globals=None, defs_w=[], w_kw_defs=None, closure=None, w_ann=None, forcename=None): diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py --- a/pypy/interpreter/pyframe.py +++ b/pypy/interpreter/pyframe.py @@ -164,12 +164,9 @@ for i in range(nfreevars): self.cells[i + ncellvars] = outer_func.closure[i] - def is_generator(self): - return self.getcode().co_flags & pycode.CO_GENERATOR - def run(self): """Start this frame's execution.""" - if self.is_generator(): + if self.getcode().co_flags & pycode.CO_GENERATOR: if self.getcode().co_flags & pycode.CO_YIELD_INSIDE_TRY: from pypy.interpreter.generator import GeneratorIteratorWithDel return self.space.wrap(GeneratorIteratorWithDel(self)) @@ -514,10 +511,10 @@ for i in range(min(len(varnames), self.getcode().co_nlocals)): name = varnames[i] w_value = self.locals_stack_w[i] - w_name = self.space.wrap(name.decode('utf-8')) if w_value is not None: - self.space.setitem(self.w_locals, w_name, w_value) + self.space.setitem_str(self.w_locals, name, w_value) else: + w_name = self.space.wrap(name.decode('utf-8')) try: self.space.delitem(self.w_locals, w_name) except OperationError as e: @@ -537,8 +534,7 @@ except ValueError: pass else: - w_name = self.space.wrap(name) - self.space.setitem(self.w_locals, w_name, w_value) + self.space.setitem_str(self.w_locals, name, w_value) @jit.unroll_safe @@ -551,13 +547,9 @@ new_fastlocals_w = [None] * numlocals for i in range(min(len(varnames), numlocals)): - w_name = self.space.wrap(varnames[i].decode('utf-8')) - try: - w_value = self.space.getitem(self.w_locals, w_name) - except OperationError, e: - if not e.match(self.space, self.space.w_KeyError): - raise - else: + name = varnames[i] + w_value = self.space.finditem_str(self.w_locals, name) + if w_value is not None: new_fastlocals_w[i] = w_value self.setfastscope(new_fastlocals_w) @@ -566,13 +558,8 @@ for i in range(len(freevarnames)): name = freevarnames[i] cell = self.cells[i] - w_name = self.space.wrap(name) - try: - w_value = self.space.getitem(self.w_locals, w_name) - except OperationError, e: - if not e.match(self.space, self.space.w_KeyError): - raise - else: + w_value = self.space.finditem_str(self.w_locals, name) + if w_value is not None: cell.set(w_value) @jit.unroll_safe diff --git a/pypy/interpreter/pytraceback.py b/pypy/interpreter/pytraceback.py --- a/pypy/interpreter/pytraceback.py +++ b/pypy/interpreter/pytraceback.py @@ -57,6 +57,7 @@ tb = operror.get_traceback() tb = PyTraceback(space, frame, last_instruction, tb) operror.set_traceback(tb) + operror.record_context(space, frame) def check_traceback(space, w_tb, msg): diff --git a/pypy/interpreter/test/test_raise.py b/pypy/interpreter/test/test_raise.py --- a/pypy/interpreter/test/test_raise.py +++ b/pypy/interpreter/test/test_raise.py @@ -369,6 +369,44 @@ else: fail("No exception raised") + def test_context_once_removed(self): + context = IndexError() + def func1(): + func2() + def func2(): + try: + 1/0 + except ZeroDivisionError as e: + assert e.__context__ is context + else: + fail('No exception raised') + try: + raise context + except: + func1() + + @py.test.mark.xfail(reason="A somewhat contrived case that may burden the " + "JIT to fully support") + def test_frame_spanning_cycle_broken(self): + context = IndexError() + def func(): + try: + 1/0 + except Exception as e1: + try: + raise context + except Exception as e2: + assert e2.__context__ is e1 + # XXX: + assert e1.__context__ is None + else: + fail('No exception raised') + try: + raise context + except: + func() + + class AppTestTraceback: def test_raise_with___traceback__(self): diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py --- a/pypy/module/__builtin__/functional.py +++ b/pypy/module/__builtin__/functional.py @@ -6,7 +6,8 @@ from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt -from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault +from pypy.interpreter.gateway import ( + interp2app, interpindirect2app, unwrap_spec) from pypy.interpreter.typedef import TypeDef, interp_attrproperty_w from rpython.rlib import jit from rpython.rlib.objectmodel import specialize @@ -307,15 +308,19 @@ class W_Range(W_Root): - def __init__(self, w_start, w_stop, w_step, w_length): + def __init__(self, w_start, w_stop, w_step, w_length, promote_step=False): self.w_start = w_start self.w_stop = w_stop self.w_step = w_step self.w_length = w_length + self.promote_step = promote_step - @unwrap_spec(w_step = WrappedDefault(1)) def descr_new(space, w_subtype, w_start, w_stop=None, w_step=None): w_start = space.index(w_start) + promote_step = False + if space.is_none(w_step): # no step argument provided + w_step = space.wrap(1) + promote_step = True if space.is_none(w_stop): # only 1 argument provided w_start, w_stop = space.newint(0), w_start else: @@ -331,7 +336,7 @@ "step argument must not be zero")) w_length = compute_range_length(space, w_start, w_stop, w_step) obj = space.allocate_instance(W_Range, w_subtype) - W_Range.__init__(obj, w_start, w_stop, w_step, w_length) + W_Range.__init__(obj, w_start, w_stop, w_step, w_length, promote_step) return space.wrap(obj) def descr_repr(self, space): @@ -386,8 +391,19 @@ return self._compute_item(space, w_index) def descr_iter(self, space): - return space.wrap(W_RangeIterator( - space, self.w_start, self.w_step, self.w_length)) + try: + start = space.int_w(self.w_start) + stop = space.int_w(self.w_stop) + step = space.int_w(self.w_step) + length = space.int_w(self.w_length) + except OperationError as e: + pass + else: + if self.promote_step: + return W_IntRangeStepOneIterator(space, start, stop) + return W_IntRangeIterator(space, start, length, step) + return W_LongRangeIterator(space, self.w_start, self.w_step, + self.w_length) def descr_reversed(self, space): # lastitem = self.start + (self.length-1) * self.step @@ -395,7 +411,7 @@ self.w_start, space.mul(space.sub(self.w_length, space.newint(1)), self.w_step)) - return space.wrap(W_RangeIterator( + return space.wrap(W_LongRangeIterator( space, w_lastitem, space.neg(self.w_step), self.w_length)) def descr_reduce(self, space): @@ -493,7 +509,22 @@ W_Range.typedef.acceptable_as_base_class = False -class W_RangeIterator(W_Root): +class W_AbstractRangeIterator(W_Root): + + def descr_iter(self, space): + return space.wrap(self) + + def descr_len(self, space): + raise NotImplementedError + + def descr_next(self, space): + raise NotImplementedError + + def descr_reduce(self, space): + raise NotImplementedError + + +class W_LongRangeIterator(W_AbstractRangeIterator): def __init__(self, space, w_start, w_step, w_len, w_index=None): self.w_start = w_start self.w_step = w_step @@ -502,9 +533,6 @@ w_index = space.newint(0) self.w_index = w_index - def descr_iter(self, space): - return space.wrap(self) - def descr_next(self, space): if space.is_true(space.lt(self.w_index, self.w_len)): w_index = space.add(self.w_index, space.newint(1)) @@ -519,23 +547,75 @@ def descr_reduce(self, space): from pypy.interpreter.mixedmodule import MixedModule + w_mod = space.getbuiltinmodule('_pickle_support') + mod = space.interp_w(MixedModule, w_mod) + w_args = space.newtuple([self.w_start, self.w_step, self.w_len, + self.w_index]) + return space.newtuple([mod.get('longrangeiter_new'), w_args]) + + +class W_IntRangeIterator(W_AbstractRangeIterator): + + def __init__(self, space, current, remaining, step): + self.current = current + self.remaining = remaining + self.step = step + + def descr_next(self, space): + return self.next(space) + + def next(self, space): + if self.remaining > 0: + item = self.current + self.current = item + self.step + self.remaining -= 1 + return space.wrap(item) + raise OperationError(space.w_StopIteration, space.w_None) + + def descr_len(self, space): + return self.get_remaining(space) + + def descr_reduce(self, space): + from pypy.interpreter.mixedmodule import MixedModule w_mod = space.getbuiltinmodule('_pickle_support') mod = space.interp_w(MixedModule, w_mod) + new_inst = mod.get('intrangeiter_new') + w = space.wrap + nt = space.newtuple - return space.newtuple( - [mod.get('rangeiter_new'), - space.newtuple([self.w_start, self.w_step, - self.w_len, self.w_index]), - ]) + tup = [w(self.current), self.get_remaining(space), w(self.step)] + return nt([new_inst, nt(tup)]) + def get_remaining(self, space): + return space.wrap(self.remaining) -W_RangeIterator.typedef = TypeDef("rangeiterator", - __iter__ = interp2app(W_RangeIterator.descr_iter), - __length_hint__ = interp2app(W_RangeIterator.descr_len), - __next__ = interp2app(W_RangeIterator.descr_next), - __reduce__ = interp2app(W_RangeIterator.descr_reduce), + +class W_IntRangeStepOneIterator(W_IntRangeIterator): + _immutable_fields_ = ['stop'] + + def __init__(self, space, start, stop): + self.current = start + self.stop = stop + self.step = 1 + + def next(self, space): + if self.current < self.stop: + item = self.current + self.current = item + 1 + return space.wrap(item) + raise OperationError(space.w_StopIteration, space.w_None) + + def get_remaining(self, space): + return space.wrap(self.stop - self.current) + + +W_AbstractRangeIterator.typedef = TypeDef("rangeiterator", + __iter__ = interp2app(W_AbstractRangeIterator.descr_iter), + __length_hint__ = interpindirect2app(W_AbstractRangeIterator.descr_len), + __next__ = interpindirect2app(W_AbstractRangeIterator.descr_next), + __reduce__ = interpindirect2app(W_AbstractRangeIterator.descr_reduce), ) -W_RangeIterator.typedef.acceptable_as_base_class = False +W_AbstractRangeIterator.typedef.acceptable_as_base_class = False class W_Map(W_Root): diff --git a/pypy/module/_pickle_support/__init__.py b/pypy/module/_pickle_support/__init__.py --- a/pypy/module/_pickle_support/__init__.py +++ b/pypy/module/_pickle_support/__init__.py @@ -19,7 +19,8 @@ 'frame_new' : 'maker.frame_new', 'traceback_new' : 'maker.traceback_new', 'generator_new' : 'maker.generator_new', - 'rangeiter_new': 'maker.rangeiter_new', + 'longrangeiter_new': 'maker.longrangeiter_new', + 'intrangeiter_new': 'maker.intrangeiter_new', 'builtin_code': 'maker.builtin_code', 'builtin_function' : 'maker.builtin_function', 'enumerate_new': 'maker.enumerate_new', diff --git a/pypy/module/_pickle_support/maker.py b/pypy/module/_pickle_support/maker.py --- a/pypy/module/_pickle_support/maker.py +++ b/pypy/module/_pickle_support/maker.py @@ -62,9 +62,15 @@ new_generator = instantiate(GeneratorIteratorWithDel) return space.wrap(new_generator) -def rangeiter_new(space, w_start, w_step, w_len, w_index): - from pypy.module.__builtin__.functional import W_RangeIterator - new_iter = W_RangeIterator(space, w_start, w_step, w_len, w_index) +def longrangeiter_new(space, w_start, w_step, w_len, w_index): + from pypy.module.__builtin__.functional import W_LongRangeIterator + new_iter = W_LongRangeIterator(space, w_start, w_step, w_len, w_index) + return space.wrap(new_iter) + +@unwrap_spec(current=int, remaining=int, step=int) +def intrangeiter_new(space, current, remaining, step): + from pypy.module.__builtin__.functional import W_IntRangeIterator + new_iter = W_IntRangeIterator(space, current, remaining, step) return space.wrap(new_iter) def operationerror_new(space): diff --git a/pypy/module/exceptions/interp_exceptions.py b/pypy/module/exceptions/interp_exceptions.py --- a/pypy/module/exceptions/interp_exceptions.py +++ b/pypy/module/exceptions/interp_exceptions.py @@ -75,9 +75,9 @@ from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.typedef import (TypeDef, GetSetProperty, descr_get_dict, descr_set_dict, descr_del_dict) -from pypy.interpreter.gateway import interp2app, unwrap_spec -from pypy.interpreter.error import OperationError -from pypy.interpreter.pytraceback import check_traceback +from pypy.interpreter.gateway import interp2app +from pypy.interpreter.error import OperationError, setup_context +from pypy.interpreter.pytraceback import PyTraceback, check_traceback from rpython.rlib import rwin32 @@ -163,7 +163,27 @@ self.suppress_context = True def descr_getcontext(self, space): - return self.w_context + w_context = self.w_context + if w_context is None: + self.w_context = w_context = self._setup_context(space) + return w_context + + def _setup_context(self, space): + """Lazily determine __context__ from w_traceback""" + # XXX: w_traceback can be overwritten: it's not necessarily the + # authoratative traceback! + last_operr = None + w_traceback = self.w_traceback + if w_traceback is not None and isinstance(w_traceback, PyTraceback): + ec = space.getexecutioncontext() + # search for __context__ beginning in the previous frame. A + # __context__ from the top most frame would have already + # been handled by OperationError.record_context + last_operr = ec.last_operr(space, w_traceback.frame.f_backref()) + if last_operr is None: + # no __context__ + return space.w_None + return setup_context(space, self, last_operr.get_w_value(space)) def descr_setcontext(self, space, w_newcontext): if not (space.is_w(w_newcontext, space.w_None) or @@ -174,7 +194,6 @@ self.w_context = w_newcontext def descr_gettraceback(self, space): - from pypy.interpreter.pytraceback import PyTraceback tb = self.w_traceback if tb is not None and isinstance(tb, PyTraceback): # tb escapes to app level (see OperationError.get_traceback) 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 @@ -24,7 +24,7 @@ class W_UnicodeObject(W_Root): import_from_mixin(StringMethods) - _immutable_fields_ = ['_value'] + _immutable_fields_ = ['_value', '_utf8?'] def __init__(w_self, unistr): assert isinstance(unistr, unicode) diff --git a/pypy/tool/release/package.py b/pypy/tool/release/package.py --- a/pypy/tool/release/package.py +++ b/pypy/tool/release/package.py @@ -133,7 +133,7 @@ modules = ['_sqlite3'] subprocess.check_call([str(pypy_c), '-c', 'import _sqlite3']) if not sys.platform == 'win32': - modules += ['_curses', 'syslog', 'gdbm', '_sqlite3'] + modules += ['_curses', 'syslog', '_gdbm', '_sqlite3'] if not options.no_tk: modules.append(('_tkinter')) for module in modules: @@ -402,10 +402,10 @@ ''' -gdbm_bit = '''gdbm +gdbm_bit = '''_gdbm ---- -The gdbm module includes code from gdbm.h, which is distributed under the terms +The _gdbm module includes code from gdbm.h, which is distributed under the terms of the GPL license version 2 or any later version. ''' _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit