Compare commits

..

34 Commits

Author SHA1 Message Date
rocky
12397d76b8 Get ready for release 2.14.3 2018-01-19 03:26:58 -05:00
rocky
2126e4cf32 Fix bug in 3.5+ async stmt ..
and in verification status message
2018-01-19 03:15:08 -05:00
rocky
4dfb85f062 Bump needed xdis version 3.6.5 2018-01-18 18:58:21 -05:00
rocky
ebb9f1a53f Python 2.6 compatibility 2018-01-18 01:25:38 -05:00
rocky
b43d4909cd We need xdis 3.6.4 or better now 2018-01-18 01:17:07 -05:00
rocky
96ddef3920 Handle 3.5.2..3.5.2 magic...
And handle magic better overal by improved xdis use
2018-01-18 01:15:19 -05:00
rocky
c24934c0c3 Fix bug in last commit 2018-01-13 15:14:59 -05:00
rocky
8f88ed8c44 test_pyenvlib.py: correct/improve status 2018-01-13 15:09:58 -05:00
rocky
c1ed5d4bfd Merge branch 'master' of github.com:rocky/python-uncompyle6 2018-01-13 08:17:35 -05:00
rocky
abf85faf79 small grammar formatting tidy 2018-01-13 08:17:28 -05:00
rocky
826c968d0a Update 2.6 stdlib test failures 2018-01-13 01:05:15 -05:00
rocky
185ec4e306 Fix 2.6 IF/THEN misclassification..
with an exception condition
2018-01-13 00:58:16 -05:00
rocky
70ddd71c0e Test for 2.7 loop try else bug 2018-01-12 22:25:42 -05:00
rocky
1485d26aa2 Bug in 2.7 try else in a loop 2018-01-12 22:19:30 -05:00
rocky
ab4daf2879 Merge branch 'master' of github.com:rocky/python-uncompyle6 2018-01-12 20:14:15 -05:00
rocky
db9eaa7503 3.x while1else checking 2018-01-12 20:14:09 -05:00
rocky
a60104517d Fix ok status on --weak-verify 2018-01-12 10:00:26 -05:00
rocky
a0d10c2d4c Improve test framework...
test_pyenvlib.py: get list of python versions from xdis
main.py: bump okay_files appropriately when --verify is off
2018-01-12 09:47:32 -05:00
rocky
c4f12e9b22 2.4 whileelse test 2018-01-11 22:00:12 -05:00
rocky
c6e20e4444 Fix whileelse bug 2018-01-11 21:52:33 -05:00
rocky
b2c082bba2 Bugs in handing new --max option 2018-01-11 10:33:38 -05:00
rocky
71a64299e8 Simplify remove slop in CALL_FUNCTION_VAR on 3.5 2018-01-11 10:13:42 -05:00
rocky
0413342ee3 Merge branch 'master' of github.com:rocky/python-uncompyle6 2018-01-11 09:53:55 -05:00
rocky
07ba16ac3a Add --max option on pyenv ...
and extend list of pyenv versions
2018-01-11 09:53:15 -05:00
rocky
a4db92ce72 funcdefdeco -> function_def_deco ...
to match AST grammar more analogously
2018-01-11 01:44:34 -05:00
rocky
5b71cee487 Add decorator tests 2018-01-10 11:15:03 -05:00
rocky
9e92f65a27 Correct Python 2.5- decorator parsing 2018-01-10 11:08:05 -05:00
rocky
6c29f726bc Correct setup.py version checking 2018-01-10 10:01:37 -05:00
rocky
22542eeab0 Correct setup.py version checking 2018-01-10 10:00:55 -05:00
rocky
e4bfa6da13 Check Python version in setup.py ...
to make sure we are running a compatible version. Fixes #146
2018-01-10 09:50:25 -05:00
rocky
4ea7b9aa2e Reinstate run tests that now work 2018-01-09 08:50:47 -05:00
rocky
abcb769fdf Fix 2.6- parsing of "for .. try/else" ...
with "continue"  inside
2018-01-09 08:36:21 -05:00
rocky
d66fedb921 Remove 2.1 for_iter duplication 2018-01-09 03:19:34 -05:00
rocky
8e6f1a5135 Small typo 2018-01-09 00:28:43 -05:00
36 changed files with 229 additions and 101 deletions

16
NEWS
View File

@@ -1,4 +1,16 @@
uncompyle6 2.14.0 2017-01-09 Samish
uncompyle6 2.14.3 2017-01-19
- Fix bug in 3.5+ await stmt
- Better version to magic handling; handle 3.5.2 .. 3.5.4 versions
- Improve/correct test_pyenvlib.py status messages
- Fix some 2.7 and 2.6 parser bugs
- Fix whilelse parsing bugs
- Correct 2.5- decorator parsing
- grammar for decorators matchies AST more a little better
- better tests in setup.py for running the right version of Python
- Fix 2.6- parsing of "for .. try/else" ... with "continue" inside
uncompyle6 2.14.2 2017-01-09 Samish
Decompilation bug fixes, mostly 3.6 and pre 2.7
@@ -16,7 +28,7 @@ Decompilation bug fixes, mostly 3.6 and pre 2.7
Python versions
- Match Python AST names more closely when possible
uncompyle6 2.14.0 2017-12-10 Dr. Gecko
uncompyle6 2.14.1 2017-12-10 Dr. Gecko
- Many decompilation bugfixes
- Grammar rule reduction and version isolation

View File

@@ -9,7 +9,7 @@
# Things that change more often go here.
copyright = """
Copyright (C) 2015-2017 Rocky Bernstein <rb@dustyfeet.com>.
Copyright (C) 2015-2018 Rocky Bernstein <rb@dustyfeet.com>.
"""
classifiers = ['Development Status :: 5 - Production/Stable',
@@ -26,6 +26,7 @@ classifiers = ['Development Status :: 5 - Production/Stable',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Topic :: Software Development :: Debuggers',
'Topic :: Software Development :: Libraries :: Python Modules',
]
@@ -40,12 +41,12 @@ entry_points = {
]}
ftp_url = None
install_requires = ['spark-parser >= 1.8.5, < 1.9.0',
'xdis >= 3.6.2, < 3.7.0', 'six']
'xdis >= 3.6.6, < 3.7.0', 'six']
license = 'MIT'
mailing_list = 'python-debugger@googlegroups.com'
modname = 'uncompyle6'
py_modules = None
short_desc = 'Python cross-version byte-code deparser'
short_desc = 'Python cross-version byte-code decompiler'
web = 'https://github.com/rocky/python-uncompyle6/'
# tracebacks in zip files are funky and not debuggable

View File

@@ -44,14 +44,8 @@
# Switch to python-2.4, sync that up and build that first since it creates a tarball which we don't want.
$ source admin-tools/setup-python-2.4.sh
$ rm ChangeLog
$ git merge master
# Update NEWS from master branch
$ git commit -m"Get ready for release $VERSION" .
# Check against older versions
$ source admin-tools/check-older-versions.sh
@@ -61,7 +55,7 @@
$ . ./admin-tools/make-dist-older.sh
$ git tag release-python-2.4-$VERSION
$ . /admin-tools/make-dist-newer.sh
$ . ./admin-tools/make-dist-newer.sh
$ git tag release-$VERSION
# Upload single package and look at Rst Formating

View File

@@ -1,7 +1,20 @@
#!/usr/bin/env python
import sys
"""Setup script for the 'uncompyle6' distribution."""
SYS_VERSION = sys.version_info[0:2]
if not ((2, 6) <= SYS_VERSION <= (3, 7)) or ((3, 0) <= SYS_VERSION <= (3, 1)):
mess = "Python Release 2.6 .. 3.7 excluding 3.0 and 3.1 are supported in this code branch."
if ((2, 4) <= SYS_VERSION <= (2, 7)):
mess += ("\nFor your Python, version %s, use the python-2.4 code/branch." %
sys.version[0:3])
elif SYS_VERSION < (2, 4) or ((3, 0) <= SYS_VERSION <= (3, 1)):
mess += ("\nThis package is not supported for Python version %s."
% sys.version[0:3])
print(mess)
raise Exception(mess)
from __pkginfo__ import \
author, author_email, install_requires, \
license, long_description, classifiers, \

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.

Binary file not shown.

View File

@@ -0,0 +1,9 @@
# From python 2.5 make_decorators.py
# Bug was in not recognizing @memoize which uses grammra rules
# using nonterminals mkfuncdeco and mkfuncdeco0
def memoize(func):
pass
def test_memoize(self):
@memoize
def double(x):
return x * 2

View File

@@ -0,0 +1,25 @@
# 2.6- Try/else in a loop with a continue which
# requires a tryelsestmtc
# From 2.6- test_codecs.py
def test_specific_values(self):
for flags in self:
if flags:
try:
self = 1
except ValueError:
continue
else:
self = 2
self = 3
# From 2.6 test_decorators.
# Bug was thinking an "except" was some sort of if/then
def call(*args):
try:
return 5
except KeyError:
return 2
except TypeError:
# Unhashable argument
return 3

View File

@@ -0,0 +1,12 @@
# From Python 2.7 ihooks.py
def ensure_fromlist(self, fromlist):
for sub in fromlist:
if sub:
if not recursive:
try:
all = 5
except AttributeError:
pass
else:
all = 6
continue

View File

@@ -0,0 +1,9 @@
# From 3.5 _collections.abc.py
# Bug was not having \n after "await self.athrow()" stmt
async def aclose(self):
try:
await self.athrow()
except (GeneratorExit):
pass
else:
raise RuntimeError

View File

@@ -0,0 +1,9 @@
# From idlelib/PyParse.py
# Bug is "if" inside a nested while/else.
def _study1(i, n):
while i:
while i:
i = 0
else:
if i:
i = 1

View File

@@ -12,13 +12,8 @@ typeset -A SKIP_TESTS
case $PYVERSION in
2.4)
SKIP_TESTS=(
[test_codecs.py]=1 # need to fix tryelse
[test_decorators.py]=1 # Syntax error decorators?
[test_dis.py]=1 # We change line numbers - duh!
[test_frozen.py]=1
[test_grp.py]=1 # Long test - might work Control flow?
[test_imp.py]=1 # Control flow?
[test_import.py]=1 # Control flow?
[test_math.py]=1 # Control flow?
[test_pwd.py]=1 # Long test - might work? Control flow?
[test_queue.py]=1 # Control flow?
@@ -29,17 +24,12 @@ case $PYVERSION in
;;
2.5)
SKIP_TESTS=(
[test_codecs.py]=1 # need to fix tryelse
[test_coercion.py]=1
[test_contextlib.py]=1
[test_decorators.py]=1 # Syntax error decorators?
[test_dis.py]=1 # We change line numbers - duh!
[test_exceptions.py]=1
[test_frozen.py]=1
[test_functools.py]=1
[test_grammar.py]=1 # Too many stmts. Handle large stmts
[test_grp.py]=1 # Long test - might work Control flow?
[test_imp.py]=1
[test_math.py]=1 # Control flow?
[test_pdb.py]=1
[test_pwd.py]=1 # Long test - might work? Control flow?
@@ -48,28 +38,17 @@ case $PYVERSION in
[test_sax.py]=1 # Control flow?
[test_trace.py]=1 # Line numbers are expected to be different
[test_types.py]=1 # Control flow?
nn [test_zipfile64.py]=1 # Runs ok but takes 204 seconds
[test_zipfile64.py]=1 # Runs ok but takes 204 seconds
)
;;
2.6)
SKIP_TESTS=(
[test_cmath.py]=1 # Control flow?
[test_codecs.py]=1 # need to fix tryelse
[test_coercion.py]=1 # Control flow?
[test_decorators.py]=1 # Syntax Error - look at
[test_frozen.py]=1 # Control flow?
[test_ftplib.py]=1 # Control flow?
[test_grp.py]=1 # Long test - might work Control flow?
[test_imp.py]=1
[test_opcodes.py]=1
[test_pwd.py]=1 # Long test - might work? Control flow?
[test_queue.py]=1 # Control flow?
[test_re.py]=1 # Probably Control flow?
[test_strftime.py]=1
[test_trace.py]=1 # Line numbers are expected to be different
# .pyenv/versions/2.6.9/lib/python2.6/lib2to3/refactor.pyc
# .pyenv/versions/2.6.9/lib/python2.6/mailbox.pyc
# .pyenv/versions/2.6.9/lib/python2.6/markupbase.pyc
# .pyenv/versions/2.6.9/lib/python2.6/pstats.pyc
# .pyenv/versions/2.6.9/lib/python2.6/pyclbr.pyc
# .pyenv/versions/2.6.9/lib/python2.6/quopri.pyc -- look at ishex, is short
# .pyenv/versions/2.6.9/lib/python2.6/random.pyc

View File

@@ -21,20 +21,25 @@ Step 2: Run the test:
from __future__ import print_function
from uncompyle6 import main, PYTHON3
import os, time, shutil, sys
import os, time, re, shutil, sys
from fnmatch import fnmatch
from uncompyle6 import main, PYTHON3
import xdis.magics as magics
#----- configure this for your needs
TEST_VERSIONS=('2.3.7', '2.4.6', '2.5.6', '2.6.9',
python_versions = [v for v in magics.python_versions if
re.match('^[0-9.]+$', v)]
# FIXME: we should remove Python versions that we don't support.
# These include Jython, and Python bytecode changes pre release.
TEST_VERSIONS=(
'pypy-2.4.0', 'pypy-2.6.1',
'pypy-5.0.1', 'pypy-5.3.1', 'pypy3.5-5.7.1-beta',
'2.7.10', '2.7.11', '2.7.12', '2.7.13', '2.7.14',
'3.0.1', '3.1.5', '3.2.6',
'3.3.5', '3.3.6',
'3.4.2', '3.5.3', '3.6.0', '3.6.3',
'native')
'native') + tuple(python_versions)
target_base = '/tmp/py-dis/'
lib_prefix = os.path.join(os.environ['HOME'], '.pyenv/versions')
@@ -48,6 +53,7 @@ PYOC = ('*.pyc', '*.pyo')
test_options = {
# name: (src_basedir, pattern, output_base_suffix)
'test': ('./test', PYOC, 'test'),
'max=': 200,
}
for vers in TEST_VERSIONS:
@@ -65,7 +71,8 @@ for vers in TEST_VERSIONS:
test_options[vers] = (os.path.join(lib_prefix, vers, 'lib', 'python'+short_vers),
PYC, 'python-lib'+short_vers)
def do_tests(src_dir, patterns, target_dir, start_with=None, do_verify=False):
def do_tests(src_dir, patterns, target_dir, start_with=None,
do_verify=False, max_files=200):
def visitor(files, dirname, names):
files.extend(
@@ -99,14 +106,14 @@ def do_tests(src_dir, patterns, target_dir, start_with=None, do_verify=False):
except ValueError:
pass
if len(files) > 200:
if len(files) > max_files:
files = [file for file in files if not 'site-packages' in file]
files = [file for file in files if not 'test' in file]
if len(files) > 200:
if len(files) > max_files:
# print("Numer of files %d - truncating to last 200" % len(files))
# files = files[-200:]
print("Numer of files %d - truncating to first 200" % len(files))
files = files[:200]
print("Numer of files %d - truncating to first %s" %
(len(files), max_files))
files = files[:max_files]
print(time.ctime())
main.main(src_dir, target_dir, files, [], do_verify=do_verify)
@@ -123,9 +130,10 @@ if __name__ == '__main__':
test_options_keys.sort()
opts, args = getopt.getopt(sys.argv[1:], '',
['start-with=', 'verify', 'weak-verify',
'coverage', 'all', ] \
'max=', 'coverage', 'all', ] \
+ test_options_keys )
vers = ''
for opt, val in opts:
if opt == '--verify':
do_verify = True
@@ -139,6 +147,8 @@ if __name__ == '__main__':
triple = test_options[opt[2:]]
vers = triple[-1]
test_dirs.append(triple)
elif opt == '--max':
test_options['max='] = int(val)
elif opt == '--all':
vers = 'all'
for val in test_options_keys:
@@ -154,7 +164,8 @@ if __name__ == '__main__':
target_dir = os.path.join(target_base, target_dir)
if os.path.exists(target_dir):
shutil.rmtree(target_dir, ignore_errors=1)
do_tests(src_dir, pattern, target_dir, start_with, do_verify)
do_tests(src_dir, pattern, target_dir, start_with,
do_verify, test_options['max='])
else:
print("### Path %s doesn't exist; skipping" % src_dir)

View File

@@ -66,7 +66,7 @@ test_options = {
PYOC, 'ok-2.7', 2.7),
'ok-3.2': (os.path.join(src_dir, 'ok_lib3.2'),
PYOC, 'ok-3.2', 3.5),
PYOC, 'ok-3.2', 3.2),
'base-2.7': (os.path.join(src_dir, 'base_tests', 'python2.7'),
PYOC, 'base_2.7', 2.7),

View File

@@ -51,7 +51,7 @@ import uncompyle6.semantics.fragments
# Export some functions
from uncompyle6.main import decompile_file
# For compaitility
# For compatibility
uncompyle_file = decompile_file
# Conventience functions so you can say:

View File

@@ -184,7 +184,12 @@ def main(in_base, out_base, files, codes, outfile=None,
print('\n# okay decompiling %s' % infile)
okay_files += 1
else:
verify_failed_files += 1
print('\n# %s\n\t%s', infile, msg)
pass
else:
okay_files += 1
pass
except verify.VerifyCmpError as e:
print(e)
verify_failed_files += 1
@@ -196,6 +201,9 @@ def main(in_base, out_base, files, codes, outfile=None,
raise
pass
pass
pass
else:
okay_files += 1
pass
elif do_verify:
sys.stderr.write("\n### uncompile successful, but no file to compare against\n")
@@ -208,7 +216,8 @@ def main(in_base, out_base, files, codes, outfile=None,
print(mess, infile)
if current_outfile:
sys.stdout.write("%s\r" %
status_msg(do_verify, tot_files, okay_files, failed_files, verify_failed_files))
status_msg(do_verify, tot_files, okay_files, failed_files,
verify_failed_files, do_verify))
sys.stdout.flush()
if current_outfile:
sys.stdout.write("\n")
@@ -229,17 +238,21 @@ else:
return ''
def status_msg(do_verify, tot_files, okay_files, failed_files,
verify_failed_files):
verify_failed_files, weak_verify):
if weak_verify == 'weak':
verification_type = 'weak'
else:
verification_type = 'strong'
if tot_files == 1:
if failed_files:
return "\n# decompile failed"
elif verify_failed_files:
return "\n# decompile verify failed"
return "\n# decompile %s verification failed" % verification_type
else:
return "\n# Successfully decompiled file"
pass
pass
mess = "decompiled %i files: %i okay, %i failed" % (tot_files, okay_files, failed_files)
if do_verify:
mess += (", %i verify failed" % verify_failed_files)
mess += (", %i %s verification failed" % (verify_failed_files, verification_type))
return mess

View File

@@ -335,15 +335,15 @@ class PythonParser(GenericASTBuilder):
def p_function_def(self, args):
'''
stmt ::= function_def
function_def ::= mkfunc store
stmt ::= funcdefdeco
funcdefdeco ::= mkfuncdeco store
mkfuncdeco ::= expr mkfuncdeco CALL_FUNCTION_1
mkfuncdeco ::= expr mkfuncdeco0 CALL_FUNCTION_1
mkfuncdeco0 ::= mkfunc
load_closure ::= load_closure LOAD_CLOSURE
load_closure ::= LOAD_CLOSURE
stmt ::= function_def
function_def ::= mkfunc store
stmt ::= function_def_deco
function_def_deco ::= mkfuncdeco store
mkfuncdeco ::= expr mkfuncdeco CALL_FUNCTION_1
mkfuncdeco ::= expr mkfuncdeco0 CALL_FUNCTION_1
mkfuncdeco0 ::= mkfunc
load_closure ::= load_closure LOAD_CLOSURE
load_closure ::= LOAD_CLOSURE
'''
def p_generator_exp(self, args):
@@ -422,7 +422,7 @@ class PythonParser(GenericASTBuilder):
for_block POP_BLOCK _come_froms
forelsestmt ::= SETUP_LOOP expr for_iter store
for_block POP_BLOCK else_suite _come_froms
for_block POP_BLOCK else_suite _come_froms
forelselaststmt ::= SETUP_LOOP expr for_iter store
for_block POP_BLOCK else_suitec _come_froms

View File

@@ -13,7 +13,6 @@ class Python21Parser(Python22Parser):
def p_forstmt21(self, args):
"""
for_iter ::= LOAD_CONST FOR_LOOP
for ::= SETUP_LOOP expr for_iter store
returns
POP_BLOCK COME_FROM

View File

@@ -66,7 +66,6 @@ class Python25Parser(Python26Parser):
classdefdeco2 ::= LOAD_CONST expr mkfunc CALL_FUNCTION_0 BUILD_CLASS
kv3 ::= expr expr STORE_MAP
kvlist ::= kvlist kv3
mkfuncdeco ::= expr mkfuncdeco CALL_FUNCTION_1
ret_cond ::= expr jmp_false_then expr RETURN_END_IF POP_TOP ret_expr_or_cond
return_if_lambda ::= RETURN_END_IF_LAMBDA POP_TOP
return_if_stmt ::= ret_expr RETURN_END_IF POP_TOP

View File

@@ -42,6 +42,8 @@ class Python26Parser(Python2Parser):
except_handler else_suite come_froms
tryelsestmtl ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
except_handler else_suitel
tryelsestmtc ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
except_handler else_suitec
_ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM POP_TOP
@@ -140,8 +142,8 @@ class Python26Parser(Python2Parser):
whilestmt ::= SETUP_LOOP testexpr returns POP_BLOCK COME_FROM
whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt jb_pop POP_BLOCK
else_suite COME_FROM
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK else_suite COME_FROM
else_suitel COME_FROM
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK else_suitel COME_FROM
return ::= ret_expr RETURN_END_IF POP_TOP
return ::= ret_expr RETURN_VALUE POP_TOP

View File

@@ -50,6 +50,9 @@ class Python27Parser(Python2Parser):
tryelsestmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
except_handler else_suite COME_FROM
tryelsestmtl ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
except_handler else_suitel JUMP_BACK COME_FROM
except_stmt ::= except_cond2 except_suite
except_cond1 ::= DUP_TOP expr COMPARE_OP
@@ -120,9 +123,9 @@ class Python27Parser(Python2Parser):
while1stmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK COME_FROM
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK _come_froms
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK POP_BLOCK
else_suite COME_FROM
else_suitel COME_FROM
whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
else_suite COME_FROM
else_suitel COME_FROM
ifstmt ::= testexpr return_if_stmts COME_FROM
ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD else_suite COME_FROM

View File

@@ -347,10 +347,10 @@ class Python3Parser(PythonParser):
COME_FROM_LOOP
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK
else_suite
else_suitel
whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
else_suite COME_FROM_LOOP
else_suitel COME_FROM_LOOP
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK
COME_FROM_LOOP
@@ -1033,6 +1033,7 @@ class Python3Parser(PythonParser):
self.check_reduce['aug_assign1'] = 'AST'
self.check_reduce['aug_assign2'] = 'AST'
self.check_reduce['while1stmt'] = 'noAST'
self.check_reduce['while1elsestmt'] = 'noAST'
self.check_reduce['annotate_tuple'] = 'noAST'
self.check_reduce['kwarg'] = 'noAST'
# FIXME: remove parser errors caused by the below
@@ -1052,12 +1053,30 @@ class Python3Parser(PythonParser):
else:
return not (isinstance(arg, str) or isinstance(arg, unicode))
elif lhs == 'while1elsestmt':
n = len(tokens)
if last == n:
# Adjust for fuzziness in parsing
last -= 1
if tokens[last] == 'COME_FROM_LOOP':
last -= 1
elif tokens[last-1] == 'COME_FROM_LOOP':
last -= 2
if tokens[last] in ('JUMP_BACK', 'CONTINUE'):
# These indicate inside a loop, but token[last]
# should not be in a loop.
# FIXME: Not quite righte: refine by using target
return True
# if SETUP_LOOP target spans the else part, then this is
# not while1else. Also do for whileTrue?
last += 1
while isinstance(tokens[last].offset, str):
while last < n and isinstance(tokens[last].offset, str):
last += 1
return tokens[first].attr == tokens[last].offset
if last == n:
return False
return tokens[first].attr >= tokens[last].offset
elif lhs == 'while1stmt':
# If there is a fall through to the COME_FROM_LOOP. then this is

View File

@@ -19,9 +19,9 @@ class Python34Parser(Python33Parser):
# FIXME the below masks a bug in not detecting COME_FROM_LOOP
# grammar rules with COME_FROM -> COME_FROM_LOOP alreadly exist
# grammar rules with COME_FROM -> COME_FROM_LOOP already exist
whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
else_suite COME_FROM
else_suitel COME_FROM
# Python 3.4+ optimizes the trailing two JUMPS away

View File

@@ -17,13 +17,21 @@ import sys
from uncompyle6 import PYTHON3, IS_PYPY
from uncompyle6.scanners.tok import Token
from xdis.bytecode import op_size
from xdis.magics import py_str2float
from xdis.magics import py_str2float, canonic_python_version
from xdis.util import code2num
# The byte code versions we support
PYTHON_VERSIONS = (1.5,
2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7,
3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7)
# The byte code versions we support.
# Note: these all have to be floats
PYTHON_VERSIONS = frozenset((1.5,
2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7,
3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7))
CANONIC2VERSION = dict((canonic_python_version[str(v)], v) for v in PYTHON_VERSIONS)
# Magic changed mid version for Python 3.5.2. Compatibility was added for
# the older 3.5 interpreter magic.
CANONIC2VERSION['3.5.2'] = 3.5
# FIXME: DRY
if PYTHON3:
@@ -301,7 +309,13 @@ def get_scanner(version, is_pypy=False, show_asm=None):
# If version is a string, turn that into the corresponding float.
if isinstance(version, str):
version = py_str2float(version)
if version not in canonic_python_version:
raise RuntimeError("Unknown Python version in xdis %s" % version)
canonic_version = canonic_python_version[version]
if canonic_version not in CANONIC2VERSION:
raise RuntimeError("Unsupported Python version %s (canonic %s)"
% (version, canonic_version))
version = CANONIC2VERSION[canonic_version]
# Pick up appropriate scanner
if version in PYTHON_VERSIONS:

View File

@@ -894,7 +894,6 @@ class Scanner2(Scanner):
self.structs.append({'type': 'if-then',
'start': start-3,
'end': pre_rtarget})
self.thens[start] = end
elif jump_op == 'JUMP_ABSOLUTE':
if_then_maybe = {'type': 'if-then',
@@ -937,13 +936,20 @@ class Scanner2(Scanner):
'end': end})
elif code_pre_rtarget == self.opc.RETURN_VALUE:
if self.version == 2.7 or pre_rtarget not in self.ignore_if:
self.structs.append({'type': 'if-then',
'start': start,
'end': rtarget})
self.thens[start] = rtarget
if self.version == 2.7 or code[pre_rtarget+1] != self.opc.JUMP_FORWARD:
self.fixed_jumps[offset] = rtarget
self.return_end_ifs.add(pre_rtarget)
# 10 is exception-match. If there is an exception match in the
# compare, then this is an exception clause not an if-then clause
if (self.code[self.prev[offset]] != self.opc.COMPARE_OP or
self.code[self.prev[offset]+1] != 10):
self.structs.append({'type': 'if-then',
'start': start,
'end': rtarget})
self.thens[start] = rtarget
if self.version == 2.7 or code[pre_rtarget+1] != self.opc.JUMP_FORWARD:
self.fixed_jumps[offset] = rtarget
self.return_end_ifs.add(pre_rtarget)
pass
pass
pass
elif op in self.pop_jump_if_or_pop:
target = self.get_target(offset, op)

View File

@@ -204,7 +204,7 @@ TABLE_DIRECT = {
'compare_chained2': ( '%[1]{pattr.replace("-", " ")} %p', (0, 19)),
# 'classdef': (), # handled by n_classdef()
'function_def': ( '\n\n%|def %c\n', -2), # -2 to handle closures
'funcdefdeco': ( '\n\n%c', 0),
'function_def_deco': ( '\n\n%c', 0),
'mkfuncdeco': ( '%|@%c\n%c', 0, 1),
'mkfuncdeco0': ( '%|def %c\n', 0),
'classdefdeco': ( '\n\n%c', 0),

View File

@@ -441,7 +441,7 @@ class SourceWalker(GenericASTTraversal, object):
if version >= 3.5:
TABLE_DIRECT.update({
'await_expr': ( 'await %c', 0),
'await_stmt': ( '%|%c', 0),
'await_stmt': ( '%|%c\n', 0),
'async_for_stmt': (
'%|async for %c in %c:\n%+%c%-\n\n', 9, 1, 25 ),
'async_forelse_stmt': (
@@ -548,7 +548,7 @@ class SourceWalker(GenericASTTraversal, object):
TABLE_R.update({
'CALL_FUNCTION_EX': ('%c(*%P)', 0, (1, 2, ', ', 100)),
# Not quite right
'CALL_FUNCTION_EX_KW': ('%c(**%C)', 0, (2,3, ',')),
'CALL_FUNCTION_EX_KW': ('%c(**%C)', 0, (2, 3, ',')),
})
def build_unpack_tuple_with_call(node):
@@ -2317,13 +2317,12 @@ class SourceWalker(GenericASTTraversal, object):
# lowest down the stack
if self.version == 3.5:
if str == '%c(%C, ':
str = '%c(*%C, %c)'
entry = ('%c(*%C, %c)', 0, p2, -2)
elif str == '%c(%C':
str = '%c(*%C)'
p2 = (1, 100, '')
entry = ('%c(*%C)', 0, (1, 100, ''))
else:
str += '*%c)'
entry = (str, 0, p2, -2)
entry = (str, 0, p2, -2)
elif op == 'CALL_FUNCTION_KW':
str += '**%c)'
entry = (str, 0, p2, -2)

View File

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