You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 01:09:52 +08:00
Compare commits
212 Commits
release-2.
...
release-py
Author | SHA1 | Date | |
---|---|---|---|
|
6d6a73eea7 | ||
|
1e3ea60055 | ||
|
e4a7641927 | ||
|
b24b46d48c | ||
|
2fbbc728b1 | ||
|
0a6c8ba909 | ||
|
d3904527e6 | ||
|
a65d7dce5b | ||
|
718a0a5d34 | ||
|
b043f6bafc | ||
|
aa207a3c77 | ||
|
747212c62c | ||
|
493e4b14a1 | ||
|
9491c67779 | ||
|
8ef5e5d12b | ||
|
222986640e | ||
|
f9d47abb2b | ||
|
31ed869a6f | ||
|
ea9e3ab3f5 | ||
|
19d2569515 | ||
|
770e988ff8 | ||
|
0fa0641974 | ||
|
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 | ||
|
c13e23cdae | ||
|
114fe11e66 | ||
|
b131c20e99 | ||
|
5db1178b3e | ||
|
fab4ebb768 | ||
|
89429339fa | ||
|
7ece296f76 | ||
|
5035d5433b | ||
|
78a5b620a7 | ||
|
e851c0d46a | ||
|
a760188724 | ||
|
ad345ef94a | ||
|
d050dd3adb | ||
|
9392103998 | ||
|
707770049f | ||
|
ec0669367f | ||
|
3f40c16587 | ||
|
66518baed0 | ||
|
21023fea74 | ||
|
66741d16ba | ||
|
e02ebef45d | ||
|
99fce6dfd7 | ||
|
7b8c5e091c | ||
|
77caf515ea | ||
|
e4c0d56947 | ||
|
4827b1e994 | ||
|
2b46e71264 | ||
|
6ed129bd7a | ||
|
c4fde6b53e | ||
|
a7d93e88b4 | ||
|
84c2932bc5 | ||
|
874b3c9d31 | ||
|
f6a997befc | ||
|
9891494142 | ||
|
f8544dfbbe | ||
|
136f42a610 | ||
|
c43e734f37 | ||
|
b00651d428 | ||
|
2327f0fdfa | ||
|
0afcd31bd5 | ||
|
6f097ff1ca | ||
|
8eb1a16f5b | ||
|
ed9fb64e72 | ||
|
d002c667ae | ||
|
da8dccbaca | ||
|
e56743cc14 | ||
|
39814fab8b | ||
|
970774ab95 | ||
|
723fa5dfed | ||
|
4d4e59c40b | ||
|
a92e6c9688 | ||
|
6c546fe6e1 | ||
|
37272ae827 | ||
|
9b1dd0f26c | ||
|
0ff0c97a95 | ||
|
3e988be075 | ||
|
eb64a03dfa | ||
|
9aa4e2b9ae | ||
|
c147514e9e | ||
|
7f2bee46b7 | ||
|
813229ac45 | ||
|
f1a947f106 | ||
|
2f51067a9d | ||
|
c8a4dcf72b | ||
|
012ff91cfb | ||
|
e3f4beeb74 | ||
|
7d58dcf6dd | ||
|
bfff1b4e9f | ||
|
e6761e13bb | ||
|
e690ddd50a | ||
|
c7c0a98982 | ||
|
eebec48308 | ||
|
45b7c1948c | ||
|
e2fb7ca3d2 | ||
|
da50394841 | ||
|
b3bda76582 | ||
|
13d5cd1a58 | ||
|
08dcc7d820 | ||
|
7755563b65 | ||
|
b43cbc050d | ||
|
db7a26d47d | ||
|
92166452c1 | ||
|
96fa3ef381 | ||
|
755415c7d8 | ||
|
b168e1de55 | ||
|
38eed14b41 | ||
|
2c993f8c32 | ||
|
65858a4c74 | ||
|
263c63e009 | ||
|
813bce4697 | ||
|
a5d2237435 | ||
|
ab6d322eca | ||
|
1a8a0df107 | ||
|
d22931cb49 | ||
|
9cc2700160 | ||
|
a5a0f45dde | ||
|
3c02fa7e36 | ||
|
0d0f836f76 | ||
|
69c93cc665 | ||
|
97576e473d | ||
|
1e324e0e8d | ||
|
7ab4e1fbdb | ||
|
abecb21671 | ||
|
8be6369bdf | ||
|
0a37709b0a | ||
|
98cd1417df | ||
|
8941417a54 | ||
|
460069ceaa | ||
|
316aa44f23 | ||
|
cbcfd53dae | ||
|
df2ca51f4a | ||
|
4f4069c6b5 | ||
|
7133540c23 | ||
|
590231741d | ||
|
a9349b8f3d | ||
|
6aa1531972 | ||
|
4fcb385dc0 | ||
|
260ddedbfd | ||
|
f8917aaf88 | ||
|
c8550d5c9e | ||
|
1aeb09cb8b | ||
|
f575234fc8 | ||
|
abcd10628a | ||
|
eb2b63ce9c | ||
|
805e17988e | ||
|
80df5dcc95 | ||
|
2bc316d6f0 | ||
|
195bbc746b | ||
|
0f56b4f476 | ||
|
94719918d4 | ||
|
f2a3721d7d | ||
|
79863ae122 | ||
|
d7f898b4fb | ||
|
fe36c9e9f6 | ||
|
76ae1592d0 | ||
|
31d387749b | ||
|
9e3026bd78 | ||
|
bfe7e7777d | ||
|
81b4941fda | ||
|
0f719d41fd | ||
|
766451cbb9 | ||
|
1e4dc52197 | ||
|
6073c77921 | ||
|
b6e53205dd | ||
|
ee6dddd25a | ||
|
968a54512b | ||
|
a81ffe8963 | ||
|
3b9e48a3b6 | ||
|
80a4ad4f1b | ||
|
50c2e1bda9 | ||
|
f4999f6300 | ||
|
0f536b18fa | ||
|
6fb879d0d8 | ||
|
411eaaeafb | ||
|
36874c72e2 | ||
|
7343575e55 | ||
|
fef0567746 | ||
|
41f360e3dc | ||
|
5d10f7a0b0 | ||
|
2a5eda631a | ||
|
a685c60606 | ||
|
d2ac293cf6 | ||
|
cd3cf5ec29 | ||
|
b54a19c6ff |
@@ -3,11 +3,7 @@ language: python
|
||||
sudo: false
|
||||
|
||||
python:
|
||||
- '3.5'
|
||||
- '2.7.12'
|
||||
- '2.6'
|
||||
- '3.3'
|
||||
- '3.4'
|
||||
- '2.7' # this is a cheat here because travis doesn't do 2.4-2.6
|
||||
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
|
@@ -30,7 +30,7 @@ another clever idea: using table-driven semantics routines, using
|
||||
format specifiers.
|
||||
|
||||
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.
|
||||
|
||||
In the fall of 2000, Hartmut Goebel
|
||||
@@ -135,9 +135,9 @@ Hartmut a decade an a half ago:
|
||||
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
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
Another approach that doesn't use grammars is to do something like
|
||||
|
@@ -2,10 +2,16 @@ include README.rst
|
||||
include ChangeLog
|
||||
include HISTORY.md
|
||||
include LICENSE
|
||||
include Makefile
|
||||
include requirements.txt
|
||||
include requirements-dev.txt
|
||||
include DECOMPYLE-2.4-CHANGELOG.txt
|
||||
include __pkginfo__.py
|
||||
recursive-include uncompyle6 *.py
|
||||
include bin/uncompyle6
|
||||
include bin/pydisassemble
|
||||
include pytest/Makefile
|
||||
include test/Makefile
|
||||
recursive-include test *.py *.pyc
|
||||
recursive-include pytest *.py
|
||||
recursive-include pytest/testdata *
|
||||
|
6
Makefile
6
Makefile
@@ -33,11 +33,11 @@ check-2.7 check-3.3 check-3.4: pytest
|
||||
|
||||
#: Tests for Python 3.2 and 3.5 - pytest doesn't work here
|
||||
# Or rather 3.5 doesn't work not on Travis
|
||||
check-3.1 check-3.2 check-3.5 check-3.6:
|
||||
check-3.0 check-3.1 check-3.2 check-3.5 check-3.6:
|
||||
$(MAKE) -C test $@
|
||||
|
||||
#:Tests for Python 2.6 (doesn't have pytest)
|
||||
check-2.6:
|
||||
check-2.4 check-2.5 check-2.6:
|
||||
$(MAKE) -C test $@
|
||||
|
||||
#:PyPy 2.6.1 or PyPy 5.0.1
|
||||
@@ -59,7 +59,7 @@ clean: clean_pyc
|
||||
|
||||
#: Create source (tarball) and wheel distribution
|
||||
dist:
|
||||
$(PYTHON) ./setup.py sdist bdist_wheel
|
||||
$(PYTHON) ./setup.py sdist bdist_egg
|
||||
|
||||
#: Remove .pyc files
|
||||
clean_pyc:
|
||||
|
74
NEWS
74
NEWS
@@ -1,3 +1,77 @@
|
||||
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
|
||||
|
||||
- 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
|
||||
- pseudo instruction THEN in 2.x
|
||||
to disambiguate if from and
|
||||
- fix bug in --verify option
|
||||
- DRY (a little) control-flow detection
|
||||
- fix syntax in tuples with one element
|
||||
- if AST rule inheritence in Python 2.5
|
||||
- NAME_MODULE removal for Python <= 2.4
|
||||
- verifycall fixes for Python <= 2.4
|
||||
- more Python lint
|
||||
|
||||
uncompyle6 2.9.7 2016-12-16
|
||||
|
||||
- Start to handle 3.5/3.6 build_map_unpack_with_call
|
||||
- Some Python 3.6 bytecode to wordcode conversion fixes
|
||||
- option -g: show start-end range when possible
|
||||
- track print_docstring move to help (used in python 3.1)
|
||||
- verify: allow RETURN_VALUE to match RETURN_END_IF
|
||||
- some 3.2 compatibility
|
||||
- Better Python 3 control flow detection by adding Pseudo ELSE opcodes
|
||||
|
||||
uncompyle6 2.9.6 2016-12-04
|
||||
|
||||
- Shorten Python3 grammars with + and *
|
||||
this requires spark parser 1.5.1
|
||||
- Add some AST reduction checks to improve
|
||||
decompile accuracy. This too requires
|
||||
spark parser 1.5.1
|
||||
|
||||
uncompyle6 2.9.6 2016-11-20
|
||||
|
||||
- Correct MANIFEST.in
|
||||
- More AST grammar checking
|
||||
- --linemapping option or linenumbers.line_number_mapping()
|
||||
Shows correspondence of lines between source
|
||||
and decompiled source
|
||||
- Some control flow adjustments in code for 2.x.
|
||||
This is probably an improvement in 2.6 and before.
|
||||
For 2.7 things are just shuffled around a little. Sigh.
|
||||
Overall I think we are getting more precise in
|
||||
or analysis even if it is not always reflected
|
||||
in the results.
|
||||
- better control flow debugging output
|
||||
- Python 2 and 3 detect structure code is more similar
|
||||
- Handle Docstrings with embedded tiple quotes (""")
|
||||
|
||||
uncompyle6 2.9.5 2016-11-13
|
||||
|
||||
- Fix Python 3 bugs:
|
||||
* improprer while 1 else
|
||||
* docstring indent
|
||||
* 3.3 default values in lambda expressions
|
||||
* start 3.0 decompilation (needs newer xdis)
|
||||
- Start grammar misparse checking
|
||||
|
||||
|
||||
uncompyle6 2.9.4 2016-11-02
|
||||
|
||||
- Handle Python 3.x function annotations
|
||||
|
74
README.rst
74
README.rst
@@ -1,4 +1,4 @@
|
||||
|buildstatus|
|
||||
|buildstatus| |Supported Python Versions|
|
||||
|
||||
uncompyle6
|
||||
==========
|
||||
@@ -44,8 +44,9 @@ Requirements
|
||||
------------
|
||||
|
||||
This project requires Python 2.6 or later, PyPy 3-2.4, or PyPy-5.0.1.
|
||||
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
|
||||
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.2-3.6 and the above-mentioned PyPy versions.
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -92,30 +93,58 @@ For usage help:
|
||||
|
||||
$ uncompyle6 -h
|
||||
|
||||
If you want strong verification of the correctness of the
|
||||
decompilation process, add the `--verify` option. But there are
|
||||
situations where this will indicate a failure, although the generated
|
||||
program is semantically equivalent. Using option `--weak-verify` will
|
||||
tell you if there is something definitely wrong. Generally, large
|
||||
swaths of code are decompiled correctly, if not the entire program.
|
||||
|
||||
You can also cross compare the results with pycdc_ . Since they work
|
||||
differently, bugs here often aren't in that, and vice versa.
|
||||
|
||||
|
||||
Known Bugs/Restrictions
|
||||
-----------------------
|
||||
|
||||
About 90% of the decompilation verifies from Python 2.3.7 to Python
|
||||
3.4.2 on the standard library packages I have on my system.
|
||||
The biggest known and possibly fixable (but hard) problem has to do
|
||||
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
|
||||
decompilation and report that.
|
||||
|
||||
(Verification is the process of decompiling bytecode, compiling with a
|
||||
Python for that byecode version, and then comparing the byetcode
|
||||
About 90% 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
|
||||
"weakly" verify. Python 2.6 drops down to 96% weakly verifying.
|
||||
Other versions drop off in quality too.
|
||||
|
||||
*Verification* is the process of decompiling bytecode, compiling with
|
||||
a Python for that bytecode version, and then comparing the bytecode
|
||||
produced by the decompiled/compiled program. Some allowance is made
|
||||
for inessential differences, but other semantically equivalent
|
||||
differences are not caught.)
|
||||
for inessential differences. But other semantically equivalent
|
||||
differences are not caught. For example ``1 and 0`` is decompiled to
|
||||
the equivalent ``0``; remnants of the first true evaluation (1) is
|
||||
lost when Python compiles this. When Python next compiles ``0`` the
|
||||
resulting code is simpler.
|
||||
|
||||
Later distributions average about 200 files. At this point, 2.7
|
||||
decompilation is definitely better than uncompyle2. A number of bugs
|
||||
have been fixed. We now handle more Python bytecodes than the old
|
||||
decompyle program that handled Python bytecodes ranging from 1.5 to
|
||||
2.4. There is some work do do on the lower end which is more
|
||||
difficult for us since we don't have a Python interpreter for versions
|
||||
1.5, 1.6 or 2.0.
|
||||
*Weak Verification*
|
||||
on the other hand doesn't check bytecode for equivalence but does
|
||||
check to see if the resulting decompiled source is a valid Python
|
||||
program by running the Python interpreter. Because the Python language
|
||||
has changed so much, for best results you should use the same Python
|
||||
Version in checking as used in the bytecode.
|
||||
|
||||
Python 3.5 largely works, but still has some bugs in it.
|
||||
Python 3.6 changes things drastically by using word codes rather than
|
||||
byte codes, and that needs to be addressed.
|
||||
Later distributions average about 200 files. There is some work to do
|
||||
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,
|
||||
and 2.0.
|
||||
|
||||
In the Python 3 series, Python support is is strongest around 3.4 or
|
||||
3.3 and drops off as you move further away from those versions. Python
|
||||
3.5 largely works, but still has some bugs in it and is missing some
|
||||
opcodes. Python 3.6 changes things drastically by using word codes
|
||||
rather than byte 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
|
||||
some versions of Python, notably Python 3.6, the magic number has
|
||||
@@ -125,6 +154,11 @@ which use their own magic and encrypt bytcode. With the exception of
|
||||
the Dropbox's old Python 2.5 interpreter this kind of thing is not
|
||||
handled.
|
||||
|
||||
We also don't handle PJOrion_ obfuscated code. For that try: PJOrion
|
||||
Deobfuscator_ to unscramble the bytecode to get valid bytecode before
|
||||
trying this tool.
|
||||
|
||||
|
||||
There is lots to do, so please dig in and help.
|
||||
|
||||
See Also
|
||||
@@ -144,3 +178,7 @@ See Also
|
||||
.. _this: https://github.com/rocky/python-uncompyle6/wiki/Deparsing-technology-and-its-use-in-exact-location-reporting
|
||||
.. |buildstatus| image:: https://travis-ci.org/rocky/python-uncompyle6.svg
|
||||
:target: https://travis-ci.org/rocky/python-uncompyle6
|
||||
.. |Supported Python Versions| image:: https://img.shields.io/pypi/pyversions/uncompyle6.svg
|
||||
:target: https://pypi.python.org/pypi/uncompyle6/
|
||||
.. _PJOrion: http://www.koreanrandom.com/forum/topic/15280-pjorion-%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%B4%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F-%D0%BE%D0%B1%D1%84
|
||||
.. _Deobfuscator: https://github.com/extremecoders-re/PjOrion-Deobfuscator
|
||||
|
@@ -9,17 +9,19 @@
|
||||
|
||||
# Things that change more often go here.
|
||||
copyright = """
|
||||
Copyright (C) 2015, 2016 Rocky Bernstein <rb@dustyfeet.com>.
|
||||
Copyright (C) 2015-2017 Rocky Bernstein <rb@dustyfeet.com>.
|
||||
"""
|
||||
|
||||
classifiers = ['Development Status :: 4 - Beta',
|
||||
classifiers = ['Development Status :: 5 - Production/Stable',
|
||||
'Intended Audience :: Developers',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.4',
|
||||
'Programming Language :: Python :: 2.5',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.1',
|
||||
'Programming Language :: Python :: 3.2',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
@@ -37,8 +39,8 @@ entry_points={
|
||||
'pydisassemble=uncompyle6.bin.pydisassemble:main',
|
||||
]}
|
||||
ftp_url = None
|
||||
install_requires = ['spark-parser >= 1.4.0, < 1.5.0',
|
||||
'xdis >= 3.2.2, < 3.3.0']
|
||||
install_requires = ['spark-parser >= 1.6.0, < 1.7.0',
|
||||
'xdis >= 3.2.4, < 3.3.0']
|
||||
license = 'MIT'
|
||||
mailing_list = 'python-debugger@googlegroups.com'
|
||||
modname = 'uncompyle6'
|
||||
|
@@ -1,6 +1,6 @@
|
||||
machine:
|
||||
python:
|
||||
version: 2.7.8
|
||||
version: 2.7.10
|
||||
environment:
|
||||
COMPILE: --compile
|
||||
|
||||
@@ -10,4 +10,4 @@ dependencies:
|
||||
- pip install -r requirements-dev.txt
|
||||
test:
|
||||
override:
|
||||
- python ./setup.py develop && make check-2.7
|
||||
- python ./setup.py develop && make check-2.6
|
||||
|
1
pytest/.gitignore
vendored
1
pytest/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/.hypothesis
|
||||
/__pycache__
|
||||
|
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)
|
78
pytest/test_docstring.py
Normal file
78
pytest/test_docstring.py
Normal file
@@ -0,0 +1,78 @@
|
||||
import sys
|
||||
from uncompyle6 import PYTHON3
|
||||
if PYTHON3:
|
||||
from io import StringIO
|
||||
minint = -sys.maxsize-1
|
||||
maxint = sys.maxsize
|
||||
else:
|
||||
from StringIO import StringIO
|
||||
minint = -sys.maxint-1
|
||||
maxint = sys.maxint
|
||||
from uncompyle6.semantics.helper import print_docstring
|
||||
|
||||
class PrintFake():
|
||||
def __init__(self):
|
||||
self.pending_newlines = 0
|
||||
self.f = StringIO()
|
||||
|
||||
def write(self, *data):
|
||||
if (len(data) == 0) or (len(data) == 1 and data[0] == ''):
|
||||
return
|
||||
out = ''.join((str(j) for j in data))
|
||||
n = 0
|
||||
for i in out:
|
||||
if i == '\n':
|
||||
n += 1
|
||||
if n == len(out):
|
||||
self.pending_newlines = max(self.pending_newlines, n)
|
||||
return
|
||||
elif n:
|
||||
self.pending_newlines = max(self.pending_newlines, n)
|
||||
out = out[n:]
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
if self.pending_newlines > 0:
|
||||
self.f.write('\n'*self.pending_newlines)
|
||||
self.pending_newlines = 0
|
||||
|
||||
for i in out[::-1]:
|
||||
if i == '\n':
|
||||
self.pending_newlines += 1
|
||||
else:
|
||||
break
|
||||
|
||||
if self.pending_newlines:
|
||||
out = out[:-self.pending_newlines]
|
||||
self.f.write(out)
|
||||
def println(self, *data):
|
||||
if data and not(len(data) == 1 and data[0] ==''):
|
||||
self.write(*data)
|
||||
self.pending_newlines = max(self.pending_newlines, 1)
|
||||
return
|
||||
pass
|
||||
|
||||
def test_docstring():
|
||||
|
||||
for doc, expect in (
|
||||
("Now is the time",
|
||||
' """Now is the time"""'),
|
||||
("""
|
||||
Now is the time
|
||||
""",
|
||||
''' """
|
||||
Now is the time
|
||||
"""''')
|
||||
|
||||
# (r'''func placeholder - ' and with ("""\nstring\n """)''',
|
||||
# """ r'''func placeholder - ' and with (\"\"\"\nstring\n\"\"\")'''"""),
|
||||
# (r"""func placeholder - ' and with ('''\nstring\n''') and \"\"\"\nstring\n\"\"\" """,
|
||||
# """ r\"\"\"func placeholder - ' and with ('''\nstring\n''') and \"\"\"\nstring\n\"\"\" \"\"\"""")
|
||||
):
|
||||
|
||||
o = PrintFake()
|
||||
# print(doc)
|
||||
# print(expect)
|
||||
print_docstring(o, ' ', doc)
|
||||
assert expect == o.f.getvalue()
|
@@ -8,6 +8,18 @@ def bug(state, slotstate):
|
||||
for key, value in slotstate.items():
|
||||
setattr(state, key, 2)
|
||||
|
||||
# From 2.7 disassemble
|
||||
# Problem is not getting while, because
|
||||
# COME_FROM not added
|
||||
def bug_loop(disassemble, tb=None):
|
||||
if tb:
|
||||
try:
|
||||
tb = 5
|
||||
except AttributeError:
|
||||
raise RuntimeError
|
||||
while tb: tb = tb.tb_next
|
||||
disassemble(tb)
|
||||
|
||||
def test_if_in_for():
|
||||
code = bug.__code__
|
||||
scan = get_scanner(PYTHON_VERSION)
|
||||
@@ -16,18 +28,35 @@ def test_if_in_for():
|
||||
n = scan.setup_code(code)
|
||||
scan.build_lines_data(code, n)
|
||||
scan.build_prev_op(n)
|
||||
fjt = scan.find_jump_targets()
|
||||
fjt = scan.find_jump_targets(False)
|
||||
assert {15: [3], 69: [66], 63: [18]} == fjt
|
||||
assert scan.structs == \
|
||||
[{'start': 0, 'end': 72, 'type': 'root'},
|
||||
{'start': 18, 'end': 66, 'type': 'if-then'},
|
||||
{'start': 15, 'end': 66, 'type': 'if-then'},
|
||||
{'start': 31, 'end': 59, 'type': 'for-loop'},
|
||||
{'start': 62, 'end': 63, 'type': 'for-else'}]
|
||||
|
||||
code = bug_loop.__code__
|
||||
n = scan.setup_code(code)
|
||||
scan.build_lines_data(code, n)
|
||||
scan.build_prev_op(n)
|
||||
fjt = scan.find_jump_targets(False)
|
||||
assert{64: [42], 67: [42, 42], 42: [16, 41], 19: [6]} == fjt
|
||||
assert scan.structs == [
|
||||
{'start': 0, 'end': 80, 'type': 'root'},
|
||||
{'start': 3, 'end': 64, 'type': 'if-then'},
|
||||
{'start': 6, 'end': 15, 'type': 'try'},
|
||||
{'start': 19, 'end': 38, 'type': 'except'},
|
||||
{'start': 45, 'end': 67, 'type': 'while-loop'},
|
||||
{'start': 70, 'end': 64, 'type': 'while-else'},
|
||||
# previous bug was not mistaking while-loop for if-then
|
||||
{'start': 48, 'end': 67, 'type': 'while-loop'}]
|
||||
|
||||
elif 3.2 < PYTHON_VERSION <= 3.4:
|
||||
scan.code = array('B', code.co_code)
|
||||
scan.build_lines_data(code)
|
||||
scan.build_prev_op()
|
||||
fjt = scan.find_jump_targets()
|
||||
fjt = scan.find_jump_targets(False)
|
||||
assert {69: [66], 63: [18]} == fjt
|
||||
assert scan.structs == \
|
||||
[{'end': 72, 'type': 'root', 'start': 0},
|
||||
|
@@ -1,150 +0,0 @@
|
||||
# std
|
||||
import os
|
||||
# test
|
||||
import pytest
|
||||
import hypothesis
|
||||
from hypothesis import strategies as st
|
||||
# uncompyle6
|
||||
from uncompyle6 import PYTHON_VERSION, deparse_code
|
||||
|
||||
|
||||
@st.composite
|
||||
def expressions(draw):
|
||||
# todo : would be nice to generate expressions using hypothesis however
|
||||
# this is pretty involved so for now just use a corpus of expressions
|
||||
# from which to select.
|
||||
return draw(st.sampled_from((
|
||||
'abc',
|
||||
'len(items)',
|
||||
'x + 1',
|
||||
'lineno',
|
||||
'container',
|
||||
'self.attribute',
|
||||
'self.method()',
|
||||
# These expressions are failing, I think these are control
|
||||
# flow problems rather than problems with FORMAT_VALUE,
|
||||
# however I need to confirm this...
|
||||
#'sorted(items, key=lambda x: x.name)',
|
||||
#'func(*args, **kwargs)',
|
||||
#'text or default',
|
||||
#'43 if life_the_universe and everything else None'
|
||||
)))
|
||||
|
||||
|
||||
@st.composite
|
||||
def format_specifiers(draw):
|
||||
"""
|
||||
Generate a valid format specifier using the rules:
|
||||
|
||||
format_spec ::= [[fill]align][sign][#][0][width][,][.precision][type]
|
||||
fill ::= <any character>
|
||||
align ::= "<" | ">" | "=" | "^"
|
||||
sign ::= "+" | "-" | " "
|
||||
width ::= integer
|
||||
precision ::= integer
|
||||
type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
|
||||
|
||||
See https://docs.python.org/2/library/string.html
|
||||
|
||||
:param draw: Let hypothesis draw from other strategies.
|
||||
|
||||
:return: An example format_specifier.
|
||||
"""
|
||||
alphabet_strategy = st.characters(min_codepoint=ord('a'), max_codepoint=ord('z'))
|
||||
fill = draw(st.one_of(alphabet_strategy, st.none()))
|
||||
align = draw(st.sampled_from(list('<>=^')))
|
||||
fill_align = (fill + align or '') if fill else ''
|
||||
|
||||
type_ = draw(st.sampled_from('bcdeEfFgGnosxX%'))
|
||||
can_have_sign = type_ in 'deEfFgGnoxX%'
|
||||
can_have_comma = type_ in 'deEfFgG%'
|
||||
can_have_precision = type_ in 'fFgG'
|
||||
can_have_pound = type_ in 'boxX%'
|
||||
can_have_zero = type_ in 'oxX'
|
||||
|
||||
sign = draw(st.sampled_from(list('+- ') + [''])) if can_have_sign else ''
|
||||
pound = draw(st.sampled_from(('#', '',))) if can_have_pound else ''
|
||||
zero = draw(st.sampled_from(('0', '',))) if can_have_zero else ''
|
||||
|
||||
int_strategy = st.integers(min_value=1, max_value=1000)
|
||||
|
||||
width = draw(st.one_of(int_strategy, st.none()))
|
||||
width = str(width) if width is not None else ''
|
||||
|
||||
comma = draw(st.sampled_from((',', '',))) if can_have_comma else ''
|
||||
if can_have_precision:
|
||||
precision = draw(st.one_of(int_strategy, st.none()))
|
||||
precision = '.' + str(precision) if precision else ''
|
||||
else:
|
||||
precision = ''
|
||||
|
||||
return ''.join((fill_align, sign, pound, zero, width, comma, precision, type_,))
|
||||
|
||||
|
||||
@st.composite
|
||||
def fstrings(draw):
|
||||
"""
|
||||
Generate a valid f-string.
|
||||
See https://www.python.org/dev/peps/pep-0498/#specification
|
||||
|
||||
:param draw: Let hypothsis draw from other strategies.
|
||||
|
||||
:return: A valid f-string.
|
||||
"""
|
||||
character_strategy = st.characters(
|
||||
blacklist_characters='\r\n\'\\s{}',
|
||||
min_codepoint=1,
|
||||
max_codepoint=1000,
|
||||
)
|
||||
is_raw = draw(st.booleans())
|
||||
integer_strategy = st.integers(min_value=0, max_value=3)
|
||||
expression_count = draw(integer_strategy)
|
||||
content = []
|
||||
for _ in range(expression_count):
|
||||
expression = draw(expressions())
|
||||
conversion = draw(st.sampled_from(('', '!s', '!r', '!a',)))
|
||||
has_specifier = draw(st.booleans())
|
||||
specifier = ':' + draw(format_specifiers()) if has_specifier else ''
|
||||
content.append('{{{}{}}}'.format(expression, conversion, specifier))
|
||||
content.append(draw(st.text(character_strategy)))
|
||||
content = ''.join(content)
|
||||
return "f{}'{}'".format('r' if is_raw else '', content)
|
||||
|
||||
|
||||
@pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6')
|
||||
@hypothesis.given(format_specifiers())
|
||||
def test_format_specifiers(format_specifier):
|
||||
"""Verify that format_specifiers generates valid specifiers"""
|
||||
try:
|
||||
exec('"{:' + format_specifier + '}".format(0)')
|
||||
except ValueError as e:
|
||||
if 'Unknown format code' not in str(e):
|
||||
raise
|
||||
|
||||
|
||||
def run_test(text):
|
||||
hypothesis.assume(len(text))
|
||||
hypothesis.assume("f'{" in text)
|
||||
expr = text + '\n'
|
||||
code = compile(expr, '<string>', 'single')
|
||||
deparsed = deparse_code(PYTHON_VERSION, code, compile_mode='single')
|
||||
recompiled = compile(deparsed.text, '<string>', 'single')
|
||||
if recompiled != code:
|
||||
assert 'dis(' + deparsed.text.strip('\n') + ')' == 'dis(' + expr.strip('\n') + ')'
|
||||
|
||||
|
||||
@pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6')
|
||||
@hypothesis.given(fstrings())
|
||||
def test_uncompyle_fstring(fstring):
|
||||
"""Verify uncompyling fstring bytecode"""
|
||||
run_test(fstring)
|
||||
|
||||
|
||||
@pytest.mark.skipif(PYTHON_VERSION < 3.6, reason='need at least python 3.6')
|
||||
@pytest.mark.parametrize('fstring', [
|
||||
"f'{abc}{abc!s}'",
|
||||
"f'{abc}0'",
|
||||
])
|
||||
def test_uncompyle_direct(fstring):
|
||||
"""useful for debugging"""
|
||||
run_test(fstring)
|
@@ -1,6 +1,6 @@
|
||||
import re
|
||||
from uncompyle6 import PYTHON_VERSION, PYTHON3, IS_PYPY # , PYTHON_VERSION
|
||||
from uncompyle6.parser import get_python_parser
|
||||
from uncompyle6.parser import get_python_parser, python_parser
|
||||
from uncompyle6.scanner import get_scanner
|
||||
|
||||
def test_grammar():
|
||||
@@ -41,7 +41,7 @@ def test_grammar():
|
||||
"""
|
||||
JUMP_BACK CONTINUE RETURN_END_IF
|
||||
COME_FROM COME_FROM_EXCEPT COME_FROM_LOOP COME_FROM_WITH
|
||||
COME_FROM_FINALLY
|
||||
COME_FROM_FINALLY ELSE
|
||||
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP
|
||||
LAMBDA_MARKER RETURN_LAST
|
||||
""".split())
|
||||
@@ -53,3 +53,11 @@ def test_grammar():
|
||||
ignore_set.add('STORE_LOCALS')
|
||||
opcode_set = set(s.opc.opname).union(ignore_set)
|
||||
check_tokens(tokens, opcode_set)
|
||||
|
||||
def test_dup_rule():
|
||||
import inspect
|
||||
python_parser(PYTHON_VERSION, inspect.currentframe().f_code,
|
||||
is_pypy=IS_PYPY,
|
||||
parser_debug={
|
||||
'dups': True, 'transition': False, 'reduce': False,
|
||||
'rules': False, 'errorstack': None, 'context': True})
|
||||
|
143
pytest/validate.py
Normal file
143
pytest/validate.py
Normal file
@@ -0,0 +1,143 @@
|
||||
# future
|
||||
from __future__ import print_function
|
||||
# std
|
||||
import os
|
||||
import dis
|
||||
import difflib
|
||||
import subprocess
|
||||
import tempfile
|
||||
# compatability
|
||||
import six
|
||||
# uncompyle6 / xdis
|
||||
from uncompyle6 import PYTHON_VERSION, deparse_code
|
||||
|
||||
|
||||
def _dis_to_text(co):
|
||||
return dis.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.
|
||||
"""
|
||||
# TODO : Use xdis for python2 compatability
|
||||
instructions1 = dis.Bytecode(co1)
|
||||
instructions2 = dis.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
|
||||
flake8
|
||||
hypothesis
|
||||
hypothesis
|
||||
six
|
2
setup.py
2
setup.py
@@ -24,6 +24,6 @@ setup(
|
||||
py_modules = py_modules,
|
||||
test_suite = 'nose.collector',
|
||||
url = web,
|
||||
setup_requires = ['nose>=1.0'],
|
||||
tests_require = ['nose>=1.0'],
|
||||
version = VERSION,
|
||||
zip_safe = zip_safe)
|
||||
|
@@ -20,7 +20,11 @@ check:
|
||||
$(MAKE) check-$$PYTHON_VERSION
|
||||
|
||||
#: 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.4 check-2.5 check-2.6 check-2.7: check-bytecode-2 check-bytecode-3 check-bytecode-1 check-2.7-ok
|
||||
|
||||
#: Run working tests from Python 3.0
|
||||
check-3.0: check-bytecode
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.0 --weak-verify $(COMPILE)
|
||||
|
||||
#: Run working tests from Python 3.1
|
||||
check-3.1: check-bytecode
|
||||
@@ -32,11 +36,11 @@ check-3.2: check-bytecode
|
||||
|
||||
#: Run working tests from Python 3.3
|
||||
check-3.3: check-bytecode
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.3 --weak-verify $(COMPILE)
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.3 --verify $(COMPILE)
|
||||
|
||||
#: Run working tests from Python 3.4
|
||||
check-3.4: check-bytecode check-3.4-ok check-2.7-ok
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.4 --weak-verify $(COMPILE)
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.4 --verify $(COMPILE)
|
||||
|
||||
#: Run working tests from Python 3.5
|
||||
check-3.5: check-bytecode
|
||||
@@ -62,7 +66,8 @@ check-bytecode-2:
|
||||
|
||||
#: Check deparsing bytecode 3.x only
|
||||
check-bytecode-3:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.1 --bytecode-3.2 --bytecode-3.3 \
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.0 \
|
||||
--bytecode-3.1 --bytecode-3.2 --bytecode-3.3 \
|
||||
--bytecode-3.4 --bytecode-3.5 --bytecode-pypy3.2
|
||||
|
||||
#: Check deparsing bytecode that works running Python 2 and Python 3
|
||||
@@ -93,13 +98,9 @@ check-bytecode-2.4:
|
||||
check-bytecode-2.5:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.5
|
||||
|
||||
#: Check deparsing Python 2.6
|
||||
check-bytecode-2.6:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.6
|
||||
|
||||
#: Check deparsing Python 2.7
|
||||
check-bytecode-2.7:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.7
|
||||
#: Check deparsing Python 3.0
|
||||
check-bytecode-3.0:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.0
|
||||
|
||||
#: Check deparsing Python 3.1
|
||||
check-bytecode-3.1:
|
||||
@@ -125,6 +126,26 @@ check-bytecode-3.5:
|
||||
check-bytecode-3.6:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.6
|
||||
|
||||
#: Get grammar coverage for Python 2.4
|
||||
grammar-coverage-2.4:
|
||||
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-24.cover $(PYTHON) test_pythonlib.py --bytecode-2.4
|
||||
SPARK_PARSER_COVERAGE=/tmp/spark-grammar-24.cover $(PYTHON) test_pyenvlib.py --2.4.6
|
||||
|
||||
#: 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
|
||||
|
||||
#: short tests for bytecodes only for this version of Python
|
||||
check-native-short:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-$(PYTHON_VERSION) --verify $(COMPILE)
|
||||
|
BIN
test/bytecode_2.4/01_ops.pyc
Normal file
BIN
test/bytecode_2.4/01_ops.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_2.5/01_ops.pyc
Normal file
BIN
test/bytecode_2.5/01_ops.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_2.5/02_try_else.pyc
Normal file
BIN
test/bytecode_2.5/02_try_else.pyc
Normal file
Binary file not shown.
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_elif_vs_continue.pyc
Normal file
BIN
test/bytecode_2.6/03_elif_vs_continue.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.6/03_if_vs_and.pyc
Normal file
BIN
test/bytecode_2.6/03_if_vs_and.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_2.6/04_if_and_bug.pyc
Normal file
BIN
test/bytecode_2.6/04_if_and_bug.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_2.7/00_docstring.pyc
Normal file
BIN
test/bytecode_2.7/00_docstring.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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.0/00_assign.pyc
Normal file
BIN
test/bytecode_3.0/00_assign.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.0/00_import.pyc
Normal file
BIN
test/bytecode_3.0/00_import.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.0/00_pass.pyc
Normal file
BIN
test/bytecode_3.0/00_pass.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.0/02_while1_if_while1.pyc
Normal file
BIN
test/bytecode_3.0/02_while1_if_while1.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.0/04_multi_assign.pyc
Normal file
BIN
test/bytecode_3.0/04_multi_assign.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.0/04_raise.pyc
Normal file
BIN
test/bytecode_3.0/04_raise.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.0/04_withas.pyc
Normal file
BIN
test/bytecode_3.0/04_withas.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.0/10_for.pyc
Normal file
BIN
test/bytecode_3.0/10_for.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.3/02_pos_args.pyc
Normal file
BIN
test/bytecode_3.3/02_pos_args.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.3/03_func_params.pyc
Normal file
BIN
test/bytecode_3.3/03_func_params.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.3/03_while_else.pyc
Normal file
BIN
test/bytecode_3.3/03_while_else.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.3/04_raise.pyc
Normal file
BIN
test/bytecode_3.3/04_raise.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.3/04_withas.pyc
Normal file
BIN
test/bytecode_3.3/04_withas.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.4/05_while_true_break.pyc
Normal file
BIN
test/bytecode_3.4/05_while_true_break.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5/02_build_map_unpack_with_call.pyc
Normal file
BIN
test/bytecode_3.5/02_build_map_unpack_with_call.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/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.
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_matrix_multiply.pyc
Normal file
BIN
test/bytecode_3.6/01_matrix_multiply.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.6/02_build_map_unpack_with_call.pyc
Normal file
BIN
test/bytecode_3.6/02_build_map_unpack_with_call.pyc
Normal file
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.
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
|
27
test/simple_source/bug25/02_try_else.py
Normal file
27
test/simple_source/bug25/02_try_else.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Python 2.5 bug
|
||||
# Was turning into tryelse when there in fact is no else.
|
||||
def options(self, section):
|
||||
try:
|
||||
opts = self._sections[section].copy()
|
||||
except KeyError:
|
||||
raise NoSectionError(section)
|
||||
opts.update(self._defaults)
|
||||
if '__name__' in opts:
|
||||
del opts['__name__']
|
||||
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
|
18
test/simple_source/bug26/03_elif_vs_continue.py
Normal file
18
test/simple_source/bug26/03_elif_vs_continue.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Bug was using continue fouling up 1st elif, by confusing
|
||||
# the "pass" for "continue" by not recognizing the if jump
|
||||
# around it. We fixed by ignoring what's done in Python 2.7
|
||||
# Better is better detection of control structures
|
||||
|
||||
def _compile_charset(charset, flags, code, fixup=None):
|
||||
# compile charset subprogram
|
||||
emit = code.append
|
||||
if fixup is None:
|
||||
fixup = 1
|
||||
for op, av in charset:
|
||||
if op is flags:
|
||||
pass
|
||||
elif op is code:
|
||||
emit(fixup(av))
|
||||
else:
|
||||
raise RuntimeError
|
||||
emit(5)
|
22
test/simple_source/bug26/03_if_vs_and.py
Normal file
22
test/simple_source/bug26/03_if_vs_and.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# From 2.6 decimal
|
||||
# Bug was not recognizing scope of if and
|
||||
# turning it into xc == 1 and xe *= yc
|
||||
def _power_exact(y, xc, yc, xe):
|
||||
yc, ye = y.int, y.exp
|
||||
while yc % 10 == 0:
|
||||
yc //= 10
|
||||
ye += 1
|
||||
|
||||
if xc == 1:
|
||||
xe *= yc
|
||||
while xe % 10 == 0:
|
||||
xe //= 10
|
||||
ye += 1
|
||||
if ye < 0:
|
||||
return None
|
||||
exponent = xe * 10**ye
|
||||
if y and xe:
|
||||
xc = exponent
|
||||
else:
|
||||
xc = 0
|
||||
return 5
|
9
test/simple_source/bug30/02_while1_if_while1.py
Normal file
9
test/simple_source/bug30/02_while1_if_while1.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# From python 3.4 sre.pyc
|
||||
while 1:
|
||||
if __file__:
|
||||
while 1:
|
||||
if __file__:
|
||||
break
|
||||
raise RuntimeError
|
||||
else:
|
||||
raise RuntimeError
|
@@ -8,3 +8,12 @@ def open(file, mode = "r", buffering = None,
|
||||
encoding = None, errors = None,
|
||||
newline = None, closefd = True) -> "IOBase":
|
||||
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
|
||||
|
3
test/simple_source/bug33/02_pos_args.py
Normal file
3
test/simple_source/bug33/02_pos_args.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# From Python 3.3.6 hmac.py
|
||||
# Problem was getting wrong placement of positional args
|
||||
digest_cons = lambda d=b'': 5
|
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
|
6
test/simple_source/bug33/03_func_params.py
Normal file
6
test/simple_source/bug33/03_func_params.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# Bug from Python 3.3 codecs.py
|
||||
# Bug is in 3.3 handling of this complicated parameter list
|
||||
def __new__(cls, encode, decode, streamreader=None, streamwriter=None,
|
||||
incrementalencoder=None, incrementaldecoder=None, name=None,
|
||||
*, _is_text_encoding=None):
|
||||
return
|
8
test/simple_source/bug33/03_while_else.py
Normal file
8
test/simple_source/bug33/03_while_else.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# Bug from 3.4 threading. Bug is handling while/else
|
||||
def acquire(self):
|
||||
with self._cond:
|
||||
while self:
|
||||
rc = False
|
||||
else:
|
||||
rc = True
|
||||
return rc
|
@@ -0,0 +1 @@
|
||||
f(**a, **b)
|
26
test/simple_source/bug35/03_async_await.py
Normal file
26
test/simple_source/bug35/03_async_await.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# 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)
|
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)
|
7
test/simple_source/bug35/03_while-if-break.py
Normal file
7
test/simple_source/bug35/03_while-if-break.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# 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
|
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
|
@@ -5,3 +5,8 @@ def some_function():
|
||||
def some_other_function():
|
||||
some_variable, = some_function()
|
||||
print(some_variable)
|
||||
|
||||
empty_tup = ()
|
||||
one_item_tup = ("item1", )
|
||||
one_item_tup_without_parentheses = "item",
|
||||
many_items_tup = ("item1", "item2", "item3")
|
||||
|
7
test/simple_source/stmts/00_docstring.py
Normal file
7
test/simple_source/stmts/00_docstring.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# uncompyle2 bug was not escaping """ properly
|
||||
r'''func placeholder - with ("""\nstring\n""")'''
|
||||
def foo():
|
||||
r'''func placeholder - ' and with ("""\nstring\n""")'''
|
||||
|
||||
def bar():
|
||||
r"""func placeholder - ' and with ('''\nstring\n''') and \"\"\"\nstring\n\"\"\" """
|
@@ -8,7 +8,7 @@ Usage-Examples:
|
||||
test_pyenvlib.py --all # decompile all tests (suite + libs)
|
||||
test_pyenvlib.py --all --verify # decomyile all tests and verify results
|
||||
test_pyenvlib.py --test # decompile only the testsuite
|
||||
test_pyenvlib.py --2.7.11 --verify # decompile and verify python lib 2.7.11
|
||||
test_pyenvlib.py --2.7.12 --verify # decompile and verify python lib 2.7.11
|
||||
|
||||
Adding own test-trees:
|
||||
|
||||
@@ -19,8 +19,6 @@ Step 2: Run the test:
|
||||
test_pyenvlib --mylib --verify # decompile verify 'mylib'
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from uncompyle6 import main, PYTHON3
|
||||
import os, time, shutil
|
||||
from fnmatch import fnmatch
|
||||
@@ -30,9 +28,10 @@ from fnmatch import fnmatch
|
||||
TEST_VERSIONS=('2.3.7', '2.4.6', '2.5.6', '2.6.9',
|
||||
'pypy-2.4.0', 'pypy-2.6.1',
|
||||
'pypy-5.0.1', 'pypy-5.3.1',
|
||||
'2.7.10', '2.7.11', '2.7.12',
|
||||
'3.1.5', '3.2.6', '3.3.5', '3.3.6',
|
||||
'3.4.2', '3.5.1')
|
||||
'2.7.10', '2.7.11', '2.7.12', '2.7.13',
|
||||
'3.0.1', '3.1.5', '3.2.6',
|
||||
'3.3.5', '3.3.6',
|
||||
'3.4.2', '3.5.1', '3.6.0')
|
||||
|
||||
target_base = '/tmp/py-dis/'
|
||||
lib_prefix = os.path.join(os.environ['HOME'], '.pyenv/versions')
|
||||
@@ -105,28 +104,40 @@ def do_tests(src_dir, patterns, target_dir, start_with=None, do_verify=False):
|
||||
if __name__ == '__main__':
|
||||
import getopt, sys
|
||||
|
||||
do_verify = False
|
||||
do_coverage = do_verify = False
|
||||
test_dirs = []
|
||||
start_with = None
|
||||
|
||||
test_options_keys = list(test_options.keys())
|
||||
test_options_keys.sort()
|
||||
opts, args = getopt.getopt(sys.argv[1:], '',
|
||||
['start-with=', 'verify', 'weak-verify', 'all', ] \
|
||||
['start-with=', 'verify', 'weak-verify',
|
||||
'coverage', 'all', ] \
|
||||
+ test_options_keys )
|
||||
vers = ''
|
||||
for opt, val in opts:
|
||||
if opt == '--verify':
|
||||
do_verify = True
|
||||
if opt == '--weak-verify':
|
||||
do_verify = 'weak'
|
||||
if opt == '--coverage':
|
||||
do_coverage = True
|
||||
elif opt == '--start-with':
|
||||
start_with = val
|
||||
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':
|
||||
vers = 'all'
|
||||
for val in test_options_keys:
|
||||
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:
|
||||
if os.path.exists(src_dir):
|
||||
target_dir = os.path.join(target_base, target_dir)
|
||||
|
@@ -27,8 +27,6 @@ Step 2: Run the test:
|
||||
test_pythonlib.py --mylib --verify # decompile verify 'mylib'
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import getopt, os, py_compile, sys, shutil, tempfile, time
|
||||
|
||||
from uncompyle6 import PYTHON_VERSION
|
||||
@@ -80,7 +78,7 @@ for vers in (2.7, 3.4, 3.5, 3.6):
|
||||
|
||||
for vers in (1.5,
|
||||
2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7,
|
||||
3.1, 3.2, 3.3,
|
||||
3.0, 3.1, 3.2, 3.3,
|
||||
3.4, 3.5, 3.6, 'pypy3.2', 'pypy2.7'):
|
||||
bytecode = "bytecode_%s" % vers
|
||||
key = "bytecode-%s" % vers
|
||||
@@ -127,8 +125,10 @@ def do_tests(src_dir, obj_patterns, target_dir, opts):
|
||||
if opts['do_compile']:
|
||||
compiled_version = opts['compiled_version']
|
||||
if compiled_version and PYTHON_VERSION != compiled_version:
|
||||
print("Not compiling: desired Python version is %s but we are running %s" %
|
||||
(compiled_version, PYTHON_VERSION), file=sys.stderr)
|
||||
sys.stderr.write("Not compiling: "
|
||||
"desired Python version is %s "
|
||||
"but we are running %s" %
|
||||
(compiled_version, PYTHON_VERSION))
|
||||
else:
|
||||
for root, dirs, basenames in os.walk(src_dir):
|
||||
file_matches(files, root, basenames, PY)
|
||||
@@ -146,8 +146,8 @@ def do_tests(src_dir, obj_patterns, target_dir, opts):
|
||||
file_matches(files, dirname, basenames, obj_patterns)
|
||||
|
||||
if not files:
|
||||
print("Didn't come up with any files to test! Try with --compile?",
|
||||
file=sys.stderr)
|
||||
sys.stderr.write("Didn't come up with any files to test! "
|
||||
"Try with --compile?")
|
||||
exit(1)
|
||||
|
||||
os.chdir(cwd)
|
||||
@@ -161,9 +161,9 @@ def do_tests(src_dir, obj_patterns, target_dir, opts):
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
print(time.ctime())
|
||||
print('Source directory: ', src_dir)
|
||||
print('Output directory: ', target_dir)
|
||||
print time.ctime()
|
||||
print 'Source directory: ', src_dir
|
||||
print 'Output directory: ', target_dir
|
||||
try:
|
||||
_, _, failed_files, failed_verify = \
|
||||
main(src_dir, target_dir, files, [],
|
||||
@@ -190,6 +190,7 @@ if __name__ == '__main__':
|
||||
test_options_keys.sort()
|
||||
opts, args = getopt.getopt(sys.argv[1:], '',
|
||||
['start-with=', 'verify', 'weak-verify', 'all', 'compile',
|
||||
'coverage',
|
||||
'no-rm'] \
|
||||
+ test_options_keys )
|
||||
if not opts: help()
|
||||
@@ -198,7 +199,8 @@ if __name__ == '__main__':
|
||||
'do_compile': False,
|
||||
'do_verify': False,
|
||||
'start_with': None,
|
||||
'rmtree' : True
|
||||
'rmtree' : True,
|
||||
'coverage' : False
|
||||
}
|
||||
|
||||
for opt, val in opts:
|
||||
@@ -217,24 +219,30 @@ if __name__ == '__main__':
|
||||
elif opt == '--all':
|
||||
for val in test_options_keys:
|
||||
test_dirs.append(test_options[val])
|
||||
elif opt == '--coverage':
|
||||
test_opts['coverage'] = True
|
||||
else:
|
||||
help()
|
||||
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
|
||||
for src_dir, pattern, target_dir, compiled_version in test_dirs:
|
||||
if os.path.isdir(src_dir):
|
||||
checked_dirs.append([src_dir, pattern, target_dir])
|
||||
else:
|
||||
print("Can't find directory %s. Skipping" % src_dir,
|
||||
file=sys.stderr)
|
||||
sys.stderr.write("Can't find directory %s. Skipping" % src_dir)
|
||||
continue
|
||||
last_compile_version = compiled_version
|
||||
pass
|
||||
|
||||
if not checked_dirs:
|
||||
print("No directories found to check", file=sys.stderr)
|
||||
sys.stderr.write("No directories found to check\n")
|
||||
sys.exit(1)
|
||||
|
||||
test_opts['compiled_version'] = last_compile_version
|
||||
|
9
tox.ini
9
tox.ini
@@ -6,13 +6,14 @@ filename = *.py
|
||||
ignore = C901,E113,E121,E122,E123,E124,E125,E126,E127,E128,E129,E201,E202,E203,E221,E222,E225,E226,E241,E242,E251,E261,E271,E272,E302,E401,E501,F401,E701,E702
|
||||
|
||||
[tox]
|
||||
envlist = py26, py27, pypy
|
||||
envlist = py27, py34, pypy
|
||||
|
||||
[testenv]
|
||||
deps =
|
||||
requests>=0.8.8
|
||||
mock>=1.0.1
|
||||
commands = python -W always setup.py nosetests {posargs}
|
||||
hypothesis
|
||||
pytest
|
||||
flake8
|
||||
commands = python -W always make test {posargs}
|
||||
|
||||
[testenv:py27]
|
||||
deps =
|
||||
|
@@ -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
|
||||
|
||||
sys.setrecursionlimit(5000)
|
||||
if hasattr(sys, 'setrecursionlimit'):
|
||||
# pyston doesn't have setrecursionlimit
|
||||
sys.setrecursionlimit(5000)
|
||||
|
||||
import uncompyle6.semantics.pysource
|
||||
import uncompyle6.semantics.fragments
|
||||
|
||||
# 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:
|
||||
# from uncompyle6 import deparse_code
|
||||
|
@@ -3,7 +3,6 @@
|
||||
#
|
||||
# Copyright (c) 2015-2016 by Rocky Bernstein <rb@dustyfeet.com>
|
||||
#
|
||||
from __future__ import print_function
|
||||
import sys, os, getopt
|
||||
|
||||
from uncompyle6.disas import disassemble_file
|
||||
@@ -26,7 +25,7 @@ Options:
|
||||
-V | --version show version and stop
|
||||
-h | --help show this message
|
||||
|
||||
""".format(program)
|
||||
""" % (program, program)
|
||||
|
||||
PATTERNS = ('*.pyc', '*.pyo')
|
||||
|
||||
@@ -37,15 +36,15 @@ Type -h for for full help.""" % program
|
||||
native = True
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
print("No file(s) given", file=sys.stderr)
|
||||
print(Usage_short, file=sys.stderr)
|
||||
sys.stderr.write("No file(s) given\n")
|
||||
sys.stderr.write(Usage_short)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
opts, files = getopt.getopt(sys.argv[1:], 'hVU',
|
||||
['help', 'version', 'uncompyle6'])
|
||||
except getopt.GetoptError as e:
|
||||
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
|
||||
except getopt.GetoptError(e):
|
||||
sys.stderr.write('%s: %s' % (os.path.basename(sys.argv[0]), e))
|
||||
sys.exit(-1)
|
||||
|
||||
for opt, val in opts:
|
||||
@@ -59,16 +58,14 @@ Type -h for for full help.""" % program
|
||||
native = False
|
||||
else:
|
||||
print(opt)
|
||||
print(Usage_short, file=sys.stderr)
|
||||
sys.stderr.write(Usage_short)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
for file in files:
|
||||
if os.path.exists(files[0]):
|
||||
disassemble_file(file, sys.stdout, native)
|
||||
else:
|
||||
print("Can't read %s - skipping" % files[0],
|
||||
file=sys.stderr)
|
||||
sys.stderr.write("Can't read %s - skipping\n" % files[0])
|
||||
pass
|
||||
pass
|
||||
return
|
||||
|
@@ -4,8 +4,7 @@
|
||||
# Copyright (c) 2015-2016 by Rocky Bernstein
|
||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
#
|
||||
from __future__ import print_function
|
||||
import sys, os, getopt, tempfile, time
|
||||
import sys, os, getopt, time
|
||||
|
||||
program, ext = os.path.splitext(os.path.basename(__file__))
|
||||
|
||||
@@ -35,7 +34,8 @@ Options:
|
||||
-p <integer> use <integer> number of processes
|
||||
-r recurse directories looking for .pyc and .pyo files
|
||||
--verify compare generated source with input byte-code
|
||||
(requires -o)
|
||||
--linemaps generated line number correspondencies between byte-code
|
||||
and generated source output
|
||||
--help show this message
|
||||
|
||||
Debugging Options:
|
||||
@@ -64,9 +64,11 @@ def usage():
|
||||
|
||||
|
||||
def main_bin():
|
||||
if not (sys.version_info[0:2] in ((2, 6), (2, 7), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6))):
|
||||
print('Error: %s requires Python 2.6, 2.7, 3.2, 3.3, 3.4, 3.5, or 3.6' % program,
|
||||
file=sys.stderr)
|
||||
if not (sys.version_info[0:2] in ((2, 4), (2, 5), (2, 6), (2, 7),
|
||||
(3, 2), (3, 3),
|
||||
(3, 4), (3, 5), (3, 6))):
|
||||
sys.stderr.write('Error: %s requires Python 2.4 2.5 2.6, 2.7, '
|
||||
'3.2, 3.3, 3.4, 3.5, or 3.6' % program)
|
||||
sys.exit(-1)
|
||||
|
||||
do_verify = recurse_dirs = False
|
||||
@@ -79,10 +81,10 @@ def main_bin():
|
||||
|
||||
try:
|
||||
opts, files = getopt.getopt(sys.argv[1:], 'hagtdrVo:c:p:',
|
||||
'help asm grammar recurse timestamp tree verify version '
|
||||
'showgrammar'.split(' '))
|
||||
except getopt.GetoptError as e:
|
||||
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
|
||||
'help asm grammar linemaps recurse timestamp tree '
|
||||
'verify version showgrammar'.split(' '))
|
||||
except getopt.GetoptError(e):
|
||||
sys.stderr.write('%s: %s\n' % (os.path.basename(sys.argv[0]), e))
|
||||
sys.exit(-1)
|
||||
|
||||
options = {}
|
||||
@@ -95,8 +97,10 @@ def main_bin():
|
||||
sys.exit(0)
|
||||
elif opt == '--verify':
|
||||
options['do_verify'] = True
|
||||
elif opt == '--linemaps':
|
||||
options['do_linemaps'] = True
|
||||
elif opt in ('--asm', '-a'):
|
||||
options['showasm'] = True
|
||||
options['showasm'] = 'after'
|
||||
options['do_verify'] = False
|
||||
elif opt in ('--tree', '-t'):
|
||||
options['showast'] = True
|
||||
@@ -114,7 +118,7 @@ def main_bin():
|
||||
elif opt in ('--recurse', '-r'):
|
||||
recurse_dirs = True
|
||||
else:
|
||||
print(opt, file=sys.stderr)
|
||||
sys.stderr.write(opt)
|
||||
usage()
|
||||
|
||||
# expand directory if specified
|
||||
@@ -137,18 +141,13 @@ def main_bin():
|
||||
if src_base:
|
||||
sb_len = len( os.path.join(src_base, '') )
|
||||
files = [f[sb_len:] for f in files]
|
||||
del sb_len
|
||||
|
||||
if not files:
|
||||
print("No files given", file=sys.stderr)
|
||||
sys.stderr.write("No files given\n")
|
||||
usage()
|
||||
|
||||
if outfile == '-':
|
||||
if 'do_verify' in options and options['do_verify'] and len(files) == 1:
|
||||
junk, outfile = tempfile.mkstemp(suffix=".pyc",
|
||||
prefix=files[0][0:-4]+'-')
|
||||
else:
|
||||
outfile = None # use stdout
|
||||
outfile = None # use stdout
|
||||
elif outfile and os.path.isdir(outfile):
|
||||
out_base = outfile; outfile = None
|
||||
elif outfile and len(files) > 1:
|
||||
@@ -224,7 +223,6 @@ def main_bin():
|
||||
except (KeyboardInterrupt, OSError):
|
||||
pass
|
||||
|
||||
|
||||
if timestamp:
|
||||
print(time.strftime(timestampfmt))
|
||||
|
||||
|
@@ -16,8 +16,6 @@ Second, we need structured instruction information for the
|
||||
want to run on Python 2.7.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
from collections import deque
|
||||
|
||||
@@ -37,10 +35,9 @@ def disco(version, co, out=None, is_pypy=False):
|
||||
|
||||
# store final output stream for case of error
|
||||
real_out = out or sys.stdout
|
||||
print('# Python %s' % version, file=real_out)
|
||||
real_out.write('# Python %s\n' % version)
|
||||
if co.co_filename:
|
||||
print('# Embedded file name: %s' % co.co_filename,
|
||||
file=real_out)
|
||||
real_out.write('# Embedded file name: %s\n' % co.co_filename)
|
||||
|
||||
scanner = get_scanner(version, is_pypy=is_pypy)
|
||||
|
||||
@@ -52,16 +49,15 @@ def disco_loop(disasm, queue, real_out):
|
||||
while len(queue) > 0:
|
||||
co = queue.popleft()
|
||||
if co.co_name != '<module>':
|
||||
print('\n# %s line %d of %s' %
|
||||
(co.co_name, co.co_firstlineno, co.co_filename),
|
||||
file=real_out)
|
||||
real_out.write('\n# %s line %d of %s\n' %
|
||||
(co.co_name, co.co_firstlineno, co.co_filename))
|
||||
tokens, customize = disasm(co)
|
||||
for t in tokens:
|
||||
if iscode(t.pattr):
|
||||
queue.append(t.pattr)
|
||||
elif iscode(t.attr):
|
||||
queue.append(t.attr)
|
||||
print(t, file=real_out)
|
||||
real_out.write(t)
|
||||
pass
|
||||
pass
|
||||
|
||||
|
61
uncompyle6/linenumbers.py
Normal file
61
uncompyle6/linenumbers.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from collections import deque
|
||||
|
||||
from xdis.code import iscode
|
||||
from xdis.load import load_file, load_module
|
||||
from xdis.main import get_opcode
|
||||
from xdis.bytecode import Bytecode, findlinestarts, offset2line
|
||||
|
||||
def line_number_mapping(pyc_filename, src_filename):
|
||||
(version, timestamp, magic_int, code1, is_pypy,
|
||||
source_size) = load_module(pyc_filename)
|
||||
try:
|
||||
code2 = load_file(src_filename)
|
||||
except SyntaxError, e:
|
||||
return str(e)
|
||||
|
||||
queue = deque([code1, code2])
|
||||
|
||||
mappings = []
|
||||
|
||||
opc = get_opcode(version, is_pypy)
|
||||
number_loop(queue, mappings, opc)
|
||||
return sorted(mappings, key=lambda x: x[1])
|
||||
|
||||
|
||||
def number_loop(queue, mappings, opc):
|
||||
while len(queue) > 0:
|
||||
code1 = queue.popleft()
|
||||
code2 = queue.popleft()
|
||||
assert code1.co_name == code2.co_name
|
||||
linestarts_orig = findlinestarts(code1)
|
||||
linestarts_uncompiled = list(findlinestarts(code2))
|
||||
mappings += [[line, offset2line(offset, linestarts_uncompiled)] for offset, line in linestarts_orig]
|
||||
bytecode1 = Bytecode(code1, opc)
|
||||
bytecode2 = Bytecode(code2, opc)
|
||||
instr2s = bytecode2.get_instructions(code2)
|
||||
seen = set([code1.co_name])
|
||||
for instr in bytecode1.get_instructions(code1):
|
||||
next_code1 = None
|
||||
if iscode(instr.argval):
|
||||
next_code1 = instr.argval
|
||||
if next_code1:
|
||||
next_code2 = None
|
||||
while not next_code2:
|
||||
try:
|
||||
instr2 = next(instr2s)
|
||||
if iscode(instr2.argval):
|
||||
next_code2 = instr2.argval
|
||||
pass
|
||||
except StopIteration:
|
||||
break
|
||||
pass
|
||||
if next_code2:
|
||||
assert next_code1.co_name == next_code2.co_name
|
||||
if next_code1.co_name not in seen:
|
||||
seen.add(next_code1.co_name)
|
||||
queue.append(next_code1)
|
||||
queue.append(next_code2)
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
pass
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user