diff --git a/test/simple_source/bug26/03_tryelse_continue.py b/test/simple_source/bug26/03_tryelse_continue.py index 83464529..e1af1dd1 100644 --- a/test/simple_source/bug26/03_tryelse_continue.py +++ b/test/simple_source/bug26/03_tryelse_continue.py @@ -23,3 +23,19 @@ def call(*args): except TypeError: # Unhashable argument return 3 + + +# From 2.6.9 pdb.py +# Here we have a "try/except" inside a "try/except/else and we can't +# distinguish which COME_FROM comes from which "try". + +def do_jump(self, arg): + try: + arg(1) + except ValueError: + arg(2) + else: + try: + arg(3) + except ValueError: + arg(4) diff --git a/uncompyle6/parsers/parse26.py b/uncompyle6/parsers/parse26.py index 4c3bf7aa..524bb482 100644 --- a/uncompyle6/parsers/parse26.py +++ b/uncompyle6/parsers/parse26.py @@ -315,7 +315,7 @@ class Python26Parser(Python2Parser): self.check_reduce['and'] = 'AST' self.check_reduce['list_for'] = 'AST' self.check_reduce['try_except'] = 'tokens' - self.check_reduce['tryelsestmt'] = 'tokens' + self.check_reduce['tryelsestmt'] = 'AST' def reduce_is_invalid(self, rule, ast, tokens, first, last): invalid = super(Python26Parser, @@ -365,29 +365,49 @@ class Python26Parser(Python2Parser): return (tokens[last-3].kind not in frozenset(('JUMP_FORWARD', 'RETURN_VALUE')) or (tokens[last-3] == 'JUMP_FORWARD' and tokens[last-3].attr != 2)) elif rule[0] == 'tryelsestmt': + # We need to distingush try_except from tryelsestmt and we do that - # by checking the jump before the END_FINALLY - # If we have: - # insn - # POP_TOP - # END_FINALLY - # COME_FROM - # then insn is neither a JUMP_FORWARD nor RETURN_VALUE, - # or if it is JUMP_FORWARD, then it can't be a JUMP_FORWARD to right after - # COME_FROM - if last == len(tokens): - last -= 1 - while tokens[last-1] == 'COME_FROM' and tokens[last-2] == 'COME_FROM': - last -= 1 - if tokens[last] == 'COME_FROM' and tokens[last-1] == 'COME_FROM': - last -= 1 - if (tokens[last] == 'COME_FROM' - and tokens[last-1] == 'END_FINALLY' - and tokens[last-2] == 'POP_TOP'): - # A jump of 2 is a jump around POP_TOP, END_FINALLY which - # would indicate try/else rather than try - return (tokens[last-3].kind in frozenset(('JUMP_FORWARD', 'RETURN_VALUE')) - and (tokens[last-3] != 'JUMP_FORWARD' or tokens[last-3].attr == 2)) + # by making sure that the jump before the except handler jumps to + # code somewhere before the end of the construct. + # This AST method is slower, but more correct than what we had + # which is given after this. + + if ast[3] == 'except_handler': + except_handler = ast[3] + if except_handler[0] == 'JUMP_FORWARD': + else_start = int(except_handler[0].pattr) + if last == len(tokens): + last -= 1 + if tokens[last] == 'COME_FROM' and isinstance: + last_offset = int(tokens[last].offset.split('_')[0]) + return else_start >= last_offset + + # Before we resorted to using the AST here is the more hacky way we + # did things. This fails with a "try" embedded inside a "try/else" + # since we can't detect COME_FROM boundaries. + # # We need to distinguish try_except from tryelsestmt and we do that + # # by checking the jump before the END_FINALLY + # # If we have: + # # insn + # # POP_TOP + # # END_FINALLY + # # COME_FROM + # # then insn is neither a JUMP_FORWARD nor RETURN_VALUE, + # # or if it is JUMP_FORWARD, then it can't be a JUMP_FORWARD to right after + # # COME_FROM + # if last == len(tokens): + # last -= 1 + # while tokens[last-1] == 'COME_FROM' and tokens[last-2] == 'COME_FROM': + # last -= 1 + # if tokens[last] == 'COME_FROM' and tokens[last-1] == 'COME_FROM': + # last -= 1 + # if (tokens[last] == 'COME_FROM' + # and tokens[last-1] == 'END_FINALLY' + # and tokens[last-2] == 'POP_TOP'): + # # A jump of 2 is a jump around POP_TOP, END_FINALLY which + # # would indicate try/else rather than try + # return (tokens[last-3].kind in frozenset(('JUMP_FORWARD', 'RETURN_VALUE')) + # and (tokens[last-3] != 'JUMP_FORWARD' or tokens[last-3].attr == 2)) return False