You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-02 16:44:46 +08:00
Merge branch 'master' into python-2.4
This commit is contained in:
6
.github/ISSUE_TEMPLATE/bug-report.md
vendored
6
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -11,7 +11,7 @@ Please remove any of the optional sections if they are not applicable.
|
||||
Prerequisites
|
||||
|
||||
* Make sure the bytecode you have can be disassembled with a
|
||||
disassembler.
|
||||
disassembler and produces valid results.
|
||||
* Don't put bytecode and corresponding source code on any service that
|
||||
requires registration to download.
|
||||
* When you open a bug report there is no privacy. If the legitimacy of
|
||||
@@ -35,7 +35,7 @@ decompiler service for versions of Python up to 2.6.
|
||||
|
||||
## How to Reproduce
|
||||
|
||||
<!-- Please show both the input you gave and the
|
||||
<!-- Please show both the *input* you gave and the
|
||||
output you got in describing how to reproduce the bug:
|
||||
|
||||
or give a complete console log with input and output
|
||||
@@ -63,7 +63,7 @@ can add that too.
|
||||
Please modify for your setup
|
||||
|
||||
- Uncompyle6 version: output from `uncompyle6 --version` or `pip show uncompyle6`
|
||||
- Python version: `python -V`
|
||||
- Python version for the version of Python the byte-compiled the file: `python -c "import sys; print(sys.version)"` where `python` is the correct Cpython or Pypy binary.
|
||||
- OS and Version: [e.g. Ubuntu bionic]
|
||||
|
||||
-->
|
||||
|
@@ -21,8 +21,9 @@ for version in $PYVERSIONS; do
|
||||
exit $?
|
||||
fi
|
||||
make clean && pip install -e .
|
||||
if ! make check; then
|
||||
if ! make check-short; then
|
||||
exit $?
|
||||
fi
|
||||
echo === $version ===
|
||||
done
|
||||
make check
|
||||
|
@@ -5,4 +5,4 @@ if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
|
||||
echo "This script should be *sourced* rather than run directly through bash"
|
||||
exit 1
|
||||
fi
|
||||
export PYVERSIONS='3.5.9 3.6.9 2.6.9 3.3.7 2.7.17 3.2.6 3.1.5 3.4.10 3.7.5'
|
||||
export PYVERSIONS='3.5.9 3.6.10 2.6.9 3.3.7 2.7.17 3.2.6 3.1.5 3.4.8 3.7.6 3.8.1'
|
||||
|
@@ -1,7 +1,11 @@
|
||||
PHONY=check test pytest
|
||||
SHELL=/bin/bash
|
||||
|
||||
PYTHON ?= python
|
||||
|
||||
#: Run all tests
|
||||
test check pytest:
|
||||
py.test
|
||||
@PYTHON_VERSION=`$(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2`; \
|
||||
if [[ $$PYTHON_VERSION > 3.2 ]] || [[ $$PYTHON_VERSION == 2.7 ]] || [[ $$PYTHON_VERSION == 2.6 ]]; then \
|
||||
py.test; \
|
||||
fi
|
||||
|
@@ -67,20 +67,14 @@ def test_grammar():
|
||||
(("l_stmts", ("lastl_stmt", "come_froms", "l_stmts")))
|
||||
)
|
||||
pass
|
||||
elif 3.0 < PYTHON_VERSION < 3.3:
|
||||
expect_right_recursive.add(
|
||||
(("l_stmts", ("lastl_stmt", "COME_FROM", "l_stmts")))
|
||||
)
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
else:
|
||||
expect_lhs.add("kwarg")
|
||||
|
||||
assert expect_lhs == set(lhs)
|
||||
|
||||
# FIXME
|
||||
if PYTHON_VERSION != 3.8:
|
||||
if PYTHON_VERSION < 3.8:
|
||||
assert expect_lhs == set(lhs)
|
||||
assert unused_rhs == set(rhs)
|
||||
|
||||
assert expect_right_recursive == right_recursive
|
||||
@@ -96,8 +90,10 @@ def test_grammar():
|
||||
]
|
||||
)
|
||||
reduced_dup_rhs = dict((k, dup_rhs[k]) for k in dup_rhs if k not in expect_dup_rhs)
|
||||
for k in reduced_dup_rhs:
|
||||
print(k, reduced_dup_rhs[k])
|
||||
if reduced_dup_rhs:
|
||||
print("\nPossible duplicate RHS that might be folded, into one of the LHS symbols")
|
||||
for k in reduced_dup_rhs:
|
||||
print(k, reduced_dup_rhs[k])
|
||||
# assert not reduced_dup_rhs, reduced_dup_rhs
|
||||
|
||||
s = get_scanner(PYTHON_VERSION, IS_PYPY)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
flake8
|
||||
hypothesis<=3.0.0
|
||||
six
|
||||
pytest==3.2.5
|
||||
pytest==3.2.5 # for 2.7 < PYTHON_VERSION <= 3.2 use pytest 2.9.2; for 3.1 2.10
|
||||
|
@@ -23,7 +23,7 @@ COVER_DIR=../tmp/grammar-cover
|
||||
# Run short tests
|
||||
check-short:
|
||||
@$(PYTHON) -V && PYTHON_VERSION=`$(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2`; \
|
||||
$(MAKE) check-bytecode-short
|
||||
$(MAKE) check-bytecode-$${PYTHON_VERSION}
|
||||
|
||||
# Run all tests
|
||||
check:
|
||||
|
BIN
test/bytecode_2.6_run/02_decorator.pyc
Normal file
BIN
test/bytecode_2.6_run/02_decorator.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5_run/02_decorator.pyc
Normal file
BIN
test/bytecode_3.5_run/02_decorator.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.6_run/04_lambda_star_default.pyc
Normal file
BIN
test/bytecode_3.6_run/04_lambda_star_default.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.7/04_async.pyc
Normal file
BIN
test/bytecode_3.7/04_async.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.7_run/04_lambda_star_default.pyc
Normal file
BIN
test/bytecode_3.7_run/04_lambda_star_default.pyc
Normal file
Binary file not shown.
@@ -1,9 +1,34 @@
|
||||
# From python 2.5 make_decorators.py
|
||||
# Bug was in not recognizing @memoize which uses grammra rules
|
||||
# Bug was in not recognizing @memoize which uses grammar rules
|
||||
# using nonterminals mkfuncdeco and mkfuncdeco0
|
||||
|
||||
# This file is RUNNABLE!
|
||||
def memoize(func):
|
||||
pass
|
||||
|
||||
def test_memoize(self):
|
||||
@memoize
|
||||
def double(x):
|
||||
return x * 2
|
||||
|
||||
# Seen in 3.7 test/test_c_locale_coercion.py
|
||||
# Bug was handling multiple decorators in 3.5+
|
||||
# simply because we didn't carry over parser rules over from
|
||||
# earlier versions.
|
||||
|
||||
x = 1
|
||||
def decorator(func):
|
||||
def inc_x():
|
||||
global x
|
||||
x += 1
|
||||
func()
|
||||
return inc_x
|
||||
|
||||
@decorator
|
||||
@decorator
|
||||
def fn():
|
||||
return
|
||||
|
||||
assert x == 1
|
||||
fn()
|
||||
assert x == 3
|
||||
|
@@ -37,5 +37,15 @@ def test_truediv():
|
||||
for y in simple_complex:
|
||||
check_div(x, y)
|
||||
|
||||
z2 = -1e1000j # Check that we can handle -inf as a complex number
|
||||
def test_plus_minus_0j():
|
||||
z1, z2 = (0j, (-0 - 0j))
|
||||
assert atan2(z1.imag, -1.0) == atan2(0.0, -1.0)
|
||||
assert atan2(z2.imag, -1.0), atan2(-0.0, -1.0)
|
||||
|
||||
# Check that we can handle -inf, and inf as a complex numbers.
|
||||
# And put it in a tuple and a list to make it harder.
|
||||
z1, z2 = (-1e1000j, 1e1000j)
|
||||
assert z1 in [-1e1000j, 1e1000j]
|
||||
assert z1 == z2
|
||||
test_truediv()
|
||||
test_plus_minus0j()
|
||||
|
@@ -45,3 +45,11 @@ d = (2, 3)
|
||||
assert f(2, **a) == {'c': 2, 'param1': 2, 'test': 'A'}
|
||||
assert f3(2, *c, **a) == {'c': 2, 'param1': 2, 'test': 2}
|
||||
assert f3(*d, **a) == {'c': 2, 'param1': 2, 'test': 3}
|
||||
|
||||
# From 3.7 test/test_collections.py
|
||||
# Bug was in getting **dict(..) right
|
||||
from collections import namedtuple
|
||||
|
||||
Point = namedtuple('Point', 'x y')
|
||||
p = Point(11, 22)
|
||||
assert p == Point(**dict(x=11, y=22))
|
||||
|
12
test/simple_source/bug37/04_async.py
Normal file
12
test/simple_source/bug37/04_async.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# from 3.7 test_contextlib_async.py
|
||||
# Bugs were not adding "async" when a function is a decorator,
|
||||
# and a misaligment when using "async with as".
|
||||
@_async_test
|
||||
async def test_enter(self):
|
||||
self.assertIs(await manager.__aenter__(), manager)
|
||||
|
||||
async with manager as context:
|
||||
async with woohoo() as x:
|
||||
x = 1
|
||||
y = 2
|
||||
assert manager is context
|
@@ -4,3 +4,13 @@ from functools import total_ordering
|
||||
@total_ordering
|
||||
class Frame:
|
||||
pass
|
||||
|
||||
|
||||
# From 3.7 test/test_c_locale_coercion.py
|
||||
# Bug is multiple decorators
|
||||
|
||||
@test
|
||||
@unittest
|
||||
class LocaleCoercionTests():
|
||||
# Test implicit reconfiguration of the environment during CLI startup
|
||||
pass
|
||||
|
@@ -159,6 +159,7 @@ case $PYVERSION in
|
||||
SKIP_TESTS=(
|
||||
[test_atexit.py]=1 #
|
||||
[test_decorators.py]=1 # Control flow wrt "if elif"
|
||||
[test_dis.py]=1 # We change line numbers - duh!
|
||||
)
|
||||
if (( batch )) ; then
|
||||
# Fails in crontab environment?
|
||||
@@ -172,16 +173,42 @@ case $PYVERSION in
|
||||
SKIP_TESTS=(
|
||||
[test_contains.py]=1 # Code "while False: yield None" is optimized away in compilation
|
||||
[test_decorators.py]=1 # Control flow wrt "if elif"
|
||||
[test_dis.py]=1 # We change line numbers - duh!
|
||||
[test_pow.py]=1 # Control flow wrt "continue"
|
||||
[test_quopri.py]=1 # Only fails on POWER
|
||||
)
|
||||
;;
|
||||
3.7)
|
||||
SKIP_TESTS=(
|
||||
[test_argparse.py]=1 #
|
||||
[test_ast.py]=1 #
|
||||
[test_atexit.py]=1 #
|
||||
[test_bdb.py]=1 #
|
||||
[test_buffer.py]=1 #
|
||||
[test_builtin.py]=1 #
|
||||
[test_cmdline.py]=1 # Interactive?
|
||||
[test_codecs-3.7.py]=1
|
||||
[test_compare.py]=1
|
||||
[test_compile.py]=1
|
||||
[test_contains.py]=1 # Code "while False: yield None" is optimized away in compilation
|
||||
[test_decorators.py]=1 # Control flow wrt "if elif"
|
||||
[test_contextlib_async.py]=1 # Investigate
|
||||
[test_context.py]=1
|
||||
[test_coroutines.py]=1 # Parse error
|
||||
[test_curses.py]=1 # Parse error
|
||||
[test_cmath.py]=1 # Syntax error - investigate
|
||||
[test_decorators.py]=1 # Control flow wrt "if elif"
|
||||
[test_dis.py]=1 # We change line numbers - duh!
|
||||
# ...
|
||||
)
|
||||
;;
|
||||
3.8)
|
||||
SKIP_TESTS=(
|
||||
[test_contains.py]=1 # Code "while False: yield None" is optimized away in compilation
|
||||
[test_collections.py]=1 # parse error
|
||||
[test_decorators.py]=1 # Control flow wrt "if elif"
|
||||
[test_dis.py]=1 # We change line numbers - duh!
|
||||
[test_pow.py]=1 # Control flow wrt "continue"
|
||||
[test_quopri.py]=1 # Only fails on POWER
|
||||
# ...
|
||||
)
|
||||
;;
|
||||
*)
|
||||
|
@@ -21,17 +21,19 @@ from uncompyle6.disas import check_object_path
|
||||
from uncompyle6.semantics import pysource
|
||||
from uncompyle6.parser import ParserError
|
||||
from uncompyle6.version import VERSION
|
||||
|
||||
# from uncompyle6.linenumbers import line_number_mapping
|
||||
|
||||
from uncompyle6.semantics.pysource import code_deparse
|
||||
from uncompyle6.semantics.fragments import code_deparse as code_deparse_fragments
|
||||
from uncompyle6.semantics.fragments import code_deparse as code_deparse_fragments
|
||||
from uncompyle6.semantics.linemap import deparse_code_with_map
|
||||
|
||||
from xdis.load import load_module
|
||||
|
||||
|
||||
def _get_outstream(outfile):
|
||||
dir = os.path.dirname(outfile)
|
||||
failed_file = outfile + '_failed'
|
||||
failed_file = outfile + "_failed"
|
||||
if os.path.exists(failed_file):
|
||||
os.remove(failed_file)
|
||||
try:
|
||||
@@ -71,7 +73,7 @@ def decompile(
|
||||
real_out = out or sys.stdout
|
||||
|
||||
def write(s):
|
||||
s += '\n'
|
||||
s += "\n"
|
||||
real_out.write(s)
|
||||
|
||||
assert iscode(co)
|
||||
@@ -100,7 +102,7 @@ def decompile(
|
||||
" (%s)" % m, run_pypy_str,
|
||||
'\n# '.join(sys_version_lines)))
|
||||
if co.co_filename:
|
||||
write('# Embedded file name: %s' % co.co_filename,)
|
||||
write("# Embedded file name: %s" % co.co_filename,)
|
||||
if timestamp:
|
||||
write('# Compiled at: %s' %
|
||||
datetime.datetime.fromtimestamp(timestamp))
|
||||
@@ -108,59 +110,70 @@ def decompile(
|
||||
real_out.write('# Size of source mod 2**32: %d bytes\n' %
|
||||
source_size)
|
||||
|
||||
debug_opts = {
|
||||
'asm': showasm,
|
||||
'ast': showast,
|
||||
'grammar': showgrammar
|
||||
}
|
||||
debug_opts = {"asm": showasm, "ast": showast, "grammar": showgrammar}
|
||||
|
||||
try:
|
||||
if mapstream:
|
||||
if isinstance(mapstream, str):
|
||||
mapstream = _get_outstream(mapstream)
|
||||
|
||||
deparsed = deparse_code_with_map(bytecode_version, co, out, showasm, showast,
|
||||
showgrammar,
|
||||
code_objects = code_objects,
|
||||
is_pypy = is_pypy,
|
||||
)
|
||||
header_count = 3+len(sys_version_lines)
|
||||
linemap = [(line_no, deparsed.source_linemap[line_no]+header_count)
|
||||
for line_no in
|
||||
sorted(deparsed.source_linemap.keys())]
|
||||
deparsed = deparse_code_with_map(
|
||||
bytecode_version,
|
||||
co,
|
||||
out,
|
||||
showasm,
|
||||
showast,
|
||||
showgrammar,
|
||||
code_objects=code_objects,
|
||||
is_pypy=is_pypy,
|
||||
)
|
||||
header_count = 3 + len(sys_version_lines)
|
||||
linemap = [
|
||||
(line_no, deparsed.source_linemap[line_no] + header_count)
|
||||
for line_no in sorted(deparsed.source_linemap.keys())
|
||||
]
|
||||
mapstream.write("\n\n# %s\n" % linemap)
|
||||
else:
|
||||
if do_fragments:
|
||||
deparse_fn = code_deparse_fragments
|
||||
else:
|
||||
deparse_fn = code_deparse
|
||||
deparsed = deparse_fn(co, out, bytecode_version,
|
||||
debug_opts = debug_opts,
|
||||
is_pypy=is_pypy)
|
||||
deparsed = deparse_fn(
|
||||
co, out, bytecode_version, debug_opts=debug_opts, is_pypy=is_pypy
|
||||
)
|
||||
pass
|
||||
return deparsed
|
||||
except pysource.SourceWalkerError, e:
|
||||
# deparsing failed
|
||||
raise pysource.SourceWalkerError(str(e))
|
||||
|
||||
|
||||
def compile_file(source_path):
|
||||
if source_path.endswith('.py'):
|
||||
if source_path.endswith(".py"):
|
||||
basename = source_path[:-3]
|
||||
else:
|
||||
basename = source_path
|
||||
|
||||
if hasattr(sys, 'pypy_version_info'):
|
||||
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')
|
||||
py_compile.compile(source_path, bytecode_path, "exec")
|
||||
return bytecode_path
|
||||
|
||||
|
||||
def decompile_file(filename, outstream=None, showasm=None, showast=False,
|
||||
showgrammar=False, source_encoding=None, mapstream=None, do_fragments=False):
|
||||
def decompile_file(
|
||||
filename,
|
||||
outstream=None,
|
||||
showasm=None,
|
||||
showast=False,
|
||||
showgrammar=False,
|
||||
source_encoding=None,
|
||||
mapstream=None,
|
||||
do_fragments=False,
|
||||
):
|
||||
"""
|
||||
decompile Python byte-code file (.pyc). Return objects to
|
||||
all of the deparsed objects found in `filename`.
|
||||
@@ -168,32 +181,68 @@ def decompile_file(filename, outstream=None, showasm=None, showast=False,
|
||||
|
||||
filename = check_object_path(filename)
|
||||
code_objects = {}
|
||||
(version, timestamp, magic_int, co, is_pypy,
|
||||
source_size) = load_module(filename, code_objects)
|
||||
(version, timestamp, magic_int, co, is_pypy, source_size) = load_module(
|
||||
filename, code_objects
|
||||
)
|
||||
|
||||
if isinstance(co, list):
|
||||
deparsed = []
|
||||
for con in co:
|
||||
deparsed.append(
|
||||
decompile(version, con, outstream, showasm, showast,
|
||||
timestamp, showgrammar, source_encoding, code_objects=code_objects,
|
||||
is_pypy=is_pypy, magic_int=magic_int),
|
||||
mapstream=mapstream)
|
||||
decompile(
|
||||
version,
|
||||
con,
|
||||
outstream,
|
||||
showasm,
|
||||
showast,
|
||||
timestamp,
|
||||
showgrammar,
|
||||
source_encoding,
|
||||
code_objects=code_objects,
|
||||
is_pypy=is_pypy,
|
||||
magic_int=magic_int,
|
||||
),
|
||||
mapstream=mapstream,
|
||||
)
|
||||
else:
|
||||
deparsed = [decompile(version, co, outstream, showasm, showast,
|
||||
timestamp, showgrammar, source_encoding,
|
||||
code_objects=code_objects, source_size=source_size,
|
||||
is_pypy=is_pypy, magic_int=magic_int,
|
||||
mapstream=mapstream, do_fragments=do_fragments)]
|
||||
deparsed = [
|
||||
decompile(
|
||||
version,
|
||||
co,
|
||||
outstream,
|
||||
showasm,
|
||||
showast,
|
||||
timestamp,
|
||||
showgrammar,
|
||||
source_encoding,
|
||||
code_objects=code_objects,
|
||||
source_size=source_size,
|
||||
is_pypy=is_pypy,
|
||||
magic_int=magic_int,
|
||||
mapstream=mapstream,
|
||||
do_fragments=do_fragments,
|
||||
)
|
||||
]
|
||||
co = None
|
||||
return deparsed
|
||||
|
||||
|
||||
# FIXME: combine into an options parameter
|
||||
def main(in_base, out_base, compiled_files, source_files, outfile=None,
|
||||
showasm=None, showast=False, do_verify=False,
|
||||
showgrammar=False, source_encoding=None, raise_on_error=False,
|
||||
do_linemaps=False, do_fragments=False):
|
||||
def main(
|
||||
in_base,
|
||||
out_base,
|
||||
compiled_files,
|
||||
source_files,
|
||||
outfile=None,
|
||||
showasm=None,
|
||||
showast=False,
|
||||
do_verify=False,
|
||||
showgrammar=False,
|
||||
source_encoding=None,
|
||||
raise_on_error=False,
|
||||
do_linemaps=False,
|
||||
do_fragments=False,
|
||||
):
|
||||
"""
|
||||
in_base base directory for input files
|
||||
out_base base directory for output files (ignored when
|
||||
@@ -216,26 +265,25 @@ def main(in_base, out_base, compiled_files, source_files, outfile=None,
|
||||
infile = os.path.join(in_base, filename)
|
||||
# print("XXX", infile)
|
||||
if not os.path.exists(infile):
|
||||
sys.stderr.write("File '%s' doesn't exist. Skipped\n"
|
||||
% infile)
|
||||
sys.stderr.write("File '%s' doesn't exist. Skipped\n" % infile)
|
||||
continue
|
||||
|
||||
if do_linemaps:
|
||||
linemap_stream = infile + '.pymap'
|
||||
linemap_stream = infile + ".pymap"
|
||||
pass
|
||||
|
||||
# print (infile, file=sys.stderr)
|
||||
|
||||
if outfile: # outfile was given as parameter
|
||||
if outfile: # outfile was given as parameter
|
||||
outstream = _get_outstream(outfile)
|
||||
elif out_base is None:
|
||||
outstream = sys.stdout
|
||||
if do_linemaps:
|
||||
linemap_stream = sys.stdout
|
||||
if do_verify:
|
||||
prefix = os.path.basename(filename) + '-'
|
||||
if prefix.endswith('.py'):
|
||||
prefix = prefix[:-len('.py')]
|
||||
prefix = os.path.basename(filename) + "-"
|
||||
if prefix.endswith(".py"):
|
||||
prefix = prefix[: -len(".py")]
|
||||
|
||||
# Unbuffer output if possible
|
||||
if sys.stdout.isatty():
|
||||
@@ -249,10 +297,10 @@ def main(in_base, out_base, compiled_files, source_files, outfile=None,
|
||||
os.dup2(tee.stdin.fileno(), sys.stdout.fileno())
|
||||
os.dup2(tee.stdin.fileno(), sys.stderr.fileno())
|
||||
else:
|
||||
if filename.endswith('.pyc'):
|
||||
if filename.endswith(".pyc"):
|
||||
current_outfile = os.path.join(out_base, filename[0:-1])
|
||||
else:
|
||||
current_outfile = os.path.join(out_base, filename) + '_dis'
|
||||
current_outfile = os.path.join(out_base, filename) + "_dis"
|
||||
pass
|
||||
pass
|
||||
|
||||
@@ -262,15 +310,25 @@ def main(in_base, out_base, compiled_files, source_files, outfile=None,
|
||||
|
||||
# Try to uncompile the input file
|
||||
try:
|
||||
deparsed = decompile_file(infile, outstream, showasm, showast, showgrammar,
|
||||
source_encoding, linemap_stream, do_fragments)
|
||||
deparsed = decompile_file(
|
||||
infile,
|
||||
outstream,
|
||||
showasm,
|
||||
showast,
|
||||
showgrammar,
|
||||
source_encoding,
|
||||
linemap_stream,
|
||||
do_fragments,
|
||||
)
|
||||
if do_fragments:
|
||||
for d in deparsed:
|
||||
last_mod = None
|
||||
offsets = d.offsets
|
||||
for e in sorted([k for k in offsets.keys() if isinstance(k[1], int)]):
|
||||
for e in sorted(
|
||||
[k for k in offsets.keys() if isinstance(k[1], int)]
|
||||
):
|
||||
if e[0] != last_mod:
|
||||
line = '=' * len(e[0])
|
||||
line = "=" * len(e[0])
|
||||
outstream.write("%s\n%s\n%s\n" % (line, e[0], line))
|
||||
last_mod = e[0]
|
||||
info = offsets[e]
|
||||
@@ -295,9 +353,11 @@ def main(in_base, out_base, compiled_files, source_files, outfile=None,
|
||||
raise
|
||||
except RuntimeError, e:
|
||||
sys.stdout.write("\n%s\n" % str(e))
|
||||
if str(e).startswith('Unsupported Python'):
|
||||
if str(e).startswith("Unsupported Python"):
|
||||
sys.stdout.write("\n")
|
||||
sys.stderr.write("\n# Unsupported bytecode in file %s\n# %s\n" % (infile, e))
|
||||
sys.stderr.write(
|
||||
"\n# Unsupported bytecode in file %s\n# %s\n" % (infile, e)
|
||||
)
|
||||
else:
|
||||
if outfile:
|
||||
outstream.close()
|
||||
@@ -314,22 +374,22 @@ def main(in_base, out_base, compiled_files, source_files, outfile=None,
|
||||
# else:
|
||||
# sys.stderr.write("\n# %s" % sys.exc_info()[1])
|
||||
# sys.stderr.write("\n# Can't uncompile %s\n" % infile)
|
||||
else: # uncompile successful
|
||||
else: # uncompile successful
|
||||
if current_outfile:
|
||||
outstream.close()
|
||||
|
||||
if do_verify:
|
||||
try:
|
||||
msg = verify.compare_code_with_srcfile(infile,
|
||||
current_outfile,
|
||||
do_verify)
|
||||
msg = verify.compare_code_with_srcfile(
|
||||
infile, current_outfile, do_verify
|
||||
)
|
||||
if not current_outfile:
|
||||
if not msg:
|
||||
print('\n# okay decompiling %s' % infile)
|
||||
print("\n# okay decompiling %s" % infile)
|
||||
okay_files += 1
|
||||
else:
|
||||
verify_failed_files += 1
|
||||
print('\n# %s\n\t%s', infile, msg)
|
||||
print("\n# %s\n\t%s", infile, msg)
|
||||
pass
|
||||
else:
|
||||
okay_files += 1
|
||||
@@ -337,7 +397,7 @@ def main(in_base, out_base, compiled_files, source_files, outfile=None,
|
||||
except verify.VerifyCmpError, e:
|
||||
print(e)
|
||||
verify_failed_files += 1
|
||||
os.rename(current_outfile, current_outfile + '_unverified')
|
||||
os.rename(current_outfile, current_outfile + "_unverified")
|
||||
sys.stderr.write("### Error Verifying %s\n" % filename)
|
||||
sys.stderr.write(str(e) + "\n")
|
||||
if not outfile:
|
||||
@@ -359,13 +419,24 @@ def main(in_base, out_base, compiled_files, source_files, outfile=None,
|
||||
else:
|
||||
okay_files += 1
|
||||
if not current_outfile:
|
||||
mess = '\n# okay decompiling'
|
||||
mess = "\n# okay decompiling"
|
||||
# mem_usage = __memUsage()
|
||||
print mess, infile
|
||||
if current_outfile:
|
||||
sys.stdout.write("%s -- %s\r" %
|
||||
(infile, status_msg(do_verify, tot_files, okay_files, failed_files,
|
||||
verify_failed_files, do_verify)))
|
||||
sys.stdout.write(
|
||||
"%s -- %s\r"
|
||||
% (
|
||||
infile,
|
||||
status_msg(
|
||||
do_verify,
|
||||
tot_files,
|
||||
okay_files,
|
||||
failed_files,
|
||||
verify_failed_files,
|
||||
do_verify,
|
||||
),
|
||||
)
|
||||
)
|
||||
try:
|
||||
# FIXME: Something is weird with Pypy here
|
||||
sys.stdout.flush()
|
||||
@@ -384,24 +455,30 @@ def main(in_base, out_base, compiled_files, source_files, outfile=None,
|
||||
|
||||
# ---- main ----
|
||||
|
||||
if sys.platform.startswith('linux') and os.uname()[2][:2] in ['2.', '3.', '4.']:
|
||||
if sys.platform.startswith("linux") and os.uname()[2][:2] in ["2.", "3.", "4."]:
|
||||
|
||||
def __memUsage():
|
||||
mi = open('/proc/self/stat', 'r')
|
||||
mi = open("/proc/self/stat", "r")
|
||||
mu = mi.readline().split()[22]
|
||||
mi.close()
|
||||
return int(mu) / 1000000
|
||||
else:
|
||||
def __memUsage():
|
||||
return ''
|
||||
|
||||
def status_msg(do_verify, tot_files, okay_files, failed_files,
|
||||
verify_failed_files, weak_verify):
|
||||
if weak_verify == 'weak':
|
||||
verification_type = 'weak '
|
||||
elif weak_verify == 'verify-run':
|
||||
verification_type = 'run '
|
||||
|
||||
else:
|
||||
|
||||
def __memUsage():
|
||||
return ""
|
||||
|
||||
|
||||
def status_msg(
|
||||
do_verify, tot_files, okay_files, failed_files, verify_failed_files, weak_verify
|
||||
):
|
||||
if weak_verify == "weak":
|
||||
verification_type = "weak "
|
||||
elif weak_verify == "verify-run":
|
||||
verification_type = "run "
|
||||
else:
|
||||
verification_type = ''
|
||||
verification_type = ""
|
||||
if tot_files == 1:
|
||||
if failed_files:
|
||||
return "\n# decompile failed"
|
||||
@@ -411,7 +488,11 @@ def status_msg(do_verify, tot_files, okay_files, failed_files,
|
||||
return "\n# Successfully decompiled file"
|
||||
pass
|
||||
pass
|
||||
mess = "decompiled %i files: %i okay, %i failed" % (tot_files, okay_files, failed_files)
|
||||
mess = "decompiled %i files: %i okay, %i failed" % (
|
||||
tot_files,
|
||||
okay_files,
|
||||
failed_files,
|
||||
)
|
||||
if do_verify:
|
||||
mess += (", %i %sverification failed" % (verify_failed_files, verification_type))
|
||||
mess += ", %i %sverification failed" % (verify_failed_files, verification_type)
|
||||
return mess
|
||||
|
@@ -99,7 +99,6 @@ class PythonParser(GenericASTBuilder):
|
||||
many arguments it has. Often it is not used.
|
||||
"""
|
||||
if rule not in self.new_rules:
|
||||
# print("XXX ", rule) # debug
|
||||
self.new_rules.add(rule)
|
||||
self.addRule(rule, nop_func)
|
||||
customize[opname] = arg_count
|
||||
|
@@ -837,11 +837,8 @@ class Python3Parser(PythonParser):
|
||||
dict_comp ::= LOAD_DICTCOMP LOAD_STR MAKE_FUNCTION_0 expr
|
||||
GET_ITER CALL_FUNCTION_1
|
||||
classdefdeco1 ::= expr classdefdeco2 CALL_FUNCTION_1
|
||||
classdefdeco1 ::= expr classdefdeco1 CALL_FUNCTION_1
|
||||
"""
|
||||
if self.version < 3.5:
|
||||
rule += """
|
||||
classdefdeco1 ::= expr classdefdeco1 CALL_FUNCTION_1
|
||||
"""
|
||||
self.addRule(rule, nop_func)
|
||||
|
||||
self.custom_classfunc_rule(
|
||||
|
@@ -90,8 +90,6 @@ class Python37Parser(Python37BaseParser):
|
||||
else_suitec ::= c_stmts
|
||||
else_suitec ::= returns
|
||||
|
||||
stmt ::= assert
|
||||
|
||||
stmt ::= classdef
|
||||
stmt ::= call_stmt
|
||||
|
||||
@@ -800,16 +798,6 @@ class Python37Parser(Python37BaseParser):
|
||||
classdefdeco ::= classdefdeco1 store
|
||||
|
||||
expr ::= LOAD_ASSERT
|
||||
assert ::= assert_expr jmp_true LOAD_ASSERT RAISE_VARARGS_1 COME_FROM
|
||||
stmt ::= assert2
|
||||
assert2 ::= assert_expr jmp_true LOAD_ASSERT expr
|
||||
CALL_FUNCTION_1 RAISE_VARARGS_1 COME_FROM
|
||||
|
||||
assert_expr ::= expr
|
||||
assert_expr ::= assert_expr_or
|
||||
assert_expr ::= assert_expr_and
|
||||
assert_expr_or ::= assert_expr jmp_true expr
|
||||
assert_expr_and ::= assert_expr jmp_false expr
|
||||
|
||||
ifstmt ::= testexpr _ifstmts_jump
|
||||
|
||||
@@ -970,7 +958,6 @@ class Python37Parser(Python37BaseParser):
|
||||
|
||||
and ::= expr JUMP_IF_FALSE_OR_POP expr COME_FROM
|
||||
and ::= expr JUMP_IF_FALSE expr COME_FROM
|
||||
and ::= expr jmp_false expr
|
||||
|
||||
## FIXME: Is the below needed or is it covered above??
|
||||
and ::= expr jmp_false expr COME_FROM
|
||||
|
@@ -424,6 +424,7 @@ class Python37BaseParser(PythonParser):
|
||||
dict_comp ::= LOAD_DICTCOMP LOAD_STR MAKE_FUNCTION_0 expr
|
||||
GET_ITER CALL_FUNCTION_1
|
||||
classdefdeco1 ::= expr classdefdeco2 CALL_FUNCTION_1
|
||||
classdefdeco1 ::= expr classdefdeco1 CALL_FUNCTION_1
|
||||
"""
|
||||
self.addRule(rule, nop_func)
|
||||
|
||||
@@ -537,12 +538,12 @@ class Python37BaseParser(PythonParser):
|
||||
"""
|
||||
stmt ::= assert_pypy
|
||||
stmt ::= assert2_pypy", nop_func)
|
||||
assert_pypy ::= JUMP_IF_NOT_DEBUG assert_expr jmp_true
|
||||
assert_pypy ::= JUMP_IF_NOT_DEBUG expr jmp_true
|
||||
LOAD_ASSERT RAISE_VARARGS_1 COME_FROM
|
||||
assert2_pypy ::= JUMP_IF_NOT_DEBUG assert_expr jmp_true
|
||||
LOAD_ASSERT expr CALL_FUNCTION_1
|
||||
RAISE_VARARGS_1 COME_FROM
|
||||
assert2_pypy ::= JUMP_IF_NOT_DEBUG assert_expr jmp_true
|
||||
assert2_pypy ::= JUMP_IF_NOT_DEBUG expr jmp_true
|
||||
LOAD_ASSERT expr CALL_FUNCTION_1
|
||||
RAISE_VARARGS_1 COME_FROM,
|
||||
""",
|
||||
|
@@ -42,6 +42,13 @@ else:
|
||||
# various templates we use odd values. Avoiding equal-precedent comparisons
|
||||
# avoids ambiguity what to do when the precedence is equal.
|
||||
|
||||
# The precidence of a key below applies the key, a node, and the its
|
||||
# *parent*. A node however sometimes sets the precidence for its
|
||||
# children. For example, "call" has precidence 2 so we don't get
|
||||
# additional the additional parenthesis of: ".. op (call())". However
|
||||
# for call's children, it parameters, we set the the precidence high,
|
||||
# say to 100, to make sure we avoid additional prenthesis in
|
||||
# call((.. op ..)).
|
||||
|
||||
PRECEDENCE = {
|
||||
'yield': 102,
|
||||
@@ -316,10 +323,18 @@ TABLE_DIRECT = {
|
||||
'compare_chained1': ( '%[3]{pattr.replace("-", " ")} %p %p', (0, 19), (-2, 19)),
|
||||
'compare_chained2': ( '%[1]{pattr.replace("-", " ")} %p', (0, 19)),
|
||||
# 'classdef': (), # handled by n_classdef()
|
||||
|
||||
# A custom rule in n_function def distinguishes whether to call this or
|
||||
# function_def_async
|
||||
'function_def': ( '\n\n%|def %c\n', -2), # -2 to handle closures
|
||||
|
||||
'function_def_deco': ( '\n\n%c', 0),
|
||||
'mkfuncdeco': ( '%|@%c\n%c', 0, 1),
|
||||
|
||||
# A custom rule in n_function def distinguishes whether to call this or
|
||||
# function_def_async
|
||||
'mkfuncdeco0': ( '%|def %c\n', 0),
|
||||
|
||||
'classdefdeco': ( '\n\n%c', 0),
|
||||
'classdefdeco1': ( '%|@%c\n%c', 0, 1),
|
||||
'kwarg': ( '%[0]{pattr}=%c', 1), # Change when Python 2 does LOAD_STR
|
||||
|
@@ -47,6 +47,7 @@ def customize_for_version(self, is_pypy, version):
|
||||
# Without PyPy
|
||||
#######################
|
||||
TABLE_DIRECT.update({
|
||||
# "assert" and "assert_expr" are added via transform rules.
|
||||
"assert": ("%|assert %c\n", (0, "assert_expr")),
|
||||
"assert2": ("%|assert %c, %c\n", (0, "assert_expr"), 3),
|
||||
|
||||
|
@@ -49,7 +49,7 @@ def customize_for_version3(self, version):
|
||||
"raise_stmt2": ("%|raise %c from %c\n", 0, 1),
|
||||
"store_locals": ("%|# inspect.currentframe().f_locals = __locals__\n",),
|
||||
"withstmt": ("%|with %c:\n%+%c%-", 0, 3),
|
||||
"withasstmt": ("%|with %c as (%c):\n%+%c%-", 0, 2, 3),
|
||||
"withasstmt": ("%|with %c as %c:\n%+%c%-", 0, 2, 3),
|
||||
}
|
||||
)
|
||||
|
||||
|
@@ -17,44 +17,47 @@
|
||||
|
||||
from xdis.code import iscode
|
||||
from xdis.util import COMPILER_FLAG_BIT
|
||||
from uncompyle6.semantics.consts import (
|
||||
INDENT_PER_LEVEL, TABLE_DIRECT)
|
||||
from uncompyle6.semantics.helper import (
|
||||
flatten_list, gen_function_parens_adjust)
|
||||
from uncompyle6.semantics.consts import INDENT_PER_LEVEL, TABLE_DIRECT
|
||||
from uncompyle6.semantics.helper import flatten_list, gen_function_parens_adjust
|
||||
|
||||
#######################
|
||||
# Python 3.5+ Changes #
|
||||
#######################
|
||||
def customize_for_version35(self, version):
|
||||
TABLE_DIRECT.update({
|
||||
'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_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, 'expr'), 7 ),
|
||||
'async_with_as_stmt': (
|
||||
'%|async with %c as %c:\n%+%|%c%-',
|
||||
(0, 'expr'), (6, 'store'), 7),
|
||||
'unmap_dict': ( '{**%C}', (0, -1, ', **') ),
|
||||
# 'unmapexpr': ( '{**%c}', 0), # done by n_unmapexpr
|
||||
|
||||
})
|
||||
TABLE_DIRECT.update(
|
||||
{
|
||||
"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_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, "expr"), 7),
|
||||
"async_with_as_stmt": (
|
||||
"%|async with %c as %c:\n%+%c%-",
|
||||
(0, "expr"),
|
||||
(6, "store"),
|
||||
7,
|
||||
),
|
||||
"unmap_dict": ("{**%C}", (0, -1, ", **")),
|
||||
# "unmapexpr": ( "{**%c}", 0), # done by n_unmapexpr
|
||||
}
|
||||
)
|
||||
|
||||
def async_call(node):
|
||||
self.f.write('async ')
|
||||
node.kind == 'call'
|
||||
self.f.write("async ")
|
||||
node.kind == "call"
|
||||
p = self.prec
|
||||
self.prec = 80
|
||||
self.template_engine(('%c(%P)', 0, (1, -4, ', ',
|
||||
100)), node)
|
||||
self.template_engine(("%c(%P)", 0, (1, -4, ", ", 100)), node)
|
||||
self.prec = p
|
||||
node.kind == 'async_call'
|
||||
node.kind == "async_call"
|
||||
self.prune()
|
||||
|
||||
self.n_async_call = async_call
|
||||
|
||||
def n_build_list_unpack(node):
|
||||
@@ -90,11 +93,13 @@ def customize_for_version35(self, version):
|
||||
if value.startswith("("):
|
||||
assert value.endswith(")")
|
||||
use_star = False
|
||||
value = value[1:-1].rstrip(" ") # Remove starting '(' and trailing ')' and additional spaces
|
||||
value = value[1:-1].rstrip(
|
||||
" "
|
||||
) # Remove starting "(" and trailing ")" and additional spaces
|
||||
if value == "":
|
||||
pass
|
||||
else:
|
||||
if value.endswith(","): # if args has only one item
|
||||
if value.endswith(","): # if args has only one item
|
||||
value = value[:-1]
|
||||
if line_number != self.line_number:
|
||||
sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
|
||||
@@ -114,16 +119,19 @@ def customize_for_version35(self, version):
|
||||
self.prec = p
|
||||
self.prune()
|
||||
return
|
||||
|
||||
self.n_build_list_unpack = n_build_list_unpack
|
||||
|
||||
def n_call(node):
|
||||
p = self.prec
|
||||
self.prec = 100
|
||||
mapping = self._get_mapping(node)
|
||||
table = mapping[0]
|
||||
key = node
|
||||
for i in mapping[1:]:
|
||||
key = key[i]
|
||||
pass
|
||||
if key.kind.startswith('CALL_FUNCTION_VAR_KW'):
|
||||
if key.kind.startswith("CALL_FUNCTION_VAR_KW"):
|
||||
# Python 3.5 changes the stack position of
|
||||
# *args: kwargs come after *args whereas
|
||||
# in earlier Pythons, *args is at the end
|
||||
@@ -137,12 +145,12 @@ def customize_for_version35(self, version):
|
||||
kwarg_pos = entry[2][1]
|
||||
args_pos = kwarg_pos - 1
|
||||
# Put last node[args_pos] after subsequent kwargs
|
||||
while node[kwarg_pos] == 'kwarg' and kwarg_pos < len(node):
|
||||
while node[kwarg_pos] == "kwarg" and kwarg_pos < len(node):
|
||||
# swap node[args_pos] with node[kwargs_pos]
|
||||
node[kwarg_pos], node[args_pos] = node[args_pos], node[kwarg_pos]
|
||||
args_pos = kwarg_pos
|
||||
kwarg_pos += 1
|
||||
elif key.kind.startswith('CALL_FUNCTION_VAR'):
|
||||
elif key.kind.startswith("CALL_FUNCTION_VAR"):
|
||||
# CALL_FUNCTION_VAR's top element of the stack contains
|
||||
# the variable argument list, then comes
|
||||
# annotation args, then keyword args.
|
||||
@@ -153,65 +161,81 @@ def customize_for_version35(self, version):
|
||||
kwargs = (argc >> 8) & 0xFF
|
||||
# FIXME: handle annotation args
|
||||
if nargs > 0:
|
||||
template = ('%c(%C, ', 0, (1, nargs+1, ', '))
|
||||
template = ("%c(%P, ", 0, (1, nargs + 1, ", ", 100))
|
||||
else:
|
||||
template = ('%c(', 0)
|
||||
template = ("%c(", 0)
|
||||
self.template_engine(template, node)
|
||||
|
||||
args_node = node[-2]
|
||||
if args_node in ('pos_arg', 'expr'):
|
||||
args_node = node[-2]
|
||||
if args_node in ("pos_arg", "expr"):
|
||||
args_node = args_node[0]
|
||||
if args_node == 'build_list_unpack':
|
||||
template = ('*%P)', (0, len(args_node)-1, ', *', 100))
|
||||
if args_node == "build_list_unpack":
|
||||
template = ("*%P)", (0, len(args_node) - 1, ", *", 100))
|
||||
self.template_engine(template, args_node)
|
||||
else:
|
||||
if len(node) - nargs > 3:
|
||||
template = ('*%c, %C)', nargs+1, (nargs+kwargs+1, -1, ', '))
|
||||
template = ("*%c, %P)", nargs + 1, (nargs + kwargs + 1, -1, ", ", 100))
|
||||
else:
|
||||
template = ('*%c)', nargs+1)
|
||||
template = ("*%c)", nargs + 1)
|
||||
self.template_engine(template, node)
|
||||
self.prec = p
|
||||
self.prune()
|
||||
else:
|
||||
gen_function_parens_adjust(key, node)
|
||||
|
||||
self.prec = 100
|
||||
self.default(node)
|
||||
|
||||
self.n_call = n_call
|
||||
|
||||
def n_function_def(node):
|
||||
n0 = node[0]
|
||||
is_code = False
|
||||
for i in list(range(len(n0)-2, -1, -1)):
|
||||
code_node = n0[i]
|
||||
if hasattr(code_node, 'attr') and iscode(code_node.attr):
|
||||
is_code = True
|
||||
def is_async_fn(node):
|
||||
code_node = node[0][0]
|
||||
for n in node[0]:
|
||||
if hasattr(n, "attr") and iscode(n.attr):
|
||||
code_node = n
|
||||
break
|
||||
pass
|
||||
pass
|
||||
|
||||
if is_code and (
|
||||
is_code = hasattr(code_node, "attr") and iscode(code_node.attr)
|
||||
return is_code and (
|
||||
code_node.attr.co_flags
|
||||
& (
|
||||
COMPILER_FLAG_BIT["COROUTINE"]
|
||||
| COMPILER_FLAG_BIT["ITERABLE_COROUTINE"]
|
||||
| COMPILER_FLAG_BIT["ASYNC_GENERATOR"]
|
||||
)
|
||||
):
|
||||
self.template_engine(('\n\n%|async def %c\n',
|
||||
-2), node)
|
||||
)
|
||||
|
||||
def n_function_def(node):
|
||||
if is_async_fn(node):
|
||||
self.template_engine(("\n\n%|async def %c\n", -2), node)
|
||||
else:
|
||||
self.template_engine(('\n\n%|def %c\n', -2),
|
||||
node)
|
||||
self.default(node)
|
||||
self.prune()
|
||||
|
||||
self.n_function_def = n_function_def
|
||||
|
||||
def n_mkfuncdeco0(node):
|
||||
if is_async_fn(node):
|
||||
self.template_engine(("%|async def %c\n", 0), node)
|
||||
else:
|
||||
self.default(node)
|
||||
self.prune()
|
||||
|
||||
self.n_mkfuncdeco0 = n_mkfuncdeco0
|
||||
|
||||
def unmapexpr(node):
|
||||
last_n = node[0][-1]
|
||||
for n in node[0]:
|
||||
self.preorder(n)
|
||||
if n != last_n:
|
||||
self.f.write(', **')
|
||||
self.f.write(", **")
|
||||
pass
|
||||
pass
|
||||
self.prune()
|
||||
pass
|
||||
|
||||
self.n_unmapexpr = unmapexpr
|
||||
|
||||
# FIXME: start here
|
||||
@@ -228,67 +252,75 @@ def customize_for_version35(self, version):
|
||||
# then the first * has already been printed.
|
||||
# Until I have a better way to check for CALL_FUNCTION_VAR,
|
||||
# will assume that if the text ends in *.
|
||||
last_was_star = self.f.getvalue().endswith('*')
|
||||
last_was_star = self.f.getvalue().endswith("*")
|
||||
|
||||
if lastnodetype.startswith('BUILD_LIST'):
|
||||
self.write('['); endchar = ']'
|
||||
elif lastnodetype.startswith('BUILD_TUPLE'):
|
||||
if lastnodetype.startswith("BUILD_LIST"):
|
||||
self.write("[")
|
||||
endchar = "]"
|
||||
elif lastnodetype.startswith("BUILD_TUPLE"):
|
||||
# Tuples can appear places that can NOT
|
||||
# have parenthesis around them, like array
|
||||
# subscripts. We check for that by seeing
|
||||
# if a tuple item is some sort of slice.
|
||||
no_parens = False
|
||||
for n in node:
|
||||
if n == 'expr' and n[0].kind.startswith('build_slice'):
|
||||
if n == "expr" and n[0].kind.startswith("build_slice"):
|
||||
no_parens = True
|
||||
break
|
||||
pass
|
||||
if no_parens:
|
||||
endchar = ''
|
||||
endchar = ""
|
||||
else:
|
||||
self.write('('); endchar = ')'
|
||||
self.write("(")
|
||||
endchar = ")"
|
||||
pass
|
||||
|
||||
elif lastnodetype.startswith('BUILD_SET'):
|
||||
self.write('{'); endchar = '}'
|
||||
elif lastnodetype.startswith('BUILD_MAP_UNPACK'):
|
||||
self.write('{*'); endchar = '}'
|
||||
elif lastnodetype.startswith('ROT_TWO'):
|
||||
self.write('('); endchar = ')'
|
||||
elif lastnodetype.startswith("BUILD_SET"):
|
||||
self.write("{")
|
||||
endchar = "}"
|
||||
elif lastnodetype.startswith("BUILD_MAP_UNPACK"):
|
||||
self.write("{*")
|
||||
endchar = "}"
|
||||
elif lastnodetype.startswith("ROT_TWO"):
|
||||
self.write("(")
|
||||
endchar = ")"
|
||||
else:
|
||||
raise TypeError('Internal Error: n_build_list expects list, tuple, set, or unpack')
|
||||
raise TypeError(
|
||||
"Internal Error: n_build_list expects list, tuple, set, or unpack"
|
||||
)
|
||||
|
||||
flat_elems = flatten_list(node)
|
||||
|
||||
self.indent_more(INDENT_PER_LEVEL)
|
||||
sep = ''
|
||||
sep = ""
|
||||
for elem in flat_elems:
|
||||
if elem in ('ROT_THREE', 'EXTENDED_ARG'):
|
||||
if elem in ("ROT_THREE", "EXTENDED_ARG"):
|
||||
continue
|
||||
assert elem == 'expr'
|
||||
assert elem == "expr"
|
||||
line_number = self.line_number
|
||||
value = self.traverse(elem)
|
||||
if elem[0] == 'tuple':
|
||||
assert value[0] == '('
|
||||
assert value[-1] == ')'
|
||||
if elem[0] == "tuple":
|
||||
assert value[0] == "("
|
||||
assert value[-1] == ")"
|
||||
value = value[1:-1]
|
||||
if value[-1] == ',':
|
||||
if value[-1] == ",":
|
||||
# singleton tuple
|
||||
value = value[:-1]
|
||||
else:
|
||||
value = '*' + value
|
||||
value = "*" + value
|
||||
if line_number != self.line_number:
|
||||
sep += '\n' + self.indent + INDENT_PER_LEVEL[:-1]
|
||||
sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
|
||||
else:
|
||||
if sep != '': sep += ' '
|
||||
if sep != "":
|
||||
sep += " "
|
||||
if not last_was_star:
|
||||
pass
|
||||
else:
|
||||
last_was_star = False
|
||||
self.write(sep, value)
|
||||
sep = ','
|
||||
if lastnode.attr == 1 and lastnodetype.startswith('BUILD_TUPLE'):
|
||||
self.write(',')
|
||||
sep = ","
|
||||
if lastnode.attr == 1 and lastnodetype.startswith("BUILD_TUPLE"):
|
||||
self.write(",")
|
||||
self.write(endchar)
|
||||
self.indent_less(INDENT_PER_LEVEL)
|
||||
|
||||
|
@@ -219,6 +219,7 @@ def customize_for_version36(self, version):
|
||||
# FIXME: decide if the below test be on kwargs == 'dict'
|
||||
if (call_function_ex.attr & 1 and
|
||||
(not isinstance(kwargs, Token) and kwargs != 'attribute')
|
||||
and kwargs != "call_kw36"
|
||||
and not kwargs[0].kind.startswith('kvlist')):
|
||||
self.call36_dict(kwargs)
|
||||
else:
|
||||
|
@@ -790,7 +790,8 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
|
||||
params.reverse()
|
||||
else:
|
||||
params.append("*%s" % code.co_varnames[argc])
|
||||
argc += 1
|
||||
if not is_lambda or self.version >= 3.6:
|
||||
argc += 1
|
||||
|
||||
# dump parameter list (with default values)
|
||||
if is_lambda:
|
||||
@@ -822,7 +823,7 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
|
||||
# created a parameter list and at the very end did a join on that?
|
||||
# Unless careful, We might lose line breaks though.
|
||||
ends_in_comma = False
|
||||
if kwonlyargcount > 0 and not is_lambda:
|
||||
if kwonlyargcount > 0:
|
||||
if not (4 & code.co_flags):
|
||||
if argc > 0:
|
||||
self.write(", *, ")
|
||||
@@ -852,7 +853,12 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
|
||||
|
||||
for i, flag in enumerate(other_kw):
|
||||
if flag:
|
||||
kw_args[i] = "%s" % kwargs[i]
|
||||
if i < len(kwargs):
|
||||
kw_args[i] = "%s" % kwargs[i]
|
||||
else:
|
||||
del kw_args[i]
|
||||
pass
|
||||
|
||||
self.write(", ".join(kw_args))
|
||||
ends_in_comma = False
|
||||
pass
|
||||
|
@@ -171,6 +171,8 @@ from uncompyle6.semantics.consts import (
|
||||
|
||||
|
||||
from uncompyle6.show import maybe_show_tree
|
||||
from uncompyle6.util import better_repr
|
||||
|
||||
|
||||
if PYTHON3:
|
||||
from io import StringIO
|
||||
@@ -482,7 +484,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
else:
|
||||
# We can't comment out like above because there may be a trailing ')'
|
||||
# that needs to be written
|
||||
assert len(node) == 3 and node[2] == "LAMBDA_MARKER"
|
||||
assert len(node) == 3 and node[2] in ("RETURN_VALUE_LAMBDA", "LAMBDA_MARKER")
|
||||
self.preorder(node[0])
|
||||
self.prune()
|
||||
|
||||
@@ -565,6 +567,9 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
if n == "LOAD_CONST" and repr(n.pattr)[0] == "-":
|
||||
self.prec = 6
|
||||
|
||||
# print(n.kind, p, "<", self.prec)
|
||||
# print(self.f.getvalue())
|
||||
|
||||
if p < self.prec:
|
||||
self.write("(")
|
||||
self.preorder(node[0])
|
||||
@@ -608,7 +613,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
for item in tup:
|
||||
self.write(sep)
|
||||
l += len(sep)
|
||||
s = repr(item)
|
||||
s = better_repr(item)
|
||||
l += len(s)
|
||||
self.write(s)
|
||||
sep = ","
|
||||
@@ -627,22 +632,10 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
attr = node.attr
|
||||
data = node.pattr
|
||||
datatype = type(data)
|
||||
if isinstance(data, float) and str(data) in frozenset(
|
||||
["nan", "-nan", "inf", "-inf"]
|
||||
):
|
||||
# float values 'nan' and 'inf' are not directly
|
||||
# representable in Python before Python 3.5. In Python 3.5
|
||||
# it is accessible via a library constant math.inf. So we
|
||||
# will canonicalize representation of these value as
|
||||
# float('nan') and float('inf')
|
||||
self.write("float('%s')" % data)
|
||||
elif isinstance(data, complex) and str(data.imag) in frozenset(
|
||||
["nan", "-nan", "inf", "-inf"]
|
||||
):
|
||||
# Likewise, complex values with 'nan' and 'inf' are not
|
||||
# directly representable in Python. So we will
|
||||
# canonicalize like we did above.
|
||||
self.write("complex('%s%sj')" % (data.real, data.imag))
|
||||
if isinstance(data, float) :
|
||||
self.write(better_repr(data))
|
||||
elif isinstance(data, complex):
|
||||
self.write(better_repr(data))
|
||||
elif isinstance(datatype, int) and data == minint:
|
||||
# convert to hex, since decimal representation
|
||||
# would result in 'LOAD_CONST; UNARY_NEGATIVE'
|
||||
@@ -2373,6 +2366,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
p_insts = self.p.insts
|
||||
self.p.insts = self.scanner.insts
|
||||
ast = python_parser.parse(self.p, tokens, customize)
|
||||
self.customize(customize)
|
||||
self.p.insts = p_insts
|
||||
except python_parser.ParserError, e:
|
||||
raise ParserError(e, tokens)
|
||||
|
@@ -121,9 +121,9 @@ class TreeTransform(GenericASTTraversal, object):
|
||||
assert jump_cond == "jmp_false"
|
||||
kind = "assert2not"
|
||||
|
||||
if call[0] != "LOAD_ASSERT":
|
||||
LOAD_ASSERT = call[0].first_child()
|
||||
if LOAD_ASSERT != "LOAD_ASSERT":
|
||||
return node
|
||||
LOAD_ASSERT = call[0]
|
||||
if isinstance(call[1], SyntaxTree):
|
||||
expr = call[1][0]
|
||||
node = SyntaxTree(
|
||||
|
43
uncompyle6/util.py
Normal file
43
uncompyle6/util.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# Python repr() for complex numbers, and a little broken for floats.
|
||||
# Until such time it is fixed, we'll do a better.
|
||||
# More could be done here though.
|
||||
|
||||
from math import copysign
|
||||
|
||||
def is_negative_zero(n):
|
||||
"""Returns true if n is -0.0"""
|
||||
return n == 0.0 and copysign(1, n) == -1
|
||||
|
||||
def better_repr(v):
|
||||
"""Work around Python's unorthogonal and unhelpful repr() for primitive float
|
||||
and complex."""
|
||||
if isinstance(v, float):
|
||||
# float values 'nan' and 'inf' are not directly
|
||||
# representable in Python before Python 3.5. In Python 3.5
|
||||
# it is accessible via a library constant math.inf. We
|
||||
# will canonicalize representation of these value as
|
||||
# float('nan') and float('inf')
|
||||
if str(v) in frozenset(["nan", "-nan", "inf", "-inf"]):
|
||||
return "float('%s')" % v
|
||||
elif is_negative_zero(v):
|
||||
return "-0.0"
|
||||
return repr(v)
|
||||
elif isinstance(v, complex):
|
||||
real = better_repr(v.real)
|
||||
imag = better_repr(v.imag)
|
||||
# FIXME: we could probably use repr() in most cases
|
||||
# sort out when that's possible.
|
||||
# The below is however round-tripable, and Python's repr() isn't.
|
||||
return "complex(%s, %s)" % (real, imag)
|
||||
elif isinstance(v, tuple):
|
||||
if len(v) == 1:
|
||||
return "(%s,)" % better_repr(v[0])
|
||||
return "(%s)" % ", ".join(better_repr(i) for i in v)
|
||||
elif isinstance(v, list):
|
||||
l = better_repr(v)
|
||||
if len(v) == 1:
|
||||
return "[%s,]" % better_repr(v[0])
|
||||
return "[%s]" % ", ".join(better_repr(i) for i in v)
|
||||
# TODO: elif deal with sets and dicts
|
||||
else:
|
||||
return repr(v)
|
Reference in New Issue
Block a user