Merge branch 'master' into python-2.4

This commit is contained in:
rocky
2020-09-05 05:58:41 -04:00
27 changed files with 263 additions and 137 deletions

11
NEWS.md
View File

@@ -1,3 +1,14 @@
3.7.4: 2020-8-05
================
* Fragment parsing was borked. This means deparsing in trepan2/trepan3k was broken
* 3.7+: narrow precedence for call tatement
* del_stmt -> delete to better match Python AST
* 3.8+ Add another `forelsestmt` (found only in a loop)
* 3.8+ Add precedence on walrus operator
* More files blackened
* bump min xdis version
3.7.3: 2020-7-25 3.7.3: 2020-7-25
================ ================

View File

@@ -33,65 +33,71 @@
# 3.4 | pip | 19.1.1 | # 3.4 | pip | 19.1.1 |
# Things that change more often go here. # Things that change more often go here.
copyright = """ copyright = """
Copyright (C) 2015-2020 Rocky Bernstein <rb@dustyfeet.com>. Copyright (C) 2015-2020 Rocky Bernstein <rb@dustyfeet.com>.
""" """
classifiers = ["Development Status :: 5 - Production/Stable", classifiers = [
"Intended Audience :: Developers", "Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Intended Audience :: Developers",
"Operating System :: OS Independent", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Programming Language :: Python", "Operating System :: OS Independent",
"Programming Language :: Python :: 2.4", "Programming Language :: Python",
"Programming Language :: Python :: 2.5", "Programming Language :: Python :: 2.4",
"Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.5",
"Programming Language :: Python :: 2.7", "Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 3.0", "Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.1", "Programming Language :: Python :: 3.0",
"Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.1",
"Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.2",
"Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.7",
"Topic :: Software Development :: Debuggers", "Programming Language :: Python :: 3.8",
"Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Debuggers",
] "Topic :: Software Development :: Libraries :: Python Modules",
]
# The rest in alphabetic order # The rest in alphabetic order
author = "Rocky Bernstein, Hartmut Goebel, John Aycock, and others" author = "Rocky Bernstein, Hartmut Goebel, John Aycock, and others"
author_email = "rb@dustyfeet.com" author_email = "rb@dustyfeet.com"
entry_points = { entry_points = {
"console_scripts": [ "console_scripts": [
"uncompyle6=uncompyle6.bin.uncompile:main_bin", "uncompyle6=uncompyle6.bin.uncompile:main_bin",
"pydisassemble=uncompyle6.bin.pydisassemble:main", "pydisassemble=uncompyle6.bin.pydisassemble:main",
]} ]
ftp_url = None }
install_requires = ["spark-parser >= 1.8.9, < 1.9.0", ftp_url = None
"xdis >= 4.7.0, <5.1.0"] install_requires = ["spark-parser >= 1.8.9, < 1.9.0", "xdis >= 5.0.4, <5.1.0"]
license = "GPL3" license = "GPL3"
mailing_list = "python-debugger@googlegroups.com" mailing_list = "python-debugger@googlegroups.com"
modname = "uncompyle6" modname = "uncompyle6"
py_modules = None py_modules = None
short_desc = "Python cross-version byte-code decompiler" short_desc = "Python cross-version byte-code decompiler"
web = "https://github.com/rocky/python-uncompyle6/" web = "https://github.com/rocky/python-uncompyle6/"
# tracebacks in zip files are funky and not debuggable # tracebacks in zip files are funky and not debuggable
zip_safe = True zip_safe = True
import os.path import os.path
def get_srcdir(): def get_srcdir():
filename = os.path.normcase(os.path.dirname(os.path.abspath(__file__))) filename = os.path.normcase(os.path.dirname(os.path.abspath(__file__)))
return os.path.realpath(filename) return os.path.realpath(filename)
srcdir = get_srcdir() srcdir = get_srcdir()
def read(*rnames): def read(*rnames):
return open(os.path.join(srcdir, *rnames)).read() return open(os.path.join(srcdir, *rnames)).read()
# Get info from files; set: long_description and VERSION # Get info from files; set: long_description and VERSION
long_description = ( read("README.rst") + "\n" ) long_description = read("README.rst") + "\n"
exec(read("uncompyle6/version.py")) exec(read("uncompyle6/version.py"))

View File

@@ -55,7 +55,7 @@
# Make packages and tag # Make packages and tag
$ . ./admin-tools/make-dist-older.sh $ . ./admin-tools/make-dist-older.sh
$ pyenv local 3.8.4 $ pyenv local 3.8.5
$ twine check dist/uncompyle6-$VERSION* $ twine check dist/uncompyle6-$VERSION*
$ ./admin-tools/make-dist-newer.sh $ ./admin-tools/make-dist-newer.sh
$ twine check dist/uncompyle6-$VERSION* $ twine check dist/uncompyle6-$VERSION*

View File

@@ -5,4 +5,4 @@ if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
echo "This script should be *sourced* rather than run directly through bash" echo "This script should be *sourced* rather than run directly through bash"
exit 1 exit 1
fi fi
export PYVERSIONS='3.5.9 3.6.10 2.6.9 3.3.7 2.7.18 3.2.6 3.1.5 3.4.10 3.7.7 3.8.4' export PYVERSIONS='3.5.9 3.6.12 2.6.9 3.3.7 2.7.18 3.2.6 3.1.5 3.4.10 3.7.9 3.8.5'

View File

@@ -50,7 +50,7 @@ for VERSION in $PYVERSIONS ; do
LOGFILE=/tmp/${MAIN}-$VERSION-$$.log LOGFILE=/tmp/${MAIN}-$VERSION-$$.log
case "$VERSION" in case "$VERSION" in
3.7.7 | 3.8.3 | 3.1.5 | 3.0.1 ) 3.7.8 | 3.8.5 | 3.1.5 | 3.0.1 )
continue continue
;; ;;
3.5.9 ) 3.5.9 )
@@ -65,7 +65,7 @@ for VERSION in $PYVERSIONS ; do
3.4.10 ) 3.4.10 )
MAX_TESTS=800 MAX_TESTS=800
;; ;;
3.6.10 ) 3.6.11 )
# MAX_TESTS=1300 # about 2139 exist # MAX_TESTS=1300 # about 2139 exist
# fails on _pyio.cpython-36.opt-1.pyc # fails on _pyio.cpython-36.opt-1.pyc
MAX_TESTS=34 MAX_TESTS=34

View File

@@ -8,7 +8,7 @@ b = [4, 5, 6]
del b[1] del b[1]
del b[:] del b[:]
# del_stmt ::= expr expr DELETE_SLICE+1 # delete ::= expr expr DELETE_SLICE+1
l = [None] * 10 l = [None] * 10
del l[-2:] del l[-2:]

View File

@@ -8,6 +8,37 @@ SKIP_TESTS=(
[test_urllib2.py]=1 # FIXME: works on uncompyle6? [test_urllib2.py]=1 # FIXME: works on uncompyle6?
[test_zipimport.py]=1 # FIXME: works on uncompyle6 [test_zipimport.py]=1 # FIXME: works on uncompyle6
# From decompyle3 excludes
# Very Simple example. Compare with 3.7 Need 3.8 parse rules for exception handling return
# for proto in p:
# try:
# drop = 5
# except StopIteration:
# continue
[test_dict.py]=1 #
# Simple example. Compare with 3.7 Need 3.8 parse rules for exception handling return
# try:
# return 5
# except KeyError:
# return res
# except TypeError:
# return 10
# These and the above may be due to new code generation or tests
# between 3.8.3 and 3.8.5 ?
[test_decorators.py]=1 #
[test_dtrace.py]=1 #
[test_exceptions.py]=1 #
[test_ftplib.py]=1 #
[test_gc.py]=1 #
[test_gzip.py]=1 #
[test_hashlib.py]=1 #
[test_iter.py]=1 #
[test_itertools.py]=1 #
[test___all__.py]=1 # it fails on its own [test___all__.py]=1 # it fails on its own
[test_argparse.py]=1 #- it fails on its own [test_argparse.py]=1 #- it fails on its own
[test_array.py]=1 #- parse error [test_array.py]=1 #- parse error

View File

@@ -52,7 +52,7 @@ for VERSION in $PYVERSIONS ; do
LOGFILE=/tmp/runtests-$VERSION-$$.log LOGFILE=/tmp/runtests-$VERSION-$$.log
case "$VERSION" in case "$VERSION" in
3.0.1 | 3.1.5 | 3.2.6 | 3.8.1 ) 3.0.1 | 3.1.5 | 3.2.6 | 3.8.5 )
continue continue
;; ;;
esac esac

View File

@@ -3,7 +3,7 @@
[flake8] [flake8]
exclude = .tox,./build,./trepan/processor/command/tmp exclude = .tox,./build,./trepan/processor/command/tmp
filename = *.py filename = *.py
ignore = C901,E113,E121,E122,E123,E124,E125,E126,E127,E128,E129,E201,E202,E203,E221,E222,E225,E226,E241,E242,E251,E261,E271,E272,E302,E401,E402,E501,F401,E701,E702 ignore = C901,E113,E121,E122,E123,E124,E125,E126,E127,E128,E129,E201,E202,E203,E221,E222,E225,E226,E241,E242,E251,E261,E271,E272,E302,E401,E402,E501,F401,E701,E702,W503
[tox] [tox]
envlist = py27, py34, pypy envlist = py27, py34, pypy

View File

@@ -352,10 +352,10 @@ class PythonParser(GenericASTBuilder):
stmt ::= with stmt ::= with
stmt ::= withasstmt stmt ::= withasstmt
stmt ::= del_stmt stmt ::= delete
del_stmt ::= DELETE_FAST delete ::= DELETE_FAST
del_stmt ::= DELETE_NAME delete ::= DELETE_NAME
del_stmt ::= DELETE_GLOBAL delete ::= DELETE_GLOBAL
stmt ::= return stmt ::= return

View File

@@ -97,9 +97,9 @@ class Python2Parser(PythonParser):
for ::= SETUP_LOOP expr for_iter store for ::= SETUP_LOOP expr for_iter store
for_block POP_BLOCK _come_froms for_block POP_BLOCK _come_froms
del_stmt ::= delete_subscript delete ::= delete_subscript
delete_subscript ::= expr expr DELETE_SUBSCR delete_subscript ::= expr expr DELETE_SUBSCR
del_stmt ::= expr DELETE_ATTR delete ::= expr DELETE_ATTR
_mklambda ::= load_closure mklambda _mklambda ::= load_closure mklambda
kwarg ::= LOAD_CONST expr kwarg ::= LOAD_CONST expr
@@ -421,17 +421,17 @@ class Python2Parser(PythonParser):
custom_seen_ops.add(opname) custom_seen_ops.add(opname)
continue continue
elif opname == "DELETE_ATTR": elif opname == "DELETE_ATTR":
self.addRule("del_stmt ::= expr DELETE_ATTR", nop_func) self.addRule("delete ::= expr DELETE_ATTR", nop_func)
custom_seen_ops.add(opname) custom_seen_ops.add(opname)
continue continue
elif opname.startswith("DELETE_SLICE"): elif opname.startswith("DELETE_SLICE"):
self.addRule( self.addRule(
""" """
del_expr ::= expr del_expr ::= expr
del_stmt ::= del_expr DELETE_SLICE+0 delete ::= del_expr DELETE_SLICE+0
del_stmt ::= del_expr del_expr DELETE_SLICE+1 delete ::= del_expr del_expr DELETE_SLICE+1
del_stmt ::= del_expr del_expr DELETE_SLICE+2 delete ::= del_expr del_expr DELETE_SLICE+2
del_stmt ::= del_expr del_expr del_expr DELETE_SLICE+3 delete ::= del_expr del_expr del_expr DELETE_SLICE+3
""", """,
nop_func, nop_func,
) )
@@ -451,7 +451,7 @@ class Python2Parser(PythonParser):
elif opname == "DELETE_SUBSCR": elif opname == "DELETE_SUBSCR":
self.addRule( self.addRule(
""" """
del_stmt ::= delete_subscript delete ::= delete_subscript
delete_subscript ::= expr expr DELETE_SUBSCR delete_subscript ::= expr expr DELETE_SUBSCR
""", """,
nop_func, nop_func,

View File

@@ -48,7 +48,7 @@ class Python23Parser(Python24Parser):
while1stmt ::= _while1test l_stmts JUMP_BACK while1stmt ::= _while1test l_stmts JUMP_BACK
POP_TOP POP_BLOCK POP_TOP POP_BLOCK
list_comp ::= BUILD_LIST_0 DUP_TOP LOAD_ATTR store list_iter del_stmt list_comp ::= BUILD_LIST_0 DUP_TOP LOAD_ATTR store list_iter delete
list_for ::= expr for_iter store list_iter JUMP_BACK come_froms POP_TOP JUMP_BACK list_for ::= expr for_iter store list_iter JUMP_BACK come_froms POP_TOP JUMP_BACK
lc_body ::= LOAD_NAME expr CALL_FUNCTION_1 POP_TOP lc_body ::= LOAD_NAME expr CALL_FUNCTION_1 POP_TOP

View File

@@ -230,9 +230,9 @@ class Python26Parser(Python2Parser):
list_iter ::= list_if JUMP_BACK list_iter ::= list_if JUMP_BACK
list_iter ::= list_if JUMP_BACK COME_FROM POP_TOP list_iter ::= list_if JUMP_BACK COME_FROM POP_TOP
list_comp ::= BUILD_LIST_0 DUP_TOP list_comp ::= BUILD_LIST_0 DUP_TOP
store list_iter del_stmt store list_iter delete
list_comp ::= BUILD_LIST_0 DUP_TOP list_comp ::= BUILD_LIST_0 DUP_TOP
store list_iter JUMP_BACK del_stmt store list_iter JUMP_BACK delete
lc_body ::= LOAD_NAME expr LIST_APPEND lc_body ::= LOAD_NAME expr LIST_APPEND
lc_body ::= LOAD_FAST expr LIST_APPEND lc_body ::= LOAD_FAST expr LIST_APPEND

View File

@@ -254,7 +254,7 @@ class Python3Parser(PythonParser):
END_FINALLY _jump END_FINALLY _jump
except_var_finalize ::= POP_BLOCK POP_EXCEPT LOAD_CONST COME_FROM_FINALLY except_var_finalize ::= POP_BLOCK POP_EXCEPT LOAD_CONST COME_FROM_FINALLY
LOAD_CONST store del_stmt LOAD_CONST store delete
except_suite ::= returns except_suite ::= returns
@@ -935,7 +935,7 @@ class Python3Parser(PythonParser):
self.addRule("continue ::= CONTINUE_LOOP", nop_func) self.addRule("continue ::= CONTINUE_LOOP", nop_func)
custom_ops_processed.add(opname) custom_ops_processed.add(opname)
elif opname == "DELETE_ATTR": elif opname == "DELETE_ATTR":
self.addRule("del_stmt ::= expr DELETE_ATTR", nop_func) self.addRule("delete ::= expr DELETE_ATTR", nop_func)
custom_ops_processed.add(opname) custom_ops_processed.add(opname)
elif opname == "DELETE_DEREF": elif opname == "DELETE_DEREF":
self.addRule( self.addRule(
@@ -949,7 +949,7 @@ class Python3Parser(PythonParser):
elif opname == "DELETE_SUBSCR": elif opname == "DELETE_SUBSCR":
self.addRule( self.addRule(
""" """
del_stmt ::= delete_subscript delete ::= delete_subscript
delete_subscript ::= expr expr DELETE_SUBSCR delete_subscript ::= expr expr DELETE_SUBSCR
""", """,
nop_func, nop_func,

View File

@@ -17,15 +17,15 @@ class Python31Parser(Python32Parser):
with ::= expr setupwith SETUP_FINALLY with ::= expr setupwith SETUP_FINALLY
suite_stmts_opt suite_stmts_opt
POP_BLOCK LOAD_CONST COME_FROM_FINALLY POP_BLOCK LOAD_CONST COME_FROM_FINALLY
load del_stmt WITH_CLEANUP END_FINALLY load delete WITH_CLEANUP END_FINALLY
# Keeps Python 3.1 withas desigator in the same position as it is in other version # Keeps Python 3.1 withas desigator in the same position as it is in other version
setupwithas31 ::= setupwithas SETUP_FINALLY load del_stmt setupwithas31 ::= setupwithas SETUP_FINALLY load delete
withasstmt ::= expr setupwithas31 store withasstmt ::= expr setupwithas31 store
suite_stmts_opt suite_stmts_opt
POP_BLOCK LOAD_CONST COME_FROM_FINALLY POP_BLOCK LOAD_CONST COME_FROM_FINALLY
load del_stmt WITH_CLEANUP END_FINALLY load delete WITH_CLEANUP END_FINALLY
store ::= STORE_NAME store ::= STORE_NAME
load ::= LOAD_FAST load ::= LOAD_FAST

View File

@@ -112,10 +112,10 @@ class Python37Parser(Python37BaseParser):
stmt ::= tryelsestmt stmt ::= tryelsestmt
stmt ::= tryfinallystmt stmt ::= tryfinallystmt
stmt ::= del_stmt stmt ::= delete
del_stmt ::= DELETE_FAST delete ::= DELETE_FAST
del_stmt ::= DELETE_NAME delete ::= DELETE_NAME
del_stmt ::= DELETE_GLOBAL delete ::= DELETE_GLOBAL
stmt ::= return stmt ::= return
return ::= ret_expr RETURN_VALUE return ::= ret_expr RETURN_VALUE
@@ -888,7 +888,7 @@ class Python37Parser(Python37BaseParser):
END_FINALLY _jump END_FINALLY _jump
except_var_finalize ::= POP_BLOCK POP_EXCEPT LOAD_CONST COME_FROM_FINALLY except_var_finalize ::= POP_BLOCK POP_EXCEPT LOAD_CONST COME_FROM_FINALLY
LOAD_CONST store del_stmt LOAD_CONST store delete
except_suite ::= returns except_suite ::= returns

View File

@@ -541,7 +541,7 @@ class Python37BaseParser(PythonParser):
self.addRule("continue ::= CONTINUE_LOOP", nop_func) self.addRule("continue ::= CONTINUE_LOOP", nop_func)
custom_ops_processed.add(opname) custom_ops_processed.add(opname)
elif opname == "DELETE_ATTR": elif opname == "DELETE_ATTR":
self.addRule("del_stmt ::= expr DELETE_ATTR", nop_func) self.addRule("delete ::= expr DELETE_ATTR", nop_func)
custom_ops_processed.add(opname) custom_ops_processed.add(opname)
elif opname == "DELETE_DEREF": elif opname == "DELETE_DEREF":
self.addRule( self.addRule(
@@ -555,7 +555,7 @@ class Python37BaseParser(PythonParser):
elif opname == "DELETE_SUBSCR": elif opname == "DELETE_SUBSCR":
self.addRule( self.addRule(
""" """
del_stmt ::= delete_subscript delete ::= delete_subscript
delete_subscript ::= expr expr DELETE_SUBSCR delete_subscript ::= expr expr DELETE_SUBSCR
""", """,
nop_func, nop_func,

View File

@@ -111,6 +111,8 @@ class Python38Parser(Python37Parser):
for38 ::= expr get_for_iter store for_block for38 ::= expr get_for_iter store for_block
forelsestmt38 ::= expr get_for_iter store for_block POP_BLOCK else_suite forelsestmt38 ::= expr get_for_iter store for_block POP_BLOCK else_suite
forelsestmt38 ::= expr get_for_iter store for_block JUMP_BACK _come_froms else_suite
forelselaststmt38 ::= expr get_for_iter store for_block POP_BLOCK else_suitec forelselaststmt38 ::= expr get_for_iter store for_block POP_BLOCK else_suitec
forelselaststmtl38 ::= expr get_for_iter store for_block POP_BLOCK else_suitel forelselaststmtl38 ::= expr get_for_iter store for_block POP_BLOCK else_suitel

View File

@@ -76,8 +76,8 @@ if PYTHON3:
intern = sys.intern intern = sys.intern
L65536 = 65536 L65536 = 65536
def long(l): def long(num):
return l return num
else: else:

View File

@@ -327,7 +327,10 @@ TABLE_DIRECT = {
# For compatibility with older Python, we'll use "%" instead of # For compatibility with older Python, we'll use "%" instead of
# a format string. # a format string.
"string_at_beginning": ('%|"%%s" %% %c\n', 0), "string_at_beginning": ('%|"%%s" %% %c\n', 0),
"call_stmt": ("%|%p\n", (0, 200)), "call_stmt": ( "%|%p\n",
# When a call statement contains only a named_expr (:=)
# the named_expr should have parenthesis around it.
(0, PRECEDENCE["named_expr"]-1)),
"break": ("%|break\n",), "break": ("%|break\n",),
"continue": ("%|continue\n",), "continue": ("%|continue\n",),
"raise_stmt0": ("%|raise\n",), "raise_stmt0": ("%|raise\n",),
@@ -436,7 +439,7 @@ MAP_R = (TABLE_R, -1)
MAP = { MAP = {
"stmt": MAP_R, "stmt": MAP_R,
"call": MAP_R, "call": MAP_R,
"del_stmt": MAP_R, "delete": MAP_R,
"store": MAP_R, "store": MAP_R,
"exprlist": MAP_R0, "exprlist": MAP_R0,
} }

View File

@@ -149,6 +149,11 @@ def customize_for_version37(self, version):
(1, "store"), (0, "get_aiter"), (3, "list_iter"), (1, "store"), (0, "get_aiter"), (3, "list_iter"),
), ),
"list_afor": (
" async for %[1]{%c} in %c%[1]{%c}",
(1, "store"), (0, "get_aiter"), (3, "list_iter"),
),
"list_if37": (" if %p%c", (0, 27), 1), "list_if37": (" if %p%c", (0, 27), 1),
"list_if37_not": (" if not %p%c", (0, 27), 1), "list_if37_not": (" if not %p%c", (0, 27), 1),
"testfalse_not_or": ("not %c or %c", (0, "expr"), (2, "expr")), "testfalse_not_or": ("not %c or %c", (0, "expr"), (2, "expr")),

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2019 by Rocky Bernstein # Copyright (c) 2019-2020 by Rocky Bernstein
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
# Python 3.8+ changes # Python 3.8+ changes
####################### #######################
from uncompyle6.semantics.consts import TABLE_DIRECT from uncompyle6.semantics.consts import PRECEDENCE, TABLE_DIRECT
def customize_for_version38(self, version): def customize_for_version38(self, version):
@@ -37,14 +37,15 @@ def customize_for_version38(self, version):
'%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', '%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
(7, 'store'), (0, 'expr'), (8, 'for_block'), (-1, 'else_suite') ), (7, 'store'), (0, 'expr'), (8, 'for_block'), (-1, 'else_suite') ),
'async_with_stmt38': ( "async_with_stmt38": (
'%|async with %c:\n%+%|%c%-', "%|async with %c:\n%+%|%c%-",
(0, 'expr'), 7), (0, "expr"), 7),
'async_with_as_stmt38': ( "async_with_as_stmt38": (
'%|async with %c as %c:\n%+%|%c%-', "%|async with %c as %c:\n%+%|%c%-",
(0, 'expr'), (6, 'store'), (0, "expr"), (6, "store"),
(7, 'suite_stmts') ), (7, "suite_stmts")
),
"call_stmt": ( "call_stmt": (
"%|%c\n", 0 "%|%c\n", 0
@@ -80,11 +81,13 @@ def customize_for_version38(self, version):
(2, 'store'), (2, 'store'),
(0, 'expr'), (0, 'expr'),
(3, 'for_block') ), (3, 'for_block') ),
'forelsestmt38': (
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', "forelsestmt38": (
(2, 'store'), "%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
(0, 'expr'), (2, "store"),
(3, 'for_block'), -2 ), (0, "expr"),
(3, "for_block"), -1 ),
'forelselaststmt38': ( 'forelselaststmt38': (
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-', '%|for %c in %c:\n%+%c%-%|else:\n%+%c%-',
(2, 'store'), (2, 'store'),
@@ -153,6 +156,6 @@ def customize_for_version38(self, version):
(2, "suite_stmts_opt"), (2, "suite_stmts_opt"),
(8, "suite_stmts_opt") ), (8, "suite_stmts_opt") ),
"named_expr": ( # AKA "walrus operator" "named_expr": ( # AKA "walrus operator"
"%c := %c", (2, "store"), (0, "expr") "%c := %p", (2, "store"), (0, "expr", PRECEDENCE["named_expr"]-1)
) )
}) })

View File

@@ -260,7 +260,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
raise GenericASTTraversalPruningException raise GenericASTTraversalPruningException
n_slice0 = n_slice1 = n_slice2 = n_slice3 = n_subscript = table_r_node n_slice0 = n_slice1 = n_slice2 = n_slice3 = n_subscript = table_r_node
n_aug_assign_1 = n_print_item = exec_stmt = print_to_item = del_stmt = table_r_node n_aug_assign_1 = n_print_item = exec_stmt = print_to_item = delete = table_r_node
n_classdefco1 = n_classdefco2 = except_cond1 = except_cond2 = table_r_node n_classdefco1 = n_classdefco2 = except_cond1 = except_cond2 = table_r_node
def n_pass(self, node): def n_pass(self, node):
@@ -1211,7 +1211,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
p_insts = self.p.insts p_insts = self.p.insts
self.p.insts = self.scanner.insts self.p.insts = self.scanner.insts
self.p.offset2inst_index = self.scanner.offset2inst_index self.p.offset2inst_index = self.scanner.offset2inst_index
ast = parser.parse(self.p, tokens, customize) ast = parser.parse(self.p, tokens, customize, code)
self.p.insts = p_insts self.p.insts = p_insts
except (parser.ParserError(e), AssertionError(e)): except (parser.ParserError(e), AssertionError(e)):
raise ParserError(e, tokens, {}) raise ParserError(e, tokens, {})
@@ -1555,7 +1555,8 @@ class FragmentsWalker(pysource.SourceWalker, object):
line_seperator = ",\n" + self.indent line_seperator = ",\n" + self.indent
sep = INDENT_PER_LEVEL[:-1] sep = INDENT_PER_LEVEL[:-1]
start = len(self.f.getvalue()) start = len(self.f.getvalue())
self.write("{") if node[0] != "dict_entry":
self.write("{")
self.set_pos_info(node[0], start, start + 1) self.set_pos_info(node[0], start, start + 1)
if self.version >= 3.0 and not self.is_pypy: if self.version >= 3.0 and not self.is_pypy:
@@ -1581,7 +1582,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
i += 2 i += 2
pass pass
pass pass
elif node[1].kind.startswith("kvlist"): elif len(node) > 1 and node[1].kind.startswith("kvlist"):
# Python 3.0..3.4 style key/value list in dict # Python 3.0..3.4 style key/value list in dict
kv_node = node[1] kv_node = node[1]
l = list(kv_node) l = list(kv_node)
@@ -1607,37 +1608,98 @@ class FragmentsWalker(pysource.SourceWalker, object):
i += 3 i += 3
pass pass
pass pass
elif node[-1].kind.startswith("BUILD_CONST_KEY_MAP"):
# Python 3.6+ style const map
keys = node[-2].pattr
values = node[:-2]
# FIXME: Line numbers?
for key, value in zip(keys, values):
self.write(sep)
self.write(repr(key))
line_number = self.line_number
self.write(":")
self.write(self.traverse(value[0]))
sep = ", "
if line_number != self.line_number:
sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
line_number = self.line_number
else:
sep += " "
pass
pass
if sep.startswith(",\n"):
self.write(sep[1:])
pass
elif node[0].kind.startswith("dict_entry"):
assert self.version >= 3.5
template = ("%C", (0, len(node[0]), ", **"))
self.template_engine(template, node[0])
sep = ""
elif node[-1].kind.startswith("BUILD_MAP_UNPACK") or node[
-1
].kind.startswith("dict_entry"):
assert self.version >= 3.5
# FIXME: I think we can intermingle dict_comp's with other
# dictionary kinds of things. The most common though is
# a sequence of dict_comp's
kwargs = node[-1].attr
template = ("**%C", (0, kwargs, ", **"))
self.template_engine(template, node)
sep = ""
pass pass
else: else:
# Python 2 style kvlist # Python 2 style kvlist. Find beginning of kvlist.
assert node[-1].kind.startswith("kvlist") indent = self.indent + " "
kv_node = node[-1] # goto kvlist line_number = self.line_number
if node[0].kind.startswith("BUILD_MAP"):
if len(node) > 1 and node[1].kind in ("kvlist", "kvlist_n"):
kv_node = node[1]
else:
kv_node = node[1:]
self.kv_map(kv_node, sep, line_number, indent)
for kv in kv_node: else:
assert kv in ("kv", "kv2", "kv3") sep = ""
# kv ::= DUP_TOP expr ROT_TWO expr STORE_SUBSCR opname = node[-1].kind
# kv2 ::= DUP_TOP expr expr ROT_THREE STORE_SUBSCR if self.is_pypy and self.version >= 3.5:
# kv3 ::= expr expr STORE_MAP if opname.startswith("BUILD_CONST_KEY_MAP"):
if kv == "kv": keys = node[-2].attr
name = self.traverse(kv[-2], indent="") # FIXME: DRY this and the above
kv[1].parent = kv_node for i in range(len(keys)):
value = self.traverse( key = keys[i]
kv[1], indent=self.indent + (len(name) + 2) * " " value = self.traverse(node[i], indent="")
) self.write(sep, key, ": ", value)
elif kv == "kv2": sep = ", "
name = self.traverse(kv[1], indent="") if line_number != self.line_number:
kv[-3].parent = kv_node sep += "\n" + self.indent + " "
value = self.traverse( line_number = self.line_number
kv[-3], indent=self.indent + (len(name) + 2) * " " pass
) pass
elif kv == "kv3": pass
name = self.traverse(kv[-2], indent="") else:
kv[0].parent = kv_node if opname.startswith("kvlist"):
value = self.traverse( list_node = node[0]
kv[0], indent=self.indent + (len(name) + 2) * " " else:
) list_node = node
self.write(sep, name, ": ", value)
sep = line_seperator assert list_node[-1].kind.startswith("BUILD_MAP")
for i in range(0, len(list_node) - 1, 2):
key = self.traverse(list_node[i], indent="")
value = self.traverse(list_node[i + 1], indent="")
self.write(sep, key, ": ", value)
sep = ", "
if line_number != self.line_number:
sep += "\n" + self.indent + " "
line_number = self.line_number
pass
pass
pass
elif opname.startswith("kvlist"):
kv_node = node[-1]
self.kv_map(node[-1], sep, line_number, indent)
pass
self.write("}") self.write("}")
finish = len(self.f.getvalue()) finish = len(self.f.getvalue())
self.set_pos_info(node, start, finish) self.set_pos_info(node, start, finish)
@@ -1942,7 +2004,7 @@ def code_deparse(
co, co,
out=StringIO(), out=StringIO(),
version=None, version=None,
is_pypy=None, is_pypy=IS_PYPY,
debug_opts=DEFAULT_DEBUG_OPTS, debug_opts=DEFAULT_DEBUG_OPTS,
code_objects={}, code_objects={},
compile_mode="exec", compile_mode="exec",
@@ -1971,9 +2033,8 @@ def code_deparse(
if version is None: if version is None:
version = sysinfo2float() version = sysinfo2float()
if is_pypy is None:
is_pypy = IS_PYPY
# store final output stream for case of error
scanner = get_scanner(version, is_pypy=is_pypy) scanner = get_scanner(version, is_pypy=is_pypy)
show_asm = debug_opts.get("asm", None) show_asm = debug_opts.get("asm", None)
@@ -2000,14 +2061,17 @@ def code_deparse(
is_pypy=is_pypy, is_pypy=is_pypy,
) )
deparsed.ast = deparsed.build_ast(tokens, customize) isTopLevel = co.co_name == "<module>"
deparsed.ast = deparsed.build_ast(tokens, customize, co, isTopLevel=isTopLevel)
assert deparsed.ast == "stmts", "Should have parsed grammar start" assert deparsed.ast == "stmts", "Should have parsed grammar start"
del tokens # save memory # save memory
del tokens
# convert leading '__doc__ = "..." into doc string # convert leading '__doc__ = "..." into doc string
assert deparsed.ast == "stmts" assert deparsed.ast == "stmts"
(deparsed.mod_globs, nonlocals) = pysource.find_globals_and_nonlocals( (deparsed.mod_globs, nonlocals) = pysource.find_globals_and_nonlocals(
deparsed.ast, set(), set(), co, version deparsed.ast, set(), set(), co, version
) )

View File

@@ -135,7 +135,7 @@ import sys
IS_PYPY = "__pypy__" in sys.builtin_module_names IS_PYPY = "__pypy__" in sys.builtin_module_names
PYTHON3 = sys.version_info >= (3, 0) PYTHON3 = sys.version_info >= (3, 0)
from xdis import iscode, COMPILER_FLAG_BIT from xdis import iscode, COMPILER_FLAG_BIT, sysinfo2float
from uncompyle6.parser import get_python_parser from uncompyle6.parser import get_python_parser
from uncompyle6.parsers.treenode import SyntaxTree from uncompyle6.parsers.treenode import SyntaxTree
@@ -980,7 +980,7 @@ class SourceWalker(GenericASTTraversal, object):
self.n_list_comp_pypy27(node) self.n_list_comp_pypy27(node)
return return
n = node[-1] n = node[-1]
elif node[-1] == "del_stmt": elif node[-1] == "delete":
if node[-2] == "JUMP_BACK": if node[-2] == "JUMP_BACK":
n = node[-3] n = node[-3]
else: else:
@@ -1660,7 +1660,7 @@ class SourceWalker(GenericASTTraversal, object):
""" """
prettyprint a dict prettyprint a dict
'dict' is something like k = {'a': 1, 'b': 42}" 'dict' is something like k = {'a': 1, 'b': 42}"
We will source-code use line breaks to guide us when to break. We will use source-code line breaks to guide us when to break.
""" """
p = self.prec p = self.prec
self.prec = 100 self.prec = 100
@@ -2565,7 +2565,7 @@ def code_deparse(
assert iscode(co) assert iscode(co)
if version is None: if version is None:
version = float(sys.version[0:3]) version = sysinfo2float()
# store final output stream for case of error # store final output stream for case of error
scanner = get_scanner(version, is_pypy=is_pypy) scanner = get_scanner(version, is_pypy=is_pypy)

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2018 Rocky Bernstein <rocky@gnu.org> # Copyright (C) 2018, 2020 Rocky Bernstein <rocky@gnu.org>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@@ -50,8 +50,11 @@ def maybe_show_tree(walker, ast):
stream = walker.showast stream = walker.showast
else: else:
stream = sys.stdout stream = sys.stdout
if (isinstance(walker.showast, dict) and walker.showast.get("Full", False) if (
and hasattr(walker, "str_with_template")): isinstance(walker.showast, dict)
and walker.showast.get("Full", False)
and hasattr(walker, "str_with_template")
):
walker.str_with_template(ast) walker.str_with_template(ast)
else: else:
stream.write(str(ast)) stream.write(str(ast))

View File

@@ -13,7 +13,6 @@ except:
from uncompyle6 import PYTHON_VERSION from uncompyle6 import PYTHON_VERSION
def better_repr(v, version): def better_repr(v, version):
"""Work around Python's unorthogonal and unhelpful repr() for primitive float """Work around Python's unorthogonal and unhelpful repr() for primitive float
and complex.""" and complex."""
@@ -46,7 +45,6 @@ def better_repr(v, version):
else: else:
return s return s
elif isinstance(v, list): elif isinstance(v, list):
better_repr(v)
if len(v) == 1: if len(v) == 1:
return "[%s,]" % better_repr(v[0], version) return "[%s,]" % better_repr(v[0], version)
return "[%s]" % ", ".join(better_repr(i) for i in v) return "[%s]" % ", ".join(better_repr(i) for i in v)

View File

@@ -12,4 +12,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# This file is suitable for sourcing inside POSIX shell as # This file is suitable for sourcing inside POSIX shell as
# well as importing into Python # well as importing into Python
VERSION="3.7.3" # noqa VERSION="3.7.4" # noqa