You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 16:59:52 +08:00
Compare commits
1 Commits
release-py
...
release-2.
Author | SHA1 | Date | |
---|---|---|---|
|
39ce40004b |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,5 +1,3 @@
|
||||
*.pyo
|
||||
*.pyc
|
||||
*_dis
|
||||
*~
|
||||
/.cache
|
||||
@@ -10,11 +8,7 @@
|
||||
/__pkginfo__.pyc
|
||||
/dist
|
||||
/how-to-make-a-release.txt
|
||||
/nose-*.egg
|
||||
/tmp
|
||||
/uncompyle6.egg-info
|
||||
/unpyc
|
||||
__pycache__
|
||||
build
|
||||
/.venv*
|
||||
/.idea
|
12
.travis.yml
12
.travis.yml
@@ -3,16 +3,14 @@ language: python
|
||||
sudo: false
|
||||
|
||||
python:
|
||||
- '2.7' # this is a cheat here because travis doesn't do 2.4-2.6
|
||||
- '2.6'
|
||||
- '2.7'
|
||||
- '3.4'
|
||||
- '3.5'
|
||||
|
||||
install:
|
||||
- pip install -e .
|
||||
- pip install -r requirements.txt
|
||||
- pip install -r requirements-dev.txt
|
||||
|
||||
script:
|
||||
- python ./setup.py develop && COMPILE='--compile' make check
|
||||
|
||||
# blacklist
|
||||
branches:
|
||||
except:
|
||||
- data-driven-pytest
|
||||
|
@@ -1,131 +0,0 @@
|
||||
This is the changelog from *decompyle*'s release 2.4 and before
|
||||
passed on by Dan Pascu
|
||||
|
||||
|
||||
release 2.4 (Dan Pascu)
|
||||
- Replaced the way code structures are identified by the parser.
|
||||
Previously, the scanner introduced some COME_FROM entries in the
|
||||
dissasembly output to mark all the destinations of jump instructions.
|
||||
Using these COME_FROM labels the parser was then able to identify the
|
||||
code structures (if tests, while loops, etc). Up to python-2.3 this was
|
||||
possible because the code structures were clearly defined and jump
|
||||
targets were always to the same points in a given strcuture making it
|
||||
easy to identify the structure. Python 2.3 however introduced optimized
|
||||
jumps to increase code performance. In the previous version of decompyle
|
||||
(2.3) we used a technique to identify the code structures and then used
|
||||
these structures to determine where the jump targets would have been if
|
||||
not optimized. Using this information we then added COME_FROM labels at
|
||||
the points where they would have been if not optimized, thus emulating
|
||||
the way decompyle worked with versions before python 2.3. However with
|
||||
the introduction of even more optimizations in python 2.4 this technique
|
||||
no longer works. Not only the jump targets are no longer an effective
|
||||
mean for the parser to identify the code structures, but also trying to
|
||||
emulate the old way things were solved when it clearly no longer works
|
||||
is not the right solution. To solve this issue, the code to identify the
|
||||
structures that we had developed in version 2.3, was used to add real
|
||||
start/end points for strcuture identification, instead of the COME_FROM
|
||||
labels. Now these new start/end labels are used by the parser to more
|
||||
precisely identify the structures and the COME_FROM labels were removed
|
||||
completely. The scanner is responsible to identify these code structures
|
||||
and use any knowledge of optimizations that python applies to determine
|
||||
the start/end points of any structure and then mark them with certain
|
||||
keywords that are understood by the parser.
|
||||
- Correctly identify certain `while 1' structures that were not
|
||||
recognized in the previous version.
|
||||
- Added support for new byte code constructs used by python 2.4
|
||||
|
||||
release 2.3.2
|
||||
- tidied up copyright and changelog information for releases 2.3 and later
|
||||
|
||||
release 2.3.1 (Dan Pascu)
|
||||
- implemented a structure detection technique that fixes problems with
|
||||
optimised jumps in Python >= 2.3. In the previous release (decompyle 2.3),
|
||||
these problems meant that some files were incorrectly decompiled and
|
||||
others could not be decompiled at all. With this new structure detection
|
||||
technique, thorough testing over the standard python libraries suggests
|
||||
that decompyle 2.3.1 can handle everything that decompyle 2.2beta1 could,
|
||||
plus new Python 2.3 bytecodes and constructs.
|
||||
|
||||
release 2.3 (Dan Pascu)
|
||||
- support for Python 2.3 added
|
||||
- use the marshal and disassembly code from their respective python
|
||||
versions, so that decompyle can manipulate bytecode independently
|
||||
of the interpreter that runs decompyle itself (for example it can
|
||||
decompile python2.3 bytecode even when running under python2.2)
|
||||
|
||||
——————————————————
|
||||
|
||||
release 2.2beta1 (hartmut Goebel)
|
||||
- support for Python 1.5 up to Python 2.2
|
||||
- no longer requires to be run with the Python interpreter version
|
||||
which generated the byte-code.
|
||||
- requires Python 2.2
|
||||
- pretty-prints docstrings, hashes, lists and tuples
|
||||
- decompyle is now a script and a package
|
||||
- added emacs mode-hint and tab-width for each file output
|
||||
- enhanced test suite: more test patterns, .pyc/.pyo included
|
||||
- avoids unnecessary 'global' statements
|
||||
- still untested: EXTENDED_ARG
|
||||
|
||||
internal changes:
|
||||
- major code overhoul: splitted into several modules, clean-ups
|
||||
- use a list of valid magics instead of the single one from imp.py
|
||||
- uses copies of 'dis.py' for every supported version. This ensures
|
||||
correct disassemling of the byte-code.
|
||||
- use a single Walker and a single Parser, thus saving time and memory
|
||||
- use augmented assign and 'print >>' internally
|
||||
- optimized 'Walker.engine', the main part of code generation
|
||||
release 0.6.0: (hartmut Goebel)
|
||||
- extended print (Python 2.0)
|
||||
- extended import (Python 2.0) (may not cover all cases)
|
||||
- augmented assign (Python 2.0) (may not cover all cases)
|
||||
- list comprehensions (Python 2.0)
|
||||
- equivalent for 'apply' (Python 1.6)
|
||||
- if .. elif .. else are now nested as expected
|
||||
- assert test, data
|
||||
- unpack list corrected (was the same as unpack tuple)
|
||||
- fixed unpack tuple (trailing semicolon was missing)
|
||||
- major speed up :-)
|
||||
- reduced memory usage (pre-alpha-0.5 has increased it a lot)
|
||||
- still missing: EXTENDED_ARG
|
||||
|
||||
pre-alpha-0.5: (hartmut Goebel)
|
||||
- *args, **kwargs
|
||||
- global
|
||||
- formal tuple parameters (eg. def a(self, (x,y,z)) )
|
||||
- actual lambda parameters (eg. X(lambda z: z**2) )
|
||||
- remove last 'return None' in procedures
|
||||
- remove last 'return locals()' in class definitions
|
||||
- docstrings
|
||||
|
||||
pre-alpha-0.4: (hartmut Goebel)
|
||||
- assert
|
||||
- try/except/finally
|
||||
- parentheses in expressions
|
||||
- nested expressions
|
||||
- extracted dissassemble() from module dis and
|
||||
removed ugly redirect of stdout, thus saved a lot of
|
||||
ugly code and a lot of memory
|
||||
|
||||
pre-alpha-0.3: (hartmut Goebel)
|
||||
- keyword arguments
|
||||
- some boolean expressions
|
||||
- and/or
|
||||
- complex conditions in if/while
|
||||
- read byte-code from .pyc without importing
|
||||
- access to the body of classes and modules
|
||||
- class and function definitions
|
||||
- a = b = c = xxx
|
||||
|
||||
pre-alpha-0.1 -> pre-alpha-0.2:
|
||||
- SET_LINENO filtered out in lexer now
|
||||
- added support for subscripts (just for Christian Tismer :-)
|
||||
- fixed bug with handling of BUILD_{LIST,TUPLE} & CALL_FUNCTION
|
||||
- dict-building support
|
||||
- comparison support
|
||||
- exec support
|
||||
- del support
|
||||
- pass support
|
||||
- slice support
|
||||
- no more extraneous (albeit legal) commas
|
||||
- finally, it excepts try [sic] but not all 42 variations of it
|
120
HISTORY.md
120
HISTORY.md
@@ -4,8 +4,7 @@ There have been a number of people who have worked on this. I am awed
|
||||
by the amount of work, number of people who have contributed to this,
|
||||
and the cleverness in the code.
|
||||
|
||||
The below is an annotated history from talking to participants
|
||||
involved and my reading of the code and sources cited.
|
||||
The below is an annotated history from my reading of the sources cited.
|
||||
|
||||
In 1998, John Aycock first wrote a grammar parser in Python,
|
||||
eventually called SPARK, that was usable inside a Python program. This
|
||||
@@ -24,14 +23,10 @@ working on his thesis, John realized SPARK could be used to deparse
|
||||
Python bytecode. In the fall of 1999, he started writing the Python
|
||||
program, "decompyle", to do this.
|
||||
|
||||
To help with control structure deparsing the instruction sequence was
|
||||
augmented with pseudo instruction COME_FROM. This code introduced
|
||||
another clever idea: using table-driven semantics routines, using
|
||||
format specifiers.
|
||||
This code introduced 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 Earley Algorithm parser was in good shape, this
|
||||
code was woefully lacking as serious Python deparser.
|
||||
The last mention of a release of SPARK from John is around 2002.
|
||||
|
||||
In the fall of 2000, Hartmut Goebel
|
||||
[took over maintaining the code](https://groups.google.com/forum/#!searchin/comp.lang.python/hartmut$20goebel/comp.lang.python/35s3mp4-nuY/UZALti6ujnQJ). The
|
||||
@@ -41,11 +36,10 @@ first subsequent public release announcement that I can find is
|
||||
From the CHANGES file found in
|
||||
[the tarball for that release](http://old-releases.ubuntu.com/ubuntu/pool/universe/d/decompyle2.2/decompyle2.2_2.2beta1.orig.tar.gz),
|
||||
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 precidence to the table
|
||||
specifiers, support for multiple versions of Python, the
|
||||
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 says he could verify against the
|
||||
entire Python library. However I have subsequently found small and relatively obscure bugs in the decompilation code.
|
||||
deparsed bytecode, and used this in an extensive set of tests that he also wrote. He could verify against the entire Python library.
|
||||
|
||||
decompyle2.2 was packaged for Debian (sarge) by
|
||||
[Ben Burton around 2002](https://packages.qa.debian.org/d/decompyle.html). As
|
||||
@@ -61,77 +55,32 @@ it doesn't look like he's done anything compiler-wise since SPARK). So
|
||||
I hope people will use the crazy-compilers service. I wish them the
|
||||
success that his good work deserves.
|
||||
|
||||
Dan Pascu did a bit of work from late 2004 to early 2006 to get this
|
||||
code to handle first Python 2.3 and then 2.4 bytecodes. Because of
|
||||
jump optimization introduced in the CPython bytecode compiler at that
|
||||
time, various JUMP instructions were classifed as going backwards, and
|
||||
COME FROM instructions were reintroduced. See
|
||||
[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
|
||||
supported. Dan says the Python 2.3 version could verify the entire
|
||||
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
|
||||
PyPI](https://pypi.python.org/pypi/uncompyle/1.1) and the era of
|
||||
public version control. (Dan's code although not public used
|
||||
[darcs](http://darcs.net/) for version control.)
|
||||
|
||||
In contrast to _decompyle_, _uncompyle_ at least in its final versions,
|
||||
runs only on Python 2.7. However it accepts bytecode back to Python
|
||||
Next we get to
|
||||
["uncompyle" and PyPI](https://pypi.python.org/pypi/uncompyle/1.1) and
|
||||
the era of git repositories. In contrast to decompyle, this now runs
|
||||
only on Python 2.7 although it accepts bytecode back to Python
|
||||
2.5. Thomas Grainger is the package owner of this, although Hartmut is
|
||||
still listed as the author.
|
||||
listed as the author.
|
||||
|
||||
The project exists not only on
|
||||
[github](https://github.com/gstarnberger/uncompyle) but also on
|
||||
[bitbucket](https://bitbucket.org/gstarnberger/uncompyle) and later
|
||||
the defunct [google
|
||||
code](https://code.google.com/archive/p/unpyc/). The git/svn history
|
||||
goes back to 2009. Somewhere in there the name was changed from
|
||||
"decompyle" to "unpyc" by Keknehv, and then to "uncompyle" by Guenther Starnberger.
|
||||
[bitbucket](https://bitbucket.org/gstarnberger/uncompyle) where the
|
||||
git history goes back to 2009. Somewhere in there the name was changed
|
||||
from "decompyle" to "uncompyle".
|
||||
|
||||
The name Thomas Grainger isn't found in (m)any of the commits in the
|
||||
several years of active development. First Keknehv worked on this up
|
||||
to Python 2.5 or so while acceping Python bytecode back to 2.0 or
|
||||
so. Then hamled made a few commits earler on, while Eike Siewertsen
|
||||
made a few commits later on. But mostly wibiti, and Guenther
|
||||
Starnberger got the code to where uncompyle2 was around 2012.
|
||||
|
||||
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
|
||||
based on code from Eloi Vanderbeken.
|
||||
|
||||
This project, `uncompyle6`, abandons that approach for various
|
||||
reasons. However the main reason is that we need offsets in fragment
|
||||
deparsing to be exactly the same, and the transformation process can
|
||||
remove instructions. _Adding_ instructions with psuedo offsets is
|
||||
however okay.
|
||||
|
||||
`Uncompyle6` however owes its existence to the fork of `uncompyle2` by
|
||||
Myst herie (Mysterie) whose first commit picks up at
|
||||
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
|
||||
Fenx's uncompyle3 which I used for inspiration for Python3 support.
|
||||
|
||||
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+
|
||||
while, handling Python bytecodes from Python versions 2.5+ and
|
||||
3.2+. In doing so, it has been expedient to separate this into three
|
||||
projects:
|
||||
|
||||
* 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)).
|
||||
several years of active development. Guenther Starnberger, Keknehv,
|
||||
hamled, and Eike Siewertsen are principle committers here.
|
||||
|
||||
This project, uncompyle6, however owes its existence to uncompyle2 by
|
||||
Myst herie (Mysterie) whose first commit seems to goes back to 2012;
|
||||
it is also based on Hartmut's code. I chose this as it seems had been
|
||||
the most actively worked on most recently.
|
||||
|
||||
Over the many years, code styles and Python features have
|
||||
changed. However brilliant the code was and still is, it hasn't really
|
||||
had a single public active maintainer. And there have been many forks
|
||||
of the code. I have spent a great deal of time trying to organize and
|
||||
modularize the code so that it can handle more Python versions more
|
||||
gracefully (with still only moderate success).
|
||||
of the code.
|
||||
|
||||
That it has been in need of an overhaul has been recognized by the
|
||||
Hartmut a decade an a half ago:
|
||||
@@ -141,26 +90,15 @@ 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 Earley-algorithm parse with lots of
|
||||
massaging of tokens and the grammar in the scanner
|
||||
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
|
||||
simulate execution symbolically and build expression trees off of
|
||||
stack results. Control flow in that apprproach still needs to be
|
||||
handled somewhat ad hoc. The two important projects that work this
|
||||
way are [unpyc3](https://code.google.com/p/unpyc3/) and most
|
||||
especially [pycdc](https://github.com/zrax/pycdc) The latter project
|
||||
is largely by Michael Hansen and Darryl Pogue. If they supported
|
||||
getting source-code fragments, did a better job in supporting 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.
|
||||
Lastly, I should mention [unpyc](https://code.google.com/p/unpyc3/)
|
||||
and most especially [pycdc](https://github.com/zrax/pycdc), largely by
|
||||
Michael Hansen and Darryl Pogue. If they supported getting source-code
|
||||
fragments and I could call it from Python, I'd probably ditch this and
|
||||
use that. From what I've seen, the code runs blindingly fast and spans
|
||||
all versions of Python.
|
||||
|
||||
Tests for the project have been, or are being, culled from all of the
|
||||
projects mentioned.
|
||||
|
||||
NB. If you find mistakes, want corrections, or want your name added
|
||||
(or removed), please contact me.
|
||||
NB. If you find mistakes, want corrections, or want your name added (or removed),
|
||||
please contact me.
|
||||
|
@@ -1,68 +0,0 @@
|
||||
# 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:
|
||||
|
||||
* _uncompyle6_ 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.
|
4
LICENSE
4
LICENSE
@@ -1,6 +1,6 @@
|
||||
Copyright (c) 2015 by Rocky Bernstein
|
||||
Copyright (c) 2000 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
Copyright (c) 1998-2002 John Aycock
|
||||
Copyright (c) 2000 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
Copyright (c) 2015 by Rocky Bernstein
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
13
MANIFEST.in
13
MANIFEST.in
@@ -1,18 +1,9 @@
|
||||
include README.rst
|
||||
include ChangeLog
|
||||
include HISTORY.md
|
||||
include HOW-TO-REPORT-A-BUG.md
|
||||
include LICENSE
|
||||
include Makefile
|
||||
include requirements.txt
|
||||
include requirements-dev.txt
|
||||
include DECOMPYLE-2.4-CHANGELOG.txt
|
||||
include ChangeLog
|
||||
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 test *.py
|
||||
recursive-include pytest *.py
|
||||
recursive-include pytest/testdata *
|
||||
|
26
Makefile
26
Makefile
@@ -23,29 +23,16 @@ check:
|
||||
@PYTHON_VERSION=`$(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2`; \
|
||||
$(MAKE) check-$$PYTHON_VERSION
|
||||
|
||||
# Run all quick tests
|
||||
check-short: pytest
|
||||
$(MAKE) -C test check-short
|
||||
|
||||
#: Tests for Python 2.7, 3.3 and 3.4
|
||||
check-2.7 check-3.3 check-3.4: pytest
|
||||
$(MAKE) -C test $@
|
||||
|
||||
#: 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.0 check-3.1 check-3.2 check-3.5 check-3.6:
|
||||
#: Tests for Python 3.5 - pytest doesn't work here
|
||||
check-3.5:
|
||||
$(MAKE) -C test $@
|
||||
|
||||
#:Tests for Python 2.6 (doesn't have pytest)
|
||||
check-2.4 check-2.5 check-2.6:
|
||||
$(MAKE) -C test $@
|
||||
|
||||
#:PyPy 2.6.1 or PyPy 5.0.1
|
||||
# Skip for now
|
||||
2.6 5.0 5.3:
|
||||
|
||||
#:PyPy pypy3-2.4.0 Python 3:
|
||||
pypy-3.2 2.4:
|
||||
check-2.6:
|
||||
$(MAKE) -C test $@
|
||||
|
||||
#: Run py.test tests
|
||||
@@ -57,7 +44,7 @@ clean: clean_pyc
|
||||
$(PYTHON) ./setup.py $@
|
||||
(cd test && $(MAKE) clean)
|
||||
|
||||
#: Create source (tarball) and wheel distribution
|
||||
#: Create source (tarball) and binary (egg) distribution
|
||||
dist:
|
||||
$(PYTHON) ./setup.py sdist bdist_egg
|
||||
|
||||
@@ -86,11 +73,6 @@ bdist_egg:
|
||||
$(PYTHON) ./setup.py bdist_egg
|
||||
|
||||
|
||||
#: Create binary wheel distribution
|
||||
bdist_wheel:
|
||||
$(PYTHON) ./setup.py bdist_wheel
|
||||
|
||||
|
||||
# It is too much work to figure out how to add a new command to distutils
|
||||
# to do the following. I'm sure distutils will someday get there.
|
||||
DISTCLEAN_FILES = build dist *.pyc
|
||||
|
322
NEWS
322
NEWS
@@ -1,325 +1,3 @@
|
||||
uncompyle6 2.11.1 2016-06-18 Fleetwood
|
||||
- Major improvements in fragment tracking
|
||||
* Add nonterminal node in extractInfo
|
||||
* tag more offsets in expressions
|
||||
* tag array subscripts
|
||||
* set YIELD value offset in a <yield> expr
|
||||
* fix a long-standing bug in not adjusting final AST when melding other deparse ASTs
|
||||
- Fixes yet again for make_function node handling; document what's up here
|
||||
- Fix bug in snowflake Python 3.5 *args kwargs
|
||||
|
||||
uncompyle6 2.10.1 2016-06-3 Marylin Frankel
|
||||
|
||||
- fix some fragments parsing bugs
|
||||
- was returning the wrong type sometimes in deparse_code_around_offset()
|
||||
- capture function name in offsets
|
||||
- track changes to ifelstrmtr node from pysource into fragments
|
||||
|
||||
uncompyle6 2.10.0 2016-05-30 Elaine Gordon
|
||||
|
||||
- Add fuzzy offset deparse lookup
|
||||
- 3.6 bugfixes
|
||||
- fix EXTENDED_ARGS handling (and in 2.6 and others)
|
||||
- semantic routine make_function fragments.py
|
||||
- MAKE_FUNCTION handling
|
||||
- CALL_FUNCTION_EX handling
|
||||
- async property on defs
|
||||
- support for CALL_FUNCTION_KW (moagstar)
|
||||
- 3.5+ UNMAP_PACK and BUILD_UNMAP_PACK handling
|
||||
- 3.5 FUNCTION_VAR bug
|
||||
- 3.x pass statement insdie while True
|
||||
- Improve 3.2 decompilation
|
||||
- Fixed -o argument processing (Gregrory)
|
||||
- Reduce scope of LOAD_ASSERT as expr to 3.4+
|
||||
- "await" statement fixes
|
||||
- 2.3, 2.4 "if 1 .." fixes
|
||||
- 3.x annotation fixes
|
||||
|
||||
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
|
||||
|
||||
- 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
|
||||
- track def keywoard-parameter line-splitting in source code better
|
||||
- bump min xdis version to mask previous xdis bug
|
||||
|
||||
uncompyle6 2.9.3 2016-10-26
|
||||
|
||||
Release forced by incompatiblity change in xdis 3.2.0.
|
||||
|
||||
- Python 3.1 bugs:
|
||||
* handle "with ... as"
|
||||
* handle "with"
|
||||
* Start handling def (...) -> yy (has bugs still)
|
||||
|
||||
- DRY Python 3.x via inheritance
|
||||
- Python 3.6 work (from Daniel Bradburn)
|
||||
* Handle 3.6 buildstring
|
||||
* Handle 3.6 handle single and multiple fstring better
|
||||
|
||||
|
||||
uncompyle6 2.9.2 2016-10-15
|
||||
|
||||
- use source-code line breaks to assist in where to break
|
||||
in tuples and maps
|
||||
- Fix Python 1.5 decompyle bugs
|
||||
- Fix some Python 2.6 and below bugs
|
||||
- DRY fragments.py code a little
|
||||
|
||||
uncompyle6 2.9.1 2016-10-09
|
||||
|
||||
- Improved Python 1.5 decompiling
|
||||
- Handle old-style pre Python 2.2 classes
|
||||
|
||||
uncompyle6 2.9.0 2016-10-09
|
||||
|
||||
- Use xdis 3.0.0 protocol load_module.
|
||||
this Forces change in requirements.txt and _pkg_info_.py
|
||||
- Start Python 1.5 decompiling; another round of work is needed to
|
||||
remove bugs
|
||||
- Simpify python 2.1 grammar
|
||||
- Fix bug with -t ... Wasn't showing source text when -t option was given
|
||||
- Fix 2.1-2.6 bug in list comprehension
|
||||
|
||||
uncompyle6 2.8.4 2016-10-08
|
||||
|
||||
- Python 3 disassembly bug fixes
|
||||
- Python 3.6 fstring bug fixes (from moagstar)
|
||||
- Python 2.1 disassembly
|
||||
- COME_FROM suffixes added in Python3
|
||||
- use .py extension in verification disassembly
|
||||
|
||||
uncompyle6 2.8.3 2016-09-11 live from NYC!
|
||||
|
||||
NOTE: this is possibly the last release before a major reworking of
|
||||
control-flow structure detection is done.
|
||||
|
||||
- Lots of bug fixes in decompilation:
|
||||
* 3.0 .. 3.4 whileTrue bug
|
||||
* 3.x function declaration deparsing:
|
||||
. 3.0 .. 3.2 *args processing
|
||||
. 3.0 .. 3.2 call name and kwargs bug
|
||||
. 3.0 .. getting parameter of *
|
||||
. 3.0 .. handling varible number of args
|
||||
. 3.0 .. "if" structure bugs
|
||||
* 3.5+ if/else bugs
|
||||
* 2.2-2.6 bugs
|
||||
. try/except control flow
|
||||
. a == b == c -like detection
|
||||
. generator detection
|
||||
. "while .. and" statement bugs
|
||||
. handle "except <cond>, <var>"
|
||||
. use older raise format in 2.x
|
||||
- scanner "disassemble" is now "ingest". True disassembly is done by xdis
|
||||
- Start accepting Python 3.1 bytecode
|
||||
- Add --weak-verify option on test_pyenvlib and test_pythonlib. This
|
||||
catches more bugs more easily
|
||||
- bump xdis requirement so we can deparse dropbox 2.5 code
|
||||
- Added H. Goebel's changes before 2.4 in DECOMPYLE-2.4-CHANGELOG.txt
|
||||
|
||||
uncompyle6 2.8.2 2016-08-29
|
||||
|
||||
- Handle Python 3.6 format string conversions !r, !s, !a
|
||||
- Start to handle 3.1 bytecode
|
||||
- Fix some PyPy translation bugs
|
||||
- We now only handle 3.6.0a3+ since that is incompatible with 3.6 before that
|
||||
|
||||
uncompyle6 2.8.1 2016-08-20
|
||||
|
||||
- Add Python 2.2 decompilation
|
||||
|
||||
- Fix bugs
|
||||
* PyPy LOOKUP_METHOD bug
|
||||
* Python 3.6 FORMAT_VALUE handles expressions now
|
||||
|
||||
uncompyle6 2.8.0 2016-08-03
|
||||
|
||||
- Start Python 3.6 support (moagstar)
|
||||
more work on PEP 498 needed
|
||||
- tidy bytecode/word output
|
||||
- numerous decompiling bugs fixed
|
||||
- grammar testing started
|
||||
- show magic number in deparsed output
|
||||
- better grammar and semantic action segregation based
|
||||
on python bytecode version
|
||||
|
||||
uncompyle6 2.7.1 2016-07-26
|
||||
|
||||
- PyPy bytecodes for 2.7 and 3.2 added
|
||||
- Instruction formatting improved slightly
|
||||
- 2.7 bytecode "continue" bug fixed
|
||||
|
||||
uncompyle6 2.7.0 2016-07-15
|
||||
|
||||
- Many Syntax and verifification bugs removed
|
||||
tested on standard libraries from 2.3.7 to 3.5.1
|
||||
and they all decompile and verify fine.
|
||||
I'm sure there are more bugs though.
|
||||
|
||||
uncompyle6 2.6.2 2016-07-11 Manhattenhenge
|
||||
|
||||
- Extend bytecodes back to 2.3
|
||||
- Fix bugs:
|
||||
* 3.x and 2.7 set comprehensions,
|
||||
* while1 loops
|
||||
* continue statements
|
||||
- DRY and segregate grammar more
|
||||
|
||||
uncompyle6 2.6.1 2016-07-08
|
||||
|
||||
- Go over Python 2.5 bytecode deparsing
|
||||
all library programs now deparse
|
||||
- Fix a couple bugs in 2.6 deparsing
|
||||
|
||||
uncompyle6 2.6.0 2016-07-07
|
||||
|
||||
- Improve Python 2.6 bytecode deparsing:
|
||||
stdlib now will deparse something
|
||||
- Better <2.6 vs. 2.7 grammar separation
|
||||
- Fix some 2.7 deparsing bugs
|
||||
- Fix bug in installing uncompyle6 script
|
||||
- Doc improvments
|
||||
|
||||
uncompyle6 2.5.0 2016-06-22 Summer Solstace
|
||||
|
||||
- Much better Python 3.2-3.5 coverage.
|
||||
3.4.6 is probably the best;3.2 and 3.5 are weaker
|
||||
- Better AST printing with -t
|
||||
- Better error reporting
|
||||
- Better fragment offset tracking
|
||||
- Some (much-needed) code refactoring
|
||||
|
||||
uncompyle6 2.4.0 2016-05-18 (in memory of Lewis Bernstein)
|
||||
|
||||
- Many Python 3 bugs fixed:
|
||||
* Python 3.2 to 3.5 libaries largely
|
||||
uncompyle and most verify
|
||||
- pydisassembler:
|
||||
* disassembles all code objects in a file
|
||||
* can select showing bytecode before
|
||||
or after uncompyle mangling, option -U
|
||||
- DRY scanner code (but more is desired)
|
||||
- Some code cleanup (but more is desired)
|
||||
- Misc Bugs fixed:
|
||||
* handle complex number unmarshaling
|
||||
* Running on Python 2 to works on Python 3.5 bytecodes now
|
||||
|
||||
uncompyle6 2.3.5 and 2.3.6 2016-05-14
|
||||
|
||||
- Python 2 class decorator fix (thanks to Tey)
|
||||
- Fix fragment parsing bugs
|
||||
- Fix some Python 3 parsing bugs:
|
||||
* Handling single in * parameter
|
||||
* "while True"
|
||||
* escape from for inside if
|
||||
* yield expressions
|
||||
- Correct history based on info from Dan Pascu
|
||||
- Fix up pip packaging, ugh.
|
||||
|
||||
uncompyle6 2.3.4 2016-05-5
|
||||
|
||||
- More Python 3.5 parsing bugs addressed
|
||||
- decompiling Python 3.5 from other Python versions works
|
||||
- test from Python 3.2
|
||||
- remove "__module__ = __name__" in 3.0 <= Python 3.2
|
||||
|
||||
uncompyle6 2.3.3 2016-05-3
|
||||
|
||||
- Fix bug in running uncompyle6 script on Python 3
|
||||
- Speed up performance on deparsing long lists by grouping in chunks of 32 and 256 items
|
||||
- DRY Python expressions between Python 2 and 3
|
||||
|
||||
uncompyle6 2.3.2 2016-05-1
|
||||
|
||||
- Add --version option standalone scripts
|
||||
- Correct License information in package
|
||||
- expose fns uncompyle_file, load_file, and load_module
|
||||
- Start to DRY Python2 and Python3 grammars Separate out 3.2, and 3.5+
|
||||
specific grammar code
|
||||
- Fix bug in 3.5+ constant map parsing
|
||||
|
||||
uncompyle6 2.3.0, 2.3.1 2016-04-30
|
||||
|
||||
- Require spark_parser >= 1.1.0
|
||||
|
||||
uncompyle6 2.2.0 2016-04-30
|
||||
|
||||
- Spark is no longer here but pulled separate package spark_parse
|
||||
|
2
PKG-INFO
2
PKG-INFO
@@ -5,6 +5,6 @@ Summary: Python byte-code to source-code converter
|
||||
Home-page: http://github.com/rocky/python-uncompyle6
|
||||
Author: Rocky
|
||||
Author-email: rb@dustyfeet.com
|
||||
License: MIT
|
||||
License: GPLv3
|
||||
Description: UNKNOWN
|
||||
Platform: UNKNOWN
|
||||
|
129
README.rst
129
README.rst
@@ -1,31 +1,24 @@
|
||||
|buildstatus| |Supported Python Versions|
|
||||
|buildstatus|
|
||||
|
||||
uncompyle6
|
||||
==========
|
||||
|
||||
A native Python cross-version Decompiler and Fragment Decompiler.
|
||||
Follows in the tradition of decompyle, uncompyle, and uncompyle2.
|
||||
A native Python bytecode Disassembler, Decompiler, Fragment Decompiler
|
||||
and bytecode library
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
*uncompyle6* translates Python bytecode back into equivalent Python
|
||||
source code. It accepts bytecodes from Python version 1.5, and 2.1 to
|
||||
3.6 or so, including PyPy bytecode and Dropbox's Python 2.5 bytecode.
|
||||
source code. It accepts bytecodes from Python version 2.5 to 3.4 or
|
||||
so and has been tested on Python running versions 2.6, 2.7, 3.3,
|
||||
3.4 and 3.5.
|
||||
|
||||
Why this?
|
||||
---------
|
||||
|
||||
There were a number of decompyle, uncompile, uncompyle2, uncompyle3
|
||||
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
|
||||
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. 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.
|
||||
|
||||
What makes this different from other CPython bytecode decompilers?: its
|
||||
What makes this different from other CPython bytecode decompilers? Its
|
||||
ability to deparse just fragments and give source-code information
|
||||
around a given bytecode offset.
|
||||
|
||||
@@ -41,13 +34,12 @@ location in more detail than just a line number. It can be also used
|
||||
when source-code information does not exist and there is just bytecode
|
||||
information.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
Other parts of the library can be used inside Python for various
|
||||
bytecode-related tasks. For example you can read in bytecode,
|
||||
i.e. perform a version-independent `marshal.loads()`, and disassemble
|
||||
the bytecode using a version of Python different from the one used to
|
||||
compile the bytecode.
|
||||
|
||||
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 1.5, 2.1-2.7, and 3.0-3.6 and the above-mentioned PyPy versions.
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -56,13 +48,13 @@ This uses setup.py, so it follows the standard Python routine:
|
||||
|
||||
::
|
||||
|
||||
pip install -e .
|
||||
pip install -r requirements.txt
|
||||
pip install -r requirements-dev.txt
|
||||
python setup.py install # may need sudo
|
||||
# or if you have pyenv:
|
||||
python setup.py develop
|
||||
|
||||
A GNU makefile is also provided so :code:`make install` (possibly as root or
|
||||
A GNU makefile is also provided so `make install` (possibly as root or
|
||||
sudo) will do the steps above.
|
||||
|
||||
Testing
|
||||
@@ -76,7 +68,7 @@ A GNU makefile has been added to smooth over setting running the right
|
||||
command, and running tests from fastest to slowest.
|
||||
|
||||
If you have remake_ installed, you can see the list of all tasks
|
||||
including tests via :code:`remake --tasks`
|
||||
including tests via `remake --tasks`
|
||||
|
||||
|
||||
Usage
|
||||
@@ -86,102 +78,31 @@ Run
|
||||
|
||||
::
|
||||
|
||||
$ uncompyle6 *compiled-python-file-pyc-or-pyo*
|
||||
./bin/uncompyle6 -h
|
||||
./bin/pydisassemble -h
|
||||
|
||||
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.
|
||||
for usage help
|
||||
|
||||
|
||||
Known Bugs/Restrictions
|
||||
-----------------------
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
"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 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.
|
||||
|
||||
*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.
|
||||
|
||||
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.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
|
||||
changes several times within a version. We support only the released
|
||||
magic. There are also customized Python interpreters, notably Dropbox,
|
||||
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.
|
||||
|
||||
Handling pathologically long lists of expressions or statements is
|
||||
slow.
|
||||
|
||||
|
||||
There is lots to do, so please dig in and help.
|
||||
Python 2 deparsing is probably as solid as the various versions of
|
||||
uncompyle2. Python 3 deparsing is okay but not as solid.
|
||||
|
||||
See Also
|
||||
--------
|
||||
|
||||
* 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 than 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
|
||||
* The HISTORY_ file.
|
||||
* `How to report a bug <https://github.com/rocky/python-uncompyle6/blob/master/HISTORY.md>`_
|
||||
* https://github.com/zrax/pycdc
|
||||
* https://code.google.com/p/unpyc3/
|
||||
|
||||
The HISTORY file.
|
||||
|
||||
.. |downloads| image:: https://img.shields.io/pypi/dd/uncompyle6.svg
|
||||
.. _trepan: https://pypi.python.org/pypi/trepan
|
||||
.. _HISTORY: https://github.com/rocky/python-uncompyle6/blob/master/HISTORY.md
|
||||
.. _debuggers: https://pypi.python.org/pypi/trepan3k
|
||||
.. _remake: https://bashdb.sf.net/remake
|
||||
.. _pycdc: https://github.com/zrax/pycdc
|
||||
.. _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,23 +9,20 @@
|
||||
|
||||
# Things that change more often go here.
|
||||
copyright = """
|
||||
Copyright (C) 2015-2017 Rocky Bernstein <rb@dustyfeet.com>.
|
||||
Copyright (C) 2015 Rocky Bernstein <rb@dustyfeet.com>.
|
||||
"""
|
||||
|
||||
classifiers = ['Development Status :: 5 - Production/Stable',
|
||||
classifiers = ['Development Status :: 3 - Alpha',
|
||||
'Intended Audience :: Developers',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2.4',
|
||||
'Programming Language :: Python :: 2.5',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3.1',
|
||||
'Programming Language :: Python :: 3.2',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Topic :: Software Development :: Debuggers',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
]
|
||||
@@ -33,35 +30,31 @@ classifiers = ['Development Status :: 5 - Production/Stable',
|
||||
# The rest in alphabetic order
|
||||
author = "Rocky Bernstein, Hartmut Goebel, John Aycock, and others"
|
||||
author_email = "rb@dustyfeet.com"
|
||||
entry_points = {
|
||||
'console_scripts': [
|
||||
'uncompyle6=uncompyle6.bin.uncompile:main_bin',
|
||||
'pydisassemble=uncompyle6.bin.pydisassemble:main',
|
||||
]}
|
||||
ftp_url = None
|
||||
install_requires = ['spark-parser >= 1.6.1, < 1.7.0',
|
||||
'xdis >= 3.3.1, < 3.4.0']
|
||||
license = 'MIT'
|
||||
# license = 'BSDish'
|
||||
mailing_list = 'python-debugger@googlegroups.com'
|
||||
modname = 'uncompyle6'
|
||||
packages = ['uncompyle6', 'uncompyle6.opcodes', 'uncompyle6.semantics', 'uncompyle6.scanners', 'uncompyle6.parsers']
|
||||
py_modules = None
|
||||
short_desc = 'Python cross-version byte-code deparser'
|
||||
short_desc = 'Python byte-code disassembler and source-code converter'
|
||||
scripts = ['bin/uncompyle6', 'bin/pydisassemble']
|
||||
|
||||
import os.path
|
||||
|
||||
|
||||
def get_srcdir():
|
||||
filename = os.path.normcase(os.path.dirname(os.path.abspath(__file__)))
|
||||
return os.path.realpath(filename)
|
||||
|
||||
ns = {}
|
||||
version = '2.3.0'
|
||||
web = 'https://github.com/rocky/python-uncompyle6/'
|
||||
|
||||
# tracebacks in zip files are funky and not debuggable
|
||||
zip_safe = True
|
||||
|
||||
|
||||
import os.path
|
||||
def get_srcdir():
|
||||
filename = os.path.normcase(os.path.dirname(os.path.abspath(__file__)))
|
||||
return os.path.realpath(filename)
|
||||
|
||||
srcdir = get_srcdir()
|
||||
|
||||
def read(*rnames):
|
||||
return open(os.path.join(srcdir, *rnames)).read()
|
||||
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
|
||||
|
||||
# Get info from files; set: long_description and VERSION
|
||||
long_description = ( read("README.rst") + '\n' )
|
||||
exec(read('uncompyle6/version.py'))
|
||||
|
78
appveyor.yml
78
appveyor.yml
@@ -1,78 +0,0 @@
|
||||
environment:
|
||||
global:
|
||||
# SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
|
||||
# /E:ON and /V:ON options are not enabled in the batch script intepreter
|
||||
# See: http://stackoverflow.com/a/13751649/163740
|
||||
CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd"
|
||||
|
||||
matrix:
|
||||
|
||||
# Pre-installed Python versions, which Appveyor may upgrade to
|
||||
# a later point release.
|
||||
# See: http://www.appveyor.com/docs/installed-software#python
|
||||
|
||||
# - PYTHON: "C:\\Python27"
|
||||
# PYTHON_VERSION: "2.7.x"
|
||||
# PYTHON_ARCH: "32"
|
||||
|
||||
- PYTHON: "C:\\Python27-x64"
|
||||
PYTHON_VERSION: "2.7.x"
|
||||
PYTHON_ARCH: "64"
|
||||
|
||||
# - PYTHON: "C:\\Python26"
|
||||
# PYTHON_VERSION: "2.6.x"
|
||||
# PYTHON_ARCH: "32"
|
||||
|
||||
# - PYTHON: "C:\\Python26-x64"
|
||||
# PYTHON_VERSION: "2.6.x"
|
||||
# PYTHON_ARCH: "64"
|
||||
|
||||
install:
|
||||
# We need wheel installed to build wheels
|
||||
- "%PYTHON%\\python.exe -m pip install wheel"
|
||||
|
||||
# Install Python (from the official .msi of http://python.org) and pip when
|
||||
# not already installed.
|
||||
- ps: if (-not(Test-Path($env:PYTHON))) { & appveyor\install.ps1 }
|
||||
|
||||
# Prepend newly installed Python to the PATH of this build (this cannot be
|
||||
# done from inside the powershell script as it would require to restart
|
||||
# the parent CMD process).
|
||||
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
|
||||
- "SET HOME=."
|
||||
|
||||
# Check that we have the expected version and architecture for Python
|
||||
- "python --version"
|
||||
- "python -c \"import struct; print(struct.calcsize('P') * 8)\""
|
||||
|
||||
# Upgrade to the latest version of pip to avoid it displaying warnings
|
||||
# about it being out of date.
|
||||
- "pip install --disable-pip-version-check --user --upgrade pip"
|
||||
|
||||
# Install the build dependencies of the project. If some dependencies contain
|
||||
# compiled extensions and are not provided as pre-built wheel packages,
|
||||
# pip will build them from source using the MSVC compiler matching the
|
||||
# target Python version and architecture
|
||||
- "%CMD_IN_ENV% pip install -r requirements.txt"
|
||||
|
||||
build_script:
|
||||
# Build the compiled extension
|
||||
- "%CMD_IN_ENV% python setup.py build"
|
||||
|
||||
test_script:
|
||||
# Run the project tests
|
||||
- "%CMD_IN_ENV% python test/test_pyenvlib.py --native --weak-verify"
|
||||
|
||||
after_test:
|
||||
# If tests are successful, create binary packages for the project.
|
||||
- "%CMD_IN_ENV% python setup.py bdist_wininst"
|
||||
- "%CMD_IN_ENV% python setup.py bdist_msi"
|
||||
- ps: "ls dist"
|
||||
|
||||
artifacts:
|
||||
# Archive the generated packages in the ci.appveyor.com build report.
|
||||
- path: dist\*
|
||||
|
||||
#on_success:
|
||||
# - TODO: upload the content of dist/*.whl to a public wheelhouse
|
||||
#
|
@@ -1,229 +0,0 @@
|
||||
# Sample script to install Python and pip under Windows
|
||||
# Authors: Olivier Grisel, Jonathan Helmus, Kyle Kastner, and Alex Willmer
|
||||
# License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
$MINICONDA_URL = "http://repo.continuum.io/miniconda/"
|
||||
$BASE_URL = "https://www.python.org/ftp/python/"
|
||||
$GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
|
||||
$GET_PIP_PATH = "C:\get-pip.py"
|
||||
|
||||
$PYTHON_PRERELEASE_REGEX = @"
|
||||
(?x)
|
||||
(?<major>\d+)
|
||||
\.
|
||||
(?<minor>\d+)
|
||||
\.
|
||||
(?<micro>\d+)
|
||||
(?<prerelease>[a-z]{1,2}\d+)
|
||||
"@
|
||||
|
||||
|
||||
function Download ($filename, $url) {
|
||||
$webclient = New-Object System.Net.WebClient
|
||||
|
||||
$basedir = $pwd.Path + "\"
|
||||
$filepath = $basedir + $filename
|
||||
if (Test-Path $filename) {
|
||||
Write-Host "Reusing" $filepath
|
||||
return $filepath
|
||||
}
|
||||
|
||||
# Download and retry up to 3 times in case of network transient errors.
|
||||
Write-Host "Downloading" $filename "from" $url
|
||||
$retry_attempts = 2
|
||||
for ($i = 0; $i -lt $retry_attempts; $i++) {
|
||||
try {
|
||||
$webclient.DownloadFile($url, $filepath)
|
||||
break
|
||||
}
|
||||
Catch [Exception]{
|
||||
Start-Sleep 1
|
||||
}
|
||||
}
|
||||
if (Test-Path $filepath) {
|
||||
Write-Host "File saved at" $filepath
|
||||
} else {
|
||||
# Retry once to get the error message if any at the last try
|
||||
$webclient.DownloadFile($url, $filepath)
|
||||
}
|
||||
return $filepath
|
||||
}
|
||||
|
||||
|
||||
function ParsePythonVersion ($python_version) {
|
||||
if ($python_version -match $PYTHON_PRERELEASE_REGEX) {
|
||||
return ([int]$matches.major, [int]$matches.minor, [int]$matches.micro,
|
||||
$matches.prerelease)
|
||||
}
|
||||
$version_obj = [version]$python_version
|
||||
return ($version_obj.major, $version_obj.minor, $version_obj.build, "")
|
||||
}
|
||||
|
||||
|
||||
function DownloadPython ($python_version, $platform_suffix) {
|
||||
$major, $minor, $micro, $prerelease = ParsePythonVersion $python_version
|
||||
|
||||
if (($major -le 2 -and $micro -eq 0) `
|
||||
-or ($major -eq 3 -and $minor -le 2 -and $micro -eq 0) `
|
||||
) {
|
||||
$dir = "$major.$minor"
|
||||
$python_version = "$major.$minor$prerelease"
|
||||
} else {
|
||||
$dir = "$major.$minor.$micro"
|
||||
}
|
||||
|
||||
if ($prerelease) {
|
||||
if (($major -le 2) `
|
||||
-or ($major -eq 3 -and $minor -eq 1) `
|
||||
-or ($major -eq 3 -and $minor -eq 2) `
|
||||
-or ($major -eq 3 -and $minor -eq 3) `
|
||||
) {
|
||||
$dir = "$dir/prev"
|
||||
}
|
||||
}
|
||||
|
||||
if (($major -le 2) -or ($major -le 3 -and $minor -le 4)) {
|
||||
$ext = "msi"
|
||||
if ($platform_suffix) {
|
||||
$platform_suffix = ".$platform_suffix"
|
||||
}
|
||||
} else {
|
||||
$ext = "exe"
|
||||
if ($platform_suffix) {
|
||||
$platform_suffix = "-$platform_suffix"
|
||||
}
|
||||
}
|
||||
|
||||
$filename = "python-$python_version$platform_suffix.$ext"
|
||||
$url = "$BASE_URL$dir/$filename"
|
||||
$filepath = Download $filename $url
|
||||
return $filepath
|
||||
}
|
||||
|
||||
|
||||
function InstallPython ($python_version, $architecture, $python_home) {
|
||||
Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home
|
||||
if (Test-Path $python_home) {
|
||||
Write-Host $python_home "already exists, skipping."
|
||||
return $false
|
||||
}
|
||||
if ($architecture -eq "32") {
|
||||
$platform_suffix = ""
|
||||
} else {
|
||||
$platform_suffix = "amd64"
|
||||
}
|
||||
$installer_path = DownloadPython $python_version $platform_suffix
|
||||
$installer_ext = [System.IO.Path]::GetExtension($installer_path)
|
||||
Write-Host "Installing $installer_path to $python_home"
|
||||
$install_log = $python_home + ".log"
|
||||
if ($installer_ext -eq '.msi') {
|
||||
InstallPythonMSI $installer_path $python_home $install_log
|
||||
} else {
|
||||
InstallPythonEXE $installer_path $python_home $install_log
|
||||
}
|
||||
if (Test-Path $python_home) {
|
||||
Write-Host "Python $python_version ($architecture) installation complete"
|
||||
} else {
|
||||
Write-Host "Failed to install Python in $python_home"
|
||||
Get-Content -Path $install_log
|
||||
Exit 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function InstallPythonEXE ($exepath, $python_home, $install_log) {
|
||||
$install_args = "/quiet InstallAllUsers=1 TargetDir=$python_home"
|
||||
RunCommand $exepath $install_args
|
||||
}
|
||||
|
||||
|
||||
function InstallPythonMSI ($msipath, $python_home, $install_log) {
|
||||
$install_args = "/qn /log $install_log /i $msipath TARGETDIR=$python_home"
|
||||
$uninstall_args = "/qn /x $msipath"
|
||||
RunCommand "msiexec.exe" $install_args
|
||||
if (-not(Test-Path $python_home)) {
|
||||
Write-Host "Python seems to be installed else-where, reinstalling."
|
||||
RunCommand "msiexec.exe" $uninstall_args
|
||||
RunCommand "msiexec.exe" $install_args
|
||||
}
|
||||
}
|
||||
|
||||
function RunCommand ($command, $command_args) {
|
||||
Write-Host $command $command_args
|
||||
Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru
|
||||
}
|
||||
|
||||
|
||||
function InstallPip ($python_home) {
|
||||
$pip_path = $python_home + "\Scripts\pip.exe"
|
||||
$python_path = $python_home + "\python.exe"
|
||||
if (-not(Test-Path $pip_path)) {
|
||||
Write-Host "Installing pip..."
|
||||
$webclient = New-Object System.Net.WebClient
|
||||
$webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH)
|
||||
Write-Host "Executing:" $python_path $GET_PIP_PATH
|
||||
& $python_path $GET_PIP_PATH
|
||||
} else {
|
||||
Write-Host "pip already installed."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function DownloadMiniconda ($python_version, $platform_suffix) {
|
||||
if ($python_version -eq "3.4") {
|
||||
$filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe"
|
||||
} else {
|
||||
$filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe"
|
||||
}
|
||||
$url = $MINICONDA_URL + $filename
|
||||
$filepath = Download $filename $url
|
||||
return $filepath
|
||||
}
|
||||
|
||||
|
||||
function InstallMiniconda ($python_version, $architecture, $python_home) {
|
||||
Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home
|
||||
if (Test-Path $python_home) {
|
||||
Write-Host $python_home "already exists, skipping."
|
||||
return $false
|
||||
}
|
||||
if ($architecture -eq "32") {
|
||||
$platform_suffix = "x86"
|
||||
} else {
|
||||
$platform_suffix = "x86_64"
|
||||
}
|
||||
$filepath = DownloadMiniconda $python_version $platform_suffix
|
||||
Write-Host "Installing" $filepath "to" $python_home
|
||||
$install_log = $python_home + ".log"
|
||||
$args = "/S /D=$python_home"
|
||||
Write-Host $filepath $args
|
||||
Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru
|
||||
if (Test-Path $python_home) {
|
||||
Write-Host "Python $python_version ($architecture) installation complete"
|
||||
} else {
|
||||
Write-Host "Failed to install Python in $python_home"
|
||||
Get-Content -Path $install_log
|
||||
Exit 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function InstallMinicondaPip ($python_home) {
|
||||
$pip_path = $python_home + "\Scripts\pip.exe"
|
||||
$conda_path = $python_home + "\Scripts\conda.exe"
|
||||
if (-not(Test-Path $pip_path)) {
|
||||
Write-Host "Installing pip..."
|
||||
$args = "install --yes pip"
|
||||
Write-Host $conda_path $args
|
||||
Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru
|
||||
} else {
|
||||
Write-Host "pip already installed."
|
||||
}
|
||||
}
|
||||
|
||||
function main () {
|
||||
InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON
|
||||
InstallPip $env:PYTHON
|
||||
}
|
||||
|
||||
main
|
@@ -1,87 +0,0 @@
|
||||
:: To build extensions for 64 bit Python 3, we need to configure environment
|
||||
:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of:
|
||||
:: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1)
|
||||
::
|
||||
:: To build extensions for 64 bit Python 2, we need to configure environment
|
||||
:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of:
|
||||
:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0)
|
||||
::
|
||||
:: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific
|
||||
:: environment configurations.
|
||||
::
|
||||
:: Note: this script needs to be run with the /E:ON and /V:ON flags for the
|
||||
:: cmd interpreter, at least for (SDK v7.0)
|
||||
::
|
||||
:: More details at:
|
||||
:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows
|
||||
:: http://stackoverflow.com/a/13751649/163740
|
||||
::
|
||||
:: Author: Olivier Grisel
|
||||
:: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
::
|
||||
:: Notes about batch files for Python people:
|
||||
::
|
||||
:: Quotes in values are literally part of the values:
|
||||
:: SET FOO="bar"
|
||||
:: FOO is now five characters long: " b a r "
|
||||
:: If you don't want quotes, don't include them on the right-hand side.
|
||||
::
|
||||
:: The CALL lines at the end of this file look redundant, but if you move them
|
||||
:: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y
|
||||
:: case, I don't know why.
|
||||
@ECHO OFF
|
||||
SET COMMAND_TO_RUN=%*
|
||||
SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows
|
||||
SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf
|
||||
|
||||
:: Extract the major and minor versions, and allow for the minor version to be
|
||||
:: more than 9. This requires the version number to have two dots in it.
|
||||
SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1%
|
||||
IF "%PYTHON_VERSION:~3,1%" == "." (
|
||||
SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1%
|
||||
) ELSE (
|
||||
SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2%
|
||||
)
|
||||
|
||||
:: Based on the Python version, determine what SDK version to use, and whether
|
||||
:: to set the SDK for 64-bit.
|
||||
IF %MAJOR_PYTHON_VERSION% == 2 (
|
||||
SET WINDOWS_SDK_VERSION="v7.0"
|
||||
SET SET_SDK_64=Y
|
||||
) ELSE (
|
||||
IF %MAJOR_PYTHON_VERSION% == 3 (
|
||||
SET WINDOWS_SDK_VERSION="v7.1"
|
||||
IF %MINOR_PYTHON_VERSION% LEQ 4 (
|
||||
SET SET_SDK_64=Y
|
||||
) ELSE (
|
||||
SET SET_SDK_64=N
|
||||
IF EXIST "%WIN_WDK%" (
|
||||
:: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/
|
||||
REN "%WIN_WDK%" 0wdf
|
||||
)
|
||||
)
|
||||
) ELSE (
|
||||
ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%"
|
||||
EXIT 1
|
||||
)
|
||||
)
|
||||
|
||||
IF %PYTHON_ARCH% == 64 (
|
||||
IF %SET_SDK_64% == Y (
|
||||
ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture
|
||||
SET DISTUTILS_USE_SDK=1
|
||||
SET MSSdk=1
|
||||
"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION%
|
||||
"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release
|
||||
ECHO Executing: %COMMAND_TO_RUN%
|
||||
call %COMMAND_TO_RUN% || EXIT 1
|
||||
) ELSE (
|
||||
ECHO Using default MSVC build environment for 64 bit architecture
|
||||
ECHO Executing: %COMMAND_TO_RUN%
|
||||
call %COMMAND_TO_RUN% || EXIT 1
|
||||
)
|
||||
) ELSE (
|
||||
ECHO Using default MSVC build environment for 32 bit architecture
|
||||
ECHO Executing: %COMMAND_TO_RUN%
|
||||
call %COMMAND_TO_RUN% || EXIT 1
|
||||
)
|
@@ -1,3 +1,77 @@
|
||||
#!/usr/bin/env python
|
||||
from uncompyle6.bin.pydisassemble import main
|
||||
main()
|
||||
# Mode: -*- python -*-
|
||||
#
|
||||
# Copyright (c) 2015 by Rocky Bernstein <rb@dustyfeet.com>
|
||||
#
|
||||
from __future__ import print_function
|
||||
import sys, os, getopt
|
||||
|
||||
program = os.path.basename(__file__)
|
||||
|
||||
__doc__ = """
|
||||
Usage: %s [OPTIONS]... FILE
|
||||
|
||||
Examples:
|
||||
%s foo.pyc
|
||||
%s foo.py
|
||||
%s -o foo.pydis foo.pyc
|
||||
%s -o /tmp foo.pyc
|
||||
|
||||
Options:
|
||||
-o <path> output decompiled files to this path:
|
||||
if multiple input files are decompiled, the common prefix
|
||||
is stripped from these names and the remainder appended to
|
||||
<path>
|
||||
--help show this message
|
||||
|
||||
""" % ((program,) * 5)
|
||||
|
||||
|
||||
Usage_short = \
|
||||
"%s [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..." % program
|
||||
|
||||
from uncompyle6 import check_python_version
|
||||
from uncompyle6.disas import disassemble_files
|
||||
|
||||
check_python_version(program)
|
||||
|
||||
outfile = '-'
|
||||
out_base = None
|
||||
|
||||
|
||||
try:
|
||||
opts, files = getopt.getopt(sys.argv[1:], 'ho:', ['help'])
|
||||
except getopt.GetoptError as e:
|
||||
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
|
||||
sys.exit(-1)
|
||||
|
||||
for opt, val in opts:
|
||||
if opt in ('-h', '--help'):
|
||||
print(__doc__)
|
||||
sys.exit(0)
|
||||
elif opt == '-o':
|
||||
outfile = val
|
||||
else:
|
||||
print(opt)
|
||||
print(Usage_short)
|
||||
sys.exit(1)
|
||||
|
||||
# argl, commonprefix works on strings, not on path parts,
|
||||
# thus we must handle the case with files in 'some/classes'
|
||||
# and 'some/cmds'
|
||||
src_base = os.path.commonprefix(files)
|
||||
if src_base[-1:] != os.sep:
|
||||
src_base = os.path.dirname(src_base)
|
||||
if src_base:
|
||||
sb_len = len( os.path.join(src_base, '') )
|
||||
files = [f[sb_len:] for f in files]
|
||||
del sb_len
|
||||
|
||||
if outfile == '-':
|
||||
outfile = None # use stdout
|
||||
elif outfile and os.path.isdir(outfile):
|
||||
out_base = outfile; outfile = None
|
||||
elif outfile and len(files) > 1:
|
||||
out_base = outfile; outfile = None
|
||||
|
||||
disassemble_files(src_base, out_base, files, outfile)
|
||||
|
214
bin/uncompyle6
214
bin/uncompyle6
@@ -1,3 +1,213 @@
|
||||
#!/usr/bin/env python
|
||||
from uncompyle6.bin.uncompile import main_bin
|
||||
main_bin()
|
||||
# Mode: -*- python -*-
|
||||
#
|
||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
# Copyright (c) 2015 by Rocky Bernstein
|
||||
|
||||
"""
|
||||
Usage: uncompyle6 [OPTIONS]... [ FILE | DIR]...
|
||||
|
||||
Examples:
|
||||
uncompyle6 foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout
|
||||
uncompyle6 -o . foo.pyc bar.pyc # decompile to ./foo.pyc_dis and ./bar.pyc_dis
|
||||
uncompyle6 -o /tmp /usr/lib/python1.5 # decompile whole library
|
||||
|
||||
Options:
|
||||
-o <path> output decompiled files to this path:
|
||||
if multiple input files are decompiled, the common prefix
|
||||
is stripped from these names and the remainder appended to
|
||||
<path>
|
||||
uncompyle6 -o /tmp bla/fasel.pyc bla/foo.pyc
|
||||
-> /tmp/fasel.pyc_dis, /tmp/foo.pyc_dis
|
||||
uncompyle6 -o /tmp bla/fasel.pyc bar/foo.pyc
|
||||
-> /tmp/bla/fasel.pyc_dis, /tmp/bar/foo.pyc_dis
|
||||
uncompyle6 -o /tmp /usr/lib/python1.5
|
||||
-> /tmp/smtplib.pyc_dis ... /tmp/lib-tk/FixTk.pyc_dis
|
||||
-c <file> attempts a disassembly after compiling <file>
|
||||
-d print timestamps
|
||||
-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)
|
||||
--help show this message
|
||||
|
||||
Debugging Options:
|
||||
--asm -a include byte-code (disables --verify)
|
||||
--grammar -g show matching grammar
|
||||
--treee -t include syntax tree (disables --verify)
|
||||
|
||||
Extensions of generated files:
|
||||
'.pyc_dis' '.pyo_dis' successfully decompiled (and verified if --verify)
|
||||
+ '_unverified' successfully decompile but --verify failed
|
||||
+ '_failed' decompile failed (contact author for enhancement)
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import sys, os, getopt, time
|
||||
|
||||
program = os.path.basename(__file__)
|
||||
|
||||
from uncompyle6 import verify, check_python_version
|
||||
from uncompyle6.main import main, status_msg
|
||||
|
||||
def usage():
|
||||
print("""usage:
|
||||
%s [--help] [--verify] [--asm] [--tree] [--grammar] [-o <path>] FILE|DIR...
|
||||
""" % program)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
check_python_version(program)
|
||||
|
||||
showasm = showast = do_verify = recurse_dirs = False
|
||||
numproc = 0
|
||||
outfile = '-'
|
||||
out_base = None
|
||||
codes = []
|
||||
timestamp = False
|
||||
timestampfmt = "# %Y.%m.%d %H:%M:%S %Z"
|
||||
|
||||
try:
|
||||
opts, files = getopt.getopt(sys.argv[1:], 'hagtdro:c:p:',
|
||||
'help asm grammar recurse timestamp tree verify '
|
||||
'showgrammar'.split(' '))
|
||||
except getopt.GetoptError as e:
|
||||
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
|
||||
sys.exit(-1)
|
||||
|
||||
options = {}
|
||||
for opt, val in opts:
|
||||
if opt in ('-h', '--help'):
|
||||
print(__doc__)
|
||||
sys.exit(0)
|
||||
elif opt == '--verify':
|
||||
options['do_verify'] = True
|
||||
elif opt in ('--asm', '-a'):
|
||||
options['showasm'] = True
|
||||
options['do_verify'] = False
|
||||
elif opt in ('--tree', '-t'):
|
||||
options['showast'] = True
|
||||
options['do_verify'] = False
|
||||
elif opt in ('--grammar', '-g'):
|
||||
options['showgrammar'] = True
|
||||
elif opt == '-o':
|
||||
outfile = val
|
||||
elif opt in ('--timestamp', '-d'):
|
||||
timestamp = True
|
||||
elif opt == '-c':
|
||||
codes.append(val)
|
||||
elif opt == '-p':
|
||||
numproc = int(val)
|
||||
elif opt in ('--recurse', '-r'):
|
||||
recurse_dirs = True
|
||||
else:
|
||||
print(opt, file=sys.stderr)
|
||||
usage()
|
||||
|
||||
# expand directory if specified
|
||||
if recurse_dirs:
|
||||
expanded_files = []
|
||||
for f in files:
|
||||
if os.path.isdir(f):
|
||||
for root, _, dir_files in os.walk(f):
|
||||
for df in dir_files:
|
||||
if df.endswith('.pyc') or df.endswith('.pyo'):
|
||||
expanded_files.append(os.path.join(root, df))
|
||||
files = expanded_files
|
||||
|
||||
# argl, commonprefix works on strings, not on path parts,
|
||||
# thus we must handle the case with files in 'some/classes'
|
||||
# and 'some/cmds'
|
||||
src_base = os.path.commonprefix(files)
|
||||
if src_base[-1:] != os.sep:
|
||||
src_base = os.path.dirname(src_base)
|
||||
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)
|
||||
usage()
|
||||
|
||||
|
||||
if outfile == '-':
|
||||
outfile = None # use stdout
|
||||
elif outfile and os.path.isdir(outfile):
|
||||
out_base = outfile; outfile = None
|
||||
elif outfile and len(files) > 1:
|
||||
out_base = outfile; outfile = None
|
||||
|
||||
if timestamp:
|
||||
print(time.strftime(timestampfmt))
|
||||
|
||||
if numproc <= 1:
|
||||
try:
|
||||
result = main(src_base, out_base, files, codes, outfile,
|
||||
**options)
|
||||
if len(files) > 1:
|
||||
mess = status_msg(do_verify, *result)
|
||||
print('# ' + mess)
|
||||
pass
|
||||
except (KeyboardInterrupt):
|
||||
pass
|
||||
except verify.VerifyCmpError:
|
||||
raise
|
||||
else:
|
||||
from multiprocessing import Process, Queue
|
||||
|
||||
try:
|
||||
from Queue import Empty
|
||||
except ImportError:
|
||||
from Queue import Empty
|
||||
|
||||
fqueue = Queue(len(files)+numproc)
|
||||
for f in files:
|
||||
fqueue.put(f)
|
||||
for i in range(numproc):
|
||||
fqueue.put(None)
|
||||
|
||||
rqueue = Queue(numproc)
|
||||
|
||||
def process_func():
|
||||
try:
|
||||
(tot_files, okay_files, failed_files, verify_failed_files) = (0, 0, 0, 0)
|
||||
while 1:
|
||||
f = fqueue.get()
|
||||
if f is None:
|
||||
break
|
||||
(t, o, f, v) = \
|
||||
main(src_base, out_base, [f], codes, outfile, **options)
|
||||
tot_files += t
|
||||
okay_files += o
|
||||
failed_files += f
|
||||
verify_failed_files += v
|
||||
except (Empty, KeyboardInterrupt):
|
||||
pass
|
||||
rqueue.put((tot_files, okay_files, failed_files, verify_failed_files))
|
||||
rqueue.close()
|
||||
|
||||
try:
|
||||
procs = [Process(target=process_func) for i in range(numproc)]
|
||||
for p in procs:
|
||||
p.start()
|
||||
for p in procs:
|
||||
p.join()
|
||||
try:
|
||||
(tot_files, okay_files, failed_files, verify_failed_files) = (0, 0, 0, 0)
|
||||
while True:
|
||||
(t, o, f, v) = rqueue.get(False)
|
||||
tot_files += t
|
||||
okay_files += o
|
||||
failed_files += f
|
||||
verify_failed_files += v
|
||||
except Empty:
|
||||
pass
|
||||
print('# decompiled %i files: %i okay, %i failed, %i verify failed' %
|
||||
(tot_files, okay_files, failed_files, verify_failed_files))
|
||||
except (KeyboardInterrupt, OSError):
|
||||
pass
|
||||
|
||||
|
||||
if timestamp:
|
||||
print(time.strftime(timestampfmt))
|
||||
|
@@ -1,13 +1,13 @@
|
||||
machine:
|
||||
python:
|
||||
version: 2.7.10
|
||||
version: 2.7.8
|
||||
environment:
|
||||
COMPILE: --compile
|
||||
|
||||
dependencies:
|
||||
override:
|
||||
- pip install -e .
|
||||
- pip install -r requirements.txt
|
||||
- pip install -r requirements-dev.txt
|
||||
test:
|
||||
override:
|
||||
- python ./setup.py develop && make check-2.6
|
||||
- python ./setup.py develop && make check-2.7
|
||||
|
1
pytest/.gitignore
vendored
1
pytest/.gitignore
vendored
@@ -1,2 +1 @@
|
||||
/.hypothesis
|
||||
/__pycache__
|
||||
|
@@ -1,6 +0,0 @@
|
||||
source ../.venv.3.6/bin/activate
|
||||
py.test -k test_CALL_FUNCTION_KW
|
||||
source ../.venv.3.5/bin/activate
|
||||
py.test -k test_CALL_FUNCTION_KW
|
||||
source ../.venv.2.7/bin/activate
|
||||
py.test -k test_CALL_FUNCTION_KW
|
@@ -1,21 +0,0 @@
|
||||
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)
|
@@ -20,20 +20,12 @@ def for_range_stmt():
|
||||
for i in range(2):
|
||||
i+1
|
||||
|
||||
# # FIXME: add this test - but for Python 2.7+ only
|
||||
# def set_comp():
|
||||
# {y for y in range(3)}
|
||||
|
||||
# FIXME: add this test
|
||||
def list_comp():
|
||||
[y for y in range(3)]
|
||||
|
||||
def get_parsed_for_fn(fn):
|
||||
code = fn.__code__ if PYTHON3 else fn.func_code
|
||||
return deparse(PYTHON_VERSION, code)
|
||||
|
||||
def check_expect(expect, parsed):
|
||||
debug = False
|
||||
debug = True
|
||||
i = 2
|
||||
max_expect = len(expect)
|
||||
for name, offset in sorted(parsed.offsets.keys()):
|
||||
@@ -168,7 +160,7 @@ return (x, y)
|
||||
-------------
|
||||
""".split("\n")
|
||||
check_expect(expect, parsed)
|
||||
########################################################
|
||||
# ########################################################
|
||||
# # try
|
||||
|
||||
# expect = """
|
||||
@@ -299,12 +291,6 @@ return
|
||||
Contained in...
|
||||
i + 1
|
||||
-----
|
||||
31
|
||||
return
|
||||
------
|
||||
Contained in...
|
||||
for i in range(2): ...
|
||||
------------------ ...
|
||||
34
|
||||
return
|
||||
------
|
||||
|
@@ -24,7 +24,7 @@ os.chdir(src_dir)
|
||||
def test_funcoutput(capfd, test_tuple, function_to_test):
|
||||
|
||||
in_file , filename_expected = test_tuple
|
||||
function_to_test(in_file, native=False)
|
||||
function_to_test(in_file)
|
||||
resout, reserr = capfd.readouterr()
|
||||
expected = open(filename_expected, "r").read()
|
||||
if resout != expected:
|
@@ -1,78 +0,0 @@
|
||||
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()
|
@@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
from uncompyle6 import PYTHON_VERSION, IS_PYPY
|
||||
from uncompyle6.scanner import get_scanner
|
||||
from array import array
|
||||
def bug(state, slotstate):
|
||||
if state:
|
||||
if slotstate is not None:
|
||||
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)
|
||||
print(PYTHON_VERSION)
|
||||
if 2.7 <= PYTHON_VERSION <= 3.0 and not IS_PYPY:
|
||||
n = scan.setup_code(code)
|
||||
scan.build_lines_data(code, n)
|
||||
scan.build_prev_op(n)
|
||||
fjt = scan.find_jump_targets(False)
|
||||
assert {15: [3], 69: [66], 63: [18]} == fjt
|
||||
assert scan.structs == \
|
||||
[{'start': 0, 'end': 72, 'type': 'root'},
|
||||
{'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(False)
|
||||
assert {69: [66], 63: [18]} == fjt
|
||||
assert scan.structs == \
|
||||
[{'end': 72, 'type': 'root', 'start': 0},
|
||||
{'end': 66, 'type': 'if-then', 'start': 6},
|
||||
{'end': 63, 'type': 'if-then', 'start': 18},
|
||||
{'end': 59, 'type': 'for-loop', 'start': 31},
|
||||
{'end': 63, 'type': 'for-else', 'start': 62}]
|
||||
else:
|
||||
assert True, "FIXME: should note fixed"
|
||||
return
|
@@ -1,65 +0,0 @@
|
||||
import re
|
||||
from uncompyle6 import PYTHON_VERSION, PYTHON3, IS_PYPY # , PYTHON_VERSION
|
||||
from uncompyle6.parser import get_python_parser, python_parser
|
||||
from uncompyle6.scanner import get_scanner
|
||||
|
||||
def test_grammar():
|
||||
|
||||
def check_tokens(tokens, opcode_set):
|
||||
remain_tokens = set(tokens) - opcode_set
|
||||
remain_tokens = set([re.sub('_\d+$','', t) for t in remain_tokens])
|
||||
remain_tokens = set([re.sub('_CONT$','', t) for t in remain_tokens])
|
||||
remain_tokens = set(remain_tokens) - opcode_set
|
||||
assert remain_tokens == set([]), \
|
||||
"Remaining tokens %s\n====\n%s" % (remain_tokens, p.dumpGrammar())
|
||||
|
||||
p = get_python_parser(PYTHON_VERSION, is_pypy=IS_PYPY)
|
||||
lhs, rhs, tokens, right_recursive = p.checkSets()
|
||||
expect_lhs = set(['expr1024', 'pos_arg'])
|
||||
unused_rhs = set(['build_list', 'call_function', 'mkfunc',
|
||||
'mklambda',
|
||||
'unpack', 'unpack_list'])
|
||||
expect_right_recursive = [['designList', ('designator', 'DUP_TOP', 'designList')]]
|
||||
if PYTHON3:
|
||||
expect_lhs.add('load_genexpr')
|
||||
|
||||
unused_rhs = unused_rhs.union(set("""
|
||||
except_pop_except genexpr classdefdeco2 listcomp
|
||||
""".split()))
|
||||
if 3.0 <= PYTHON_VERSION:
|
||||
expect_lhs.add("annotate_arg")
|
||||
expect_lhs.add("annotate_tuple")
|
||||
unused_rhs.add("mkfunc_annotate")
|
||||
pass
|
||||
else:
|
||||
expect_lhs.add('kwarg')
|
||||
assert expect_lhs == set(lhs)
|
||||
assert unused_rhs == set(rhs)
|
||||
assert expect_right_recursive == right_recursive
|
||||
s = get_scanner(PYTHON_VERSION, IS_PYPY)
|
||||
ignore_set = set(
|
||||
"""
|
||||
JUMP_BACK CONTINUE RETURN_END_IF
|
||||
COME_FROM COME_FROM_EXCEPT
|
||||
COME_FROM_EXCEPT_CLAUSE
|
||||
COME_FROM_LOOP COME_FROM_WITH
|
||||
COME_FROM_FINALLY ELSE
|
||||
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP
|
||||
LAMBDA_MARKER RETURN_LAST
|
||||
""".split())
|
||||
if 2.6 <= PYTHON_VERSION <= 2.7:
|
||||
opcode_set = set(s.opc.opname).union(ignore_set)
|
||||
check_tokens(tokens, opcode_set)
|
||||
elif PYTHON_VERSION == 3.4:
|
||||
ignore_set.add('LOAD_CLASSNAME')
|
||||
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})
|
10
pytest/test_load.py
Normal file
10
pytest/test_load.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import sys
|
||||
from uncompyle6.load import load_file, check_object_path, load_module
|
||||
|
||||
def test_load():
|
||||
"""Basic test of load_file, check_object_path and load_module"""
|
||||
co = load_file(__file__)
|
||||
obj_path = check_object_path(__file__)
|
||||
version, timestamp, magic_int, co2 = load_module(obj_path)
|
||||
assert sys.version[0:3] == str(version)
|
||||
assert co == co2
|
@@ -9,6 +9,8 @@ def test_single_mode():
|
||||
'i = j % 4',
|
||||
'i = {}',
|
||||
'i = []',
|
||||
'while i < 1 or stop:\n i\n',
|
||||
'while i < 1 or stop:\n print%s\n' % ('(i)' if PYTHON3 else ' i'),
|
||||
'for i in range(10):\n i\n',
|
||||
'for i in range(10):\n for j in range(10):\n i + j\n',
|
||||
'try:\n i\nexcept Exception:\n j\nelse:\n k\n'
|
||||
|
17
pytest/testdata/if-2.7.right
vendored
17
pytest/testdata/if-2.7.right
vendored
@@ -1,12 +1,13 @@
|
||||
# Python 2.7
|
||||
# Embedded file name: simple_source/branching/05_if.py
|
||||
|
||||
6 0 LOAD_NAME 0 'True'
|
||||
3 POP_JUMP_IF_FALSE 15 'to 15'
|
||||
6 0 LOAD_NAME 'True'
|
||||
3 POP_JUMP_IF_FALSE '15'
|
||||
|
||||
7 6 LOAD_NAME 'False'
|
||||
9 STORE_NAME 'b'
|
||||
12 JUMP_FORWARD '15'
|
||||
15_0 COME_FROM '12'
|
||||
15 LOAD_CONST ''
|
||||
18 RETURN_VALUE ''
|
||||
|
||||
7 6 LOAD_NAME 1 'False'
|
||||
9 STORE_NAME 2 'b'
|
||||
12 JUMP_FORWARD 0 'to 15'
|
||||
15_0 COME_FROM '12'
|
||||
15 LOAD_CONST 0 ''
|
||||
18 RETURN_VALUE
|
||||
|
21
pytest/testdata/ifelse-2.7.right
vendored
21
pytest/testdata/ifelse-2.7.right
vendored
@@ -1,15 +1,16 @@
|
||||
# Python 2.7
|
||||
# Embedded file name: simple_source/branching/05_ifelse.py
|
||||
|
||||
3 0 LOAD_NAME 0 'True'
|
||||
3 POP_JUMP_IF_FALSE 15 'to 15'
|
||||
3 0 LOAD_NAME 'True'
|
||||
3 POP_JUMP_IF_FALSE '15'
|
||||
|
||||
4 6 LOAD_CONST 0 1
|
||||
9 STORE_NAME 1 'b'
|
||||
12 JUMP_FORWARD 6 'to 21'
|
||||
4 6 LOAD_CONST 1
|
||||
9 STORE_NAME 'b'
|
||||
12 JUMP_FORWARD '21'
|
||||
|
||||
6 15 LOAD_CONST 2
|
||||
18 STORE_NAME 'd'
|
||||
21_0 COME_FROM '12'
|
||||
21 LOAD_CONST ''
|
||||
24 RETURN_VALUE ''
|
||||
|
||||
6 15 LOAD_CONST 1 2
|
||||
18 STORE_NAME 2 'd'
|
||||
21_0 COME_FROM '12'
|
||||
21 LOAD_CONST 2 ''
|
||||
24 RETURN_VALUE
|
||||
|
@@ -1,146 +0,0 @@
|
||||
# future
|
||||
from __future__ import print_function
|
||||
# std
|
||||
import os
|
||||
import difflib
|
||||
import subprocess
|
||||
import tempfile
|
||||
import functools
|
||||
from StringIO import StringIO
|
||||
# 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=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,2 @@
|
||||
pytest>=3.0.0
|
||||
pytest
|
||||
flake8
|
||||
hypothesis
|
||||
|
@@ -1,2 +1 @@
|
||||
# Pick up stuff from setup.py
|
||||
-e .
|
||||
spark_parser >= 1.1.0
|
||||
|
38
setup.py
38
setup.py
@@ -2,28 +2,36 @@
|
||||
|
||||
"""Setup script for the 'uncompyle6' distribution."""
|
||||
|
||||
from __pkginfo__ import \
|
||||
author, author_email, install_requires, \
|
||||
license, long_description, classifiers, \
|
||||
entry_points, modname, py_modules, \
|
||||
short_desc, VERSION, web, \
|
||||
zip_safe
|
||||
# Get the package information used in setup().
|
||||
# from __pkginfo__ import \
|
||||
# author, author_email, classifiers, \
|
||||
# install_requires, license, long_description, \
|
||||
# modname, packages, py_modules, \
|
||||
# short_desc, version, web, zip_safe
|
||||
|
||||
from __pkginfo__ import \
|
||||
author, author_email, \
|
||||
long_description, \
|
||||
modname, packages, py_modules, scripts, \
|
||||
short_desc, version, web, zip_safe
|
||||
|
||||
__import__('pkg_resources')
|
||||
from setuptools import setup
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
setup(
|
||||
author = author,
|
||||
author_email = author_email,
|
||||
classifiers = classifiers,
|
||||
# classifiers = classifiers,
|
||||
description = short_desc,
|
||||
entry_points = entry_points,
|
||||
install_requires = install_requires,
|
||||
license = license,
|
||||
# install_requires = install_requires,
|
||||
# license = license,
|
||||
long_description = long_description,
|
||||
name = modname,
|
||||
packages = find_packages(),
|
||||
py_modules = py_modules,
|
||||
name = modname,
|
||||
packages = packages,
|
||||
test_suite = 'nose.collector',
|
||||
url = web,
|
||||
tests_require = ['nose>=1.0'],
|
||||
version = VERSION,
|
||||
setup_requires = ['nose>=1.0'],
|
||||
scripts = scripts,
|
||||
version = version,
|
||||
zip_safe = zip_safe)
|
||||
|
138
test/Makefile
138
test/Makefile
@@ -3,7 +3,7 @@ PHONY=check clean dist distclean test test-unit test-functional rmChangeLog clea
|
||||
GIT2CL ?= git2cl
|
||||
PYTHON ?= python
|
||||
|
||||
PYTHON_VERSION = $(shell $(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2)
|
||||
PYTHON_VERSION = $(shell $(PYTHON) -V | cut -d ' ' -f 2 | cut -d'.' -f1,2)
|
||||
NATIVE_CHECK = check-$(PYTHON_VERSION)
|
||||
|
||||
# Set COMPILE='--compile' to force compilation before check
|
||||
@@ -16,94 +16,52 @@ check-short:
|
||||
|
||||
# Run all tests
|
||||
check:
|
||||
$(MAKE) check-$(PYTHON_VERSION)
|
||||
@$(PYTHON) -V && PYTHON_VERSION=`$(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2`; \
|
||||
$(MAKE) check-$$PYTHON_VERSION
|
||||
|
||||
#: Run working tests from Python 2.6 or 2.7
|
||||
check-2.4 check-2.5 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
|
||||
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
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.1 --weak-verify $(COMPILE)
|
||||
|
||||
#: Run working tests from Python 3.2
|
||||
check-3.2: check-bytecode
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.2 --weak-verify $(COMPILE)
|
||||
check-2.6 check-2.7: check-bytecode-sans-3.5 check-2.7-ok
|
||||
|
||||
#: 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.5
|
||||
check-3.5: check-bytecode
|
||||
|
||||
#: 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 --verify $(COMPILE)
|
||||
|
||||
#: Run working tests from Python 3.5
|
||||
check-3.5: check-bytecode
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.5 --verify $(COMPILE)
|
||||
|
||||
#: Run working tests from Python 3.6
|
||||
check-3.6: check-bytecode
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.6 --verify $(COMPILE)
|
||||
|
||||
#: Check deparsing only, but from a different Python version
|
||||
check-disasm:
|
||||
$(PYTHON) dis-compare.py
|
||||
|
||||
#: Check deparsing bytecode 1.x only
|
||||
check-bytecode-1:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-1.5
|
||||
|
||||
#: Check deparsing bytecode 2.x only
|
||||
#: Check deparsing bytecode only
|
||||
check-bytecode-2:
|
||||
$(PYTHON) test_pythonlib.py \
|
||||
--bytecode-2.1 --bytecode-2.2 --bytecode-2.3 --bytecode-2.4 \
|
||||
--bytecode-2.5 --bytecode-2.6 --bytecode-2.7 --bytecode-pypy2.7
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.5 --bytecode-2.6 --bytecode-2.7
|
||||
|
||||
#: Check deparsing bytecode 3.x only
|
||||
check-bytecode-3:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.0 \
|
||||
--bytecode-3.1 --bytecode-3.2 --bytecode-3.3 \
|
||||
--bytecode-3.4 --bytecode-3.5 --bytecode-3.6 --bytecode-pypy3.2
|
||||
#: Check deparsing bytecode only
|
||||
check-bytecode:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.5 --bytecode-2.6 --bytecode-2.7 \
|
||||
--bytecode-3.2 --bytecode-3.3 --bytecode-3.4 --bytecode-3.5
|
||||
|
||||
#: Check deparsing bytecode that works running Python 2 and Python 3
|
||||
check-bytecode: check-bytecode-3
|
||||
$(PYTHON) test_pythonlib.py \
|
||||
--bytecode-2.1 --bytecode-2.2 --bytecode-2.3 --bytecode-2.4 \
|
||||
--bytecode-2.5 --bytecode-2.6 --bytecode-2.7 \
|
||||
--bytecode-pypy2.7 --bytecode-1
|
||||
|
||||
|
||||
#: Check deparsing Python 2.1
|
||||
check-bytecode-2.1:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.1
|
||||
|
||||
#: Check deparsing Python 2.2
|
||||
check-bytecode-2.2:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.2
|
||||
|
||||
#: Check deparsing Python 2.3
|
||||
check-bytecode-2.3:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.3
|
||||
|
||||
#: Check deparsing Python 2.4
|
||||
check-bytecode-2.4:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.4
|
||||
#: Check deparsing bytecode only
|
||||
check-bytecode-sans-3.5:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.5 --bytecode-2.6 --bytecode-2.7 \
|
||||
--bytecode-3.2 --bytecode-3.3 --bytecode-3.4
|
||||
|
||||
#: Check deparsing Python 2.5
|
||||
check-bytecode-2.5:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.5
|
||||
|
||||
#: Check deparsing Python 3.0
|
||||
check-bytecode-3.0:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.0
|
||||
#: Check deparsing Python 2.6
|
||||
check-bytecode-2.6:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.6
|
||||
|
||||
#: Check deparsing Python 3.1
|
||||
check-bytecode-3.1:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.1
|
||||
#: Check deparsing Python 2.7
|
||||
check-bytecode-2.7:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-2.7
|
||||
|
||||
#: Check deparsing Python 3.2
|
||||
check-bytecode-3.2:
|
||||
@@ -121,41 +79,9 @@ check-bytecode-3.4:
|
||||
check-bytecode-3.5:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.5
|
||||
|
||||
#: Check deparsing Python 3.6
|
||||
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) --weak-verify $(COMPILE)
|
||||
|
||||
#: Run longer Python 2.6's lib files known to be okay
|
||||
check-2.4-ok:
|
||||
$(PYTHON) test_pythonlib.py --ok-2.4 --verify $(COMPILE)
|
||||
|
||||
#: Run longer Python 2.6's lib files known to be okay
|
||||
check-2.6-ok:
|
||||
$(PYTHON) test_pythonlib.py --ok-2.6 --verify $(COMPILE)
|
||||
$(PYTHON) test_pythonlib.py --bytecode-$(PYTHON_VERSION) --verify $(COMPILE)
|
||||
|
||||
#: Run longer Python 2.7's lib files known to be okay
|
||||
check-2.7-ok:
|
||||
@@ -169,18 +95,6 @@ check-3.2-ok:
|
||||
check-3.4-ok:
|
||||
$(PYTHON) test_pythonlib.py --ok-3.4 --verify $(COMPILE)
|
||||
|
||||
#: PyPy of some sort. E.g. [PyPy 5.0.1 with GCC 4.8.4]
|
||||
# Skip for now
|
||||
2.6:
|
||||
|
||||
#: PyPy 5.0.x with Python 2.7 ...
|
||||
pypy-2.7 5.0 5.3:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-pypy2.7 --verify
|
||||
|
||||
#: PyPy 2.4.x with Python 3.2 ...
|
||||
pypy-3.2 2.4:
|
||||
$(PYTHON) test_pythonlib.py --bytecode-pypy3.2 --verify
|
||||
|
||||
clean: clean-py-dis clean-dis clean-unverified
|
||||
|
||||
clean-dis:
|
||||
@@ -189,6 +103,6 @@ clean-dis:
|
||||
clean-unverified:
|
||||
find . -name '*_unverified' -exec rm -v '{}' ';'
|
||||
|
||||
#: Clean temporary compile/decompile/verify directories in /tmp
|
||||
#: Clean temporary compile/decompile/verify direcotries in /tmp
|
||||
clean-py-dis:
|
||||
rm -fr /tmp/py-dis-* || true
|
||||
|
@@ -2,15 +2,11 @@
|
||||
""" Trivial helper program to bytecompile and run an uncompile
|
||||
"""
|
||||
import os, sys, py_compile
|
||||
assert len(sys.argv) >= 2
|
||||
assert len(sys.argv) == 2
|
||||
path = sys.argv[1]
|
||||
short = os.path.basename(path)
|
||||
version = sys.version[0:3]
|
||||
for path in sys.argv[1:]:
|
||||
short = os.path.basename(path)
|
||||
if hasattr(sys, 'pypy_version_info'):
|
||||
cfile = "bytecode_pypy%s/%s" % (version, short) + 'c'
|
||||
else:
|
||||
cfile = "bytecode_%s/%s" % (version, short) + 'c'
|
||||
print("byte-compiling %s to %s" % (path, cfile))
|
||||
py_compile.compile(path, cfile)
|
||||
if isinstance(version, str) or version >= (2, 6, 0):
|
||||
os.system("../bin/uncompyle6 -a -t %s" % cfile)
|
||||
cfile = "bytecode_%s/%s" % (version, short) + 'c'
|
||||
print("byte-compiling %s to %s" % (path, cfile))
|
||||
py_compile.compile(path, cfile)
|
||||
os.system("../bin/uncompyle6 -a -t %s" % cfile)
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user