Merge branch 'master' into python-2.4

This commit is contained in:
rocky
2019-04-13 23:40:40 -04:00
20 changed files with 311 additions and 63 deletions

View File

@@ -18,15 +18,19 @@ def test_grammar():
right_recursive, dup_rhs) = p.check_sets()
# We have custom rules that create the below
expect_lhs = set(['pos_arg', 'get_iter', 'attribute'])
expect_lhs = set(['pos_arg', 'attribute'])
if PYTHON_VERSION < 3.8:
expect_lhs.add('get_iter')
unused_rhs = set(['list', 'mkfunc',
'mklambda',
'unpack',])
expect_right_recursive = set([('designList',
('store', 'DUP_TOP', 'designList'))])
if PYTHON_VERSION != 3.7:
if PYTHON_VERSION < 3.7:
unused_rhs.add('call')
if PYTHON_VERSION > 2.6:
@@ -61,7 +65,11 @@ def test_grammar():
expect_lhs.add('kwarg')
assert expect_lhs == set(lhs)
assert unused_rhs == set(rhs)
# FIXME
if PYTHON_VERSION != 3.8:
assert unused_rhs == set(rhs)
assert expect_right_recursive == right_recursive
expect_dup_rhs = frozenset([('COME_FROM',), ('CONTINUE',), ('JUMP_ABSOLUTE',),

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# Mode: -*- python -*-
#
# Copyright (c) 2015-2017 by Rocky Bernstein
# Copyright (c) 2015-2017, 2019 by Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
import sys, os, getopt, time
@@ -29,7 +29,8 @@ Options:
-> /tmp/bla/fasel.pyc_dis, /tmp/bar/foo.pyc_dis
uncompyle6 -o /tmp /usr/lib/python1.5
-> /tmp/smtplib.pyc_dis ... /tmp/lib-tk/FixTk.pyc_dis
-c <file> attempts a disassembly after compiling <file>
--compile | -c <python-file>
attempts a decompilation after compiling <python-file>
-d print timestamps
-p <integer> use <integer> number of processes
-r recurse directories looking for .pyc and .pyo files
@@ -42,10 +43,10 @@ Options:
--help show this message
Debugging Options:
--asm -a include byte-code (disables --verify)
--grammar -g show matching grammar
--tree -t include syntax tree (disables --verify)
--tree++ add template rules to --tree when possible
--asm | -a include byte-code (disables --verify)
--grammar | -g show matching grammar
--tree | -t include syntax tree (disables --verify)
--tree++ add template rules to --tree when possible
Extensions of generated files:
'.pyc_dis' '.pyo_dis' successfully decompiled (and verified if --verify)
@@ -60,10 +61,7 @@ from uncompyle6.main import main, status_msg
from uncompyle6.version import VERSION
def usage():
print("""usage:
%s [--verify | --weak-verify ] [--asm] [--tree[+]] [--grammar] [-o <path>] FILE|DIR...
%s [--help | -h | --version | -V]
""" % (program, program))
print(__doc__)
sys.exit(1)
@@ -77,13 +75,13 @@ def main_bin():
numproc = 0
outfile = '-'
out_base = None
codes = []
source_paths = []
timestamp = False
timestampfmt = "# %Y.%m.%d %H:%M:%S %Z"
try:
opts, files = getopt.getopt(sys.argv[1:], 'hagtdrVo:c:p:',
'help asm grammar linemaps recurse '
opts, pyc_paths = getopt.getopt(sys.argv[1:], 'hac:gtdrVo:p:',
'help asm compile= grammar linemaps recurse '
'timestamp tree tree+ '
'fragments verify verify-run version '
'weak-verify '
@@ -125,8 +123,8 @@ def main_bin():
outfile = val
elif opt in ('--timestamp', '-d'):
timestamp = True
elif opt == '-c':
codes.append(val)
elif opt in ('--compile', '-c'):
source_paths.append(val)
elif opt == '-p':
numproc = int(val)
elif opt in ('--recurse', '-r'):
@@ -138,25 +136,25 @@ def main_bin():
# expand directory if specified
if recurse_dirs:
expanded_files = []
for f in files:
for f in pyc_paths:
if os.path.isdir(f):
for root, _, dir_files in os.walk(f):
for df in dir_files:
if df.endswith('.pyc') or df.endswith('.pyo'):
expanded_files.append(os.path.join(root, df))
files = expanded_files
pyc_paths = expanded_files
# argl, commonprefix works on strings, not on path parts,
# thus we must handle the case with files in 'some/classes'
# and 'some/cmds'
src_base = os.path.commonprefix(files)
src_base = os.path.commonprefix(pyc_paths)
if src_base[-1:] != os.sep:
src_base = os.path.dirname(src_base)
if src_base:
sb_len = len( os.path.join(src_base, '') )
files = [f[sb_len:] for f in files]
pyc_paths = [f[sb_len:] for f in pyc_paths]
if not files:
if not pyc_paths:
sys.stderr.write("No files given\n")
usage()
@@ -164,7 +162,7 @@ def main_bin():
outfile = None # use stdout
elif outfile and os.path.isdir(outfile):
out_base = outfile; outfile = None
elif outfile and len(files) > 1:
elif outfile and len(pyc_paths) > 1:
out_base = outfile; outfile = None
if timestamp:
@@ -172,10 +170,10 @@ def main_bin():
if numproc <= 1:
try:
result = main(src_base, out_base, files, None, outfile,
result = main(src_base, out_base, pyc_paths, source_paths, outfile,
**options)
result = list(result) + [options.get('do_verify', None)]
if len(files) > 1:
if len(pyc_paths) > 1:
mess = status_msg(do_verify, *result)
print('# ' + mess)
pass
@@ -191,8 +189,8 @@ def main_bin():
except ImportError:
from queue import Empty
fqueue = Queue(len(files)+numproc)
for f in files:
fqueue = Queue(len(pyc_paths)+numproc)
for f in pyc_paths:
fqueue.put(f)
for i in range(numproc):
fqueue.put(None)

View File

@@ -12,7 +12,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime, os, subprocess, sys
from uncompyle6 import verify, IS_PYPY, PYTHON_VERSION
@@ -131,6 +130,22 @@ def decompile(
# deparsing failed
raise pysource.SourceWalkerError(str(e))
def compile_file(source_path):
if source_path.endswith('.py'):
basename = source_path[:-3]
else:
basename = source_path
if hasattr(sys, 'pypy_version_info'):
bytecode_path = "%s-pypy%s.pyc" % (basename, PYTHON_VERSION)
else:
bytecode_path = "%s-%s.pyc" % (basename, PYTHON_VERSION)
print("compiling %s to %s" % (source_path, bytecode_path))
py_compile.compile(source_path, bytecode_path, 'exec')
return bytecode_path
def decompile_file(filename, outstream=None, showasm=None, showast=False,
showgrammar=False, mapstream=None, do_fragments=False):
"""
@@ -162,7 +177,7 @@ def decompile_file(filename, outstream=None, showasm=None, showast=False,
# FIXME: combine into an options parameter
def main(in_base, out_base, files, codes, outfile=None,
def main(in_base, out_base, compiled_files, source_files, outfile=None,
showasm=None, showast=False, do_verify=False,
showgrammar=False, raise_on_error=False,
do_linemaps=False, do_fragments=False):
@@ -172,8 +187,6 @@ def main(in_base, out_base, files, codes, outfile=None,
files list of filenames to be uncompyled (relative to in_base)
outfile write output to this filename (overwrites out_base)
Note: `codes` is not use. Historical compatability?
For redirecting output to
- <filename> outfile=<filename> (out_base is ignored)
- files below out_base out_base=...
@@ -183,7 +196,10 @@ def main(in_base, out_base, files, codes, outfile=None,
current_outfile = outfile
linemap_stream = None
for filename in files:
for source_path in source_files:
compiled_files.append(compile_file(source_path))
for filename in compiled_files:
infile = os.path.join(in_base, filename)
# print("XXX", infile)
if not os.path.exists(infile):

View File

@@ -159,7 +159,7 @@ class Python35Parser(Python34Parser):
call_token = tokens[i+1]
rule = 'call ::= expr unmapexpr ' + call_token.kind
self.addRule(rule, nop_func)
elif opname == 'BEFORE_ASYNC_WITH':
elif opname == 'BEFORE_ASYNC_WITH' and self.version < 3.8:
# Some Python 3.5+ async additions
rules_str = """
async_with_stmt ::= expr

View File

@@ -67,6 +67,22 @@ class Python36Parser(Python35Parser):
for_block POP_BLOCK
COME_FROM_LOOP
stmt ::= async_for_stmt36
async_for_stmt36 ::= SETUP_LOOP expr
GET_AITER
LOAD_CONST YIELD_FROM
SETUP_EXCEPT GET_ANEXT LOAD_CONST
YIELD_FROM
store
POP_BLOCK JUMP_BACK COME_FROM_EXCEPT DUP_TOP
LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
END_FINALLY for_block
COME_FROM
POP_TOP POP_TOP POP_TOP POP_EXCEPT
POP_TOP POP_BLOCK
COME_FROM_LOOP
async_forelse_stmt ::= SETUP_LOOP expr
GET_AITER
LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
@@ -362,6 +378,7 @@ class Python36Parser(Python35Parser):
pass
pass
return False
class Python36ParserSingle(Python36Parser, PythonParserSingle):
pass

View File

@@ -30,6 +30,7 @@ class Python37Parser(Python36Parser):
"""
# Where does the POP_TOP really belong?
stmt ::= import37
stmt ::= async_for_stmt37
import37 ::= import POP_TOP
async_for_stmt ::= SETUP_LOOP expr
@@ -45,6 +46,19 @@ class Python37Parser(Python36Parser):
POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP POP_BLOCK
COME_FROM_LOOP
# Order of LOAD_CONST YIELD_FROM is switched from 3.6 to save a LOAD_CONST
async_for_stmt37 ::= SETUP_LOOP expr
GET_AITER
SETUP_EXCEPT GET_ANEXT
LOAD_CONST YIELD_FROM
store
POP_BLOCK JUMP_BACK COME_FROM_EXCEPT DUP_TOP
LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
END_FINALLY for_block COME_FROM
POP_TOP POP_TOP POP_TOP POP_EXCEPT
POP_TOP POP_BLOCK
COME_FROM_LOOP
async_forelse_stmt ::= SETUP_LOOP expr
GET_AITER
SETUP_EXCEPT GET_ANEXT LOAD_CONST
@@ -92,6 +106,18 @@ class Python37Parser(Python36Parser):
JUMP_ABSOLUTE END_FINALLY COME_FROM
for_block POP_BLOCK
else_suite COME_FROM_LOOP
stmt ::= async_for_stmt36
async_for_stmt36 ::= SETUP_LOOP expr
GET_AITER
LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
YIELD_FROM
store
POP_BLOCK JUMP_BACK COME_FROM_EXCEPT DUP_TOP
LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
END_FINALLY continues COME_FROM
POP_TOP POP_TOP POP_TOP POP_EXCEPT
POP_TOP POP_BLOCK
COME_FROM_LOOP
""")
super(Python37Parser, self).customize_grammar_rules(tokens, customize)

View File

@@ -25,10 +25,52 @@ class Python38Parser(Python37Parser):
def p_38misc(self, args):
"""
stmt ::= async_for_stmt38
stmt ::= for38
stmt ::= forelsestmt38
stmt ::= forelselaststmt38
stmt ::= forelselaststmtl38
stmt ::= tryfinally38
stmt ::= try_except_ret38
stmt ::= try_except38
stmt ::= whilestmt38
stmt ::= whileTruestmt38
stmt ::= call
# FIXME this should be restricted to being inside a try block
stmt ::= except_ret38
# FIXME this should be added only when seeing GET_AITER or YIELD_FROM
async_for_stmt38 ::= expr
GET_AITER
SETUP_FINALLY
GET_ANEXT
LOAD_CONST
YIELD_FROM
POP_BLOCK
store for_block
COME_FROM_FINALLY
END_ASYNC_FOR
async_with_stmt ::= expr BEFORE_ASYNC_WITH GET_AWAITABLE LOAD_CONST YIELD_FROM
SETUP_ASYNC_WITH POP_TOP
suite_stmts
POP_TOP POP_BLOCK
BEGIN_FINALLY
WITH_CLEANUP_START
GET_AWAITABLE LOAD_CONST YIELD_FROM
WITH_CLEANUP_FINISH END_FINALLY
async_with_as_stmt ::= expr BEFORE_ASYNC_WITH GET_AWAITABLE LOAD_CONST YIELD_FROM
SETUP_ASYNC_WITH store
suite_stmts
POP_TOP POP_BLOCK
BEGIN_FINALLY
WITH_CLEANUP_START
GET_AWAITABLE LOAD_CONST YIELD_FROM
WITH_CLEANUP_FINISH END_FINALLY
return ::= ret_expr ROT_TWO POP_TOP RETURN_VALUE
for38 ::= expr get_iter store for_block JUMP_BACK
for38 ::= expr for_iter store for_block JUMP_BACK
@@ -37,18 +79,55 @@ class Python38Parser(Python37Parser):
forelsestmt38 ::= expr for_iter store for_block POP_BLOCK else_suite
forelselaststmt38 ::= expr for_iter store for_block POP_BLOCK else_suitec
forelselaststmtl38 ::= expr for_iter store for_block POP_BLOCK else_suitel
whilestmt ::= testexpr l_stmts_opt COME_FROM JUMP_BACK POP_BLOCK
whilestmt ::= testexpr l_stmts_opt JUMP_BACK POP_BLOCK
whilestmt ::= testexpr returns POP_BLOCK
while1elsestmt ::= l_stmts JUMP_BACK
whileelsestmt ::= testexpr l_stmts_opt JUMP_BACK POP_BLOCK
whileTruestmt ::= l_stmts_opt JUMP_BACK POP_BLOCK
whilestmt38 ::= testexpr l_stmts_opt COME_FROM JUMP_BACK POP_BLOCK
whilestmt38 ::= testexpr l_stmts_opt JUMP_BACK POP_BLOCK
whilestmt38 ::= testexpr returns POP_BLOCK
whilestmt38 ::= testexpr l_stmts JUMP_BACK
# while1elsestmt ::= l_stmts JUMP_BACK
whileTruestmt ::= l_stmts JUMP_BACK POP_BLOCK
while1stmt ::= l_stmts COME_FROM_LOOP
while1stmt ::= l_stmts COME_FROM JUMP_BACK COME_FROM_LOOP
while1elsestmt ::= l_stmts JUMP_BACK
whileTruestmt ::= l_stmts_opt JUMP_BACK NOP
whileTruestmt ::= l_stmts_opt JUMP_BACK POP_BLOCK NOP
whileTruestmt38 ::= l_stmts JUMP_BACK
for_block ::= l_stmts_opt _come_from_loops JUMP_BACK
except_cond1 ::= DUP_TOP expr COMPARE_OP jmp_false
POP_TOP POP_TOP POP_TOP
POP_EXCEPT
try_except ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
except_handler38
try_except38 ::= SETUP_FINALLY POP_BLOCK POP_TOP suite_stmts_opt
except_handler38a
try_except_ret38 ::= SETUP_FINALLY expr POP_BLOCK
RETURN_VALUE except_ret38a
except_ret38 ::= SETUP_FINALLY expr ROT_FOUR POP_BLOCK POP_EXCEPT
CALL_FINALLY RETURN_VALUE COME_FROM_FINALLY
suite_stmts_opt END_FINALLY
except_ret38a ::= COME_FROM_FINALLY POP_TOP POP_TOP POP_TOP
expr ROT_FOUR
POP_EXCEPT RETURN_VALUE END_FINALLY
except_handler38 ::= JUMP_FORWARD COME_FROM_FINALLY
except_stmts END_FINALLY opt_come_from_except
except_handler38a ::= COME_FROM_FINALLY POP_TOP POP_TOP POP_TOP
POP_EXCEPT POP_TOP stmts END_FINALLY
tryfinallystmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
BEGIN_FINALLY COME_FROM_FINALLY suite_stmts_opt
END_FINALLY
tryfinally38 ::= SETUP_FINALLY POP_BLOCK CALL_FINALLY
returns
COME_FROM_FINALLY END_FINALLY suite_stmts
tryfinally38 ::= SETUP_FINALLY POP_BLOCK CALL_FINALLY
returns
COME_FROM_FINALLY POP_FINALLY returns
END_FINALLY
tryfinally_return_stmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
BEGIN_FINALLY COME_FROM_FINALLY
POP_FINALLY suite_stmts_opt END_FINALLY
"""
def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
@@ -57,17 +136,76 @@ class Python38Parser(Python37Parser):
def customize_grammar_rules(self, tokens, customize):
self.remove_rules("""
stmt ::= async_for_stmt37
stmt ::= for
stmt ::= forelsestmt
stmt ::= try_except36
async_for_stmt37 ::= SETUP_LOOP expr
GET_AITER
SETUP_EXCEPT GET_ANEXT
LOAD_CONST YIELD_FROM
store
POP_BLOCK JUMP_BACK COME_FROM_EXCEPT DUP_TOP
LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
END_FINALLY for_block COME_FROM
POP_TOP POP_TOP POP_TOP POP_EXCEPT
POP_TOP POP_BLOCK
COME_FROM_LOOP
for ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK
for ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK NOP
for_block ::= l_stmts_opt COME_FROM_LOOP JUMP_BACK
forelsestmt38 ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suite
forelselaststmt38 ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suitec
forelselaststmtl38 ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suitel
forelsestmt ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suite
forelselaststmt ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suitec
forelselaststmtl ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suitel
try_except ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
except_handler opt_come_from_except
tryfinallystmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
LOAD_CONST COME_FROM_FINALLY suite_stmts_opt
END_FINALLY
tryfinally36 ::= SETUP_FINALLY returns
COME_FROM_FINALLY suite_stmts_opt END_FINALLY
tryfinally_return_stmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
LOAD_CONST COME_FROM_FINALLY
""")
super(Python37Parser, self).customize_grammar_rules(tokens, customize)
self.check_reduce['ifstmt'] = 'tokens'
self.check_reduce['whileTruestmt38'] = 'tokens'
def reduce_is_invalid(self, rule, ast, tokens, first, last):
invalid = super(Python38Parser,
self).reduce_is_invalid(rule, ast,
tokens, first, last)
if invalid:
return invalid
if rule[0] == 'ifstmt':
# Make sure jumps don't extend beyond the end of the if statement.
l = last
if l == len(tokens):
l -= 1
if isinstance(tokens[l].offset, str):
last_offset = int(tokens[l].offset.split('_')[0], 10)
else:
last_offset = tokens[l].offset
for i in range(first, l):
t = tokens[i]
if t.kind == 'POP_JUMP_IF_FALSE':
if t.attr > last_offset:
return True
pass
pass
pass
elif rule[0] == 'whileTruestmt38':
t = tokens[last-1]
if t.kind == 'JUMP_BACK':
return t.attr != tokens[first].offset
pass
return False
class Python38ParserSingle(Python38Parser, PythonParserSingle):
pass

View File

@@ -880,7 +880,7 @@ class Scanner3(Scanner):
if_end = self.get_target(pre_rtarget)
# If the jump target is back, we are looping
if (if_end < pre_rtarget and
if (if_end < pre_rtarget and self.version < 3.8 and
(code[prev_op[if_end]] == self.opc.SETUP_LOOP)):
if (if_end > start):
return
@@ -924,7 +924,7 @@ class Scanner3(Scanner):
# Python 3.5 may remove as dead code a JUMP
# instruction after a RETURN_VALUE. So we check
# based on seeing SETUP_EXCEPT various places.
if code[rtarget] == self.opc.SETUP_EXCEPT:
if self.version < 3.8 and code[rtarget] == self.opc.SETUP_EXCEPT:
return
# Check that next instruction after pops and jump is
# not from SETUP_EXCEPT
@@ -936,7 +936,7 @@ class Scanner3(Scanner):
if next_op in targets:
for try_op in targets[next_op]:
come_from_op = code[try_op]
if come_from_op == self.opc.SETUP_EXCEPT:
if self.version < 3.8 and come_from_op == self.opc.SETUP_EXCEPT:
return
pass
pass

View File

@@ -11,9 +11,10 @@ before reduction and don't reduce when there is a problem.
def checker(ast, in_loop, errors):
if ast is None:
return
in_loop = in_loop or ast.kind in ('while1stmt', 'whileTruestmt',
in_loop = (in_loop or (ast.kind in ('while1stmt', 'whileTruestmt',
'whilestmt', 'whileelsestmt', 'while1elsestmt',
'for_block')
'for_block'))
or ast.kind.startswith('async_for'))
if ast.kind in ('aug_assign1', 'aug_assign2') and ast[0][0] == 'and':
text = str(ast)
error_text = '\n# improper augmented assigment (e.g. +=, *=, ...):\n#\t' + '\n# '.join(text.split("\n")) + '\n'

View File

@@ -285,14 +285,16 @@ def customize_for_version3(self, version):
'await_expr': ( 'await %c', 0),
'await_stmt': ( '%|%c\n', 0),
'async_for_stmt': (
'%|async for %c in %c:\n%+%c%-\n\n', 9, 1, 25 ),
'%|async for %c in %c:\n%+%|%c%-\n\n', 9, 1, 25 ),
'async_forelse_stmt': (
'%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
9, 1, 25, (27, 'else_suite') ),
'async_with_stmt': (
'%|async with %c:\n%+%c%-', 0, 7),
'%|async with %c:\n%+%|%c%-',
(0, 'expr'), 7 ),
'async_with_as_stmt': (
'%|async with %c as %c:\n%+%c%-', 0, 6, 7),
'%|async with %c as %c:\n%+%|%c%-',
(0, 'expr'), (6, 'store'), 7),
'unmap_dict': ( '{**%C}', (0, -1, ', **') ),
# 'unmapexpr': ( '{**%c}', 0), # done by n_unmapexpr
@@ -516,6 +518,10 @@ def customize_for_version3(self, version):
'tryfinally_return_stmt':
( '%|try:\n%+%c%-%|finally:\n%+%|return%-\n\n', 1 ),
'async_for_stmt36': (
'%|async for %c in %c:\n%+%c%-%-\n\n',
(9, 'store'), (1, 'expr'), (18, 'for_block') ),
'call_ex' : (
'%c(%p)',
(0, 'expr'), (1, 100)),
@@ -914,13 +920,16 @@ def customize_for_version3(self, version):
PRECEDENCE['attribute37'] = 2
TABLE_DIRECT.update({
'attribute37': ( '%c.%[1]{pattr}', 0 ),
'async_forelse_stmt': (
'%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
7, 1, 17, (25, 'else_suite') ),
(7, 'store'), (1, 'expr'), (17, 'for_block'), (25, 'else_suite') ),
'async_for_stmt': (
'%|async for %c in %c:\n%+%c%-%-\n\n',
7, 1, 17),
(7, 'store'), (1, 'expr'), (17, 'for_block')),
'async_for_stmt37': (
'%|async for %c in %c:\n%+%c%-%-\n\n',
(7, 'store'), (1, 'expr'), (16, 'for_block') ),
'attribute37': ( '%c.%[1]{pattr}', 0 ),
'compare_chained1a_37': ( ' %[3]{pattr.replace("-", " ")} %p %p',
(0, 19),
(-4, 19)),
@@ -936,9 +945,31 @@ def customize_for_version3(self, version):
########################
# Python 3.8+ changes
#######################
for lhs in 'for forelsestmt forelselaststmt forelselaststmtl'.split():
del TABLE_DIRECT[lhs]
# FIXME: pytest doesn't add proper keys in testing. Reinstate after we have fixed pytest.
# for lhs in 'for forelsestmt forelselaststmt '
# 'forelselaststmtl tryfinally38'.split():
# del TABLE_DIRECT[lhs]
TABLE_DIRECT.update({
'async_for_stmt38': (
'%|async for %c in %c:\n%+%c%-%-\n\n',
(7, 'store'), (0, 'expr'), (8, 'for_block') ),
'async_with_stmt38': (
'%|async with %c:\n%+%|%c%-',
(0, 'expr'), 7),
'async_with_as_stmt38': (
'%|async with %c as %c:\n%+%|%c%-',
(0, 'expr'), (6, 'store'),
(7, 'suite_stmts') ),
'except_handler38a': (
'%c', (-2, 'stmts') ),
'except_ret38a': (
'return %c', (4, 'expr') ),
'except_ret38': ( '%|return %c\n', (1, 'expr') ),
'for38': (
'%|for %c in %c:\n%+%c%-\n\n',
(2, 'store'),
@@ -959,8 +990,22 @@ def customize_for_version3(self, version):
(2, 'store'),
(0, 'expr'),
(3, 'for_block'), -2 ),
'whilestmt38': ( '%|while %c:\n%+%c%-\n\n',
(0, 'testexpr'), (1, 'l_stmts') ),
'whileTruestmt38': ( '%|while True:\n%+%c%-\n\n',
(0, 'l_stmts') ),
'tryfinally38': (
'%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
(3, 'returns'), 6 ),
'try_except38': (
'%|try:\n%+%c\n%-%|except:\n%|%-%c\n\n',
(-2, 'suite_stmts_opt'), (-1, 'except_handler38a') ),
'try_except_ret38': (
'%|try:\n%+%|return %c%-\n%|except:\n%+%|%c%-\n\n',
(1, 'expr'), (-1, 'except_ret38a') ),
})
pass
pass # version >= 3.8
pass
pass # version >= 3.6

View File

@@ -1815,7 +1815,6 @@ class SourceWalker(GenericASTTraversal, object):
# except_cond3 is only in Python <= 2.6
n_except_cond3 = n_except_cond2
def template_engine(self, entry, startnode):
"""The format template interpetation engine. See the comment at the
beginning of this module for the how we interpret format
@@ -1861,8 +1860,8 @@ class SourceWalker(GenericASTTraversal, object):
index = entry[arg]
if isinstance(index, tuple):
assert node[index[0]] == index[1], (
"at %s[%d], expected %s node; got %s" % (
node.kind, arg, node[index[0]].kind, index[1])
"at %s[%d], expected '%s' node; got '%s'" % (
node.kind, arg, index[1], node[index[0]].kind)
)
index = index[0]
assert isinstance(index, int), (