Merge branch 'master' into python-2.4

This commit is contained in:
rocky
2016-12-16 22:56:07 -05:00
17 changed files with 135 additions and 36 deletions

View File

@@ -20,6 +20,8 @@ classifiers = ['Development Status :: 5 - Production/Stable',
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.1',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
@@ -38,7 +40,7 @@ entry_points={
]}
ftp_url = None
install_requires = ['spark-parser >= 1.5.1, < 1.6.0',
'xdis >= 3.2.3, < 3.3.0']
'xdis >= 3.2.4, < 3.3.0']
license = 'MIT'
mailing_list = 'python-debugger@googlegroups.com'
modname = 'uncompyle6'

View File

@@ -41,7 +41,7 @@ def test_grammar():
"""
JUMP_BACK CONTINUE RETURN_END_IF
COME_FROM COME_FROM_EXCEPT COME_FROM_LOOP COME_FROM_WITH
COME_FROM_FINALLY
COME_FROM_FINALLY ELSE
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP
LAMBDA_MARKER RETURN_LAST
""".split())

View File

@@ -22,9 +22,9 @@ check:
#: Run working tests from Python 2.6 or 2.7
check-2.4 check-2.5 check-2.6 check-2.7: check-bytecode-2 check-bytecode-3 check-bytecode-1 check-2.7-ok
#: Run working tests from Python 3.1
#: Run working tests from Python 3.0
check-3.0: check-bytecode
@echo Python 3.0 testing not done yet
$(PYTHON) test_pythonlib.py --bytecode-3.0 --weak-verify $(COMPILE)
#: Run working tests from Python 3.1
check-3.1: check-bytecode
@@ -36,11 +36,11 @@ check-3.2: check-bytecode
#: Run working tests from Python 3.3
check-3.3: check-bytecode
$(PYTHON) test_pythonlib.py --bytecode-3.3 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.3 --verify $(COMPILE)
#: Run working tests from Python 3.4
check-3.4: check-bytecode check-3.4-ok check-2.7-ok
$(PYTHON) test_pythonlib.py --bytecode-3.4 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.4 --verify $(COMPILE)
#: Run working tests from Python 3.5
check-3.5: check-bytecode

Binary file not shown.

View File

@@ -0,0 +1 @@
f(**a, **b)

View File

@@ -141,7 +141,6 @@ def main_bin():
if src_base:
sb_len = len( os.path.join(src_base, '') )
files = [f[sb_len:] for f in files]
del sb_len
if not files:
sys.stderr.write("No files given\n")

View File

@@ -71,6 +71,11 @@ class PythonParser(GenericASTBuilder):
"""Customized format and print for our kind of tokens
which gets called in debugging grammar reduce rules
"""
def fix(c):
s = str(c)
i = s.find('_')
return s if i == -1 else s[:i]
prefix = ''
if parent and tokens:
p_token = tokens[parent]
@@ -79,8 +84,11 @@ class PythonParser(GenericASTBuilder):
else:
prefix = ' '
if hasattr(p_token, 'offset'):
prefix += "%3s " % str(p_token.offset)
prefix += " "
prefix += "%3s" % fix(p_token.offset)
if len(rule[1]) > 1:
prefix += '-%-3s ' % fix(tokens[i-1].offset)
else:
prefix += ' '
else:
prefix = ' '
@@ -265,8 +273,7 @@ class PythonParser(GenericASTBuilder):
# Zero or more COME_FROMs
# loops can have this
_come_from ::= _come_from COME_FROM
_come_from ::=
_come_from ::= COME_FROM*
# Zero or one COME_FROM
# And/or expressions have this

View File

@@ -136,15 +136,24 @@ class Python3Parser(PythonParser):
iflaststmtl ::= testexpr c_stmts_opt JUMP_BACK
iflaststmtl ::= testexpr c_stmts_opt JUMP_BACK COME_FROM_LOOP
# These are used to keep AST indices the same
jf_else ::= JUMP_FORWARD ELSE
ja_else ::= JUMP_ABSOLUTE ELSE
# Note: in if/else kinds of statements, we err on the side
# of missing "else" clauses. Therefore we include grammar
# rules with and without ELSE.
ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD else_suite COME_FROM
ifelsestmt ::= testexpr c_stmts_opt jf_else else_suite _come_from
ifelsestmtc ::= testexpr c_stmts_opt JUMP_ABSOLUTE else_suitec
ifelsestmtc ::= testexpr c_stmts_opt ja_else else_suitec
ifelsestmtr ::= testexpr return_if_stmts return_stmts
ifelsestmtl ::= testexpr c_stmts_opt JUMP_BACK else_suitel
# FIXME: this feels like a hack. Is it just 1 or two
# COME_FROMs? the parsed tree for this and even with just the
# one COME_FROM for Python 2.7 seems to associate the
@@ -364,14 +373,17 @@ class Python3Parser(PythonParser):
'''
def p_expr3(self, args):
'''
"""
conditional ::= expr jmp_false expr jf_else expr COME_FROM
conditionalnot ::= expr jmp_true expr jf_else expr COME_FROM
expr ::= LOAD_CLASSNAME
# Python 3.4+
expr ::= LOAD_CLASSDEREF
# Python3 drops slice0..slice3
'''
"""
@staticmethod
def call_fn_name(token):
@@ -579,9 +591,10 @@ class Python3Parser(PythonParser):
self.add_unique_rule(rule, 'kvlist_n', 1, customize)
rule = "mapexpr ::= BUILD_MAP_n kvlist_n"
elif self.version >= 3.5:
rule = kvlist_n + ' ::= ' + 'expr ' * (token.attr*2)
self.add_unique_rule(rule, opname, token.attr, customize)
rule = "mapexpr ::= %s %s" % (kvlist_n, opname)
if opname != 'BUILD_MAP_WITH_CALL':
rule = kvlist_n + ' ::= ' + 'expr ' * (token.attr*2)
self.add_unique_rule(rule, opname, token.attr, customize)
rule = "mapexpr ::= %s %s" % (kvlist_n, opname)
else:
rule = kvlist_n + ' ::= ' + 'expr expr STORE_MAP ' * token.attr
self.add_unique_rule(rule, opname, token.attr, customize)

View File

@@ -46,6 +46,26 @@ class Python35Parser(Python34Parser):
yield_from ::= expr GET_YIELD_FROM_ITER LOAD_CONST YIELD_FROM
"""
def add_custom_rules(self, tokens, customize):
super(Python35Parser, self).add_custom_rules(tokens, customize)
for i, token in enumerate(tokens):
opname = token.type
if opname == 'BUILD_MAP_UNPACK_WITH_CALL':
nargs = token.attr % 256
map_unpack_n = "map_unpack_%s" % nargs
rule = map_unpack_n + ' ::= ' + 'expr ' * (nargs)
self.add_unique_rule(rule, opname, token.attr, customize)
rule = "unmapexpr ::= %s %s" % (map_unpack_n, opname)
self.add_unique_rule(rule, opname, token.attr, customize)
call_token = tokens[i+1]
if self.version == 3.5:
rule = 'call_function ::= expr unmapexpr ' + call_token.type
self.add_unique_rule(rule, opname, token.attr, customize)
pass
pass
return
class Python35ParserSingle(Python35Parser, PythonParserSingle):
pass

View File

@@ -17,6 +17,9 @@ class Python36Parser(Python35Parser):
"""
fstring_multi ::= fstring_expr_or_strs BUILD_STRING
fstring_expr_or_strs ::= fstring_expr_or_str+
func_args36 ::= expr BUILD_TUPLE_0
call_function ::= func_args36 unmapexpr CALL_FUNCTION_EX
"""
def add_custom_rules(self, tokens, customize):
@@ -45,6 +48,7 @@ class Python36Parser(Python35Parser):
""" % (fstring_expr_or_str_n, fstring_expr_or_str_n, "fstring_expr_or_str " * v)
self.add_unique_doc_rules(rules_str, customize)
class Python36ParserSingle(Python36Parser, PythonParserSingle):
pass

View File

@@ -226,7 +226,7 @@ class Scanner(object):
if op < self.opc.HAVE_ARGUMENT:
return 1
else:
return 3
return 2 if self.version >= 3.6 else 3
def remove_mid_line_ifs(self, ifs):
"""

View File

@@ -225,6 +225,14 @@ class Scanner3(Scanner):
jump_idx += 1
pass
pass
elif inst.offset in self.else_start:
end_offset = self.else_start[inst.offset]
tokens.append(Token('ELSE',
None, repr(end_offset),
offset='%s' % (inst.offset),
has_arg = True, opc=self.opc))
pass
pattr = inst.argrepr
opname = inst.opname
@@ -424,6 +432,7 @@ class Scanner3(Scanner):
self.fixed_jumps = {}
self.ignore_if = set()
self.build_statement_indices()
self.else_start = {}
# Containers filled by detect_structure()
self.not_continue = set()
@@ -553,9 +562,15 @@ class Scanner3(Scanner):
Get target offset for op located at given <offset>.
"""
op = self.code[offset]
target = self.code[offset+1] + self.code[offset+2] * 256
if op in op3.hasjrel:
target += offset + 3
if self.version >= 3.6:
target = self.code[offset+1]
if op in op3.hasjrel:
target += offset + 2
else:
target = self.code[offset+1] + self.code[offset+2] * 256
if op in op3.hasjrel:
target += offset + 3
return target
def detect_structure(self, offset, targets):
@@ -758,15 +773,28 @@ class Scanner3(Scanner):
code[prev_op[prev_op[rtarget]]] != self.opc.JUMP_ABSOLUTE)):
rtarget = prev_op[rtarget]
# Does the "if" jump just beyond a jump op, then this can be
# a block inside an "if" statement
# Does the "jump if" jump beyond a jump op?
# That is, we have something like:
# POP_JUMP_IF_FALSE HERE
# ...
# JUMP_FORWARD
# HERE:
#
# If so, this can be block inside an "if" statement
# or a conditional assignment like:
# x = 1 if x else 2
#
# There are other contexts we may need to consider
# like whether the target is "END_FINALLY"
# or if the condition jump is to a forward location
if self.is_jump_forward(prev_op[rtarget]):
if_end = self.get_target(prev_op[rtarget])
rrtarget = prev_op[rtarget]
if_end = self.get_target(rrtarget)
# Is this a loop and not an "if" statement?
if ((if_end < prev_op[rtarget]) and
# If the jump target is back, we are looping
if (if_end < rrtarget and
(code[prev_op[if_end]] == self.opc.SETUP_LOOP)):
if(if_end > start):
if (if_end > start):
return
end = self.restrict_to_parent(if_end, parent)
@@ -776,10 +804,15 @@ class Scanner3(Scanner):
'end': prev_op[rtarget]})
self.not_continue.add(prev_op[rtarget])
if rtarget < end:
self.structs.append({'type': 'if-else',
if rtarget < end and (
code[rtarget] not in (self.opc.END_FINALLY,
self.opc.JUMP_ABSOLUTE) and
code[prev_op[rrtarget]] not in (self.opc.POP_EXCEPT,
self.opc.END_FINALLY)):
self.structs.append({'type': 'else',
'start': rtarget,
'end': end})
self.else_start[rtarget] = end
elif code[prev_op[rtarget]] == self.opc.RETURN_VALUE:
self.structs.append({'type': 'if-then',
'start': start,
@@ -869,7 +902,9 @@ class Scanner3(Scanner):
op = self.code[i]
if op == self.opc.END_FINALLY:
if count_END_FINALLY == count_SETUP_:
assert self.code[self.prev_op[i]] in (JUMP_ABSOLUTE, JUMP_FORWARD, RETURN_VALUE)
assert self.code[self.prev_op[i]] in (JUMP_ABSOLUTE,
JUMP_FORWARD,
RETURN_VALUE)
self.not_continue.add(self.prev_op[i])
return self.prev_op[i]
count_END_FINALLY += 1

View File

@@ -579,7 +579,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.set_pos_info(node[-3], start, len(self.f.getvalue()))
start = len(self.f.getvalue())
self.preorder(ast[iter_index])
self.set_pos_info(iter_index, start, len(self.f.getvalue()))
self.set_pos_info(ast[iter_index], start, len(self.f.getvalue()))
self.prec = p
def comprehension_walk3(self, node, iter_index, code_index=-5):

View File

@@ -217,7 +217,7 @@ def make_function3_annotate(self, node, isLambda, nested=1,
if (len(code.co_consts) > 0 and
code.co_consts[0] is not None and not isLambda): # ugly
# docstring exists, dump it
self.print_docstring(indent, code.co_consts[0])
print_docstring(self, indent, code.co_consts[0])
code._tokens = None # save memory
assert ast == 'stmts'

View File

@@ -350,7 +350,7 @@ MAP_R = (TABLE_R, -1)
MAP = {
'stmt': MAP_R,
'call_function': MAP_R,
'call_function': MAP_R,
'del_stmt': MAP_R,
'designator': MAP_R,
'exprlist': MAP_R0,
@@ -631,6 +631,19 @@ class SourceWalker(GenericASTTraversal, object):
TABLE_DIRECT.update({
'LOAD_CLASSDEREF': ( '%{pattr}', ),
})
if version >= 3.5:
def n_unmapexpr(node):
last_n = node[0][-1]
for n in node[0]:
self.preorder(n)
if n != last_n:
self.f.write(', **')
pass
pass
self.prune()
pass
self.n_unmapexpr = n_unmapexpr
if version >= 3.6:
########################
# Python 3.6+ Additions
@@ -654,7 +667,6 @@ class SourceWalker(GenericASTTraversal, object):
def n_fstring_single(node):
f_conversion(node)
self.default(node)
self.n_fstring_single = n_fstring_single
return

View File

@@ -317,6 +317,9 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
elif tokens1[i1].type == 'LOAD_NAME' and tokens2[i2].type == 'LOAD_CONST' \
and tokens1[i1].pattr == 'None' and tokens2[i2].pattr is None:
pass
elif tokens1[i1].type == 'RETURN_VALUE' and \
tokens2[i2].type == 'RETURN_END_IF':
pass
else:
raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1],
tokens2[i2], tokens1, tokens2)
@@ -351,6 +354,9 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
if is_pypy:
# For PYPY for now we don't care about PYPY_SOURCE_IS_UTF8:
flags2 &= ~0x0100 # PYPY_SOURCE_IS_UTF8
# We also don't care about COROUTINE or GENERATOR for now
flags1 &= ~0x000000a0
flags2 &= ~0x000000a0
if flags1 != flags2:
raise CmpErrorMember(name, 'co_flags',
pretty_flags(flags1),

View File

@@ -1,3 +1,3 @@
# This file is suitable for sourcing inside bash as
# well as importing into Python
VERSION='2.9.7'
VERSION='2.9.8'