You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 09:22:40 +08:00
Compare commits
147 Commits
release-py
...
release-2.
Author | SHA1 | Date | |
---|---|---|---|
|
1093ef5c5b | ||
|
dcaca27821 | ||
|
4a47822904 | ||
|
4e9555a7f6 | ||
|
d1c0413b79 | ||
|
246495febd | ||
|
91b86ac156 | ||
|
26cd91046e | ||
|
b42c66e091 | ||
|
364827a2f2 | ||
|
819458564c | ||
|
486f313532 | ||
|
84fd71b73b | ||
|
50687e6317 | ||
|
b35546157f | ||
|
7755dddd94 | ||
|
ce1e841255 | ||
|
68f0f79030 | ||
|
bf195a234f | ||
|
87db833f62 | ||
|
8081decf7c | ||
|
e5008693a1 | ||
|
810649799c | ||
|
d4be647bce | ||
|
4a898ff4c1 | ||
|
cb6925beec | ||
|
2665f292c5 | ||
|
33be34c6fb | ||
|
3bbc94847d | ||
|
3a8d4e1a12 | ||
|
87e005a7ba | ||
|
5477ca294d | ||
|
31c28d0220 | ||
|
659e28d686 | ||
|
8a33a583cd | ||
|
8a776176e2 | ||
|
03498963d4 | ||
|
47dbc57f3d | ||
|
39b9810587 | ||
|
8cdaac93ab | ||
|
a9f7a3c6d0 | ||
|
495bdd7b64 | ||
|
b4ded92822 | ||
|
be9194c223 | ||
|
45bd8e4058 | ||
|
bb24df596d | ||
|
6acec471e3 | ||
|
41343c27b7 | ||
|
9e34654b38 | ||
|
b9703cf6b4 | ||
|
792df2a7a7 | ||
|
b4a6c3c319 | ||
|
4199bc7f61 | ||
|
91e1d2538f | ||
|
6773a66b99 | ||
|
ed6cb9af79 | ||
|
a91cd71667 | ||
|
6f82ae3642 | ||
|
4e05c741e3 | ||
|
fdcb90f661 | ||
|
f416473562 | ||
|
5856802902 | ||
|
4f2ae2f603 | ||
|
ea1651d8ca | ||
|
be769da401 | ||
|
cb3c5e7119 | ||
|
39e3582e72 | ||
|
a0c090932e | ||
|
d1e118afa3 | ||
|
f7da8fd8ab | ||
|
3b1dd9d1c4 | ||
|
91fd1ce732 | ||
|
a46e7cbfa4 | ||
|
d46873c44d | ||
|
54e50771ab | ||
|
160ec0d9cc | ||
|
e1111e3f50 | ||
|
65913778a5 | ||
|
cf21fff38b | ||
|
29122340e6 | ||
|
1e3ea60055 | ||
|
2fbbc728b1 | ||
|
0a6c8ba909 | ||
|
d3904527e6 | ||
|
b043f6bafc | ||
|
aa207a3c77 | ||
|
747212c62c | ||
|
493e4b14a1 | ||
|
9491c67779 | ||
|
8ef5e5d12b | ||
|
222986640e | ||
|
f9d47abb2b | ||
|
31ed869a6f | ||
|
19d2569515 | ||
|
9348411056 | ||
|
e71dd010d7 | ||
|
dadd1c5c45 | ||
|
99af1c9ffe | ||
|
3dc766d0a9 | ||
|
357005c814 | ||
|
41d63a0261 | ||
|
1cb2cd7a82 | ||
|
9ec312ba5e | ||
|
597d51951e | ||
|
cc2321f49e | ||
|
476a1c8ab5 | ||
|
545a46dffa | ||
|
8333e4ae93 | ||
|
e9057f378a | ||
|
36b75abd90 | ||
|
1528537ca4 | ||
|
6b8ae29267 | ||
|
33ec66a82f | ||
|
b0493d1984 | ||
|
7f37c60c42 | ||
|
e2fd308928 | ||
|
6d7cec002a | ||
|
9c49b5d54b | ||
|
8dc23e2cdc | ||
|
a01b8be054 | ||
|
114fe11e66 | ||
|
b131c20e99 | ||
|
5db1178b3e | ||
|
7ece296f76 | ||
|
5035d5433b | ||
|
78a5b620a7 | ||
|
e851c0d46a | ||
|
a760188724 | ||
|
ad345ef94a | ||
|
d050dd3adb | ||
|
9392103998 | ||
|
707770049f | ||
|
ec0669367f | ||
|
3f40c16587 | ||
|
66518baed0 | ||
|
21023fea74 | ||
|
66741d16ba | ||
|
e02ebef45d | ||
|
99fce6dfd7 | ||
|
7b8c5e091c | ||
|
77caf515ea | ||
|
e4c0d56947 | ||
|
4827b1e994 | ||
|
2b46e71264 | ||
|
84c2932bc5 | ||
|
874b3c9d31 | ||
|
f6a997befc |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -16,3 +16,5 @@
|
|||||||
/unpyc
|
/unpyc
|
||||||
__pycache__
|
__pycache__
|
||||||
build
|
build
|
||||||
|
/.venv*
|
||||||
|
/.idea
|
@@ -9,6 +9,7 @@ python:
|
|||||||
- '3.3'
|
- '3.3'
|
||||||
- '3.4'
|
- '3.4'
|
||||||
- '3.2'
|
- '3.2'
|
||||||
|
- '3.6'
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- pip install -r requirements.txt
|
- pip install -r requirements.txt
|
||||||
|
699
ChangeLog
699
ChangeLog
@@ -1,6 +1,702 @@
|
|||||||
|
2017-05-06 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/version.py: Get ready for release 2.9.11
|
||||||
|
|
||||||
|
2017-05-06 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/Makefile: fix PYTHON variable setting in test/Makefile
|
||||||
|
|
||||||
|
2017-05-06 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug32/01_try_except_raise.py,
|
||||||
|
test/simple_source/bug32/03_if.py, uncompyle6/parsers/parse32.py,
|
||||||
|
uncompyle6/parsers/parse33.py: Fix more Python3.2 parser errors
|
||||||
|
|
||||||
|
2017-05-05 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse32.py, uncompyle6/scanners/scanner3.py:
|
||||||
|
Improve Python 3.2 decompilation ... by removing a lot of the control-flow labels of 3.3+
|
||||||
|
|
||||||
|
2017-05-05 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* .travis.yml: Try CI testing on Python 3.6
|
||||||
|
|
||||||
|
2017-05-02 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/01_map_unpack.py, uncompyle6/parser.py,
|
||||||
|
uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse35.py,
|
||||||
|
uncompyle6/semantics/pysource.py: Bang more on BUIlD_MAP_UNPACK there are still bugs. Note: {**{'x': 1}, **{'y': 2}} and {{'x': 1}, **{'y': 2}} generate the same Python 3.5+ bytecode.
|
||||||
|
|
||||||
|
2017-05-02 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/01_map_unpack.py, uncompyle6/parser.py,
|
||||||
|
uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse35.py:
|
||||||
|
BUILD_MAP_UNPACK'ing of dictionaries in 3.5
|
||||||
|
|
||||||
|
2017-05-01 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/semantics/pysource.py: Remove extra unpack *. Issue #98
|
||||||
|
|
||||||
|
2017-04-29 R. Bernstein <rocky@users.noreply.github.com>
|
||||||
|
|
||||||
|
* HISTORY.md: Update HISTORY.md
|
||||||
|
|
||||||
|
2017-04-29 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/01_map_unpack.py,
|
||||||
|
uncompyle6/parsers/parse35.py, uncompyle6/semantics/pysource.py:
|
||||||
|
Handle BUILD_MAP_UNPACK in a build_list
|
||||||
|
|
||||||
|
2017-04-27 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/semantics/pysource.py: A hacky way to get
|
||||||
|
CALL_FUNCTION_EX_KW to work.
|
||||||
|
|
||||||
|
2017-04-26 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/semantics/pysource.py: remove debug code
|
||||||
|
|
||||||
|
2017-04-25 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug36/01_call_function.py,
|
||||||
|
uncompyle6/parsers/parse36.py, uncompyle6/scanners/scanner36.py,
|
||||||
|
uncompyle6/semantics/pysource.py: Python 3.6 CALL_FUNCTION_EX first
|
||||||
|
attempt
|
||||||
|
|
||||||
|
2017-04-22 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parser.py, uncompyle6/parsers/parse34.py: Reduse scope
|
||||||
|
of LOAD_ASSERT as expr to 3.4+
|
||||||
|
|
||||||
|
2017-04-22 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parser.py, uncompyle6/verify.py: LOAD_ASSERT can also
|
||||||
|
be an expr This may have the undesirable property that assert statements might
|
||||||
|
get tagged with equivalant low-level Python code that uses "raise
|
||||||
|
AssertionError", but so be it. Fixes #103
|
||||||
|
|
||||||
|
2017-04-22 R. Bernstein <rocky@users.noreply.github.com>
|
||||||
|
|
||||||
|
* HISTORY.md: Update HISTORY.md
|
||||||
|
|
||||||
|
2017-04-22 R. Bernstein <rocky@users.noreply.github.com>
|
||||||
|
|
||||||
|
* HISTORY.md: Update HISTORY.md
|
||||||
|
|
||||||
|
2017-04-22 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* history.md: history keeps gettting amended
|
||||||
|
|
||||||
|
2017-04-22 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* readme.rst: document python 3.x status
|
||||||
|
|
||||||
|
2017-04-22 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/03_async_await.py,
|
||||||
|
uncompyle6/parsers/parse35.py, uncompyle6/semantics/pysource.py: add
|
||||||
|
await expr fixes #111
|
||||||
|
|
||||||
|
2017-04-22 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : update test
|
||||||
|
|
||||||
|
2017-04-22 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug33/02_pos_args.py,
|
||||||
|
uncompyle6/parsers/parse3.py, uncompyle6/semantics/make_function.py:
|
||||||
|
3.3+ bug in handling single kwarg after * towards fixing issue #110
|
||||||
|
|
||||||
|
2017-04-20 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/02_async_for.py,
|
||||||
|
uncompyle6/parsers/parse35.py: add async for with pass statement fixes #109
|
||||||
|
|
||||||
|
2017-04-19 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/03_while-if-break.py,
|
||||||
|
uncompyle6/parsers/parse3.py: 3.5 ifelsestmtl grammar bug. fixes #108
|
||||||
|
|
||||||
|
2017-04-18 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/03_async_await.py,
|
||||||
|
uncompyle6/parsers/parse35.py: expand await stmt handling fixes #107
|
||||||
|
|
||||||
|
2017-04-18 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug33/01_delete_deref.py,
|
||||||
|
uncompyle6/parsers/parse32.py, uncompyle6/semantics/pysource.py: add
|
||||||
|
delete_deref grammar rule fixes issue #106
|
||||||
|
|
||||||
|
2017-04-17 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug36/01_extended_arg.py,
|
||||||
|
test/simple_source/bug36/01_if_file.py: rename test case to
|
||||||
|
something more appropriate
|
||||||
|
|
||||||
|
2017-04-17 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug36/01_if_file.py: fix botched test case thanks to zm908 for pointing this out
|
||||||
|
|
||||||
|
2017-04-16 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse3.py: comment on what's up with last
|
||||||
|
change
|
||||||
|
|
||||||
|
2017-04-16 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug22/03_if1.py,
|
||||||
|
test/simple_source/bug31/02_ifelse_comprehension.py,
|
||||||
|
uncompyle6/parsers/parse3.py: python 3.x ifelse in comprehension fixes issue #91
|
||||||
|
|
||||||
|
2017-04-16 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : add 2.7 complex test
|
||||||
|
|
||||||
|
2017-04-15 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/01_map_unpack.py,
|
||||||
|
uncompyle6/semantics/pysource.py: correct bug in 3.5+ build_list
|
||||||
|
with unpack
|
||||||
|
|
||||||
|
2017-04-15 r. bernstein <rocky@users.noreply.github.com>
|
||||||
|
|
||||||
|
* how-to-report-a-bug.md: update how-to-report-a-bug.md
|
||||||
|
|
||||||
|
2017-04-15 r. bernstein <rocky@users.noreply.github.com>
|
||||||
|
|
||||||
|
* how-to-report-a-bug.md: update how-to-report-a-bug.md
|
||||||
|
|
||||||
|
2017-04-15 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug36/01_if_file.py,
|
||||||
|
uncompyle6/parsers/parse36.py: 3.6 generates wonky extended_arg in
|
||||||
|
expression fixes issue #102
|
||||||
|
|
||||||
|
2017-04-15 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* how-to-report-a-bug.md, manifest.in: add how to report a bug add test case for ... if 1 else ...
|
||||||
|
|
||||||
|
2017-04-14 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/01_map_unpack.py,
|
||||||
|
uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse35.py,
|
||||||
|
uncompyle6/semantics/pysource.py: python 3.5+ build_unmap_pack rules towards addressing issue #98
|
||||||
|
|
||||||
|
2017-04-14 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/scanners/scanner3.py: reduce adding return_end_if in
|
||||||
|
3.5+ the whole control flow determination has to be redone in a less
|
||||||
|
haphazard way using real flow-control analysis. hopefully that's on
|
||||||
|
the way. in the meantime we have this hack.
|
||||||
|
|
||||||
|
2017-04-14 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug27+/03_if_1_else.py,
|
||||||
|
test/simple_source/bug27+/03_if_true_else.py: better names for a
|
||||||
|
test
|
||||||
|
|
||||||
|
2017-04-13 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug27+/03_if_true_else.py,
|
||||||
|
uncompyle6/parser.py, uncompyle6/parsers/parse3.py,
|
||||||
|
uncompyle6/semantics/consts.py: add if1else. fixes #101
|
||||||
|
|
||||||
|
2017-04-13 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse3.py: in 3.x come_from should include
|
||||||
|
come_from_except
|
||||||
|
|
||||||
|
2017-04-13 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse35.py: towards fixing issue #92
|
||||||
|
|
||||||
|
2017-04-13 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse23.py, uncompyle6/semantics/pysource.py:
|
||||||
|
add python 2.3 rule for "if 1: ..." fully fixes #97 for python 2.3. python 2.4 was fixed in a previous
|
||||||
|
commit.
|
||||||
|
|
||||||
|
2017-04-12 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse3.py,
|
||||||
|
uncompyle6/semantics/make_function.py: annotate args type need to be
|
||||||
|
expr's not constants
|
||||||
|
|
||||||
|
2017-04-12 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse24.py: handle python 2.4 "if 1...."
|
||||||
|
|
||||||
|
2017-04-11 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug31/04_def_annotate.py,
|
||||||
|
uncompyle6/semantics/fragments.py,
|
||||||
|
uncompyle6/semantics/make_function.py: bang on 3.x annotations
|
||||||
|
|
||||||
|
2017-04-11 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug31/04_def_annotate.py,
|
||||||
|
uncompyle6/parsers/parse3.py, uncompyle6/semantics/pysource.py:
|
||||||
|
towards fixing annotated decorator functions... and annotate functions
|
||||||
|
|
||||||
|
2017-04-10 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse2.py, uncompyle6/scanners/scanner27.py,
|
||||||
|
uncompyle6/semantics/check_ast.py, uncompyle6/semantics/pysource.py:
|
||||||
|
misc bugs parse2.py: restore accidently-removed while1stmt rule scanner27.py:
|
||||||
|
grammar typo check_ast: add while1else to list of looping constructs
|
||||||
|
pysource.py: call_function_var_kw_args with positional args rule is
|
||||||
|
different?
|
||||||
|
|
||||||
|
2017-04-10 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/stmts/02_while1else.py,
|
||||||
|
uncompyle6/parsers/parse2.py, uncompyle6/parsers/parse3.py,
|
||||||
|
uncompyle6/parsers/parse35.py: add more while1else grammar rules towards addressing issue #93
|
||||||
|
|
||||||
|
2017-04-09 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : one more function_var test for 3.3
|
||||||
|
|
||||||
|
2017-04-09 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/def/10_kw+pos_args-bug.py,
|
||||||
|
uncompyle6/parsers/parse3.py, uncompyle6/semantics/pysource.py:
|
||||||
|
another python 3.5 function_var bug fixes #94
|
||||||
|
|
||||||
|
2017-04-09 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : commit 4199bc7f617e387fb03fc06939cd17366dc15c5e author: rocky
|
||||||
|
<rb@dustyfeet.com> date: sun apr 9 05:30:45 2017 -0400
|
||||||
|
|
||||||
|
2017-04-03 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : commit 6773a66b99d07e48290a77dbbbe3c71cc39c31ba author: rocky
|
||||||
|
<rb@dustyfeet.com> date: mon apr 3 06:53:12 2017 -0400
|
||||||
|
|
||||||
|
2017-03-27 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : commit a91cd716670be09d3cef34e1bb36a67f96f91712 author: rocky
|
||||||
|
<rb@dustyfeet.com> date: mon mar 27 07:08:59 2017 -0400
|
||||||
|
|
||||||
|
2017-03-19 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* __pkginfo__.py: use more-recent xdis
|
||||||
|
|
||||||
|
2017-03-15 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* history.md, test/simple_source/bug33/01_if_try_except.py: grammar
|
||||||
|
typo and add another test
|
||||||
|
|
||||||
|
2017-03-12 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/scanners/scanner3.py: python 3.0 doesn't have
|
||||||
|
pop_jump_if...
|
||||||
|
|
||||||
|
2017-03-12 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* readme.rst: note problem in handling pathologically long lists
|
||||||
|
|
||||||
|
2017-03-07 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/scanners/scanner3.py: small cleanup - remove
|
||||||
|
pop_jump_tf
|
||||||
|
|
||||||
|
2017-03-05 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* pytest/test_grammar.py, uncompyle6/parsers/parse3.py,
|
||||||
|
uncompyle6/parsers/parse33.py, uncompyle6/scanners/scanner3.py: more
|
||||||
|
accurate ranges of try blocks in 3.x
|
||||||
|
|
||||||
|
2017-03-05 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug33/01_try_except.py: more accurate ranges of
|
||||||
|
try blocks in 3.x
|
||||||
|
|
||||||
|
2017-03-04 r. bernstein <rocky@users.noreply.github.com>
|
||||||
|
|
||||||
|
* : merge pull request #84 from
|
||||||
|
moagstar/property_based_test_function_call property based test function call
|
||||||
|
|
||||||
|
2017-03-04 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* readme.rst: readme updates for 3.5 and 1.5
|
||||||
|
|
||||||
|
2017-03-04 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug32/01_named_and_kwargs.py,
|
||||||
|
uncompyle6/parsers/parse3.py: bug found by hypothesis in creating
|
||||||
|
function calls
|
||||||
|
|
||||||
|
2017-03-04 daniel bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* pytest/test_function_call.py: marked all function call tests as
|
||||||
|
failing until they pass across all python versions
|
||||||
|
|
||||||
|
2017-03-04 daniel bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* pytest/test_function_call.py: added minimal examples for various
|
||||||
|
function call opcodes
|
||||||
|
|
||||||
|
2017-03-04 daniel bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* pytest/test_function_call.py: added property based test for
|
||||||
|
verifying uncompylation of function calls. a number of minimal
|
||||||
|
examples for the various function call opcodes have been generated
|
||||||
|
with the majority marked as expected failure until python 3.6 opcode
|
||||||
|
support is complete. i'm hoping this will make it easier to figure
|
||||||
|
out what needs to be done to support the new opcodes and changed
|
||||||
|
semntics for function calls
|
||||||
|
|
||||||
|
2017-03-03 daniel bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* pytest/test_function_call.py: reduced errors when generating
|
||||||
|
function call instances
|
||||||
|
|
||||||
|
2017-03-03 daniel bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* pytest/test_function_call.py: added test file for function calls
|
||||||
|
|
||||||
|
2017-03-03 daniel bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* .gitignore: added .idea to gitignore
|
||||||
|
|
||||||
|
2017-03-03 daniel bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* .gitignore: added .venv to gitignore
|
||||||
|
|
||||||
|
2017-03-01 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/scanner.py, uncompyle6/scanners/scanner2.py,
|
||||||
|
uncompyle6/scanners/scanner3.py, uncompyle6/verify.py: come_from for
|
||||||
|
3.x pop_except, dry with op_name() ... start adding come_froms for pop_except in preparation for getting
|
||||||
|
tryelse blocks correct. simpler opname access functions: - self.op_name(op) is self.opc.opname[op] - self.op_name_from_offset(offset) is
|
||||||
|
self.opc.opname[self.code[offset]] verify.py: not all offsets are ints
|
||||||
|
|
||||||
|
2017-02-28 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* readme.rst, uncompyle6/parser.py, uncompyle6/parsers/parse26.py:
|
||||||
|
python 2.6 a == b or c == d == 3 grammar bug
|
||||||
|
|
||||||
|
2017-02-28 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : 2.6 a == b or x == y == z bug
|
||||||
|
|
||||||
|
2017-02-28 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug26/03_double_equals.py,
|
||||||
|
uncompyle6/semantics/consts.py: predidence of cmp_list: x == y == z the x, y, z should not have parenthesis around pairs of them (x ==
|
||||||
|
y) or (y == z)
|
||||||
|
|
||||||
|
2017-02-28 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parser.py, uncompyle6/parsers/parse27.py: python 2.7
|
||||||
|
check jump targets of "and"
|
||||||
|
|
||||||
|
2017-02-25 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* ChangeLog, NEWS, __pkginfo__.py, uncompyle6/version.py: Get ready
|
||||||
|
for release 2.9.10
|
||||||
|
|
||||||
|
2017-02-25 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parser.py, uncompyle6/parsers/parse26.py: Python 2.6
|
||||||
|
parsing bugs .. and some parser list nonterminal cleanup
|
||||||
|
|
||||||
|
2017-02-24 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug25/03_if_for.py,
|
||||||
|
uncompyle6/parsers/parse26.py: Python 2.6 control flow bug with
|
||||||
|
added COME_FROM
|
||||||
|
|
||||||
|
2017-02-22 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug25/02_try_else.py,
|
||||||
|
uncompyle6/parsers/parse25.py: Python 2.5 wasn't handling tryelse
|
||||||
|
properly
|
||||||
|
|
||||||
|
2017-02-20 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : New test doesn't --verify correctly. Sigh.
|
||||||
|
|
||||||
|
2017-02-20 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug33/02_while1.py: Add test for last while1
|
||||||
|
bug fix
|
||||||
|
|
||||||
|
2017-02-20 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse35.py:
|
||||||
|
Python 3.x needs more "while 1" grammar rules
|
||||||
|
|
||||||
|
2017-02-20 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse35.py,
|
||||||
|
uncompyle6/scanners/scanner3.py: Some Python 3.4 bugss fixed by
|
||||||
|
using 3.5 rules
|
||||||
|
|
||||||
|
2017-02-20 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/exception/02_try_finally.py,
|
||||||
|
uncompyle6/parsers/parse3.py, uncompyle6/scanners/scanner3.py: More
|
||||||
|
COME_FROM's in Python 3... Need this to find boundaries of simple if better
|
||||||
|
|
||||||
|
2017-02-19 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse26.py: Marginally better for Python 2.6
|
||||||
|
but... control flow is still wrong.
|
||||||
|
|
||||||
|
2017-02-10 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : commit f9d47abb2be7c3839df06c0ed69d3d513694af4e Author: rocky
|
||||||
|
<rb@dustyfeet.com> Date: Fri Feb 10 02:07:04 2017 -0500
|
||||||
|
|
||||||
|
2017-02-10 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug22/01_ops.py, test/test_pythonlib.py: Beef
|
||||||
|
up grammar coverage
|
||||||
|
|
||||||
|
2017-01-29 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/Makefile, test/simple_source/bug22/01_ops.py,
|
||||||
|
uncompyle6/parsers/parse25.py, uncompyle6/semantics/consts.py,
|
||||||
|
uncompyle6/semantics/pysource.py: Changes based on grammar coverage
|
||||||
|
info
|
||||||
|
|
||||||
|
2017-01-29 R. Bernstein <rocky@users.noreply.github.com>
|
||||||
|
|
||||||
|
* : Merge pull request #83 from rocky/coverage Coverage
|
||||||
|
|
||||||
|
2017-01-29 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug22/01_ops.py, test/test_pyenvlib.py: Add
|
||||||
|
--coverage to test_pyenvlib and ... improve grammar coverage on 2.7
|
||||||
|
|
||||||
|
2017-01-29 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : commit 3dc766d0a9537842470c7b4f79e8ccb3d5a46843 Author: rocky
|
||||||
|
<rb@dustyfeet.com> Date: Sun Jan 29 07:34:49 2017 -0500
|
||||||
|
|
||||||
|
2017-01-29 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/test_pythonlib.py: Add --coverage option. WOOT!
|
||||||
|
|
||||||
|
2017-01-27 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* __pkginfo__.py: Bump min spark_parser version
|
||||||
|
|
||||||
|
2017-01-24 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/scanners/scanner2.py, uncompyle6/semantics/consts.py,
|
||||||
|
uncompyle6/semantics/pysource.py: More 2.6, 2.7 control flow Todo more COME_FROMs but now need to check targets better. In some
|
||||||
|
cases we're relying on grammar ambiguity to work out right and in
|
||||||
|
2.7 it doesn't
|
||||||
|
|
||||||
|
2017-01-24 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse2.py, uncompyle6/parsers/parse27.py,
|
||||||
|
uncompyle6/scanners/scanner2.py, uncompyle6/semantics/consts.py,
|
||||||
|
uncompyle6/semantics/pysource.py: More 2.6, 2.7 control-flow bugs Wasn't limiting exception clause to try finally. Probably still has
|
||||||
|
bugs in try-finally nesting Add another 2.6/2.7 COME_FROM to try to limit if/end scope better
|
||||||
|
|
||||||
|
2017-01-23 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/scanners/scanner2.py,
|
||||||
|
uncompyle6/scanners/scanner26.py, uncompyle6/verify.py: Improve
|
||||||
|
Python 2.6 & 2.7 verification
|
||||||
|
|
||||||
|
2017-01-22 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse30.py, uncompyle6/verify.py: Fix up Python
|
||||||
|
3.0 handling
|
||||||
|
|
||||||
|
2017-01-21 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : commit 545a46dffaa0fe2246dd7cc1b560c58db525c2b4 Author: rocky
|
||||||
|
<rb@dustyfeet.com> Date: Sat Jan 21 06:24:31 2017 -0500
|
||||||
|
|
||||||
|
2017-01-20 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse3.py, uncompyle6/scanners/scanner3.py,
|
||||||
|
uncompyle6/semantics/pysource.py: Handle BUILD_CONST_KEY_MAP as a
|
||||||
|
varargs custom rules with BUILD_CONST_KEY_MAP are pinned to the specific
|
||||||
|
number of args seen.
|
||||||
|
|
||||||
|
2017-01-19 R. Bernstein <rocky@users.noreply.github.com>
|
||||||
|
|
||||||
|
* : Merge pull request #81 from moagstar/BUILD_CONST_KEY_MAP fixed bug with BUILD_CONST_KEY_MAP
|
||||||
|
|
||||||
|
2017-01-19 R. Bernstein <rocky@users.noreply.github.com>
|
||||||
|
|
||||||
|
* : Merge pull request #80 from moagstar/BUILD_CONST_KEY_MAP Build const key map
|
||||||
|
|
||||||
|
2017-01-18 Daniel Bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* uncompyle6/semantics/pysource.py: added generation of dict display
|
||||||
|
from BUILD_CONST_KEY_MAP
|
||||||
|
|
||||||
|
2017-01-18 Daniel Bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* pytest/test_build_const_key_map.py: fixed typo
|
||||||
|
|
||||||
|
2017-01-18 Daniel Bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* pytest/test_build_const_key_map.py: added some more test cases for
|
||||||
|
BUILD_CONST_KEY_MAP
|
||||||
|
|
||||||
|
2017-01-17 Daniel Bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* pytest/test_build_const_key_map.py: simplified test cases for
|
||||||
|
test_build_const_key_map
|
||||||
|
|
||||||
|
2017-01-17 Daniel Bradburn <moagstar@gmail.com>
|
||||||
|
|
||||||
|
* pytest/test_build_const_key_map.py, pytest/validate.py: added
|
||||||
|
validation code for checking decompilation of an expression
|
||||||
|
|
||||||
|
2017-01-15 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse3.py, uncompyle6/scanners/scanner3.py:
|
||||||
|
Handle 3.6 BUILD_CONST_KEYMAP
|
||||||
|
|
||||||
|
2017-01-15 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/scanners/scanner2.py: Python 2.1 doesn't have FOR_ITER
|
||||||
|
or GET_ITER... adjust locgic for this fact
|
||||||
|
|
||||||
|
2017-01-12 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/__init__.py: sys.recursionlimit is optional, not
|
||||||
|
essential
|
||||||
|
|
||||||
|
2017-01-11 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : commit b131c20e99514d3a969a51e841d3a823017f1beb Author: rocky
|
||||||
|
<rb@dustyfeet.com> Date: Wed Jan 11 21:32:26 2017 -0500
|
||||||
|
|
||||||
|
2017-01-11 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* ChangeLog, NEWS: Get ready for release 2.10.9
|
||||||
|
|
||||||
|
2017-01-11 R. Bernstein <rocky@users.noreply.github.com>
|
||||||
|
|
||||||
|
* : Merge pull request #79 from rocky/revert-78-patch-1 Revert "fix bug : not generate all files when use "-ro""
|
||||||
|
|
||||||
|
2017-01-11 R. Bernstein <rocky@users.noreply.github.com>
|
||||||
|
|
||||||
|
* : Merge pull request #78 from jlugjb/patch-1 fix bug : not generate all files when use "-ro"
|
||||||
|
|
||||||
|
2017-01-10 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/03_double_star_unpack.py,
|
||||||
|
uncompyle6/parsers/parse3.py, uncompyle6/semantics/pysource.py:
|
||||||
|
Improve BUILD_xxx_UNPACK slightly
|
||||||
|
|
||||||
|
2017-01-09 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/03_async_await.py,
|
||||||
|
uncompyle6/parsers/parse3.py, uncompyle6/semantics/pysource.py: Add
|
||||||
|
async_call_function for 3.5+
|
||||||
|
|
||||||
|
2017-01-09 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : Reinstate test
|
||||||
|
|
||||||
|
2017-01-08 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* : Works now
|
||||||
|
|
||||||
|
2017-01-08 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse30.py, uncompyle6/scanners/scanner3.py:
|
||||||
|
Python 3.0 decompile bugs
|
||||||
|
|
||||||
|
2017-01-08 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/scanners/scanner2.py, uncompyle6/scanners/scanner3.py,
|
||||||
|
uncompyle6/scanners/scanner30.py: Towards better 3.0 decompilation Sync scanner2 and scanner3 better
|
||||||
|
|
||||||
|
2017-01-08 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/03_while-if-break.py,
|
||||||
|
uncompyle6/parsers/parse35.py, uncompyle6/scanner.py,
|
||||||
|
uncompyle6/scanners/scanner3.py: Fix 3.5, 3.6 while true if/break
|
||||||
|
bug
|
||||||
|
|
||||||
|
2017-01-08 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/__init__.py, uncompyle6/main.py,
|
||||||
|
uncompyle6/semantics/consts.py, uncompyle6/semantics/fragments.py,
|
||||||
|
uncompyle6/semantics/pysource.py: Misc cleanups Favor "decompile" over "uncompyle" since "decompile" is in common
|
||||||
|
use Reduce size of pysource.py by splitting out constants
|
||||||
|
|
||||||
|
2017-01-08 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/03_async_await.py,
|
||||||
|
uncompyle6/parsers/parse35.py, uncompyle6/scanners/scanner3.py,
|
||||||
|
uncompyle6/semantics/pysource.py: Add 3.5+ async with/for .. scanner3.py: 3.6 bytecode vs wordcode fix
|
||||||
|
|
||||||
|
2017-01-07 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug35/03_async_await.py,
|
||||||
|
uncompyle6/parsers/parse35.py, uncompyle6/semantics/pysource.py:
|
||||||
|
Start to add 3.5+ await and async
|
||||||
|
|
||||||
|
2017-01-07 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/simple_source/bug31/04_def_annotate.py,
|
||||||
|
uncompyle6/semantics/make_function.py: More Python 3 annotation bugs
|
||||||
|
|
||||||
|
2017-01-07 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse3.py, uncompyle6/parsers/parse31.py,
|
||||||
|
uncompyle6/parsers/parse32.py,
|
||||||
|
uncompyle6/semantics/make_function.py,
|
||||||
|
uncompyle6/semantics/pysource.py: Fix some errors in deparsing
|
||||||
|
Python 3 annotations
|
||||||
|
|
||||||
|
2017-01-07 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/semantics/make_function.py: Small Pyhton 3.x annotate
|
||||||
|
bug
|
||||||
|
|
||||||
|
2017-01-03 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* README.rst: Note what's up with Python 3 decompile quality
|
||||||
|
|
||||||
|
2017-01-03 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/scanners/scanner3.py: 3.5 continue check is needed on
|
||||||
|
3.6
|
||||||
|
|
||||||
|
2017-01-03 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* test/test_pyenvlib.py, uncompyle6/parsers/parse36.py,
|
||||||
|
uncompyle6/scanners/scanner3.py: Towards better 3.6 support
|
||||||
|
|
||||||
|
2017-01-02 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse35.py, uncompyle6/scanners/scanner3.py:
|
||||||
|
Python 3.5 continue detection bug
|
||||||
|
|
||||||
|
2017-01-01 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/scanners/scanner3.py: add come_from for setup_finally
|
||||||
|
and setup_except
|
||||||
|
|
||||||
|
2017-01-01 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* uncompyle6/parsers/parse35.py, uncompyle6/scanners/scanner3.py:
|
||||||
|
Towards fixing Python 3.5 return bugs
|
||||||
|
|
||||||
|
2017-01-01 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
|
* README.rst: Note how to verify correctness ... with --verify, --weak-verify and cross checking with pycdc
|
||||||
|
|
||||||
2016-12-31 rocky <rb@dustyfeet.com>
|
2016-12-31 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
* uncompyle6/version.py: Get ready for release 2.9.9
|
* ChangeLog, NEWS, uncompyle6/version.py: Get ready for release
|
||||||
|
2.9.9
|
||||||
|
|
||||||
2016-12-31 rocky <rb@dustyfeet.com>
|
2016-12-31 rocky <rb@dustyfeet.com>
|
||||||
|
|
||||||
@@ -5330,4 +6026,3 @@
|
|||||||
2012-06-05 Mysterie <kajusska@gmail.com>
|
2012-06-05 Mysterie <kajusska@gmail.com>
|
||||||
|
|
||||||
* first commit
|
* first commit
|
||||||
|
|
||||||
|
53
HISTORY.md
53
HISTORY.md
@@ -30,7 +30,7 @@ another clever idea: using table-driven semantics routines, using
|
|||||||
format specifiers.
|
format specifiers.
|
||||||
|
|
||||||
The last mention of a release of SPARK from John is around 2002. As
|
The last mention of a release of SPARK from John is around 2002. As
|
||||||
released, although the Early Algorithm parser was in good shape, this
|
released, although the Earley Algorithm parser was in good shape, this
|
||||||
code was woefully lacking as serious Python deparser.
|
code was woefully lacking as serious Python deparser.
|
||||||
|
|
||||||
In the fall of 2000, Hartmut Goebel
|
In the fall of 2000, Hartmut Goebel
|
||||||
@@ -44,7 +44,8 @@ it appears that Hartmut did most of the work to get this code to
|
|||||||
accept the full Python language. He added precedence to the table
|
accept the full Python language. He added precedence to the table
|
||||||
specifiers, support for multiple versions of Python, the
|
specifiers, support for multiple versions of Python, the
|
||||||
pretty-printing of docstrings, lists, and hashes. He also wrote test and verification routines of
|
pretty-printing of docstrings, lists, and hashes. He also wrote test and verification routines of
|
||||||
deparsed bytecode, and used this in an extensive set of tests that he also wrote. He could verify against the entire Python library.
|
deparsed bytecode, and used this in an extensive set of tests that he also wrote. He says he could verify against the
|
||||||
|
entire Python library. However I have subsequently found small and relatively obscure bugs in the decompilation code.
|
||||||
|
|
||||||
decompyle2.2 was packaged for Debian (sarge) by
|
decompyle2.2 was packaged for Debian (sarge) by
|
||||||
[Ben Burton around 2002](https://packages.qa.debian.org/d/decompyle.html). As
|
[Ben Burton around 2002](https://packages.qa.debian.org/d/decompyle.html). As
|
||||||
@@ -65,10 +66,12 @@ code to handle first Python 2.3 and then 2.4 bytecodes. Because of
|
|||||||
jump optimization introduced in the CPython bytecode compiler at that
|
jump optimization introduced in the CPython bytecode compiler at that
|
||||||
time, various JUMP instructions were classifed as going backwards, and
|
time, various JUMP instructions were classifed as going backwards, and
|
||||||
COME FROM instructions were reintroduced. See
|
COME FROM instructions were reintroduced. See
|
||||||
RELEASE-2.4-CHANGELOG.txt for more details here. There wasn't a public
|
[RELEASE-2.4-CHANGELOG.txt](https://github.com/rocky/python-uncompyle6/blob/master/DECOMPYLE-2.4-CHANGELOG.txt)
|
||||||
|
for more details here. There wasn't a public
|
||||||
release of RELEASE-2.4 and bytecodes other than Python 2.4 weren't
|
release of RELEASE-2.4 and bytecodes other than Python 2.4 weren't
|
||||||
supported. Dan says the Python 2.3 version could verify the entire
|
supported. Dan says the Python 2.3 version could verify the entire
|
||||||
python library.
|
Python library. But given subsequent bugs found like simply
|
||||||
|
recognizing complex-number constants in bytecode, decompilation wasn't perfect.
|
||||||
|
|
||||||
Next we get to ["uncompyle" and
|
Next we get to ["uncompyle" and
|
||||||
PyPI](https://pypi.python.org/pypi/uncompyle/1.1) and the era of
|
PyPI](https://pypi.python.org/pypi/uncompyle/1.1) and the era of
|
||||||
@@ -95,17 +98,17 @@ so. Then hamled made a few commits earler on, while Eike Siewertsen
|
|||||||
made a few commits later on. But mostly wibiti, and Guenther
|
made a few commits later on. But mostly wibiti, and Guenther
|
||||||
Starnberger got the code to where uncompyle2 was around 2012.
|
Starnberger got the code to where uncompyle2 was around 2012.
|
||||||
|
|
||||||
In uncompyle2 decompilation of python bytecode 2.5 & 2.6 is done by
|
In `uncompyle`, decompilation of python bytecode 2.5 & 2.6 is done by
|
||||||
transforming the byte code into a a pseudo 2.7 python bytecode and is
|
transforming the byte code into a a pseudo 2.7 python bytecode and is
|
||||||
based on code from Eloi Vanderbeken.
|
based on code from Eloi Vanderbeken.
|
||||||
|
|
||||||
This project, uncompyle6, abandons that approach for various
|
This project, `uncompyle6`, abandons that approach for various
|
||||||
reasons. However the main reason is that we need offsets in fragment
|
reasons. However the main reason is that we need offsets in fragment
|
||||||
deparsing to be exactly the same, and the transformation process can
|
deparsing to be exactly the same, and the transformation process can
|
||||||
remove instructions. Adding instructions with psuedo_offsets is
|
remove instructions. _Adding_ instructions with psuedo offsets is
|
||||||
however okay.
|
however okay.
|
||||||
|
|
||||||
Uncompyle6, however owes its existence to the fork of uncompyle2 by
|
`Uncompyle6` however owes its existence to the fork of `uncompyle2` by
|
||||||
Myst herie (Mysterie) whose first commit picks up at
|
Myst herie (Mysterie) whose first commit picks up at
|
||||||
2012. I chose this since it seemed to have been at that time the most
|
2012. I chose this since it seemed to have been at that time the most
|
||||||
actively, if briefly, worked on. Also starting around 2012 is Dark
|
actively, if briefly, worked on. Also starting around 2012 is Dark
|
||||||
@@ -115,9 +118,12 @@ I started working on this late 2015, mostly to add fragment support.
|
|||||||
In that, I decided to make this runnable on Python 3.2+ and Python 2.6+
|
In that, I decided to make this runnable on Python 3.2+ and Python 2.6+
|
||||||
while, handling Python bytecodes from Python versions 2.5+ and
|
while, handling Python bytecodes from Python versions 2.5+ and
|
||||||
3.2+. In doing so, it has been expedient to separate this into three
|
3.2+. In doing so, it has been expedient to separate this into three
|
||||||
projects: load loading and disassembly (xdis), parsing and tree
|
projects:
|
||||||
building (spark_parser), and grammar and semantic actions for
|
|
||||||
decompiling (uncompyle6).
|
* bytecode loading and disassembly ([xdis](https://pypi.python.org/pypi/xdis)),
|
||||||
|
* parsing and tree building ([spark_parser](https://pypi.python.org/pypi/spark_parser)),
|
||||||
|
* this project - grammar and semantic actions for decompiling
|
||||||
|
([uncompyle6](https://pypi.python.org/pypi/spark_parser)).
|
||||||
|
|
||||||
|
|
||||||
Over the many years, code styles and Python features have
|
Over the many years, code styles and Python features have
|
||||||
@@ -135,23 +141,26 @@ Hartmut a decade an a half ago:
|
|||||||
NB. This is not a masterpiece of software, but became more like a hack.
|
NB. This is not a masterpiece of software, but became more like a hack.
|
||||||
Probably a complete rewrite would be sensefull. hG/2000-12-27
|
Probably a complete rewrite would be sensefull. hG/2000-12-27
|
||||||
|
|
||||||
This project deparses using an Early-algorithm parse with lots of
|
This project deparses using an Earley-algorithm parse with lots of
|
||||||
massaging of tokens and the grammar in the scanner
|
massaging of tokens and the grammar in the scanner
|
||||||
phase. Early-algorithm parsers are context free and tend to be linear
|
phase. Earley-algorithm parsers are context free and tend to be linear
|
||||||
if the grammar is LR or left recursive.
|
if the grammar is LR or left recursive.
|
||||||
|
|
||||||
Another approach that doesn't use grammars is to do something like
|
Another approach that doesn't use grammars is to do something like
|
||||||
simulate execution symbolically and build expression trees off of
|
simulate execution symbolically and build expression trees off of
|
||||||
stack results. The two important projects that work this way are
|
stack results. Control flow in that apprproach still needs to be
|
||||||
[unpyc3](https://code.google.com/p/unpyc3/) and most especially
|
handled somewhat ad hoc. The two important projects that work this
|
||||||
[pycdc](https://github.com/zrax/pycdc) The latter project is largely
|
way are [unpyc3](https://code.google.com/p/unpyc3/) and most
|
||||||
by Michael Hansen and Darryl Pogue. If they supported getting
|
especially [pycdc](https://github.com/zrax/pycdc) The latter project
|
||||||
source-code fragments and I could call it from Python, I'd probably
|
is largely by Michael Hansen and Darryl Pogue. If they supported
|
||||||
ditch this and use that. From what I've seen, the code runs blindingly
|
getting source-code fragments, did a better job in supporting Python
|
||||||
fast and spans all versions of Python.
|
more fully, and had a way I could call it from Python, I'd probably
|
||||||
|
would have ditched this and used that. The code runs blindingly fast
|
||||||
|
and spans all versions of Python, although more recently Python 3
|
||||||
|
support has been lagging.
|
||||||
|
|
||||||
Tests for the project have been, or are being, culled from all of the
|
Tests for the project have been, or are being, culled from all of the
|
||||||
projects mentioned.
|
projects mentioned.
|
||||||
|
|
||||||
NB. If you find mistakes, want corrections, or want your name added (or removed),
|
NB. If you find mistakes, want corrections, or want your name added
|
||||||
please contact me.
|
(or removed), please contact me.
|
||||||
|
63
HOW-TO-REPORT-A-BUG.md
Normal file
63
HOW-TO-REPORT-A-BUG.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# How to report a Bug
|
||||||
|
|
||||||
|
## The difficulty of the problem
|
||||||
|
|
||||||
|
There is no Python decompiler yet, that I know about that will
|
||||||
|
decompyle everything. This one probably does the
|
||||||
|
best job of *any* Python decompiler. But it is a constant work in progress: Python keeps changing, and so does its code generation.
|
||||||
|
|
||||||
|
I have found bugs in *every* Python decompiler I have tried. Even
|
||||||
|
those where authors/maintainers claim that they have used it on
|
||||||
|
the entire Python standard library. And I don't mean that
|
||||||
|
the program doesn't come out with the same Python source instructions,
|
||||||
|
but that the program is *semantically* not equivalent.
|
||||||
|
|
||||||
|
So it is likely you'll find a mistranslation in decompiling.
|
||||||
|
|
||||||
|
## What to send (minimum requirements)
|
||||||
|
|
||||||
|
The basic requirement is pretty simple:
|
||||||
|
|
||||||
|
* Python bytecode
|
||||||
|
* Source text
|
||||||
|
|
||||||
|
## What to send (additional helpful information)
|
||||||
|
|
||||||
|
Some kind folks also give the invocation they used and the output
|
||||||
|
which usually includes an error message produced. This is helpful. I
|
||||||
|
can figure out what OS you are running this on and what version of
|
||||||
|
*uncomplye6* was used. Therefore, if you don't provide the input
|
||||||
|
command and the output from that, please give:
|
||||||
|
|
||||||
|
* _uncompile6_ version used
|
||||||
|
* OS that you used this on
|
||||||
|
* Python interpreter version used
|
||||||
|
|
||||||
|
|
||||||
|
### But I don't *have* the source code!
|
||||||
|
|
||||||
|
Sure, I get it. No problem. There is Python assembly code on parse
|
||||||
|
errors, so simply by hand decompile that. To get a full disassembly, use pydisasm from the [xdis](https://pypi.python.org/pypi/xdis) package. Opcodes are described in the documentation for the [dis](https://docs.python.org/3.6/library/dis.html) module.
|
||||||
|
|
||||||
|
### But I don't *have* the source code and am incapable of figuring how how to do a hand disassembly!
|
||||||
|
|
||||||
|
Well, you could learn. No one is born into this world knowing how to disassemble Python bytecode. And as Richard Feynman once said, "What one fool can learn, so can another."
|
||||||
|
|
||||||
|
## Narrowing the problem
|
||||||
|
|
||||||
|
I don't need the entire source code base for which one file or module
|
||||||
|
can't be decompiled. I just need that one file or module only. If
|
||||||
|
there are several files, file a bug report for each file.
|
||||||
|
|
||||||
|
Python modules can get quite large, and usually decompilation problems
|
||||||
|
occur in a single function or maybe the main-line code but not any of
|
||||||
|
the functions or classes. So please chop down the source code by
|
||||||
|
removing those parts that do to decompile properly.
|
||||||
|
|
||||||
|
By doing this, you'll probably have a better sense of what exactly is
|
||||||
|
the problem. Perhaps you can find the boundary of what decompiles, and
|
||||||
|
what doesn't. That is useful. Or maybe the same file will decompile
|
||||||
|
properly on a neighboring version of Python. That is helpful too.
|
||||||
|
|
||||||
|
In sum, the more you can isolate or narrow the problem, the more
|
||||||
|
likley the problem will be fixed and fixed sooner.
|
@@ -1,6 +1,7 @@
|
|||||||
include README.rst
|
include README.rst
|
||||||
include ChangeLog
|
include ChangeLog
|
||||||
include HISTORY.md
|
include HISTORY.md
|
||||||
|
include HOW-TO-REPORT-A-BUG.md
|
||||||
include LICENSE
|
include LICENSE
|
||||||
include Makefile
|
include Makefile
|
||||||
include requirements.txt
|
include requirements.txt
|
||||||
|
24
NEWS
24
NEWS
@@ -1,5 +1,27 @@
|
|||||||
|
uncompyle6 2.9.11 2016-04-06
|
||||||
|
|
||||||
|
- Better support for Python 3.5+ BUILD_MAP_UNPACK
|
||||||
|
- Start 3.6 CALL_FUNCTION_EX support
|
||||||
|
- Many decompilation bug fixes. (Many more remain). See ChangeLog
|
||||||
|
|
||||||
|
uncompyle6 2.9.10 2016-02-25
|
||||||
|
|
||||||
|
- Python grammar rule fixes
|
||||||
|
- Add ability to get grammar coverage on runs
|
||||||
|
- Handle Python 3.6 opcode BUILD_CONST_KEYMAP
|
||||||
|
|
||||||
uncompyle6 2.9.9 2016-12-16
|
uncompyle6 2.9.9 2016-12-16
|
||||||
|
|
||||||
|
- Remaining Python 3.5 ops handled
|
||||||
|
(this also means more Python 3.6 ops are handled)
|
||||||
|
- Python 3.5 and 3.6 async and await handled
|
||||||
|
- Python 3.0 decompilation improved
|
||||||
|
- Python 3 annotations fixed
|
||||||
|
- Better control-flow detection
|
||||||
|
- Code cleanups and misc bug fixes
|
||||||
|
|
||||||
|
uncompyle6 2.9.8 2016-12-16
|
||||||
|
|
||||||
- Better control-flow detection
|
- Better control-flow detection
|
||||||
- pseudo instruction THEN in 2.x
|
- pseudo instruction THEN in 2.x
|
||||||
to disambiguate if from and
|
to disambiguate if from and
|
||||||
@@ -11,8 +33,6 @@ uncompyle6 2.9.9 2016-12-16
|
|||||||
- verifycall fixes for Python <= 2.4
|
- verifycall fixes for Python <= 2.4
|
||||||
- more Python lint
|
- more Python lint
|
||||||
|
|
||||||
uncompyle6 2.9.8 2016-12-16
|
|
||||||
|
|
||||||
uncompyle6 2.9.7 2016-12-16
|
uncompyle6 2.9.7 2016-12-16
|
||||||
|
|
||||||
- Start to handle 3.5/3.6 build_map_unpack_with_call
|
- Start to handle 3.5/3.6 build_map_unpack_with_call
|
||||||
|
38
README.rst
38
README.rst
@@ -11,8 +11,8 @@ Introduction
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
*uncompyle6* translates Python bytecode back into equivalent Python
|
*uncompyle6* translates Python bytecode back into equivalent Python
|
||||||
source code. It accepts bytecodes from Python version 2.1 to 3.6 or
|
source code. It accepts bytecodes from Python version 1.5, and 2.1 to
|
||||||
so, including PyPy bytecode and Dropbox's Python 2.5 bytecode.
|
3.6 or so, including PyPy bytecode and Dropbox's Python 2.5 bytecode.
|
||||||
|
|
||||||
Why this?
|
Why this?
|
||||||
---------
|
---------
|
||||||
@@ -21,7 +21,8 @@ There were a number of decompyle, uncompile, uncompyle2, uncompyle3
|
|||||||
forks around. All of them came basically from the same code base, and
|
forks around. All of them came basically from the same code base, and
|
||||||
almost all of them no were no longer actively maintained. Only one
|
almost all of them no were no longer actively maintained. Only one
|
||||||
handled Python 3, and even there, only 3.2 or 3.3 depending on which
|
handled Python 3, and even there, only 3.2 or 3.3 depending on which
|
||||||
code is used. This code pulls these together and moves forward. It
|
code is used. This code pulls these together and moves forward. This
|
||||||
|
project has the most complete support for Python 3.3 and above. It
|
||||||
also addresses a number of open issues in the previous forks.
|
also addresses a number of open issues in the previous forks.
|
||||||
|
|
||||||
What makes this different from other CPython bytecode decompilers?: its
|
What makes this different from other CPython bytecode decompilers?: its
|
||||||
@@ -44,9 +45,9 @@ Requirements
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
This project requires Python 2.6 or later, PyPy 3-2.4, or PyPy-5.0.1.
|
This project requires Python 2.6 or later, PyPy 3-2.4, or PyPy-5.0.1.
|
||||||
Python versions 2.3-2.7 are supported in the python-2.4 branch.
|
Python versions 2.4-2.7 are supported in the python-2.4 branch.
|
||||||
The bytecode files it can read has been tested on Python bytecodes from
|
The bytecode files it can read has been tested on Python bytecodes from
|
||||||
versions 2.1-2.7, and 3.2-3.6 and the above-mentioned PyPy versions.
|
versions 1.5, 2.1-2.7, and 3.0-3.6 and the above-mentioned PyPy versions.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
@@ -93,6 +94,16 @@ For usage help:
|
|||||||
|
|
||||||
$ uncompyle6 -h
|
$ 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.
|
||||||
|
|
||||||
|
|
||||||
Known Bugs/Restrictions
|
Known Bugs/Restrictions
|
||||||
-----------------------
|
-----------------------
|
||||||
@@ -102,7 +113,7 @@ with handling control flow. All of the Python decompilers I have looked
|
|||||||
at have the same problem. In some cases we can detect an erroneous
|
at have the same problem. In some cases we can detect an erroneous
|
||||||
decompilation and report that.
|
decompilation and report that.
|
||||||
|
|
||||||
About 90% of the decompilation of Python standard library packages in
|
Over 98% of the decompilation of Python standard library packages in
|
||||||
Python 2.7.12 verifies correctly. Over 99% of Python 2.7 and 3.3-3.5
|
Python 2.7.12 verifies correctly. Over 99% of Python 2.7 and 3.3-3.5
|
||||||
"weakly" verify. Python 2.6 drops down to 96% weakly verifying.
|
"weakly" verify. Python 2.6 drops down to 96% weakly verifying.
|
||||||
Other versions drop off in quality too.
|
Other versions drop off in quality too.
|
||||||
@@ -128,10 +139,12 @@ on the lower end Python versions which is more difficult for us to
|
|||||||
handle since we don't have a Python interpreter for versions 1.5, 1.6,
|
handle since we don't have a Python interpreter for versions 1.5, 1.6,
|
||||||
and 2.0.
|
and 2.0.
|
||||||
|
|
||||||
Python 3.0 support is weak; Python 3.5 largely works, but still has
|
In the Python 3 series, Python support is is strongest around 3.4 or
|
||||||
some bugs in it. Python 3.6 changes things drastically by using word
|
3.3 and drops off as you move further away from those versions. Python
|
||||||
codes rather than byte codes. That has been addressed, but then it also
|
3.6 changes things drastically by using word codes rather than byte
|
||||||
changes function call opcodes and its semantics.
|
codes. That has been addressed, but then it also changes function call
|
||||||
|
opcodes and its semantics and has more problems with control flow than
|
||||||
|
3.5 has.
|
||||||
|
|
||||||
Currently not all Python magic numbers are supported. Specifically in
|
Currently not all Python magic numbers are supported. Specifically in
|
||||||
some versions of Python, notably Python 3.6, the magic number has
|
some versions of Python, notably Python 3.6, the magic number has
|
||||||
@@ -145,13 +158,16 @@ We also don't handle PJOrion_ obfuscated code. For that try: PJOrion
|
|||||||
Deobfuscator_ to unscramble the bytecode to get valid bytecode before
|
Deobfuscator_ to unscramble the bytecode to get valid bytecode before
|
||||||
trying this tool.
|
trying this tool.
|
||||||
|
|
||||||
|
Handling pathologically long lists of expressions or statements is
|
||||||
|
slow.
|
||||||
|
|
||||||
|
|
||||||
There is lots to do, so please dig in and help.
|
There is lots to do, so please dig in and help.
|
||||||
|
|
||||||
See Also
|
See Also
|
||||||
--------
|
--------
|
||||||
|
|
||||||
* https://github.com/zrax/pycdc : supports all versions of Python and is written in C++
|
* https://github.com/zrax/pycdc : supports all versions of Python and is written in C++. Support for later Python 3 versions is a bit lacking though.
|
||||||
* https://code.google.com/archive/p/unpyc3/ : supports Python 3.2 only. The above projects use a different decompiling technique what is used here.
|
* https://code.google.com/archive/p/unpyc3/ : supports Python 3.2 only. The above projects use a different decompiling technique what is used here.
|
||||||
* https://github.com/figment/unpyc3/ : fork of above, but supports Python 3.3 only. Include some fixes like supporting function annotations
|
* https://github.com/figment/unpyc3/ : fork of above, but supports Python 3.3 only. Include some fixes like supporting function annotations
|
||||||
* The HISTORY_ file.
|
* The HISTORY_ file.
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
# Things that change more often go here.
|
# Things that change more often go here.
|
||||||
copyright = """
|
copyright = """
|
||||||
Copyright (C) 2015, 2016 Rocky Bernstein <rb@dustyfeet.com>.
|
Copyright (C) 2015-2017 Rocky Bernstein <rb@dustyfeet.com>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classifiers = ['Development Status :: 5 - Production/Stable',
|
classifiers = ['Development Status :: 5 - Production/Stable',
|
||||||
@@ -39,8 +39,8 @@ entry_points={
|
|||||||
'pydisassemble=uncompyle6.bin.pydisassemble:main',
|
'pydisassemble=uncompyle6.bin.pydisassemble:main',
|
||||||
]}
|
]}
|
||||||
ftp_url = None
|
ftp_url = None
|
||||||
install_requires = ['spark-parser >= 1.5.1, < 1.6.0',
|
install_requires = ['spark-parser >= 1.6.0, < 1.7.0',
|
||||||
'xdis >= 3.2.4, < 3.3.0']
|
'xdis >= 3.3.0, < 3.4.0']
|
||||||
license = 'MIT'
|
license = 'MIT'
|
||||||
mailing_list = 'python-debugger@googlegroups.com'
|
mailing_list = 'python-debugger@googlegroups.com'
|
||||||
modname = 'uncompyle6'
|
modname = 'uncompyle6'
|
||||||
|
21
pytest/test_build_const_key_map.py
Normal file
21
pytest/test_build_const_key_map.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import pytest
|
||||||
|
# uncompyle6
|
||||||
|
from uncompyle6 import PYTHON_VERSION
|
||||||
|
from validate import validate_uncompyle
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6')
|
||||||
|
@pytest.mark.parametrize('text', (
|
||||||
|
"{0.: 'a', -1: 'b'}", # BUILD_MAP
|
||||||
|
"{'a':'b'}", # BUILD_MAP
|
||||||
|
"{0: 1}", # BUILD_MAP
|
||||||
|
"{b'0':1, b'2':3}", # BUILD_CONST_KEY_MAP
|
||||||
|
"{0: 1, 2: 3}", # BUILD_CONST_KEY_MAP
|
||||||
|
"{'a':'b','c':'d'}", # BUILD_CONST_KEY_MAP
|
||||||
|
"{0: 1, 2: 3}", # BUILD_CONST_KEY_MAP
|
||||||
|
"{'a': 1, 'b': 2}", # BUILD_CONST_KEY_MAP
|
||||||
|
"{'a':'b','c':'d'}", # BUILD_CONST_KEY_MAP
|
||||||
|
"{0.0:'b',0.1:'d'}", # BUILD_CONST_KEY_MAP
|
||||||
|
))
|
||||||
|
def test_build_const_key_map(text):
|
||||||
|
validate_uncompyle(text)
|
128
pytest/test_function_call.py
Normal file
128
pytest/test_function_call.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# std
|
||||||
|
import string
|
||||||
|
# 3rd party
|
||||||
|
from hypothesis import given, assume, strategies as st
|
||||||
|
import pytest
|
||||||
|
# uncompyle
|
||||||
|
from validate import validate_uncompyle
|
||||||
|
|
||||||
|
|
||||||
|
alpha = st.sampled_from(string.ascii_lowercase)
|
||||||
|
numbers = st.sampled_from(string.digits)
|
||||||
|
alphanum = st.sampled_from(string.ascii_lowercase + string.digits)
|
||||||
|
expressions = st.sampled_from([x for x in string.ascii_lowercase + string.digits] + ['x+1'])
|
||||||
|
|
||||||
|
|
||||||
|
@st.composite
|
||||||
|
def function_calls(draw):
|
||||||
|
"""
|
||||||
|
Strategy factory for generating function calls.
|
||||||
|
|
||||||
|
:param draw: Callable which draws examples from other strategies.
|
||||||
|
|
||||||
|
:return: The function call text.
|
||||||
|
"""
|
||||||
|
list1 = st.lists(alpha, min_size=0, max_size=1)
|
||||||
|
list3 = st.lists(alpha, min_size=0, max_size=3)
|
||||||
|
|
||||||
|
positional_args = draw(list3)
|
||||||
|
named_args = [x + '=0' for x in draw(list3)]
|
||||||
|
star_args = ['*' + x for x in draw(list1)]
|
||||||
|
double_star_args = ['**' + x for x in draw(list1)]
|
||||||
|
|
||||||
|
arguments = positional_args + named_args + star_args + double_star_args
|
||||||
|
draw(st.randoms()).shuffle(arguments)
|
||||||
|
arguments = ','.join(arguments)
|
||||||
|
|
||||||
|
function_call = 'fn({arguments})'.format(arguments=arguments)
|
||||||
|
try:
|
||||||
|
# TODO: Figure out the exact rules for ordering of positional, named,
|
||||||
|
# star args, double star args and in which versions the various
|
||||||
|
# types of arguments are supported so we don't need to check that the
|
||||||
|
# expression compiles like this.
|
||||||
|
compile(function_call, '<string>', 'single')
|
||||||
|
except:
|
||||||
|
assume(False)
|
||||||
|
return function_call
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_CALL_FUNCTION():
|
||||||
|
validate_uncompyle("fn(w,m,f)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_CONST_KEY_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(w=0,m=0,**v)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_MAP_BUILD_MAP_UNPACK_WITH_CALL_BUILD_TUPLE_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(a=0,**g)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_CALL_FUNCTION_KW():
|
||||||
|
validate_uncompyle("fn(j=0)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(*g,**j)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_MAP_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(*z,u=0)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_TUPLE_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(**a)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_MAP_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(b,b,b=0,*a)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_TUPLE_BUILD_TUPLE_UNPACK_WITH_CALL_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(*c,v)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail()
|
||||||
|
def test_BUILD_CONST_KEY_MAP_CALL_FUNCTION_EX():
|
||||||
|
validate_uncompyle("fn(i=0,y=0,*p)")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason='skipping property based test until all individual tests are passing')
|
||||||
|
@given(function_calls())
|
||||||
|
def test_function_call(function_call):
|
||||||
|
validate_uncompyle(function_call)
|
||||||
|
|
||||||
|
|
||||||
|
examples = set()
|
||||||
|
generate_examples = False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not generate_examples, reason='not generating examples')
|
||||||
|
@given(function_calls())
|
||||||
|
def test_generate_hypothesis(function_call):
|
||||||
|
examples.add(function_call)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not generate_examples, reason='not generating examples')
|
||||||
|
def test_generate_examples():
|
||||||
|
import dis
|
||||||
|
example_opcodes = {}
|
||||||
|
for example in examples:
|
||||||
|
opcodes = tuple(sorted(set(
|
||||||
|
instruction.opname
|
||||||
|
for instruction in dis.Bytecode(example)
|
||||||
|
if instruction.opname not in ('LOAD_CONST', 'LOAD_NAME', 'RETURN_VALUE')
|
||||||
|
)))
|
||||||
|
example_opcodes[opcodes] = example
|
||||||
|
for k, v in example_opcodes.items():
|
||||||
|
print('def test_' + '_'.join(k) + '():\n validate_uncompyle("' + v + '")\n\n')
|
||||||
|
return
|
@@ -40,7 +40,9 @@ def test_grammar():
|
|||||||
ignore_set = set(
|
ignore_set = set(
|
||||||
"""
|
"""
|
||||||
JUMP_BACK CONTINUE RETURN_END_IF
|
JUMP_BACK CONTINUE RETURN_END_IF
|
||||||
COME_FROM COME_FROM_EXCEPT COME_FROM_LOOP COME_FROM_WITH
|
COME_FROM COME_FROM_EXCEPT
|
||||||
|
COME_FROM_EXCEPT_CLAUSE
|
||||||
|
COME_FROM_LOOP COME_FROM_WITH
|
||||||
COME_FROM_FINALLY ELSE
|
COME_FROM_FINALLY ELSE
|
||||||
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP
|
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP
|
||||||
LAMBDA_MARKER RETURN_LAST
|
LAMBDA_MARKER RETURN_LAST
|
||||||
|
147
pytest/validate.py
Normal file
147
pytest/validate.py
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# future
|
||||||
|
from __future__ import print_function
|
||||||
|
# std
|
||||||
|
import os
|
||||||
|
import difflib
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import functools
|
||||||
|
# compatability
|
||||||
|
import six
|
||||||
|
# uncompyle6 / xdis
|
||||||
|
from uncompyle6 import PYTHON_VERSION, IS_PYPY, deparse_code
|
||||||
|
# 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)
|
||||||
|
Bytecode = functools.partial(Bytecode, opc=opc)
|
||||||
|
|
||||||
|
|
||||||
|
def _dis_to_text(co):
|
||||||
|
return Bytecode(co).dis()
|
||||||
|
|
||||||
|
|
||||||
|
def print_diff(original, uncompyled):
|
||||||
|
"""
|
||||||
|
Try and display a pretty html line difference between the original and
|
||||||
|
uncompyled code and bytecode if elinks and BeautifulSoup are installed
|
||||||
|
otherwise just show the diff.
|
||||||
|
|
||||||
|
:param original: Text describing the original code object.
|
||||||
|
:param uncompyled: Text describing the uncompyled code object.
|
||||||
|
"""
|
||||||
|
original_lines = original.split('\n')
|
||||||
|
uncompyled_lines = uncompyled.split('\n')
|
||||||
|
args = original_lines, uncompyled_lines, 'original', 'uncompyled'
|
||||||
|
try:
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
diff = difflib.HtmlDiff().make_file(*args)
|
||||||
|
diff = BeautifulSoup(diff, "html.parser")
|
||||||
|
diff.select_one('table[summary="Legends"]').extract()
|
||||||
|
except ImportError:
|
||||||
|
print('\nTo display diff highlighting run:\n pip install BeautifulSoup4')
|
||||||
|
diff = difflib.HtmlDiff().make_table(*args)
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||||
|
f.write(str(diff).encode('utf-8'))
|
||||||
|
|
||||||
|
try:
|
||||||
|
print()
|
||||||
|
html = subprocess.check_output([
|
||||||
|
'elinks',
|
||||||
|
'-dump',
|
||||||
|
'-no-references',
|
||||||
|
'-dump-color-mode',
|
||||||
|
'1',
|
||||||
|
f.name,
|
||||||
|
]).decode('utf-8')
|
||||||
|
print(html)
|
||||||
|
except:
|
||||||
|
print('\nFor side by side diff install elinks')
|
||||||
|
diff = difflib.Differ().compare(original_lines, uncompyled_lines)
|
||||||
|
print('\n'.join(diff))
|
||||||
|
finally:
|
||||||
|
os.unlink(f.name)
|
||||||
|
|
||||||
|
|
||||||
|
def are_instructions_equal(i1, i2):
|
||||||
|
"""
|
||||||
|
Determine if two instructions are approximately equal,
|
||||||
|
ignoring certain fields which we allow to differ, namely:
|
||||||
|
|
||||||
|
* code objects are ignore (should probaby be checked) due to address
|
||||||
|
* line numbers
|
||||||
|
|
||||||
|
:param i1: left instruction to compare
|
||||||
|
:param i2: right instruction to compare
|
||||||
|
|
||||||
|
:return: True if the two instructions are approximately equal, otherwise False.
|
||||||
|
"""
|
||||||
|
result = (1==1
|
||||||
|
and i1.opname == i2.opname
|
||||||
|
and i1.opcode == i2.opcode
|
||||||
|
and i1.arg == i2.arg
|
||||||
|
# ignore differences due to code objects
|
||||||
|
# TODO : Better way of ignoring address
|
||||||
|
and (i1.argval == i2.argval or '<code object' in str(i1.argval))
|
||||||
|
# TODO : Should probably recurse to check code objects
|
||||||
|
and (i1.argrepr == i2.argrepr or '<code object' in i1.argrepr)
|
||||||
|
and i1.offset == i2.offset
|
||||||
|
# ignore differences in line numbers
|
||||||
|
#and i1.starts_line
|
||||||
|
and i1.is_jump_target == i2.is_jump_target
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def are_code_objects_equal(co1, co2):
|
||||||
|
"""
|
||||||
|
Determine if two code objects are approximately equal,
|
||||||
|
see are_instructions_equal for more information.
|
||||||
|
|
||||||
|
:param i1: left code object to compare
|
||||||
|
:param i2: right code object to compare
|
||||||
|
|
||||||
|
:return: True if the two code objects are approximately equal, otherwise False.
|
||||||
|
"""
|
||||||
|
instructions1 = Bytecode(co1)
|
||||||
|
instructions2 = Bytecode(co2)
|
||||||
|
for opcode1, opcode2 in zip(instructions1, instructions2):
|
||||||
|
if not are_instructions_equal(opcode1, opcode2):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def validate_uncompyle(text, mode='exec'):
|
||||||
|
"""
|
||||||
|
Validate decompilation of the given source code.
|
||||||
|
|
||||||
|
:param text: Source to validate decompilation of.
|
||||||
|
"""
|
||||||
|
original_code = compile(text, '<string>', mode)
|
||||||
|
original_dis = _dis_to_text(original_code)
|
||||||
|
original_text = text
|
||||||
|
|
||||||
|
deparsed = deparse_code(PYTHON_VERSION, original_code,
|
||||||
|
compile_mode=mode, out=six.StringIO())
|
||||||
|
uncompyled_text = deparsed.text
|
||||||
|
uncompyled_code = compile(uncompyled_text, '<string>', 'exec')
|
||||||
|
|
||||||
|
if not are_code_objects_equal(uncompyled_code, original_code):
|
||||||
|
|
||||||
|
uncompyled_dis = _dis_to_text(uncompyled_text)
|
||||||
|
|
||||||
|
def output(text, dis):
|
||||||
|
width = 60
|
||||||
|
return '\n\n'.join([
|
||||||
|
' SOURCE CODE '.center(width, '#'),
|
||||||
|
text.strip(),
|
||||||
|
' BYTECODE '.center(width, '#'),
|
||||||
|
dis
|
||||||
|
])
|
||||||
|
|
||||||
|
original = output(original_text, original_dis)
|
||||||
|
uncompyled = output(uncompyled_text, uncompyled_dis)
|
||||||
|
print_diff(original, uncompyled)
|
||||||
|
|
||||||
|
assert 'original' == 'uncompyled'
|
@@ -1,3 +1,4 @@
|
|||||||
pytest
|
pytest
|
||||||
flake8
|
flake8
|
||||||
hypothesis
|
hypothesis
|
||||||
|
six
|
@@ -3,7 +3,7 @@ PHONY=check clean dist distclean test test-unit test-functional rmChangeLog clea
|
|||||||
GIT2CL ?= git2cl
|
GIT2CL ?= git2cl
|
||||||
PYTHON ?= python
|
PYTHON ?= python
|
||||||
|
|
||||||
PYTHON_VERSION = $(shell $(PYTHON) -V | cut -d ' ' -f 2 | cut -d'.' -f1,2)
|
PYTHON_VERSION = $(shell $(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2)
|
||||||
NATIVE_CHECK = check-$(PYTHON_VERSION)
|
NATIVE_CHECK = check-$(PYTHON_VERSION)
|
||||||
|
|
||||||
# Set COMPILE='--compile' to force compilation before check
|
# Set COMPILE='--compile' to force compilation before check
|
||||||
@@ -16,11 +16,10 @@ check-short:
|
|||||||
|
|
||||||
# Run all tests
|
# Run all tests
|
||||||
check:
|
check:
|
||||||
@$(PYTHON) -V && PYTHON_VERSION=`$(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2`; \
|
$(MAKE) check-$(PYTHON_VERSION)
|
||||||
$(MAKE) check-$$PYTHON_VERSION
|
|
||||||
|
|
||||||
#: Run working tests from Python 2.6 or 2.7
|
#: Run working tests from Python 2.6 or 2.7
|
||||||
check-2.6 check-2.7: check-bytecode-2 check-bytecode-3 check-bytecode-1 check-2.7-ok
|
check-2.6 check-2.7: check-bytecode-2 check-bytecode-3 check-bytecode-1 check-native-short
|
||||||
|
|
||||||
#: Run working tests from Python 3.0
|
#: Run working tests from Python 3.0
|
||||||
check-3.0: check-bytecode
|
check-3.0: check-bytecode
|
||||||
@@ -98,6 +97,21 @@ check-bytecode-2.4:
|
|||||||
check-bytecode-2.5:
|
check-bytecode-2.5:
|
||||||
$(PYTHON) test_pythonlib.py --bytecode-2.5
|
$(PYTHON) test_pythonlib.py --bytecode-2.5
|
||||||
|
|
||||||
|
#: Get grammar coverage for Python 2.5
|
||||||
|
grammar-coverage-2.5:
|
||||||
|
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-25.cover $(PYTHON) test_pythonlib.py --bytecode-2.5
|
||||||
|
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-25.cover $(PYTHON) test_pyenvlib.py --2.5.6
|
||||||
|
|
||||||
|
#: Get grammar coverage for Python 2.6
|
||||||
|
grammar-coverage-2.6:
|
||||||
|
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-26.cover $(PYTHON) test_pythonlib.py --bytecode-2.6
|
||||||
|
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-26.cover $(PYTHON) test_pyenvlib.py --2.6.9
|
||||||
|
|
||||||
|
#: Get grammar coverage for Python 2.7
|
||||||
|
grammar-coverage-2.7:
|
||||||
|
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-27.cover $(PYTHON) test_pythonlib.py --bytecode-2.7
|
||||||
|
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-27.cover $(PYTHON) test_pyenvlib.py --2.7.13
|
||||||
|
|
||||||
#: Check deparsing Python 2.6
|
#: Check deparsing Python 2.6
|
||||||
check-bytecode-2.6:
|
check-bytecode-2.6:
|
||||||
$(PYTHON) test_pythonlib.py --bytecode-2.6 --weak-verify
|
$(PYTHON) test_pythonlib.py --bytecode-2.6 --weak-verify
|
||||||
|
BIN
test/bytecode_2.3/03_if1.pyc
Normal file
BIN
test/bytecode_2.3/03_if1.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_2.6/01_ops.pyc
Normal file
BIN
test/bytecode_2.6/01_ops.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_2.6/03_double_equals.pyc
Normal file
BIN
test/bytecode_2.6/03_double_equals.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_2.6/03_if_for.pyc
Normal file
BIN
test/bytecode_2.6/03_if_for.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_2.7/01_ops.pyc
Normal file
BIN
test/bytecode_2.7/01_ops.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_2.7/02_complex.pyc
Normal file
BIN
test/bytecode_2.7/02_complex.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_2.7/02_while1else.pyc
Normal file
BIN
test/bytecode_2.7/02_while1else.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_2.7/03_double_equals.pyc
Normal file
BIN
test/bytecode_2.7/03_double_equals.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_2.7/03_if_1_else.pyc
Normal file
BIN
test/bytecode_2.7/03_if_1_else.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.1/02_ifelse_comprehension.pyc
Normal file
BIN
test/bytecode_3.1/02_ifelse_comprehension.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.1/03_if_1_else.pyc
Normal file
BIN
test/bytecode_3.1/03_if_1_else.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.2/01_delete_deref.pyc
Normal file
BIN
test/bytecode_3.2/01_delete_deref.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.2/01_named_and_kwargs.pyc
Normal file
BIN
test/bytecode_3.2/01_named_and_kwargs.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.2/01_try_except_raise.pyc
Normal file
BIN
test/bytecode_3.2/01_try_except_raise.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.2/03_if.pyc
Normal file
BIN
test/bytecode_3.2/03_if.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.3/02_while1else.pyc
Normal file
BIN
test/bytecode_3.3/02_while1else.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.5/01_map_unpack.pyc
Normal file
BIN
test/bytecode_3.5/01_map_unpack.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5/01_named_and_kwargs.pyc
Normal file
BIN
test/bytecode_3.5/01_named_and_kwargs.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5/02_async_for.pyc
Normal file
BIN
test/bytecode_3.5/02_async_for.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5/02_try_finally.pyc
Normal file
BIN
test/bytecode_3.5/02_try_finally.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5/02_while1else.pyc
Normal file
BIN
test/bytecode_3.5/02_while1else.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5/03_async_await.pyc
Normal file
BIN
test/bytecode_3.5/03_async_await.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5/03_double_star_unpack.pyc
Normal file
BIN
test/bytecode_3.5/03_double_star_unpack.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5/03_while-if-break.pyc
Normal file
BIN
test/bytecode_3.5/03_while-if-break.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5/04_def_annotate.pyc
Normal file
BIN
test/bytecode_3.5/04_def_annotate.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.5/10_kw+pos_args-bug.pyc
Normal file
BIN
test/bytecode_3.5/10_kw+pos_args-bug.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/00_assign.pyc
Normal file
BIN
test/bytecode_3.6/00_assign.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/00_docstring.pyc
Normal file
BIN
test/bytecode_3.6/00_docstring.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/00_import.pyc
Normal file
BIN
test/bytecode_3.6/00_import.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/01_call_function.pyc
Normal file
BIN
test/bytecode_3.6/01_call_function.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/01_extended_arg.pyc-notyet
Normal file
BIN
test/bytecode_3.6/01_extended_arg.pyc-notyet
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/01_matrix_multiply.pyc
Normal file
BIN
test/bytecode_3.6/01_matrix_multiply.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.6/03_async_await.pyc
Normal file
BIN
test/bytecode_3.6/03_async_await.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/03_while-if-break.pyc
Normal file
BIN
test/bytecode_3.6/03_while-if-break.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/03_while_else.pyc
Normal file
BIN
test/bytecode_3.6/03_while_else.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/04_raise.pyc
Normal file
BIN
test/bytecode_3.6/04_raise.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/05_try_finally_pass.pyc
Normal file
BIN
test/bytecode_3.6/05_try_finally_pass.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/10_if_break_finally.pyc
Normal file
BIN
test/bytecode_3.6/10_if_break_finally.pyc
Normal file
Binary file not shown.
18
test/simple_source/bug22/01_ops.py
Normal file
18
test/simple_source/bug22/01_ops.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Statements to beef up grammar coverage rules
|
||||||
|
# Force "inplace" ops
|
||||||
|
y = +10 # UNARY_POSITIVE
|
||||||
|
y /= 1 # INPLACE_DIVIDE
|
||||||
|
y %= 4 # INPLACE_MODULO
|
||||||
|
y **= 1 # INPLACE POWER
|
||||||
|
y >>= 2 # INPLACE_RSHIFT
|
||||||
|
y <<= 2 # INPLACE_LSHIFT
|
||||||
|
y //= 1 # INPLACE_TRUE_DIVIDE
|
||||||
|
y &= 1 # INPLACE_AND
|
||||||
|
y ^= 1 # INPLACE_XOR
|
||||||
|
|
||||||
|
`y` # UNARY_CONVERT - No in Python 3.x
|
||||||
|
|
||||||
|
# Beef up augassign and STORE_SLICE+3
|
||||||
|
x = [1,2,3,4,5]
|
||||||
|
x[0:1] = 1
|
||||||
|
x[0:3] += 1, 2, 3
|
7
test/simple_source/bug22/03_if1.py
Normal file
7
test/simple_source/bug22/03_if1.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# From https://github.com/ToontownInfinite /src/otp/avatar/LocalAvatar.py#L364
|
||||||
|
if 1:
|
||||||
|
def jumpLandAnimFix(self, jumpTime):
|
||||||
|
return 5
|
||||||
|
|
||||||
|
def jumpLand(self):
|
||||||
|
return 6
|
@@ -1,7 +1,6 @@
|
|||||||
# Python 2.5 bug
|
# Python 2.5 bug
|
||||||
# Was turning into tryelse when there in fact is no else.
|
# Was turning into tryelse when there in fact is no else.
|
||||||
def options(self, section):
|
def options(self, section):
|
||||||
"""Return a list of option names for the given section name."""
|
|
||||||
try:
|
try:
|
||||||
opts = self._sections[section].copy()
|
opts = self._sections[section].copy()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -10,3 +9,19 @@ def options(self, section):
|
|||||||
if '__name__' in opts:
|
if '__name__' in opts:
|
||||||
del opts['__name__']
|
del opts['__name__']
|
||||||
return opts.keys()
|
return opts.keys()
|
||||||
|
|
||||||
|
# From python2.5/distutils/command/register.py
|
||||||
|
def post_to_server(self, urllib2):
|
||||||
|
try:
|
||||||
|
result = 5
|
||||||
|
except urllib2.HTTPError, e:
|
||||||
|
result = e.code, e.msg
|
||||||
|
except urllib2.URLError, e:
|
||||||
|
result = 500
|
||||||
|
else:
|
||||||
|
if self.show_response:
|
||||||
|
result = 10
|
||||||
|
result = 200
|
||||||
|
if self.show_response:
|
||||||
|
result = 8
|
||||||
|
return result
|
||||||
|
7
test/simple_source/bug25/03_if_for.py
Normal file
7
test/simple_source/bug25/03_if_for.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# From Python 2.6. distutils/sysconfig.py
|
||||||
|
def get_config_vars(_config_vars, args):
|
||||||
|
if _config_vars:
|
||||||
|
if args == 1:
|
||||||
|
if args < 8:
|
||||||
|
for key in ('LDFLAGS', 'BASECFLAGS'):
|
||||||
|
_config_vars[key] = 4
|
6
test/simple_source/bug26/03_double_equals.py
Normal file
6
test/simple_source/bug26/03_double_equals.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# From Python 2.7 parse_starttag HTMLParser.pyc
|
||||||
|
attrvalue = [1,2]
|
||||||
|
while attrvalue:
|
||||||
|
if attrvalue[:1] == 5 or \
|
||||||
|
attrvalue[:1] == 2 == attrvalue[-1:]:
|
||||||
|
attrvalue = 10
|
1
test/simple_source/bug27+/03_if_1_else.py
Normal file
1
test/simple_source/bug27+/03_if_1_else.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
1 if 1 else __file__
|
12
test/simple_source/bug31/02_ifelse_comprehension.py
Normal file
12
test/simple_source/bug31/02_ifelse_comprehension.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Python 2.7 sqlalchemy-1.013/sql/crud.py
|
||||||
|
def _extend_values_for_multiparams(compiler, stmt, c):
|
||||||
|
c(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
(compiler() if compiler()
|
||||||
|
else compiler())
|
||||||
|
if c in stmt else compiler(),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
for i in enumerate(stmt)
|
||||||
|
)
|
@@ -8,3 +8,23 @@ def open(file, mode = "r", buffering = None,
|
|||||||
encoding = None, errors = None,
|
encoding = None, errors = None,
|
||||||
newline = None, closefd = True) -> "IOBase":
|
newline = None, closefd = True) -> "IOBase":
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
def foo(x: 'an argument that defaults to 5' = 5):
|
||||||
|
print(x)
|
||||||
|
|
||||||
|
def div(a: dict(type=float, help='the dividend'),
|
||||||
|
b: dict(type=float, help='the divisor (must be different than 0)')
|
||||||
|
) -> dict(type=float, help='the result of dividing a by b'):
|
||||||
|
"""Divide a by b"""
|
||||||
|
return a / b
|
||||||
|
|
||||||
|
class TestSignatureObject(unittest.TestCase):
|
||||||
|
def test_signature_on_wkwonly(self):
|
||||||
|
def test(*, a:float, b:str) -> int:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SupportsInt(_Protocol):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def __int__(self) -> int:
|
||||||
|
pass
|
||||||
|
@@ -18,3 +18,12 @@ def __init__(self, defaults=None, dict_type=_default_dict,
|
|||||||
default_section=DEFAULTSECT,
|
default_section=DEFAULTSECT,
|
||||||
interpolation=_UNSET):
|
interpolation=_UNSET):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Bug found by hypothesis in creating function calls
|
||||||
|
# thanks to moagstar
|
||||||
|
def fn(a, b, d):
|
||||||
|
return (a, b, d)
|
||||||
|
|
||||||
|
b = {'b': 1,
|
||||||
|
'd': 2}
|
||||||
|
fn(a=0, **b)
|
||||||
|
9
test/simple_source/bug32/01_try_except_raise.py
Normal file
9
test/simple_source/bug32/01_try_except_raise.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# From 3.2 _abcoll.py
|
||||||
|
def pop(self):
|
||||||
|
it = iter(self)
|
||||||
|
try:
|
||||||
|
value = next(it)
|
||||||
|
except StopIteration:
|
||||||
|
raise KeyError
|
||||||
|
self.discard(value)
|
||||||
|
return value
|
11
test/simple_source/bug32/03_if.py
Normal file
11
test/simple_source/bug32/03_if.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# From 3.2 shlex.py
|
||||||
|
def _samefile(os, src, dst):
|
||||||
|
if hasattr(os.path, 'samefile'):
|
||||||
|
try:
|
||||||
|
return os.path.samefile(src, dst)
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# All other platforms: check for same pathname.
|
||||||
|
return (os.path.normcase(os.path.abspath(src)) ==
|
||||||
|
os.path.normcase(os.path.abspath(dst)))
|
4
test/simple_source/bug33/01_delete_deref.py
Normal file
4
test/simple_source/bug33/01_delete_deref.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
def a():
|
||||||
|
del y
|
||||||
|
def b():
|
||||||
|
return y
|
16
test/simple_source/bug33/01_if_try_except.py
Normal file
16
test/simple_source/bug33/01_if_try_except.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# From 3.3.5 _osx_support.py
|
||||||
|
def _get_system_version():
|
||||||
|
if __file__ is None:
|
||||||
|
try:
|
||||||
|
m = 5
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
m = 10
|
||||||
|
finally:
|
||||||
|
m = 15
|
||||||
|
if m is not None:
|
||||||
|
m = 20
|
||||||
|
|
||||||
|
return m
|
16
test/simple_source/bug33/01_try_except.py
Normal file
16
test/simple_source/bug33/01_try_except.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# From 3.3.5 _osx_support.py
|
||||||
|
def _get_system_version():
|
||||||
|
if __file__ is None:
|
||||||
|
try:
|
||||||
|
m = 5
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
m = 10
|
||||||
|
finally:
|
||||||
|
m = 15
|
||||||
|
if m is not None:
|
||||||
|
m = 20
|
||||||
|
|
||||||
|
return m
|
@@ -1,3 +1,6 @@
|
|||||||
# From Python 3.3.6 hmac.py
|
# From Python 3.3.6 hmac.py
|
||||||
# Problem was getting wrong placement of positional args
|
# Problem was getting wrong placement of positional args
|
||||||
digest_cons = lambda d=b'': 5
|
digest_cons = lambda d=b'': 5
|
||||||
|
|
||||||
|
# Handle single kwarg
|
||||||
|
lambda *, d=0: None
|
||||||
|
14
test/simple_source/bug33/02_while1.py
Normal file
14
test/simple_source/bug33/02_while1.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# From Python 3.4 mailcap
|
||||||
|
def readmailcapfile(caps):
|
||||||
|
while 1:
|
||||||
|
line = 'abc'
|
||||||
|
if line[0] == '#' or line == '':
|
||||||
|
continue
|
||||||
|
key, fields = (1,2)
|
||||||
|
if not (key and fields):
|
||||||
|
continue
|
||||||
|
if key in caps:
|
||||||
|
caps[key].append(fields)
|
||||||
|
else:
|
||||||
|
caps[key] = [fields]
|
||||||
|
return caps
|
9
test/simple_source/bug35/01_map_unpack.py
Normal file
9
test/simple_source/bug35/01_map_unpack.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Python 3.5+ PEP 448 - Additional Unpacking Generalizations for dictionaries
|
||||||
|
{**{}}
|
||||||
|
{**{'a': 1, 'b': 2}}
|
||||||
|
## {**{'x': 1}, **{'y': 2}}
|
||||||
|
# {'c': 1, {'d': 2}, **{'e': 3}}
|
||||||
|
[*[]]
|
||||||
|
{**{0:0 for a in b}}
|
||||||
|
## {**{}, **{}}
|
||||||
|
## {**{}, **{}, **{}}
|
3
test/simple_source/bug35/02_async_for.py
Normal file
3
test/simple_source/bug35/02_async_for.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
async def a(b, c):
|
||||||
|
async for b in c:
|
||||||
|
pass
|
32
test/simple_source/bug35/03_async_await.py
Normal file
32
test/simple_source/bug35/03_async_await.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Python 3.5+ async and await
|
||||||
|
async def await_test(asyncio):
|
||||||
|
reader, writer = await asyncio.open_connection(80)
|
||||||
|
await bar()
|
||||||
|
|
||||||
|
async def afor_test():
|
||||||
|
|
||||||
|
async for i in [1,2,3]:
|
||||||
|
x = i
|
||||||
|
|
||||||
|
|
||||||
|
async def afor_else_test():
|
||||||
|
|
||||||
|
async for i in [1,2,3]:
|
||||||
|
x = i
|
||||||
|
else:
|
||||||
|
z = 4
|
||||||
|
|
||||||
|
|
||||||
|
async def awith_test():
|
||||||
|
async with i:
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
async def awith_as_test():
|
||||||
|
async with 1 as i:
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
async def f(z):
|
||||||
|
await z
|
||||||
|
|
||||||
|
async def g(z):
|
||||||
|
return await z
|
9
test/simple_source/bug35/03_double_star_unpack.py
Normal file
9
test/simple_source/bug35/03_double_star_unpack.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Bug in Python 3.5 is getting the two star'd arguments right.
|
||||||
|
def sum(a,b,c,d):
|
||||||
|
return a + b + c + d
|
||||||
|
|
||||||
|
args=(1,2)
|
||||||
|
sum(*args, *args)
|
||||||
|
|
||||||
|
# FIXME: this is handled incorrectly
|
||||||
|
# (*c,) = (3,4)
|
14
test/simple_source/bug35/03_while-if-break.py
Normal file
14
test/simple_source/bug35/03_while-if-break.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Python 3.5 and 3.6 break inside a
|
||||||
|
# while True and if / break
|
||||||
|
def display_date(loop):
|
||||||
|
while True:
|
||||||
|
if loop.time():
|
||||||
|
break
|
||||||
|
x = 5
|
||||||
|
|
||||||
|
# Another loop to test 3.5 ifelsestmtl grammar rule
|
||||||
|
while loop:
|
||||||
|
if x:
|
||||||
|
True
|
||||||
|
else:
|
||||||
|
True
|
5
test/simple_source/bug36/01_call_function.py
Normal file
5
test/simple_source/bug36/01_call_function.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Python 3.6's changes for calling functions.
|
||||||
|
# See https://github.com/rocky/python-uncompyle6/issues/58
|
||||||
|
# CALL_FUNCTION_EX takes 2 to 3 arguments on the stack: the function, the tuple of positional arguments,
|
||||||
|
# and optionally the dict of keyword arguments if bit 0 of oparg is 1.
|
||||||
|
a(*[])
|
2
test/simple_source/bug36/01_extended_arg.py
Normal file
2
test/simple_source/bug36/01_extended_arg.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
if __file__:
|
||||||
|
0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0
|
@@ -8,3 +8,20 @@ def __init__(self, defaults=None, dict_type=_default_dict,
|
|||||||
default_section=DEFAULTSECT,
|
default_section=DEFAULTSECT,
|
||||||
interpolation=_UNSET):
|
interpolation=_UNSET):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# From 3.5 sqlalchemy/orm/__init__.py
|
||||||
|
# Python 3.5 changes the stack position of where * args are (furthest down the stack)
|
||||||
|
# Python 3.6+ replaces CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
|
||||||
|
def deferred(*columns, **kw):
|
||||||
|
return ColumnProperty(deferred=True, *columns, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
# From sqlalchemy/sql/selectable.py
|
||||||
|
class GenerativeSelect():
|
||||||
|
def __init__(self,
|
||||||
|
ClauseList,
|
||||||
|
util,
|
||||||
|
order_by=None):
|
||||||
|
self._order_by_clause = ClauseList(
|
||||||
|
*util.to_list(order_by),
|
||||||
|
_literal_as_text=5)
|
||||||
|
11
test/simple_source/exception/02_try_finally.py
Normal file
11
test/simple_source/exception/02_try_finally.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# From 2.6.9 cmd.py
|
||||||
|
try:
|
||||||
|
if __file__:
|
||||||
|
x = 2
|
||||||
|
x = 3
|
||||||
|
finally:
|
||||||
|
if x and __file__:
|
||||||
|
try:
|
||||||
|
x = 1
|
||||||
|
except:
|
||||||
|
pass
|
8
test/simple_source/stmts/02_while1else.py
Normal file
8
test/simple_source/stmts/02_while1else.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# From Python-3.5.2/Lib/multiprocessing/connection.py
|
||||||
|
|
||||||
|
def PipeClient(address):
|
||||||
|
while 1:
|
||||||
|
z = 2
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return
|
@@ -30,10 +30,10 @@ from fnmatch import fnmatch
|
|||||||
TEST_VERSIONS=('2.3.7', '2.4.6', '2.5.6', '2.6.9',
|
TEST_VERSIONS=('2.3.7', '2.4.6', '2.5.6', '2.6.9',
|
||||||
'pypy-2.4.0', 'pypy-2.6.1',
|
'pypy-2.4.0', 'pypy-2.6.1',
|
||||||
'pypy-5.0.1', 'pypy-5.3.1',
|
'pypy-5.0.1', 'pypy-5.3.1',
|
||||||
'2.7.10', '2.7.11', '2.7.12',
|
'2.7.10', '2.7.11', '2.7.12', '2.7.13',
|
||||||
'3.0.1', '3.1.5', '3.2.6',
|
'3.0.1', '3.1.5', '3.2.6',
|
||||||
'3.3.5', '3.3.6',
|
'3.3.5', '3.3.6',
|
||||||
'3.4.2', '3.5.1')
|
'3.4.2', '3.5.1', '3.6.0')
|
||||||
|
|
||||||
target_base = '/tmp/py-dis/'
|
target_base = '/tmp/py-dis/'
|
||||||
lib_prefix = os.path.join(os.environ['HOME'], '.pyenv/versions')
|
lib_prefix = os.path.join(os.environ['HOME'], '.pyenv/versions')
|
||||||
@@ -106,28 +106,40 @@ def do_tests(src_dir, patterns, target_dir, start_with=None, do_verify=False):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import getopt, sys
|
import getopt, sys
|
||||||
|
|
||||||
do_verify = False
|
do_coverage = do_verify = False
|
||||||
test_dirs = []
|
test_dirs = []
|
||||||
start_with = None
|
start_with = None
|
||||||
|
|
||||||
test_options_keys = list(test_options.keys())
|
test_options_keys = list(test_options.keys())
|
||||||
test_options_keys.sort()
|
test_options_keys.sort()
|
||||||
opts, args = getopt.getopt(sys.argv[1:], '',
|
opts, args = getopt.getopt(sys.argv[1:], '',
|
||||||
['start-with=', 'verify', 'weak-verify', 'all', ] \
|
['start-with=', 'verify', 'weak-verify',
|
||||||
|
'coverage', 'all', ] \
|
||||||
+ test_options_keys )
|
+ test_options_keys )
|
||||||
|
vers = ''
|
||||||
for opt, val in opts:
|
for opt, val in opts:
|
||||||
if opt == '--verify':
|
if opt == '--verify':
|
||||||
do_verify = True
|
do_verify = True
|
||||||
if opt == '--weak-verify':
|
if opt == '--weak-verify':
|
||||||
do_verify = 'weak'
|
do_verify = 'weak'
|
||||||
|
if opt == '--coverage':
|
||||||
|
do_coverage = True
|
||||||
elif opt == '--start-with':
|
elif opt == '--start-with':
|
||||||
start_with = val
|
start_with = val
|
||||||
elif opt[2:] in test_options_keys:
|
elif opt[2:] in test_options_keys:
|
||||||
test_dirs.append(test_options[opt[2:]])
|
triple = test_options[opt[2:]]
|
||||||
|
vers = triple[-1]
|
||||||
|
test_dirs.append(triple)
|
||||||
elif opt == '--all':
|
elif opt == '--all':
|
||||||
|
vers = 'all'
|
||||||
for val in test_options_keys:
|
for val in test_options_keys:
|
||||||
test_dirs.append(test_options[val])
|
test_dirs.append(test_options[val])
|
||||||
|
|
||||||
|
if do_coverage:
|
||||||
|
os.environ['SPARK_PARSER_COVERAGE'] = (
|
||||||
|
'/tmp/spark-grammar-%s.cover' % vers
|
||||||
|
)
|
||||||
|
|
||||||
for src_dir, pattern, target_dir in test_dirs:
|
for src_dir, pattern, target_dir in test_dirs:
|
||||||
if os.path.exists(src_dir):
|
if os.path.exists(src_dir):
|
||||||
target_dir = os.path.join(target_base, target_dir)
|
target_dir = os.path.join(target_base, target_dir)
|
||||||
|
@@ -190,6 +190,7 @@ if __name__ == '__main__':
|
|||||||
test_options_keys.sort()
|
test_options_keys.sort()
|
||||||
opts, args = getopt.getopt(sys.argv[1:], '',
|
opts, args = getopt.getopt(sys.argv[1:], '',
|
||||||
['start-with=', 'verify', 'weak-verify', 'all', 'compile',
|
['start-with=', 'verify', 'weak-verify', 'all', 'compile',
|
||||||
|
'coverage',
|
||||||
'no-rm'] \
|
'no-rm'] \
|
||||||
+ test_options_keys )
|
+ test_options_keys )
|
||||||
if not opts: help()
|
if not opts: help()
|
||||||
@@ -198,7 +199,8 @@ if __name__ == '__main__':
|
|||||||
'do_compile': False,
|
'do_compile': False,
|
||||||
'do_verify': False,
|
'do_verify': False,
|
||||||
'start_with': None,
|
'start_with': None,
|
||||||
'rmtree' : True
|
'rmtree' : True,
|
||||||
|
'coverage' : False
|
||||||
}
|
}
|
||||||
|
|
||||||
for opt, val in opts:
|
for opt, val in opts:
|
||||||
@@ -217,11 +219,18 @@ if __name__ == '__main__':
|
|||||||
elif opt == '--all':
|
elif opt == '--all':
|
||||||
for val in test_options_keys:
|
for val in test_options_keys:
|
||||||
test_dirs.append(test_options[val])
|
test_dirs.append(test_options[val])
|
||||||
|
elif opt == '--coverage':
|
||||||
|
test_opts['coverage'] = True
|
||||||
else:
|
else:
|
||||||
help()
|
help()
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if test_opts['coverage']:
|
||||||
|
os.environ['SPARK_PARSER_COVERAGE'] = (
|
||||||
|
'/tmp/spark-grammar-python-lib%s.cover' % test_dirs[0][-1]
|
||||||
|
)
|
||||||
|
|
||||||
last_compile_version = None
|
last_compile_version = None
|
||||||
for src_dir, pattern, target_dir, compiled_version in test_dirs:
|
for src_dir, pattern, target_dir, compiled_version in test_dirs:
|
||||||
if os.path.isdir(src_dir):
|
if os.path.isdir(src_dir):
|
||||||
|
@@ -41,13 +41,18 @@ PYTHON_VERSION_STR = "%s.%s" % (sys.version_info[0], sys.version_info[1])
|
|||||||
|
|
||||||
IS_PYPY = '__pypy__' in sys.builtin_module_names
|
IS_PYPY = '__pypy__' in sys.builtin_module_names
|
||||||
|
|
||||||
sys.setrecursionlimit(5000)
|
if hasattr(sys, 'setrecursionlimit'):
|
||||||
|
# pyston doesn't have setrecursionlimit
|
||||||
|
sys.setrecursionlimit(5000)
|
||||||
|
|
||||||
import uncompyle6.semantics.pysource
|
import uncompyle6.semantics.pysource
|
||||||
import uncompyle6.semantics.fragments
|
import uncompyle6.semantics.fragments
|
||||||
|
|
||||||
# Export some functions
|
# Export some functions
|
||||||
from uncompyle6.main import uncompyle_file
|
from uncompyle6.main import decompile_file
|
||||||
|
|
||||||
|
# For compaitility
|
||||||
|
uncompyle_file = decompile_file
|
||||||
|
|
||||||
# Conventience functions so you can say:
|
# Conventience functions so you can say:
|
||||||
# from uncompyle6 import deparse_code
|
# from uncompyle6 import deparse_code
|
||||||
|
@@ -11,7 +11,7 @@ from uncompyle6.linenumbers import line_number_mapping
|
|||||||
|
|
||||||
from xdis.load import load_module
|
from xdis.load import load_module
|
||||||
|
|
||||||
def uncompyle(
|
def decompile(
|
||||||
bytecode_version, co, out=None, showasm=None, showast=False,
|
bytecode_version, co, out=None, showasm=None, showast=False,
|
||||||
timestamp=None, showgrammar=False, code_objects={},
|
timestamp=None, showgrammar=False, code_objects={},
|
||||||
source_size=None, is_pypy=False, magic_int=None):
|
source_size=None, is_pypy=False, magic_int=None):
|
||||||
@@ -48,8 +48,10 @@ def uncompyle(
|
|||||||
# deparsing failed
|
# deparsing failed
|
||||||
raise pysource.SourceWalkerError(str(e))
|
raise pysource.SourceWalkerError(str(e))
|
||||||
|
|
||||||
|
# For compatiblity
|
||||||
|
uncompyle = decompile
|
||||||
|
|
||||||
def uncompyle_file(filename, outstream=None, showasm=None, showast=False,
|
def decompile_file(filename, outstream=None, showasm=None, showast=False,
|
||||||
showgrammar=False):
|
showgrammar=False):
|
||||||
"""
|
"""
|
||||||
decompile Python byte-code file (.pyc)
|
decompile Python byte-code file (.pyc)
|
||||||
@@ -62,16 +64,20 @@ def uncompyle_file(filename, outstream=None, showasm=None, showast=False,
|
|||||||
|
|
||||||
if type(co) == list:
|
if type(co) == list:
|
||||||
for con in co:
|
for con in co:
|
||||||
uncompyle(version, con, outstream, showasm, showast,
|
decompile(version, con, outstream, showasm, showast,
|
||||||
timestamp, showgrammar, code_objects=code_objects,
|
timestamp, showgrammar, code_objects=code_objects,
|
||||||
is_pypy=is_pypy, magic_int=magic_int)
|
is_pypy=is_pypy, magic_int=magic_int)
|
||||||
else:
|
else:
|
||||||
uncompyle(version, co, outstream, showasm, showast,
|
decompile(version, co, outstream, showasm, showast,
|
||||||
timestamp, showgrammar,
|
timestamp, showgrammar,
|
||||||
code_objects=code_objects, source_size=source_size,
|
code_objects=code_objects, source_size=source_size,
|
||||||
is_pypy=is_pypy, magic_int=magic_int)
|
is_pypy=is_pypy, magic_int=magic_int)
|
||||||
co = None
|
co = None
|
||||||
|
|
||||||
|
# For compatiblity
|
||||||
|
uncompyle_file = decompile_file
|
||||||
|
|
||||||
|
|
||||||
# FIXME: combine into an options parameter
|
# FIXME: combine into an options parameter
|
||||||
def main(in_base, out_base, files, codes, outfile=None,
|
def main(in_base, out_base, files, codes, outfile=None,
|
||||||
showasm=None, showast=False, do_verify=False,
|
showasm=None, showast=False, do_verify=False,
|
||||||
@@ -101,12 +107,6 @@ def main(in_base, out_base, files, codes, outfile=None,
|
|||||||
|
|
||||||
tot_files = okay_files = failed_files = verify_failed_files = 0
|
tot_files = okay_files = failed_files = verify_failed_files = 0
|
||||||
|
|
||||||
# for code in codes:
|
|
||||||
# version = sys.version[:3] # "2.5"
|
|
||||||
# with open(code, "r") as f:
|
|
||||||
# co = compile(f.read(), "", "exec")
|
|
||||||
# uncompyle(sys.version[:3], co, sys.stdout, showasm=showasm, showast=showast)
|
|
||||||
|
|
||||||
for filename in files:
|
for filename in files:
|
||||||
infile = os.path.join(in_base, filename)
|
infile = os.path.join(in_base, filename)
|
||||||
if not os.path.exists(infile):
|
if not os.path.exists(infile):
|
||||||
@@ -142,7 +142,7 @@ def main(in_base, out_base, files, codes, outfile=None,
|
|||||||
|
|
||||||
# Try to uncompile the input file
|
# Try to uncompile the input file
|
||||||
try:
|
try:
|
||||||
uncompyle_file(infile, outstream, showasm, showast, showgrammar)
|
decompile_file(infile, outstream, showasm, showast, showgrammar)
|
||||||
tot_files += 1
|
tot_files += 1
|
||||||
except (ValueError, SyntaxError, ParserError, pysource.SourceWalkerError) as e:
|
except (ValueError, SyntaxError, ParserError, pysource.SourceWalkerError) as e:
|
||||||
sys.stdout.write("\n")
|
sys.stdout.write("\n")
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2015-2016 Rocky Bernstein
|
# Copyright (c) 2015-2017 Rocky Bernstein
|
||||||
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
||||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||||
# Copyright (c) 1999 John Aycock
|
# Copyright (c) 1999 John Aycock
|
||||||
@@ -28,6 +28,22 @@ nop_func = lambda self, args: None
|
|||||||
|
|
||||||
class PythonParser(GenericASTBuilder):
|
class PythonParser(GenericASTBuilder):
|
||||||
|
|
||||||
|
def __init__(self, AST, start, debug):
|
||||||
|
super(PythonParser, self).__init__(AST, start, debug)
|
||||||
|
self.collect = frozenset(
|
||||||
|
['stmts', 'except_stmts', '_stmts', 'load_attrs',
|
||||||
|
'exprlist', 'kvlist', 'kwargs', 'come_froms', '_come_from',
|
||||||
|
# Python < 3
|
||||||
|
'print_items',
|
||||||
|
# PyPy:
|
||||||
|
'kvlist_n'])
|
||||||
|
|
||||||
|
def ast_first_offset(self, ast):
|
||||||
|
if hasattr(ast, 'offset'):
|
||||||
|
return ast.offset
|
||||||
|
else:
|
||||||
|
return self.ast_first_offset(ast[0])
|
||||||
|
|
||||||
def add_unique_rule(self, rule, opname, count, customize):
|
def add_unique_rule(self, rule, opname, count, customize):
|
||||||
"""Add rule to grammar, but only if it hasn't been added previously
|
"""Add rule to grammar, but only if it hasn't been added previously
|
||||||
opname and count are used in the customize() semantic the actions
|
opname and count are used in the customize() semantic the actions
|
||||||
@@ -115,11 +131,7 @@ class PythonParser(GenericASTBuilder):
|
|||||||
return token.type
|
return token.type
|
||||||
|
|
||||||
def nonterminal(self, nt, args):
|
def nonterminal(self, nt, args):
|
||||||
collect = ('stmts', 'exprlist', 'kvlist', '_stmts', 'print_items', 'kwargs',
|
if nt in self.collect and len(args) > 1:
|
||||||
# PYPY:
|
|
||||||
'kvlist_n')
|
|
||||||
|
|
||||||
if nt in collect and len(args) > 1:
|
|
||||||
#
|
#
|
||||||
# Collect iterated thingies together. That is rather than
|
# Collect iterated thingies together. That is rather than
|
||||||
# stmts -> stmts stmt -> stmts stmt -> ...
|
# stmts -> stmts stmt -> stmts stmt -> ...
|
||||||
@@ -389,8 +401,7 @@ class PythonParser(GenericASTBuilder):
|
|||||||
import_cont ::= LOAD_CONST LOAD_CONST import_as_cont
|
import_cont ::= LOAD_CONST LOAD_CONST import_as_cont
|
||||||
import_as_cont ::= IMPORT_FROM designator
|
import_as_cont ::= IMPORT_FROM designator
|
||||||
|
|
||||||
load_attrs ::= LOAD_ATTR
|
load_attrs ::= LOAD_ATTR+
|
||||||
load_attrs ::= load_attrs LOAD_ATTR
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def p_list_comprehension(self, args):
|
def p_list_comprehension(self, args):
|
||||||
@@ -497,9 +508,13 @@ class PythonParser(GenericASTBuilder):
|
|||||||
expr ::= conditional
|
expr ::= conditional
|
||||||
conditional ::= expr jmp_false expr JUMP_FORWARD expr COME_FROM
|
conditional ::= expr jmp_false expr JUMP_FORWARD expr COME_FROM
|
||||||
conditional ::= expr jmp_false expr JUMP_ABSOLUTE expr
|
conditional ::= expr jmp_false expr JUMP_ABSOLUTE expr
|
||||||
|
|
||||||
expr ::= conditionalnot
|
expr ::= conditionalnot
|
||||||
conditionalnot ::= expr jmp_true expr _jump expr COME_FROM
|
conditionalnot ::= expr jmp_true expr _jump expr COME_FROM
|
||||||
|
|
||||||
|
expr ::= conditionalTrue
|
||||||
|
conditionalTrue ::= expr JUMP_FORWARD expr COME_FROM
|
||||||
|
|
||||||
ret_expr ::= expr
|
ret_expr ::= expr
|
||||||
ret_expr ::= ret_and
|
ret_expr ::= ret_and
|
||||||
ret_expr ::= ret_or
|
ret_expr ::= ret_or
|
||||||
|
@@ -44,6 +44,7 @@ class Python2Parser(PythonParser):
|
|||||||
while1stmt ::= SETUP_LOOP l_stmts JUMP_BACK COME_FROM
|
while1stmt ::= SETUP_LOOP l_stmts JUMP_BACK COME_FROM
|
||||||
while1stmt ::= SETUP_LOOP l_stmts JUMP_BACK POP_BLOCK COME_FROM
|
while1stmt ::= SETUP_LOOP l_stmts JUMP_BACK POP_BLOCK COME_FROM
|
||||||
|
|
||||||
|
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK POP_BLOCK else_suite COME_FROM
|
||||||
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK else_suite COME_FROM
|
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK else_suite COME_FROM
|
||||||
|
|
||||||
exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
|
exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
|
||||||
@@ -126,6 +127,7 @@ class Python2Parser(PythonParser):
|
|||||||
assert_expr_and ::= assert_expr jmp_false expr
|
assert_expr_and ::= assert_expr jmp_false expr
|
||||||
|
|
||||||
ifstmt ::= testexpr _ifstmts_jump
|
ifstmt ::= testexpr _ifstmts_jump
|
||||||
|
ifstmt ::= testexpr return_if_stmts COME_FROM
|
||||||
|
|
||||||
testexpr ::= testfalse
|
testexpr ::= testfalse
|
||||||
testexpr ::= testtrue
|
testexpr ::= testtrue
|
||||||
@@ -144,6 +146,8 @@ class Python2Parser(PythonParser):
|
|||||||
|
|
||||||
ifelsestmtr ::= testexpr return_if_stmts return_stmts
|
ifelsestmtr ::= testexpr return_if_stmts return_stmts
|
||||||
|
|
||||||
|
ifelsestmtr ::= testexpr return_if_stmts COME_FROM return_stmts
|
||||||
|
|
||||||
ifelsestmtl ::= testexpr c_stmts_opt JUMP_BACK else_suitel
|
ifelsestmtl ::= testexpr c_stmts_opt JUMP_BACK else_suitel
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2016 Rocky Bernstein
|
# Copyright (c) 2016-2017 Rocky Bernstein
|
||||||
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
|
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
|
||||||
# Copyright (c) 1999 John Aycock
|
# Copyright (c) 1999 John Aycock
|
||||||
|
|
||||||
@@ -14,6 +14,17 @@ class Python23Parser(Python24Parser):
|
|||||||
|
|
||||||
def p_misc23(self, args):
|
def p_misc23(self, args):
|
||||||
'''
|
'''
|
||||||
|
# Python 2.4 only adds something like the below for if 1:
|
||||||
|
# However we will just treat it as a noop (which of course messes up
|
||||||
|
# simple verify of bytecode.
|
||||||
|
# See also below in reduce_is_invalid where we check that the JUMP_FORWARD
|
||||||
|
# target matches the COME_FROM target
|
||||||
|
stmt ::= if1_stmt
|
||||||
|
if1_stmt ::= JUMP_FORWARD JUMP_IF_FALSE THEN POP_TOP COME_FROM
|
||||||
|
stmts
|
||||||
|
JUMP_FORWARD COME_FROM POP_TOP COME_FROM
|
||||||
|
|
||||||
|
|
||||||
# Used to keep semantic positions the same across later versions
|
# Used to keep semantic positions the same across later versions
|
||||||
# of Python
|
# of Python
|
||||||
_while1test ::= SETUP_LOOP JUMP_FORWARD JUMP_IF_FALSE POP_TOP COME_FROM
|
_while1test ::= SETUP_LOOP JUMP_FORWARD JUMP_IF_FALSE POP_TOP COME_FROM
|
||||||
@@ -33,6 +44,23 @@ class Python23Parser(Python24Parser):
|
|||||||
lc_body ::= LOAD_FAST expr LIST_APPEND
|
lc_body ::= LOAD_FAST expr LIST_APPEND
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
def add_custom_rules(self, tokens, customize):
|
||||||
|
super(Python23Parser, self).add_custom_rules(tokens, customize)
|
||||||
|
|
||||||
|
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||||
|
invalid = super(Python24Parser,
|
||||||
|
self).reduce_is_invalid(rule, ast,
|
||||||
|
tokens, first, last)
|
||||||
|
if invalid:
|
||||||
|
return invalid
|
||||||
|
|
||||||
|
# FiXME: this code never gets called...
|
||||||
|
lhs = rule[0]
|
||||||
|
if lhs == 'nop_stmt':
|
||||||
|
return not int(tokens[first].pattr) == tokens[last].offset
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
class Python23ParserSingle(Python23Parser, PythonParserSingle):
|
class Python23ParserSingle(Python23Parser, PythonParserSingle):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@@ -14,6 +14,15 @@ class Python24Parser(Python25Parser):
|
|||||||
|
|
||||||
def p_misc24(self, args):
|
def p_misc24(self, args):
|
||||||
'''
|
'''
|
||||||
|
# Python 2.4 only adds something like the below for if 1:
|
||||||
|
# However we will just treat it as a noop (which of course messes up
|
||||||
|
# simple verify of bytecode.
|
||||||
|
# See also below in reduce_is_invalid where we check that the JUMP_FORWARD
|
||||||
|
# target matches the COME_FROM target
|
||||||
|
stmt ::= nop_stmt
|
||||||
|
nop_stmt ::= JUMP_FORWARD POP_TOP COME_FROM
|
||||||
|
|
||||||
|
|
||||||
# 2.5+ has two LOAD_CONSTs, one for the number '.'s in a relative import
|
# 2.5+ has two LOAD_CONSTs, one for the number '.'s in a relative import
|
||||||
# keep positions similar to simplify semantic actions
|
# keep positions similar to simplify semantic actions
|
||||||
|
|
||||||
@@ -37,6 +46,25 @@ class Python24Parser(Python25Parser):
|
|||||||
gen_comp_body ::= expr YIELD_VALUE
|
gen_comp_body ::= expr YIELD_VALUE
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
def add_custom_rules(self, tokens, customize):
|
||||||
|
super(Python24Parser, self).add_custom_rules(tokens, customize)
|
||||||
|
if self.version == 2.4:
|
||||||
|
self.check_reduce['nop_stmt'] = 'tokens'
|
||||||
|
|
||||||
|
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||||
|
invalid = super(Python24Parser,
|
||||||
|
self).reduce_is_invalid(rule, ast,
|
||||||
|
tokens, first, last)
|
||||||
|
if invalid:
|
||||||
|
return invalid
|
||||||
|
|
||||||
|
# FiXME: this code never gets called...
|
||||||
|
lhs = rule[0]
|
||||||
|
if lhs == 'nop_stmt':
|
||||||
|
return not int(tokens[first].pattr) == tokens[last].offset
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
class Python24ParserSingle(Python24Parser, PythonParserSingle):
|
class Python24ParserSingle(Python24Parser, PythonParserSingle):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2016 Rocky Bernstein
|
# Copyright (c) 2016-2017 Rocky Bernstein
|
||||||
"""
|
"""
|
||||||
spark grammar differences over Python2.6 for Python 2.5.
|
spark grammar differences over Python2.6 for Python 2.5.
|
||||||
"""
|
"""
|
||||||
@@ -20,13 +20,18 @@ class Python25Parser(Python26Parser):
|
|||||||
return_if_stmt ::= ret_expr RETURN_END_IF JUMP_BACK
|
return_if_stmt ::= ret_expr RETURN_END_IF JUMP_BACK
|
||||||
|
|
||||||
# Python 2.6 uses ROT_TWO instead of the STORE_xxx
|
# Python 2.6 uses ROT_TWO instead of the STORE_xxx
|
||||||
|
# withas is allowed as a "from future" in 2.5
|
||||||
setupwithas ::= DUP_TOP LOAD_ATTR store LOAD_ATTR CALL_FUNCTION_0
|
setupwithas ::= DUP_TOP LOAD_ATTR store LOAD_ATTR CALL_FUNCTION_0
|
||||||
setup_finally
|
setup_finally
|
||||||
|
|
||||||
store ::= STORE_FAST
|
store ::= STORE_FAST
|
||||||
store ::= STORE_NAME
|
store ::= STORE_NAME
|
||||||
|
|
||||||
# Python 2.6 omits ths LOAD_FAST DELETE_FAST below
|
tryelsestmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
|
||||||
|
try_middle else_suite COME_FROM
|
||||||
|
|
||||||
|
# Python 2.6 omits the LOAD_FAST DELETE_FAST below
|
||||||
|
# withas is allowed as a "from future" in 2.5
|
||||||
withasstmt ::= expr setupwithas designator suite_stmts_opt
|
withasstmt ::= expr setupwithas designator suite_stmts_opt
|
||||||
POP_BLOCK LOAD_CONST COME_FROM
|
POP_BLOCK LOAD_CONST COME_FROM
|
||||||
with_cleanup
|
with_cleanup
|
||||||
@@ -46,18 +51,6 @@ class Python25Parser(Python26Parser):
|
|||||||
tokens, first, last)
|
tokens, first, last)
|
||||||
if invalid:
|
if invalid:
|
||||||
return invalid
|
return invalid
|
||||||
lhs = rule[0]
|
|
||||||
if lhs in ('tryelsestmt', ):
|
|
||||||
# The end of the else part of try/else come_from has to come
|
|
||||||
# from an END_FINALLY statement
|
|
||||||
if tokens[last-1].type.startswith('COME_FROM'):
|
|
||||||
end_finally_offset = int(tokens[last-1].pattr)
|
|
||||||
current = first
|
|
||||||
while current < last:
|
|
||||||
offset = tokens[current].offset
|
|
||||||
if offset == end_finally_offset:
|
|
||||||
return tokens[current].type != 'END_FINALLY'
|
|
||||||
current += 1
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user