Compare commits

...

50 Commits

Author SHA1 Message Date
rocky
e56088b566 Need parens in unpack in 2.4ish 2019-07-03 20:04:45 -04:00
rocky
40d2ef3071 Merge branch 'master' into python-2.4 2019-07-03 19:38:47 -04:00
rocky
e39a902e56 Get ready for release 3.3.5 2019-07-03 19:37:29 -04:00
rocky
e2914ed552 More excpet_cond futzing 2019-07-03 19:26:36 -04:00
rocky
5afa14a945 More except_cond futzing 2019-07-03 19:23:23 -04:00
rocky
4f5ad533c3 Reinstate except_cond{2,3} rules 2019-07-03 19:21:39 -04:00
rocky
f425db33b7 except_cond3 needs to be in 2.x 2019-07-03 19:16:09 -04:00
rocky
7f7487206a Reinstate except_cond{2,3} rules 2019-07-03 18:59:24 -04:00
rocky
82d8e0cd47 master merge hell 2019-07-03 18:36:53 -04:00
rocky
1c21e1c9d2 Merge branch 'master' into python-2.4 2019-07-03 18:35:11 -04:00
rocky
68c5b2338f Clearer 3.3 "yeild_from" semantic handling 2019-07-01 12:54:38 -04:00
rocky
e55a0410c9 weak-verify -> syntax-verify. More bytecode tests 2019-07-01 10:23:43 -04:00
R. Bernstein
0fe8961418 Merge pull request #269 from rocky/if-elif-else-more
If elif else more
2019-07-01 09:52:11 -04:00
R. Bernstein
8cd331a32b Merge pull request #273 from rocky/py3-annotation-args
Fix handling py3 annotation args + defparam comma issue
2019-06-30 18:54:58 -04:00
rocky
4c76931807 Update tests related to branch 2019-06-30 18:20:40 -04:00
x0ret
7b7f794913 Fix handling py3 annotation args + defparam comma issue 2019-07-01 01:28:32 +04:30
rocky
50e46531ce Adjust 3.x grammar rules to include annotate args 2019-06-29 23:33:21 -04:00
rocky
cd2072b8e3 Merge branch 'master' into python-2.4 2019-06-29 15:57:22 -04:00
R. Bernstein
67ef34977f Merge pull request #270 from rocky/py3-star-args
Fix issue in 3.x star args function signatures
2019-06-29 15:57:02 -04:00
rocky
32c7b8f23d Add tests for x0ret's recent varrg fixes 2019-06-29 15:50:47 -04:00
x0ret
2f06d1eeb0 Fix issue in 3.x star args function signatures 2019-06-29 21:53:02 +04:30
rocky
999f1fb0f9 Mostly x0ret's while(1)/if fixes ..
plus a potential test
2019-06-29 07:01:45 -04:00
x0ret
76eef9a149 Handle if elif else case for 3.5 2019-06-29 06:57:23 -04:00
rocky
c8b945fb56 Handling if elif else more 2019-06-29 06:57:23 -04:00
rocky
a1e7c16dbe Fix bugs introduced by last commit 2019-06-29 06:19:02 -04:00
rocky
35f14e4357 Small assert message change 2019-06-29 05:08:23 -04:00
rocky
49d1a50354 Merge branch 'master' of github.com:rocky/python-uncompyle6 2019-06-29 04:59:46 -04:00
rocky
0dc19a8fdd Correct 3.4 "yield from" semantic action bug 2019-06-29 04:59:03 -04:00
R. Bernstein
f6aa8b2baf Merge pull request #264 from rocky/ifelif-27
if/elif for 2.5-2.7
2019-06-24 05:50:53 -04:00
rocky
887a006849 if/elif for 2.5-2.7
Specifically simple_source/03_if_elif.py
2019-06-23 21:29:15 -04:00
rocky
e26c7407a0 Small changes to document some of the complexity. 2019-06-23 20:00:00 -04:00
rocky
18bb1bc9e3 Fix Python 2.xisms 2019-06-23 18:15:53 -04:00
rocky
c0e8ce22af Merge branch 'master' into python-2.4 2019-06-23 17:51:21 -04:00
R. Bernstein
69823af553 Merge pull request #262 from rocky/ifelif
reinstate some elif's
2019-06-23 17:27:57 -04:00
x0ret
e96498eaf0 Adjust ifelsestmtr grammer 2019-06-24 01:28:33 +04:30
rocky
9d6d6a355d Start to reinstate elif's 2019-06-21 07:13:05 -04:00
R. Bernstein
04c53c1086 Merge pull request #261 from rocky/load-code
LOAD_CONST -> LOAD_CODE where appropriate
2019-06-21 06:34:43 -04:00
rocky
96866f94a7 Adjust grammar checker to ignore LOAD_CODE 2019-06-19 15:54:16 -04:00
rocky
d371839c99 A few more LOAD_CONST->LOAD_CODE 2019-06-19 15:38:58 -04:00
rocky
24afe072b7 LOAD_CONST -> LOAD_CODE where appropriate 2019-06-19 14:43:07 -04:00
rocky
e2d7f01298 Handle 2-arg asserts in 3.6+ish
Changed files have also been reformatted via the blacken formatter
2019-06-18 22:09:16 -04:00
rocky
72a95e7cce Add back in validate. 2019-06-17 02:00:55 -04:00
rocky
b39112b601 One more deparse_code removal 2019-06-16 22:30:56 -04:00
rocky
3983aa1b92 One more deparse_code removal 2019-06-16 22:30:05 -04:00
rocky
8d85e78960 Merge branch 'master' into python-2.4 2019-06-16 22:00:33 -04:00
rocky
20b513fc81 Merge branch 'master' of github.com:rocky/python-uncompyle6 2019-06-16 21:58:23 -04:00
rocky
d369017122 remove deprecated deparse_code 2019-06-16 21:57:56 -04:00
rocky
d3eca29934 Merge branch 'master' into python-2.4 2019-06-15 10:09:44 -04:00
rocky
6675ea2cd0 Control flow yet again 2019-06-15 10:09:13 -04:00
rocky
4b82806d6c Flow control bites again.
See related appveyor https://ci.appveyor.com/project/rocky/python-decompile3/builds/25301153/job/x0we0dpgb3apgk1v
2019-06-15 07:18:30 -04:00
48 changed files with 1569 additions and 931 deletions

16
NEWS.md
View File

@@ -1,4 +1,18 @@
3.3.4 2019-05-19 Fleetwood at 65
3.3.5 2019-07-03 Pre Independence Day
=====================================
Again, most of the work in this is release is thanks to x0ret.
- Handle annotation args in Python 3.x
- Fix vararg and function signatures in 3.x
- Some 3.x < 3.6 while(1)/if fixes - others remain
- Start reinstating else if -> elif
- LOAD_CONST -> LOAD_CODE where appropriate
- option `weak-verify` is now `syntax-verify`
- code cleanups, start using "blacken" to reformat text
3.3.4 2019-06-19 Fleetwood at 65
================================
Most of the work in this is release is thanks to x0ret.

View File

@@ -122,17 +122,6 @@ For usage help:
$ uncompyle6 -h
If you want strong verification of the correctness of the
decompilation process, add the `--verify` option. But there are
situations where this will indicate a failure, although the generated
program is semantically equivalent. Using option `--weak-verify` will
tell you if there is something definitely wrong. Generally, large
swaths of code are decompiled correctly, if not the entire program.
You can also cross compare the results with pycdc_ . Since they work
differently, bugs here often aren't in that, and vice versa.
Verification
------------
@@ -140,22 +129,25 @@ In older versions of Python it was possible to verify bytecode by
decompiling bytecode, and then compiling using the Python interpreter
for that bytecode version. Having done this the bytecode produced
could be compared with the original bytecode. However as Python's code
generation got better, this is no longer feasible.
generation got better, this no longer was feasible.
The verification that we use that doesn't check bytecode for
equivalence but does check to see if the resulting decompiled source
is a valid Python program by running the Python interpreter. Because
the Python language has changed so much, for best results you should
use the same Python version in checking as was used in creating the
bytecode.
If you want Python syntax verification of the correctness of the
decompilation process, add the `--syntax-verify` option. However since
Python syntax changes, you should use this option if the bytecode is
the right bytecode for the Python interpreter that will be checking
the syntax.
There are however an interesting class of these programs that is
readily available give stronger verification: those programs that
when run check some computation, or even better themselves.
You can also cross compare the results with another python decompiler
like pycdc_ . Since they work differently, bugs here often aren't in
that, and vice versa.
And already Python has a set of programs like this: the test suite
for the standard library that comes with Python. We have some
code in `test/stdlib` to facilitate this kind of checking.
There is an interesting class of these programs that is readily
available give stronger verification: those programs that when run
test themselves. Our test suite includes these.
And Python comes with another a set of programs like this: its test
suite for the standard library. We have some code in `test/stdlib` to
facilitate this kind of checking too.
Known Bugs/Restrictions
-----------------------

View File

@@ -61,7 +61,7 @@ build_script:
test_script:
# Run the project tests
- "%CMD_IN_ENV% python test/test_pyenvlib.py --native --weak-verify"
- "%CMD_IN_ENV% python test/test_pyenvlib.py --native --syntax-verify"
after_test:
# If tests are successful, create binary packages for the project.

View File

@@ -9,6 +9,7 @@ def test_grammar():
remain_tokens = set(tokens) - opcode_set
remain_tokens = set([re.sub(r'_\d+$','', t) for t in remain_tokens])
remain_tokens = set([re.sub('_CONT$','', t) for t in remain_tokens])
remain_tokens = set([re.sub('LOAD_CODE$','', t) for t in remain_tokens])
remain_tokens = set(remain_tokens) - opcode_set
assert remain_tokens == set([]), \
"Remaining tokens %s\n====\n%s" % (remain_tokens, p.dump_grammar())
@@ -88,7 +89,7 @@ def test_grammar():
COME_FROM_EXCEPT_CLAUSE
COME_FROM_LOOP COME_FROM_WITH
COME_FROM_FINALLY ELSE
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_STR
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_STR LOAD_CODE
LAMBDA_MARKER
RETURN_END_IF RETURN_END_IF_LAMBDA RETURN_VALUE_LAMBDA RETURN_LAST
""".split())

View File

@@ -1,25 +1,28 @@
# future
from __future__ import print_function
# std
import os
import difflib
import subprocess
import tempfile
from StringIO import StringIO
import functools
# uncompyle6 / xdis
from uncompyle6 import PYTHON_VERSION, IS_PYPY, deparse_code
from uncompyle6 import PYTHON_VERSION, PYTHON3, IS_PYPY, code_deparse
# TODO : I think we can get xdis to support the dis api (python 3 version) by doing something like this there
from xdis.bytecode import Bytecode
from xdis.main import get_opcode
opc = get_opcode(PYTHON_VERSION, IS_PYPY)
from StringIO import StringIO
Bytecode = functools.partial(Bytecode, opc=opc)
import six
if PYTHON3:
from io import StringIO
else:
from StringIO import StringIO
def _dis_to_text(co):
return Bytecode(co).dis()
try:
import functools
Bytecode = functools.partial(Bytecode, opc=opc)
def _dis_to_text(co):
return Bytecode(co).dis()
except:
pass
def print_diff(original, uncompyled):
"""
@@ -42,11 +45,8 @@ def print_diff(original, uncompyled):
print('\nTo display diff highlighting run:\n pip install BeautifulSoup4')
diff = difflib.HtmlDiff().make_table(*args)
f = tempfile.NamedTemporaryFile(delete=False)
try:
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(str(diff).encode('utf-8'))
finally:
f.close()
try:
print()
@@ -63,7 +63,8 @@ def print_diff(original, uncompyled):
print('\nFor side by side diff install elinks')
diff = difflib.Differ().compare(original_lines, uncompyled_lines)
print('\n'.join(diff))
os.unlink(f.name)
finally:
os.unlink(f.name)
def are_instructions_equal(i1, i2):
@@ -124,11 +125,10 @@ def validate_uncompyle(text, mode='exec'):
original_dis = _dis_to_text(original_code)
original_text = text
deparsed = deparse_code(PYTHON_VERSION, original_code,
compile_mode=mode,
out=StringIO(),
is_pypy=IS_PYPY)
deparsed = code_deparse(original_code,
out=six.StringIO(),
version=PYTHON_VERSION,
compile_mode=mode)
uncompyled_text = deparsed.text
uncompyled_code = compile(uncompyled_text, '<string>', 'exec')

View File

@@ -34,47 +34,47 @@ check-2.4 check-2.5 check-2.6 check-2.7: check-bytecode-2 check-bytecode-3 check
#: Run working tests from Python 3.0
check-3.0: check-bytecode
$(PYTHON) test_pythonlib.py --bytecode-3.0-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.0 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.0 --syntax-verify $(COMPILE)
#: Run working tests from Python 3.1
check-3.1: check-bytecode
$(PYTHON) test_pythonlib.py --bytecode-3.1-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.1 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.1 --syntax-verify $(COMPILE)
#: Run working tests from Python 3.2
check-3.2: check-bytecode
$(PYTHON) test_pythonlib.py --bytecode-3.2-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.2 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.2 --syntax-verify $(COMPILE)
#: Run working tests from Python 3.3
check-3.3: check-bytecode
$(PYTHON) test_pythonlib.py --bytecode-3.3-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.3 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.3 --syntax-verify $(COMPILE)
#: Run working tests from Python 3.4
check-3.4: check-bytecode check-3.4-ok check-2.7-ok
$(PYTHON) test_pythonlib.py --bytecode-3.4-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.4 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.4 --syntax-verify $(COMPILE)
#: Run working tests from Python 3.5
check-3.5: check-bytecode
$(PYTHON) test_pythonlib.py --bytecode-3.5-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.5 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.5 --syntax-verify $(COMPILE)
#: Run working tests from Python 3.6
check-3.6: check-bytecode
$(PYTHON) test_pythonlib.py --bytecode-3.6-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.6 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.6 --syntax-verify $(COMPILE)
#: Run working tests from Python 3.7
check-3.7: check-bytecode
$(PYTHON) test_pythonlib.py --bytecode-3.7-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.7 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-3.7 --syntax-verify $(COMPILE)
# #: Run working tests from Python 3.8
# check-3.8: check-bytecode
# $(PYTHON) test_pythonlib.py --bytecode-3.8-run --verify-run
# $(PYTHON) test_pythonlib.py --bytecode-3.8 --weak-verify $(COMPILE)
# $(PYTHON) test_pythonlib.py --bytecode-3.8 --syntax-verify $(COMPILE)
# FIXME
#: this is called when running under pypy3.5-5.8.0 or pypy2-5.6.0
@@ -230,61 +230,61 @@ grammar-coverage-3.7:
#: Check deparsing Python 2.6
check-bytecode-2.6:
$(PYTHON) test_pythonlib.py --bytecode-2.6-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-2.6 --weak-verify
$(PYTHON) test_pythonlib.py --bytecode-2.6 --syntax-verify
#: Check deparsing Python 2.7
check-bytecode-2.7:
$(PYTHON) test_pythonlib.py --bytecode-2.7-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-2.7 --weak-verify
$(PYTHON) test_pythonlib.py --bytecode-2.7 --syntax-verify
#: Check deparsing Python 3.0
check-bytecode-3.0:
$(PYTHON) test_pythonlib.py --bytecode-3.0-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.0 --weak-verify
$(PYTHON) test_pythonlib.py --bytecode-3.0 --syntax-verify
#: Check deparsing Python 3.1
check-bytecode-3.1:
$(PYTHON) test_pythonlib.py --bytecode-3.1-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.1 --weak-verify
$(PYTHON) test_pythonlib.py --bytecode-3.1 --syntax-verify
#: Check deparsing Python 3.2
check-bytecode-3.2:
$(PYTHON) test_pythonlib.py --bytecode-3.2-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.2 --weak-verify
$(PYTHON) test_pythonlib.py --bytecode-3.2 --syntax-verify
#: Check deparsing Python 3.3
check-bytecode-3.3:
$(PYTHON) test_pythonlib.py --bytecode-3.3-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.3 --weak-verify
$(PYTHON) test_pythonlib.py --bytecode-3.3 --syntax-verify
#: Check deparsing Python 3.4
check-bytecode-3.4:
$(PYTHON) test_pythonlib.py --bytecode-3.4-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.4 --weak-verify
$(PYTHON) test_pythonlib.py --bytecode-3.4 --syntax-verify
#: Check deparsing Python 3.5
check-bytecode-3.5:
$(PYTHON) test_pythonlib.py --bytecode-3.5-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.5 --weak-verify
$(PYTHON) test_pythonlib.py --bytecode-3.5 --syntax-verify
#: Check deparsing Python 3.6
check-bytecode-3.6:
$(PYTHON) test_pythonlib.py --bytecode-3.6-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.6 --weak-verify
$(PYTHON) test_pythonlib.py --bytecode-3.6 --syntax-verify
#: Check deparsing Python 3.7
check-bytecode-3.7:
$(PYTHON) test_pythonlib.py --bytecode-3.7-run --verify-run
$(PYTHON) test_pythonlib.py --bytecode-3.7 --weak-verify
$(PYTHON) test_pythonlib.py --bytecode-3.7 --syntax-verify
# #: Check deparsing Python 3.8
# check-bytecode-3.8:
# $(PYTHON) test_pythonlib.py --bytecode-3.8-run --verify-run
# $(PYTHON) test_pythonlib.py --bytecode-3.8 --weak-verify
# $(PYTHON) test_pythonlib.py --bytecode-3.8 --syntax-verify
#: short tests for bytecodes only for this version of Python
check-native-short:
$(PYTHON) test_pythonlib.py --bytecode-$(PYTHON_VERSION) --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-$(PYTHON_VERSION) --syntax-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --bytecode-$(PYTHON_VERSION)-run --verify-run $(COMPILE)
#: Run longer Python 2.6's lib files known to be okay
@@ -293,19 +293,19 @@ check-2.4-ok:
#: Run longer Python 2.6's lib files known to be okay
check-2.6-ok:
$(PYTHON) test_pythonlib.py --ok-2.6 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --ok-2.6 --syntax-verify $(COMPILE)
#: Run longer Python 2.7's lib files known to be okay
check-2.7-ok:
$(PYTHON) test_pythonlib.py --ok-2.7 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --ok-2.7 --syntax-verify $(COMPILE)
#: Run longer Python 3.2's lib files known to be okay
check-3.2-ok:
$(PYTHON) test_pythonlib.py --ok-3.2 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --ok-3.2 --syntax-verify $(COMPILE)
#: Run longer Python 3.4's lib files known to be okay
check-3.4-ok:
$(PYTHON) test_pythonlib.py --ok-3.4 --weak-verify $(COMPILE)
$(PYTHON) test_pythonlib.py --ok-3.4 --syntax-verify $(COMPILE)
#: PyPy of some sort. E.g. [PyPy 5.0.1 with GCC 4.8.4]
# Skip for now

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

@@ -42,7 +42,7 @@ for VERSION in $PYVERSION ; do
echo Python Version $(pyenv local) > $LOGFILE
echo "" >> $LOGFILE
typeset -i ALL_FILES_STARTTIME=$(date +%s)
python ./test_pyenvlib.py --max ${MAX_TESTS} --weak-verify --$VERSION >>$LOGFILE 2>&1
python ./test_pyenvlib.py --max ${MAX_TESTS} --syntax-verify --$VERSION >>$LOGFILE 2>&1
rc=$?
echo Python Version $(pyenv local) >> $LOGFILE

View File

@@ -31,9 +31,28 @@ def test9(arg_1=55, *varargs: int, y=5, **kwargs):
def test10(args_1, b: 'annotating b', c: int) -> float:
return 5.4
class IOBase:
def test11(*, name):
return args, name
def test12(a, *args, name):
return a, args
pass
def test13(*args, name):
return args, name
def test14(*args, name: int=1, qname):
return args, name, qname
def test15(*args, name='S', fname, qname=4):
return args, name, fname, qname
# From 3.4 /asyncio/streams.py open_connection
_DEFAULT_LIMIT = 5
def test16(host=None, port=None, *,
loop=None, limit=_DEFAULT_LIMIT, **kwds):
return host, port, loop, limit, kwds
# Python 3.1 _pyio.py uses the -> "IOBase" annotation
def o(f, mode = "r", buffering = None) -> "IOBase":
return (f, mode, buffering)
@@ -109,6 +128,10 @@ def ann2(args_1, b: int = 5, **kwargs: float) -> float:
assert ann2.__annotations__['return'] == float
assert b == 5
class TestSignatureObject():
def test_signature_on_wkwonly(self):
def test(x:int=55, *args: (int, str), c='test', a:float, kwargs:str="S", **b: int) -> int:
pass
assert test1(1, 5) == (1, 5, 4, {})
assert test1(1, 5, 6, foo='bar') == (1, 5, 6, {'foo': 'bar'})
@@ -121,3 +144,9 @@ assert test6(2.3, 4, 5) == (2.3, 4, 5)
ann1(1, 'test', 5)
ann2(1)
### FIXME: fill in...
assert test12(1, 2, 3, name='hi') == (1, (2, 3)), "a, *args, name"
assert test13(1, 2, 3, name='hi') == ((1, 2, 3), 'hi'), "*args, name"
assert test16('localhost', loop=2, limit=3, a='b') == ('localhost', None, 2, 3, {'a': 'b'})

View File

@@ -0,0 +1,34 @@
# Testing "while 1" versus "while" handling with if/elif/else's
def while_test(a, b, c):
while a != 2:
if b:
a += 1
elif c:
c = 0
else:
break
return a, b, c
def while1_test(a, b, c):
while 1:
if a != 2:
if b:
a = 3
b = 0
elif c:
c = 0
else:
a += b + c
break
return a, b, c
assert while_test(2, 0, 0) == (2, 0, 0), "no while loops"
assert while_test(0, 1, 0) == (2, 1, 0), "two while loops of b branch"
assert while_test(0, 0, 0) == (0, 0, 0), "0 while loops, else branch"
# FIXME: put this in a timer, and try with a=2
assert while1_test(4, 1, 1) == (3, 0, 0), "three while1 loops"
assert while1_test(4, 0, 0) == (4, 0, 0), " one while1 loop"

View File

@@ -1,5 +1,5 @@
# Adapted from Python 3.6 trace.py
# Bug was in handling BUID_TUPLE_UNPACK created via
# Bug was in handling BUILD_TUPLE_UNPACK created via
# *opts.arguments
import argparse
parser = argparse.ArgumentParser()
@@ -7,4 +7,4 @@ parser.add_argument('filename', nargs='?')
parser.add_argument('arguments', nargs=argparse.REMAINDER)
opts = parser.parse_args(["foo", "a", "b"])
argv = opts.filename, *opts.arguments
assert argv == ('foo', 'a', 'b')
assert argv == ('foo', 'a', 'b'), "Reconstruct tuple using '*' and BUILD_TUPLE_UNPACK"

View File

@@ -8,4 +8,7 @@ def x(s):
if not k.startswith('_')
}
assert x((('_foo', None),)) == {}
# Yes, the print() is funny. This is
# to test though a 2-arg assert where
# the 2nd argument is not a string.
assert x((('_foo', None),)) == {}, print("See issue #162")

View File

@@ -11,6 +11,9 @@
def _walk_dir(dir, dfile, ddir=None):
yield from _walk_dir(dir, ddir=dfile)
def ybug(g):
yield from g
# From 3.5.1 _wakrefset.py
#
# 3.5:

View File

@@ -136,7 +136,7 @@ if __name__ == '__main__':
test_options_keys = list(test_options.keys())
test_options_keys.sort()
opts, args = getopt.getopt(sys.argv[1:], '',
['start-with=', 'verify', 'verify-run', 'weak-verify',
['start-with=', 'verify', 'verify-run', 'syntax-verify',
'max=', 'coverage', 'all', ] \
+ test_options_keys )
vers = ''
@@ -144,7 +144,7 @@ if __name__ == '__main__':
for opt, val in opts:
if opt == '--verify':
do_verify = 'strong'
elif opt == '--weak-verify':
elif opt == '--syntax-verify':
do_verify = 'weak'
elif opt == '--verify-run':
do_verify = 'verify-run'

View File

@@ -193,7 +193,7 @@ if __name__ == '__main__':
test_options_keys.sort()
opts, args = getopt.getopt(sys.argv[1:], '',
['start-with=', 'verify', 'verify-run',
'weak-verify', 'all',
'syntax-verify', 'all',
'compile', 'coverage',
'no-rm'] \
+ test_options_keys )
@@ -210,7 +210,7 @@ if __name__ == '__main__':
for opt, val in opts:
if opt == '--verify':
test_opts['do_verify'] = 'strong'
elif opt == '--weak-verify':
elif opt == '--syntax-verify':
test_opts['do_verify'] = 'weak'
elif opt == '--verify-run':
test_opts['do_verify'] = 'verify-run'

View File

@@ -51,14 +51,8 @@ import uncompyle6.semantics.fragments
# Export some functions
from uncompyle6.main import decompile_file
# For compatibility
uncompyle_file = decompile_file
# Convenience functions so you can say:
# from uncompyle6 import (code_deparse, deparse_code2str)
code_deparse = uncompyle6.semantics.pysource.code_deparse
deparse_code2str = uncompyle6.semantics.pysource.deparse_code2str
# This is deprecated:
deparse_code = uncompyle6.semantics.pysource.deparse_code
code_deparse = uncompyle6.semantics.pysource.code_deparse

View File

@@ -37,7 +37,7 @@ Options:
--fragments use fragments deparser
--verify compare generated source with input byte-code
--verify-run compile generated source, run it and check exit code
--weak-verify compile generated source
--syntax-verify compile generated source
--linemaps generated line number correspondencies between byte-code
and generated source output
--encoding <encoding>
@@ -86,9 +86,9 @@ def main_bin():
'help asm compile= grammar linemaps recurse '
'timestamp tree tree+ '
'fragments verify verify-run version '
'weak-verify '
'syntax-verify '
'showgrammar encoding='.split(' '))
except getopt.GetoptError(e):
except getopt.GetoptError, e:
sys.stderr.write('%s: %s\n' %
(os.path.basename(sys.argv[0]), e))
sys.exit(-1)
@@ -103,7 +103,7 @@ def main_bin():
sys.exit(0)
elif opt == '--verify':
options['do_verify'] = 'strong'
elif opt == '--weak-verify':
elif opt == '--syntax-verify':
options['do_verify'] = 'weak'
elif opt == '--fragments':
options['do_fragments'] = True

View File

@@ -282,7 +282,7 @@ def main(in_base, out_base, compiled_files, source_files, outfile=None,
sys.stdout.write("\n")
sys.stderr.write("\nLast file: %s " % (infile))
raise
except RuntimeError(e):
except RuntimeError, e:
sys.stdout.write("\n%s\n" % str(e))
if str(e).startswith('Unsupported Python'):
sys.stdout.write("\n")

View File

@@ -496,6 +496,7 @@ class PythonParser(GenericASTBuilder):
def p_expr(self, args):
'''
expr ::= _mklambda
expr ::= LOAD_CODE
expr ::= LOAD_FAST
expr ::= LOAD_NAME
expr ::= LOAD_CONST

View File

@@ -457,7 +457,7 @@ class Python2Parser(PythonParser):
if i > 0 and tokens[i-1] == 'LOAD_LAMBDA':
self.addRule('mklambda ::= %s LOAD_LAMBDA %s' %
('pos_arg ' * token.attr, opname), nop_func)
rule = 'mkfunc ::= %s LOAD_CONST %s' % ('expr ' * token.attr, opname)
rule = 'mkfunc ::= %s LOAD_CODE %s' % ('expr ' * token.attr, opname)
elif opname_base == 'MAKE_CLOSURE':
# FIXME: use add_unique_rules to tidy this up.
if i > 0 and tokens[i-1] == 'LOAD_LAMBDA':
@@ -472,7 +472,7 @@ class Python2Parser(PythonParser):
('expr ' * token.attr, opname))], customize)
pass
self.add_unique_rules([
('mkfunc ::= %s load_closure LOAD_CONST %s' %
('mkfunc ::= %s load_closure LOAD_CODE %s' %
('expr ' * token.attr, opname))], customize)
if self.version >= 2.7:

View File

@@ -102,6 +102,8 @@ class Python26Parser(Python2Parser):
def p_stmt26(self, args):
"""
stmt ::= ifelsestmtr
# We use filler as a placeholder to keep nonterminal positions
# the same across different grammars so that the same semantic actions
# can be used
@@ -173,6 +175,9 @@ class Python26Parser(Python2Parser):
iflaststmt ::= testexpr_then c_stmts_opt JUMP_ABSOLUTE come_froms POP_TOP
iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE come_froms POP_TOP
# "if"/"else" statement that ends in a RETURN
ifelsestmtr ::= testexpr_then return_if_stmts returns
testexpr_then ::= testtrue_then
testexpr_then ::= testfalse_then
testtrue_then ::= expr jmp_true_then

View File

@@ -127,6 +127,8 @@ class Python27Parser(Python2Parser):
def p_stmt27(self, args):
"""
stmt ::= ifelsestmtr
# assert condition
assert ::= assert_expr jmp_true LOAD_ASSERT RAISE_VARARGS_1
@@ -179,6 +181,9 @@ class Python27Parser(Python2Parser):
ifelsestmtl ::= testexpr c_stmts_opt JUMP_BACK else_suitel
ifelsestmtl ::= testexpr c_stmts_opt CONTINUE else_suitel
# "if"/"else" statement that ends in a RETURN
ifelsestmtr ::= testexpr return_if_stmts COME_FROM returns
# Common with 2.6
return_if_lambda ::= RETURN_END_IF_LAMBDA COME_FROM
stmt ::= if_expr_lambda

File diff suppressed because it is too large Load Diff

View File

@@ -73,7 +73,7 @@ class Python32Parser(Python3Parser):
args_pos, args_kw, annotate_args = token.attr
# Check that there are 2 annotated params?
rule = (('mkfunc_annotate ::= %s%sannotate_tuple '
'LOAD_CONST LOAD_CONST EXTENDED_ARG %s') %
'LOAD_CONST LOAD_CODE EXTENDED_ARG %s') %
(('pos_arg ' * (args_pos)),
('annotate_arg ' * (annotate_args-1)), opname))
self.add_unique_rule(rule, opname, token.attr, customize)

View File

@@ -31,6 +31,14 @@ class Python36Parser(Python35Parser):
def p_36misc(self, args):
"""sstmt ::= sstmt RETURN_LAST
# long except clauses in a loop can sometimes cause a JUMP_BACK to turn into a
# JUMP_FORWARD to a JUMP_BACK. And when this happens there is an additional
# ELSE added to the except_suite. With better flow control perhaps we can
# sort this out better.
except_suite ::= c_stmts_opt POP_EXCEPT jump_except ELSE
except_suite_finalize ::= SETUP_FINALLY c_stmts_opt except_var_finalize END_FINALLY
_jump ELSE
# 3.6 redoes how return_closure works. FIXME: Isolate to LOAD_CLOSURE
return_closure ::= LOAD_CLOSURE DUP_TOP STORE_NAME RETURN_VALUE RETURN_LAST

View File

@@ -78,6 +78,12 @@ class Python37Parser(Python36Parser):
attribute37 ::= expr LOAD_METHOD
expr ::= attribute37
# long except clauses in a loop can sometimes cause a JUMP_BACK to turn into a
# JUMP_FORWARD to a JUMP_BACK. And when this happens there is an additional
# ELSE added to the except_suite. With better flow control perhaps we can
# sort this out better.
except_suite ::= c_stmts_opt POP_EXCEPT jump_except ELSE
# FIXME: generalize and specialize
call ::= expr CALL_METHOD_0

View File

@@ -287,6 +287,8 @@ class Scanner2(Scanner):
op_name = 'LOAD_DICTCOMP'
elif const.co_name == '<setcomp>':
op_name = 'LOAD_SETCOMP'
else:
op_name = "LOAD_CODE"
# verify() uses 'pattr' for comparison, since 'attr'
# now holds Code(const) and thus can not be used
# for comparison (todo: think about changing this)

View File

@@ -173,6 +173,8 @@ class Scanner26(scan.Scanner2):
op_name = 'LOAD_DICTCOMP'
elif const.co_name == '<setcomp>':
op_name = 'LOAD_SETCOMP'
else:
op_name = "LOAD_CODE"
# verify uses 'pattr' for comparison, since 'attr'
# now holds Code(const) and thus can not be used
# for comparison (todo: think about changing this)

File diff suppressed because it is too large Load Diff

View File

@@ -22,18 +22,29 @@ if PYTHON3:
intern = sys.intern
class Token: # Python 2.4 can't have empty ()
"""
Class representing a byte-code instruction.
A byte-code token is equivalent to Python 3's dis.instruction or
the contents of one line as output by dis.dis().
"""
# FIXME: match Python 3.4's terms:
# linestart = starts_line
# attr = argval
# pattr = argrepr
def __init__(self, opname, attr=None, pattr=None, offset=-1,
linestart=None, op=None, has_arg=None, opc=None):
def __init__(
self,
opname,
attr=None,
pattr=None,
offset=-1,
linestart=None,
op=None,
has_arg=None,
opc=None,
):
self.kind = intern(opname)
self.has_arg = has_arg
self.attr = attr
@@ -46,6 +57,7 @@ class Token: # Python 2.4 can't have empty ()
if opc is None:
from xdis.std import _std_api
self.opc = _std_api.opc
else:
self.opc = opc
@@ -58,10 +70,9 @@ class Token: # Python 2.4 can't have empty ()
""" '==' on kind and "pattr" attributes.
It is okay if offsets and linestarts are different"""
if isinstance(o, Token):
return (
(self.kind == o.kind)
and ((self.pattr == o.pattr) or self.attr == o.attr)
)
return (self.kind == o.kind) and (
(self.pattr == o.pattr) or self.attr == o.attr
)
else:
# ?? do we need this?
return self.kind == o
@@ -80,61 +91,69 @@ class Token: # Python 2.4 can't have empty ()
# ('%9s %-18s %r' % (self.offset, self.kind, pattr)))
def __str__(self):
return self.format(line_prefix='')
return self.format(line_prefix="")
def format(self, line_prefix=''):
def format(self, line_prefix=""):
if self.linestart:
prefix = '\n%s%4d ' % (line_prefix, self.linestart)
prefix = "\n%s%4d " % (line_prefix, self.linestart)
else:
prefix = ' ' * (6 + len(line_prefix))
offset_opname = '%6s %-17s' % (self.offset, self.kind)
prefix = (" " * (6 + len(line_prefix)))
offset_opname = "%6s %-17s" % (self.offset, self.kind)
if not self.has_arg:
return "%s%s" % (prefix, offset_opname)
if isinstance(self.attr, int):
argstr = "%6d " % self.attr
else:
argstr = ' '*7
argstr = (" " * 7)
name = self.kind
if self.has_arg:
pattr = self.pattr
if self.opc:
if self.op in self.opc.JREL_OPS:
if not self.pattr.startswith('to '):
if not self.pattr.startswith("to "):
pattr = "to " + self.pattr
elif self.op in self.opc.JABS_OPS:
self.pattr = str(self.pattr)
if not self.pattr.startswith('to '):
if not self.pattr.startswith("to "):
pattr = "to " + str(self.pattr)
pass
elif self.op in self.opc.CONST_OPS:
if name == 'LOAD_STR':
if name == "LOAD_STR":
pattr = self.attr
elif name == 'LOAD_CODE':
return "%s%s%s %s" % (prefix, offset_opname, argstr, pattr)
elif name == "LOAD_CODE":
return "%s%s%s %s" % (prefix, offset_opname, argstr, pattr)
else:
return "%s%s %r" % (prefix, offset_opname, pattr)
return "%s%s %r" % (prefix, offset_opname, pattr)
elif self.op in self.opc.hascompare:
if isinstance(self.attr, int):
pattr = self.opc.cmp_op[self.attr]
return "%s%s%s %s" % (prefix, offset_opname, argstr, pattr)
return "%s%s%s %s" % (prefix, offset_opname, argstr, pattr)
elif self.op in self.opc.hasvargs:
return "%s%s%s" % (prefix, offset_opname, argstr)
return "%s%s%s" % (prefix, offset_opname, argstr)
elif name == 'LOAD_ASSERT':
return "%s%s %s" % (prefix, offset_opname, pattr)
elif self.op in self.opc.NAME_OPS:
if self.opc.version >= 3.0:
return "%s%s%s %s" % (prefix, offset_opname, argstr, self.attr)
elif name == 'EXTENDED_ARG':
return "%s%s%s 0x%x << %s = %s" % (prefix, offset_opname, argstr, self.attr,
self.opc.EXTENDED_ARG_SHIFT, pattr)
return "%s%s%s %s" % (prefix, offset_opname, argstr, self.attr)
elif name == "EXTENDED_ARG":
return "%s%s%s 0x%x << %s = %s" % (
prefix,
offset_opname,
argstr,
self.attr,
self.opc.EXTENDED_ARG_SHIFT,
pattr,
)
# And so on. See xdis/bytecode.py get_instructions_bytes
pass
elif re.search(r'_\d+$', self.kind):
return "%s%s%s" % (prefix, offset_opname, argstr)
elif re.search(r"_\d+$", self.kind):
return "%s%s%s" % (prefix, offset_opname, argstr)
else:
pattr = ''
return "%s%s%s %r" % (prefix, offset_opname, argstr, pattr)
pattr = ""
return "%s%s%s %r" % (prefix, offset_opname, argstr, pattr)
def __hash__(self):
return hash(self.kind)
@@ -142,4 +161,5 @@ class Token: # Python 2.4 can't have empty ()
def __getitem__(self, i):
raise IndexError
NoneToken = Token('LOAD_CONST', offset=-1, attr=None, pattr=None)
NoneToken = Token("LOAD_CONST", offset=-1, attr=None, pattr=None)

View File

@@ -350,23 +350,34 @@ TABLE_DIRECT = {
'testtrue': ( 'not %p',
(0, PRECEDENCE['unary_not']) ),
# Generally the args here are 0: (some sort of) "testexpr",
# 1: (some sort of) "cstmts_opt",
# 2 or 3: "else_suite"
# But unfortunately there are irregularities, For example, 2.6- uses "testexpr_then"
# and sometimes "cstmts" instead of "cstmts_opt" happens.
# Down the line we might isolate these into version-specific rules.
'ifelsestmt': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
'ifelsestmtc': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
'ifelsestmtl': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
'ifelsestmtr': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 2 ),
'ifelsestmtr2': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 3 ), # has COME_FROM in position 2
# "elif" forms are not generated by the parser but are created through tree
# transformations. See "n_ifelsestmt".
'ifelifstmt': ( '%|if %c:\n%+%c%-%c', 0, 1, 3 ),
'elifelifstmt': ( '%|elif %c:\n%+%c%-%c', 0, 1, 3 ),
'elifstmt': ( '%|elif %c:\n%+%c%-', 0, 1 ),
'elifelsestmt': ( '%|elif %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
'ifelsestmtr': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 2 ),
'ifelsestmtr2': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 3 ), # has COME_FROM
'elifelsestmtr': ( '%|elif %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 2 ),
'elifelsestmtr2': ( '%|elif %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 3 ), # has COME_FROM
'elifelsestmtr2': ( '%|elif %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 3 ), # has COME_FROM in position 2
'whileTruestmt': ( '%|while True:\n%+%c%-\n\n', 1 ),
'whilestmt': ( '%|while %c:\n%+%c%-\n\n', 1, 2 ),
'while1stmt': ( '%|while 1:\n%+%c%-\n\n', 1 ),
'while1elsestmt': ( '%|while 1:\n%+%c%-%|else:\n%+%c%-\n\n', 1, -2 ),
'whileelsestmt': ( '%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n', 1, 2, -2 ),
'whileelsestmt2': ( '%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n', 1, 2, -3 ),
'whileelselaststmt': ( '%|while %c:\n%+%c%-%|else:\n%+%c%-', 1, 2, -2 ),
# Note: Python 3.8+ changes this
@@ -398,7 +409,9 @@ TABLE_DIRECT = {
'tf_tryelsestmt': ( '%c%-%c%|else:\n%+%c', 1, 3, 4 ),
'tryfinallystmt': ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', 1, 5 ),
'except': ( '%|except:\n%+%c%-', 3 ),
'except_cond1': ( '%|except %c:\n', 1 ),
'except_cond1': ( '%|except %c:\n', (1, 'expr') ),
'except_cond2': ( '%|except %c as %c:\n',
(1, 'expr'), (5, 'store') ),
'except_suite': ( '%+%c%-%C', 0, (1, maxint, '') ),
# In Python 3.6, this is more complicated in the presence of "returns"

View File

@@ -57,6 +57,10 @@ def customize_for_version(self, is_pypy, version):
from uncompyle6.semantics.customize3 import customize_for_version3
customize_for_version3(self, version)
else: # < 3.0
TABLE_DIRECT.update({
'except_cond3' : ( '%|except %c, %c:\n',
(1, 'expr'), (-2, 'store') )
})
if 2.4 <= version <= 2.6:
TABLE_DIRECT.update({
'comp_for': ( ' for %c in %c', 3, 1 ),

View File

@@ -26,8 +26,6 @@ def customize_for_version25(self, version):
# Import style for 2.5+
########################
TABLE_DIRECT.update({
'except_cond3' : ( '%|except %c, %c:\n',
(1, 'expr'), (-2, 'store') ),
'importmultiple': ( '%|import %c%c\n', 2, 3 ),
'import_cont' : ( ', %c', 2 ),
# With/as is allowed as "from future" thing in 2.5

View File

@@ -26,27 +26,31 @@ from uncompyle6.semantics.customize36 import customize_for_version36
from uncompyle6.semantics.customize37 import customize_for_version37
from uncompyle6.semantics.customize38 import customize_for_version38
def customize_for_version3(self, version):
TABLE_DIRECT.update({
'comp_for' : ( ' for %c in %c',
(2, 'store') , (0, 'expr') ),
'conditionalnot' : ( '%c if not %c else %c',
(2, 'expr') , (0, 'expr'), (4, 'expr') ),
'except_cond2' : ( '%|except %c as %c:\n', 1, 5 ),
'function_def_annotate': ( '\n\n%|def %c%c\n', -1, 0),
# When a generator is a single parameter of a function,
# it doesn't need the surrounding parenethesis.
'call_generator' : ('%c%P', 0, (1, -1, ', ', 100)),
'importmultiple' : ( '%|import %c%c\n', 2, 3 ),
'import_cont' : ( ', %c', 2 ),
'kwarg' : ( '%[0]{attr}=%c', 1),
'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),
})
TABLE_DIRECT.update(
{
"comp_for": (" for %c in %c", (2, "store"), (0, "expr")),
"conditionalnot": (
"%c if not %c else %c",
(2, "expr"),
(0, "expr"),
(4, "expr"),
),
"except_cond2": ("%|except %c as %c:\n", 1, 5),
"function_def_annotate": ("\n\n%|def %c%c\n", -1, 0),
# When a generator is a single parameter of a function,
# it doesn't need the surrounding parenethesis.
"call_generator": ("%c%P", 0, (1, -1, ", ", 100)),
"importmultiple": ("%|import %c%c\n", 2, 3),
"import_cont": (", %c", 2),
"kwarg": ("%[0]{attr}=%c", 1),
"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),
}
)
assert version >= 3.0
@@ -61,7 +65,7 @@ def customize_for_version3(self, version):
# ----------
# * subclass_code - the code for the subclass body
subclass_info = None
if node == 'classdefdeco2':
if node == "classdefdeco2":
if self.version >= 3.6:
class_name = node[1][1].attr
elif self.version <= 3.3:
@@ -72,17 +76,17 @@ def customize_for_version3(self, version):
else:
build_class = node[0]
if self.version >= 3.6:
if build_class == 'build_class_kw':
if build_class == "build_class_kw":
mkfunc = build_class[1]
assert mkfunc == 'mkfunc'
assert mkfunc == "mkfunc"
subclass_info = build_class
if hasattr(mkfunc[0], 'attr') and iscode(mkfunc[0].attr):
if hasattr(mkfunc[0], "attr") and iscode(mkfunc[0].attr):
subclass_code = mkfunc[0].attr
else:
assert mkfunc[0] == 'load_closure'
assert mkfunc[0] == "load_closure"
subclass_code = mkfunc[1].attr
assert iscode(subclass_code)
if build_class[1][0] == 'load_closure':
if build_class[1][0] == "load_closure":
code_node = build_class[1][1]
else:
code_node = build_class[1][0]
@@ -91,72 +95,72 @@ def customize_for_version3(self, version):
class_name = node[1][0].attr
build_class = node[0]
assert 'mkfunc' == build_class[1]
assert "mkfunc" == build_class[1]
mkfunc = build_class[1]
if mkfunc[0] in ('kwargs', 'no_kwargs'):
if mkfunc[0] in ("kwargs", "no_kwargs"):
if 3.0 <= self.version <= 3.2:
for n in mkfunc:
if hasattr(n, 'attr') and iscode(n.attr):
if hasattr(n, "attr") and iscode(n.attr):
subclass_code = n.attr
break
elif n == 'expr':
elif n == "expr":
subclass_code = n[0].attr
pass
pass
else:
for n in mkfunc:
if hasattr(n, 'attr') and iscode(n.attr):
if hasattr(n, "attr") and iscode(n.attr):
subclass_code = n.attr
break
pass
pass
if node == 'classdefdeco2':
if node == "classdefdeco2":
subclass_info = node
else:
subclass_info = node[0]
elif build_class[1][0] == 'load_closure':
elif build_class[1][0] == "load_closure":
# Python 3 with closures not functions
load_closure = build_class[1]
if hasattr(load_closure[-3], 'attr'):
if hasattr(load_closure[-3], "attr"):
# Python 3.3 classes with closures work like this.
# Note have to test before 3.2 case because
# index -2 also has an attr.
subclass_code = load_closure[-3].attr
elif hasattr(load_closure[-2], 'attr'):
elif hasattr(load_closure[-2], "attr"):
# Python 3.2 works like this
subclass_code = load_closure[-2].attr
else:
raise 'Internal Error n_classdef: cannot find class body'
if hasattr(build_class[3], '__len__'):
raise "Internal Error n_classdef: cannot find class body"
if hasattr(build_class[3], "__len__"):
if not subclass_info:
subclass_info = build_class[3]
elif hasattr(build_class[2], '__len__'):
elif hasattr(build_class[2], "__len__"):
subclass_info = build_class[2]
else:
raise 'Internal Error n_classdef: cannot superclass name'
elif self.version >= 3.6 and node == 'classdefdeco2':
raise "Internal Error n_classdef: cannot superclass name"
elif self.version >= 3.6 and node == "classdefdeco2":
subclass_info = node
subclass_code = build_class[1][0].attr
elif not subclass_info:
if mkfunc[0] in ('no_kwargs', 'kwargs'):
if mkfunc[0] in ("no_kwargs", "kwargs"):
subclass_code = mkfunc[1].attr
else:
subclass_code = mkfunc[0].attr
if node == 'classdefdeco2':
if node == "classdefdeco2":
subclass_info = node
else:
subclass_info = node[0]
if (node == 'classdefdeco2'):
self.write('\n')
if node == "classdefdeco2":
self.write("\n")
else:
self.write('\n\n')
self.write("\n\n")
self.currentclass = str(class_name)
self.write(self.indent, 'class ', self.currentclass)
self.write(self.indent, "class ", self.currentclass)
self.print_super_classes3(subclass_info)
self.println(':')
self.println(":")
# class body
self.indent_more()
@@ -165,11 +169,12 @@ def customize_for_version3(self, version):
self.currentclass = cclass
if len(self.param_stack) > 1:
self.write('\n\n')
self.write("\n\n")
else:
self.write('\n\n\n')
self.write("\n\n\n")
self.prune()
self.n_classdef3 = n_classdef3
if version == 3.0:
@@ -178,28 +183,28 @@ def customize_for_version3(self, version):
# since we pick up the iteration variable some other way and
# we definitely don't include in the source _[dd].
def n_comp_iter(node):
if node[0] == 'expr':
if node[0] == "expr":
n = node[0][0]
if (n == 'LOAD_FAST' and
n.pattr[0:2] == '_['):
if n == "LOAD_FAST" and n.pattr[0:2] == "_[":
self.prune()
pass
pass
# Not this special case, procede as normal...
# Not this special case, proceed as normal...
self.default(node)
self.n_comp_iter = n_comp_iter
if version >= 3.3:
elif version == 3.3:
# FIXME: perhaps this can be folded into the 3.4+ case?
def n_yield_from(node):
self.write('yield from')
self.write(' ')
if 3.3 <= self.version <= 3.4:
self.preorder(node[0][0][0][0])
elif self.version >= 3.5:
self.preorder(node[0])
else:
assert False, "dunno about this python version"
self.prune() # stop recursing
assert node[0] == "expr"
assert node[0][0] == "get_iter"
# Skip over yield_from.expr.get_iter which adds an
# extra iter(). Maybe we can do in tranformation phase instead?
template = ("yield from %c", (0, "expr"))
self.template_engine(template, node[0][0])
self.prune()
self.n_yield_from = n_yield_from
if 3.2 <= version <= 3.4:
@@ -211,11 +216,11 @@ def customize_for_version3(self, version):
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"):
# We may want to fill this in...
# But it is distinct from CALL_FUNCTION_VAR below
pass
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.
@@ -229,12 +234,15 @@ def customize_for_version3(self, version):
# kwargs == 0 is handled by the table entry
# Should probably handle it here though.
if nargs == 0:
template = ('%c(*%c, %C)',
0, -2, (1, kwargs+1, ', '))
template = ("%c(*%c, %C)", 0, -2, (1, kwargs + 1, ", "))
else:
template = ('%c(%C, *%c, %C)',
0, (1, nargs+1, ', '),
-2, (-2-kwargs, -2, ', '))
template = (
"%c(%C, *%c, %C)",
0,
(1, nargs + 1, ", "),
-2,
(-2 - kwargs, -2, ", "),
)
self.template_engine(template, node)
self.prune()
else:
@@ -243,6 +251,7 @@ def customize_for_version3(self, version):
self.n_call = n_call
elif version < 3.2:
def n_call(node):
mapping = self._get_mapping(node)
key = node
@@ -251,17 +260,26 @@ def customize_for_version3(self, version):
pass
gen_function_parens_adjust(key, node)
self.default(node)
self.n_call = n_call
def n_mkfunc_annotate(node):
if self.version >= 3.3 or node[-2] == 'kwargs':
# Handling EXTENDED_ARG before MAKE_FUNCTION ...
if node[-2] == "EXTENDED_ARG":
i = -1
else:
i = 0
if self.version <= 3.2:
code = node[-2 + i]
elif self.version >= 3.3 or node[-2] == "kwargs":
# LOAD_CONST code object ..
# LOAD_CONST 'x0' if >= 3.3
# EXTENDED_ARG
# MAKE_FUNCTION ..
code = node[-4]
elif node[-3] == 'expr':
code = node[-3 + i]
elif node[-3] == "expr":
code = node[-3][0]
else:
# LOAD_CONST code object ..
@@ -269,42 +287,51 @@ def customize_for_version3(self, version):
code = node[-3]
self.indent_more()
for annotate_last in range(len(node)-1, -1, -1):
if node[annotate_last] == 'annotate_tuple':
for annotate_last in range(len(node) - 1, -1, -1):
if node[annotate_last] == "annotate_tuple":
break
# FIXME: the real situation is that when derived from
# function_def_annotate we the name has been filled in.
# But when derived from funcdefdeco it hasn't Would like a better
# way to distinquish.
if self.f.getvalue()[-4:] == 'def ':
if self.f.getvalue()[-4:] == "def ":
self.write(code.attr.co_name)
# FIXME: handle and pass full annotate args
make_function3_annotate(self, node, is_lambda=False,
code_node=code, annotate_last=annotate_last)
make_function3_annotate(
self, node, is_lambda=False, code_node=code, annotate_last=annotate_last
)
if len(self.param_stack) > 1:
self.write('\n\n')
self.write("\n\n")
else:
self.write('\n\n\n')
self.write("\n\n\n")
self.indent_less()
self.prune() # stop recursing
self.prune() # stop recursing
self.n_mkfunc_annotate = n_mkfunc_annotate
TABLE_DIRECT.update({
'tryelsestmtl3': ( '%|try:\n%+%c%-%c%|else:\n%+%c%-',
(1, 'suite_stmts_opt'),
(3, 'except_handler'),
(5, 'else_suitel') ),
})
TABLE_DIRECT.update(
{
"tryelsestmtl3": (
"%|try:\n%+%c%-%c%|else:\n%+%c%-",
(1, "suite_stmts_opt"),
(3, "except_handler"),
(5, "else_suitel"),
)
}
)
if version >= 3.4:
#######################
# Python 3.4+ Changes #
#######################
TABLE_DIRECT.update({
'LOAD_CLASSDEREF': ( '%{pattr}', ),
})
TABLE_DIRECT.update(
{
"LOAD_CLASSDEREF": ("%{pattr}",),
"yield_from": ("yield from %c", (0, "expr")),
}
)
if version >= 3.5:
customize_for_version35(self, version)
if version >= 3.6:
@@ -314,8 +341,8 @@ def customize_for_version3(self, version):
if version >= 3.8:
customize_for_version38(self, version)
pass # version >= 3.8
pass # 3.7
pass # 3.6
pass # 3.5
pass # 3.4
pass # 3.7
pass # 3.6
pass # 3.5
pass # 3.4
return

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2018 by Rocky Bernstein
# Copyright (c) 2015-2019 by Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
# This program is free software: you can redistribute it and/or modify
@@ -67,7 +67,7 @@ def make_function3_annotate(self, node, is_lambda, nested=1,
i = -1
j = annotate_last-1
l = -len(node)
while j >= l and node[j].kind in ('annotate_arg' 'annotate_tuple'):
while j >= l and node[j].kind in ('annotate_arg', 'annotate_tuple'):
annotate_args[annotate_tup[i]] = node[j][0]
i -= 1
j -= 1
@@ -142,8 +142,6 @@ def make_function3_annotate(self, node, is_lambda, nested=1,
i = len(paramnames) - len(defparams)
suffix = ''
no_paramnames = len(paramnames[:i]) == 0
for param in paramnames[:i]:
self.write(suffix, param)
suffix = ', '
@@ -164,7 +162,6 @@ def make_function3_annotate(self, node, is_lambda, nested=1,
suffix = ''
for n in node:
if n == 'pos_arg':
no_paramnames = False
self.write(suffix)
param = paramnames[i]
self.write(param)
@@ -198,19 +195,16 @@ def make_function3_annotate(self, node, is_lambda, nested=1,
# self.println(indent, '#flags:\t', int(code.co_flags))
ends_in_comma = False
if kwonlyargcount > 0:
if no_paramnames:
if not code_has_star_arg(code):
if argc > 0:
self.write(", *, ")
else:
self.write("*, ")
pass
if not code_has_star_arg(code):
if argc > 0:
self.write(", *, ")
else:
self.write(", ")
self.write("*, ")
pass
ends_in_comma = True
else:
if argc > 0:
self.write(', ')
self.write(", ")
ends_in_comma = True
kw_args = [None] * kwonlyargcount
@@ -230,8 +224,8 @@ def make_function3_annotate(self, node, is_lambda, nested=1,
pass
# handling other args
ann_other_kw = [c == None for c in kw_args]
for i, flag in enumerate(ann_other_kw):
other_kw = [c == None for c in kw_args]
for i, flag in enumerate(other_kw):
if flag:
n = kwargs[i]
if n in annotate_dict:
@@ -239,7 +233,8 @@ def make_function3_annotate(self, node, is_lambda, nested=1,
else:
kw_args[i] = "%s" % n
self.write(', '.join(kw_args), ', ')
self.write(', '.join(kw_args))
ends_in_comma = False
else:
if argc == 0:
@@ -705,7 +700,6 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
kw_pairs = 0
i = len(paramnames) - len(defparams)
no_paramnames = len(paramnames[:i]) == 0
# build parameters
params = []
@@ -768,52 +762,35 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
# Unless careful, We might lose line breaks though.
ends_in_comma = False
if kwonlyargcount > 0:
if no_paramnames:
if not (4 & code.co_flags):
if argc > 0:
self.write(", *, ")
else:
self.write("*, ")
pass
if not (4 & code.co_flags):
if argc > 0:
self.write(", *, ")
else:
self.write(", ")
ends_in_comma = True
self.write("*, ")
pass
ends_in_comma = True
else:
if argc > 0:
self.write(', ')
self.write(", ")
ends_in_comma = True
# FIXME: this is not correct for 3.5. or 3.6 (which works different)
# and 3.7?
if 3.0 <= self.version <= 3.2:
kwargs = node[0]
last = len(kwargs)-1
i = 0
for n in node[0]:
if n == 'kwarg':
self.write('%s=' % n[0].attr)
self.preorder(n[1])
if i < last:
self.write(', ')
ends_in_comma = True
pass
else:
ends_in_comma = False
pass
i += 1
pass
pass
elif self.version <= 3.5:
# FIXME this is not qute right for 3.5
for n in node:
if n == 'pos_arg':
continue
elif self.version >= 3.4 and not (n.kind in ('kwargs', 'no_kwargs', 'kwarg')):
continue
else:
self.preorder(n)
ends_in_comma = False
break
if 3.0 <= self.version <= 3.5:
kw_args = [None] * kwonlyargcount
kw_nodes = node[0]
if kw_nodes == "kwargs":
for n in kw_nodes:
name = eval(n[0].pattr)
default = self.traverse(n[1], indent='')
idx = kwargs.index(name)
kw_args[idx] = "%s=%s" % (name, default)
other_kw = [c == None for c in kw_args]
for i, flag in enumerate(other_kw):
if flag:
kw_args[i] = "%s" % kwargs[i]
self.write(', '.join(kw_args))
ends_in_comma = False
elif self.version >= 3.6:
# argc = node[-1].attr
# co = node[-3].attr
@@ -862,16 +839,16 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
pass
# handle others
if ann_dict:
ann_other_kw = [c == None for c in kw_args]
other_kw = [c == None for c in kw_args]
for i, flag in enumerate(other_kw):
if flag:
n = kwargs[i]
if ann_dict and n in annotate_dict:
kw_args[i] = "%s: %s" %(n, annotate_dict[n])
else:
kw_args[i] = "%s" % n
for i, flag in enumerate(ann_other_kw):
if flag:
n = kwargs[i]
if n in annotate_dict:
kw_args[i] = "%s: %s" %(n, annotate_dict[n])
else:
kw_args[i] = "%s" % n
self.write(', '.join(kw_args))
ends_in_comma = False

View File

@@ -646,15 +646,48 @@ class SourceWalker(GenericASTTraversal, object):
self.println()
self.prune() # stop recursing
# preprocess is used for handling chains of
# if elif elif
def n_ifelsestmt(self, node, preprocess=False):
"""
Here we turn:
if ...
else
if ..
into:
if ..
elif ...
[else ...]
where appropriate
"""
else_suite = node[3]
n = else_suite[0]
old_stmts = None
if len(n) == 1 == len(n[0]) and n[0] == '_stmts':
n = n[0][0][0]
elif n[0].kind in ('lastc_stmt', 'lastl_stmt'):
if len(n) == 1 == len(n[0]) and n[0] == 'stmt':
n = n[0][0]
elif n[0].kind in ('lastc_stmt', 'lastl_stmt'):
n = n[0]
if n[0].kind in ('ifstmt', 'iflaststmt', 'iflaststmtl', 'ifelsestmtl', 'ifelsestmtc'):
# This seems needed for Python 2.5-2.7
n = n[0]
pass
pass
elif ( len(n) > 1 and 1 == len(n[0]) and n[0] == 'stmt'
and n[1].kind == "stmt" ):
else_suite_stmts = n[0]
if else_suite_stmts[0].kind not in ('ifstmt', 'iflaststmt', 'ifelsestmtl'):
if not preprocess:
self.default(node)
return
old_stmts = n
n = else_suite_stmts[0]
else:
if not preprocess:
self.default(node)
@@ -674,6 +707,18 @@ class SourceWalker(GenericASTTraversal, object):
elif n.kind in ('ifelsestmt', 'ifelsestmtc', 'ifelsestmtl'):
n.kind = 'elifelsestmt'
if not preprocess:
if old_stmts:
if n.kind == "elifstmt":
trailing_else = SyntaxTree("stmts", old_stmts[1:])
# We use elifelsestmtr because it has 3 nodes
elifelse_stmt = SyntaxTree(
'elifelsestmtr', [n[0], n[1], trailing_else])
node[3] = elifelse_stmt
pass
else:
# Other cases for n.kind may happen here
return
pass
self.default(node)
n_ifelsestmtc = n_ifelsestmtl = n_ifelsestmt
@@ -1782,9 +1827,15 @@ class SourceWalker(GenericASTTraversal, object):
self.write(', ')
self.prune()
return
for n in node[1:]:
if n[0].kind == 'unpack':
n[0].kind = 'unpack_w_parens'
# In Python 2.4, unpack is used in (a, b, c) of:
# except RuntimeError, (a, b, c):
if self.version < 2.7:
node.kind = 'unpack_w_parens'
self.default(node)
n_unpack_w_parens = n_unpack
@@ -2271,22 +2322,6 @@ DEFAULT_DEBUG_OPTS = {
'grammar': False
}
# This interface is deprecated. Use simpler code_deparse.
def deparse_code(version, co, out=sys.stdout, showasm=None, showast=False,
showgrammar=False, code_objects={}, compile_mode='exec',
is_pypy=IS_PYPY, walker=SourceWalker):
debug_opts = {
'asm': showasm,
'ast': showast,
'grammar': showgrammar
}
return code_deparse(co, out,
version=version,
debug_opts=debug_opts,
code_objects=code_objects,
compile_mode=compile_mode,
is_pypy=is_pypy, walker=walker)
def code_deparse(co, out=sys.stdout, version=None, debug_opts=DEFAULT_DEBUG_OPTS,
code_objects={}, compile_mode='exec', is_pypy=IS_PYPY, walker=SourceWalker):
"""
@@ -2387,7 +2422,7 @@ def deparse_code2str(code, out=sys.stdout, version=None,
"""Return the deparsed text for a Python code object. `out` is where any intermediate
output for assembly or tree output will be sent.
"""
return deparse_code(version, code, out, showasm=debug_opts.get('asm', None),
return code_deparse(code, out, version, showasm=debug_opts.get('asm', None),
showast=debug_opts.get('tree', None),
showgrammar=debug_opts.get('grammar', None), code_objects=code_objects,
compile_mode=compile_mode, is_pypy=is_pypy, walker=walker).text

View File

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