diff --git a/MANIFEST b/MANIFEST
new file mode 100755
index 00000000..7f00a26d
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,22 @@
+README
+compile_tests
+setup.cfg
+setup.py
+test/*
+uncompyle2/__init__.py
+uncompyle2/disas.py
+uncompyle2/magics.py
+uncompyle2/parser.py
+uncompyle2/scanner.py
+uncompyle2/scanner25.py
+uncompyle2/scanner26.py
+uncompyle2/scanner27.py
+uncompyle2/spark.py
+uncompyle2/verify.py
+uncompyle2/walker.py
+uncompyle2/opcode/__init__.py
+uncompyle2/opcode/opcode_23.py
+uncompyle2/opcode/opcode_24.py
+uncompyle2/opcode/opcode_25.py
+uncompyle2/opcode/opcode_26.py
+uncompyle2/opcode/opcode_27.py
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100755
index 9ca6c9be..00000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,11 +0,0 @@
-include MANIFEST
-include MANIFEST.in
-include README
-include ANNOUNCE CHANGES TODO
-include uncompyle
-include test_pythonlib
-include test_one
-include compile_tests
-graft test
-graft scripts
-global-exclude *~ .*~
diff --git a/README b/README
index b930ea94..244eaadf 100755
--- a/README
+++ b/README
@@ -1,59 +1,49 @@
+uncompyle2
+==========
- uncompyle2
- A Python 2.5, 2.6, 2.7 byte-code decompiler, written in Python 2.7
- 0.13
- 2012-6-5
+A Python 2.5, 2.6, 2.7 byte-code decompiler, written in Python 2.7
Introduction
------------
'uncompyle2' converts Python byte-code back into equivalent Python
-source. It accepts byte-code from Python version 2.5, 2.6 & 2.7. Additionally,
-it will only run on Python 2.7.
+source code. It accepts byte-code from Python version 2.5 to 2.7.
+Additionally, it will only run on Python 2.7.
The generated source is very readable: docstrings, lists, tuples and
hashes get pretty-printed.
-'uncompyle2' may also verify the equivalence of the generated source by
-by compiling it and comparing both byte-codes.
-
'uncompyle2' is based on John Aycock's generic small languages compiler
'spark' (http://www.csr.uvic.ca/~aycock/python/) and his prior work on
'uncompyle'.
-Additional note (3 July 2004, Ben Burton):
+### Additional note (3 July 2004, Ben Burton):
- The original website from which this software was obtained is no longer
- available. It has now become a commercial decompilation service, with
- no software available for download.
+The original website from which this software was obtained is no longer
+available. It has now become a commercial decompilation service, with
+no software available for download.
- Any developers seeking to make alterations or enhancements to this code
- should therefore consider these debian packages an appropriate starting
- point.
+Any developers seeking to make alterations or enhancements to this code
+should therefore consider these debian packages an appropriate starting
+point.
-Additional note (5 June 2012):
+### Additional note (5 June 2012):
- The decompilation of python bytecode 2.5 & 2.6 is based on the work of
- Eloi Vanderbeken. bytecode is translated to a pseudo 2.7 python bytecode
- and then decompiled.
+The decompilation of python bytecode 2.5 & 2.6 is based on the work of
+Eloi Vanderbeken. bytecode is translated to a pseudo 2.7 python bytecode
+and then decompiled.
Features
--------
- * decompiles Python byte-code into equivalent Python source
+- decompiles Python byte-code into equivalent Python source
+- decompiles byte-code from Python version 2.5, 2.6, 2.7
+- pretty-prints docstrings, hashes, lists and tuples
+- reads directly from .pyc/.pyo files, bulk-decompile whole directories
+- output may be written to file, a directory or to stdout
+- option for including byte-code disassembly into generated source
- * decompiles byte-code from Python version 2.5, 2.6, 2.7
-
- * pretty-prints docstrings, hashes, lists and tuples
-
- * reads directly from .pyc/.pyo files, bulk-decompile whole
- directories
-
- * output may be written to file, a directory or to stdout
-
- * option for including byte-code disassembly into generated source
-
- For a list of changes please refer to the 'CHANGES' file.
+For a list of changes please refer to the 'CHANGES' file.
Requirements
@@ -70,27 +60,26 @@ the source distribution.
Creating RPMS:
- python setup.py bdist_rpm
+ python setup.py bdist_rpm
- If you need to force the python interpreter to eg. pyton2:
- python2 setup.py bdist_rpm --python=python2
+If you need to force the python interpreter to eg. pyton2:
+ python2 setup.py bdist_rpm --python=python2
-Installation from the source distribution:
+### Installation from the source distribution:
- python setup.py install
+ python setup.py install
- To install to a user's home-dir:
- python setup.py install --home=
+To install to a user's home-dir:
- To install to another prefix (eg. /usr/local)
- python setup.py install --prefix=/usr/local
+ python setup.py install --home=
- If you need to force the python interpreter to eg. pyton2:
- python2 setup.py install
+To install to another prefix (eg. /usr/local)
- For more information on 'Installing Python Modules' please refer to
- http://www.python.org/doc/current/inst/inst.html
+ python setup.py install --prefix=/usr/local
+
+For more information on 'Installing Python Modules' please refer to
+http://www.python.org/doc/current/inst/inst.html
Usage
@@ -103,4 +92,6 @@ uncompyle2 --help prints long usage
Known Bugs/Restrictions
-----------------------
-I have some known bug in the 2.5 & 2.6 decompilation version but this will be fixed in a few.
+No support for python 3.2
+
+It currently reconstructs most of Python code but probably needs to be tested more thoroughly. All feedback welcome
diff --git a/test_pythonlib.py b/test_pythonlib.py
index baed1a35..447a6385 100755
--- a/test_pythonlib.py
+++ b/test_pythonlib.py
@@ -13,10 +13,10 @@ Usage-Examples:
Adding own test-trees:
Step 1) Edit this file and add a new entry to 'test_options', eg.
- test_options['mylib'] = ('/usr/lib/mylib', PYOC, 'mylib')
+ test_options['mylib'] = ('/usr/lib/mylib', PYOC, 'mylib')
Step 2: Run the test:
- test_pythonlib --mylib # decompile 'mylib'
- test_pythonlib --mylib --verify # decompile verify 'mylib'
+ test_pythonlib --mylib # decompile 'mylib'
+ test_pythonlib --mylib --verify # decompile verify 'mylib'
'''
from uncompyle2 import main, verify
@@ -105,7 +105,7 @@ if __name__ == '__main__':
test_dirs.append(test_options[val])
else:
help()
-
+
for src_dir, pattern, target_dir in test_dirs:
for libpath in lib_prefix:
testpath = os.path.join(libpath, src_dir)
@@ -114,6 +114,7 @@ if __name__ == '__main__':
if os.path.exists(testlibfile) or os.path.exists(testfile):
src_dir = testpath
checked_dirs.append([src_dir, pattern, target_dir])
+ break
for src_dir, pattern, target_dir in checked_dirs:
target_dir = os.path.join(target_base, target_dir)
diff --git a/uncompyle2/disas.py b/uncompyle2/disas.py
index 4884bb32..74f43d2f 100755
--- a/uncompyle2/disas.py
+++ b/uncompyle2/disas.py
@@ -139,7 +139,8 @@ def disassemble_string(code, lasti=-1, varnames=None, names=None,
print '(' + cmp_op[oparg] + ')',
print
-disco = disassemble # XXX For backwards compatibility
+disco = disassemble
+# XXX For backwards compatibility
def findlabels(code):
"""Detect all offsets in a byte code which are jump targets.
diff --git a/uncompyle2/scanner25.py b/uncompyle2/scanner25.py
index 23153f94..46587b4e 100755
--- a/uncompyle2/scanner25.py
+++ b/uncompyle2/scanner25.py
@@ -505,7 +505,7 @@ class Scanner25(scan.Scanner):
last_stmt = s
slist += [s] * (s-i)
i = s
- slist += [len(code)] * (len(code)-len(slist))
+ slist += [end] * (end-len(slist))
def next_except_jump(self, start):
'''
@@ -610,7 +610,7 @@ class Scanner25(scan.Scanner):
test = self.prev[next_line_byte]
if test == pos:
loop_type = 'while 1'
- else:
+ elif self.code[test] in hasjabs+hasjrel:
self.ignore_if.add(test)
test_target = self.get_target(test)
if test_target > (jump_back+3):
diff --git a/uncompyle2/scanner26.py b/uncompyle2/scanner26.py
index 75046b78..14d9797c 100755
--- a/uncompyle2/scanner26.py
+++ b/uncompyle2/scanner26.py
@@ -502,7 +502,7 @@ class Scanner26(scan.Scanner):
last_stmt = s
slist += [s] * (s-i)
i = s
- slist += [len(code)] * (len(code)-len(slist))
+ slist += [end] * (end-len(slist))
def next_except_jump(self, start):
'''
@@ -607,7 +607,7 @@ class Scanner26(scan.Scanner):
test = self.prev[next_line_byte]
if test == pos:
loop_type = 'while 1'
- else:
+ elif self.code[test] in hasjabs+hasjrel:
self.ignore_if.add(test)
test_target = self.get_target(test)
if test_target > (jump_back+3):
diff --git a/uncompyle2/scanner27.py b/uncompyle2/scanner27.py
index 02f2c3ac..4a7509c2 100755
--- a/uncompyle2/scanner27.py
+++ b/uncompyle2/scanner27.py
@@ -18,11 +18,10 @@ import scanner as scan
class Scanner27(scan.Scanner):
def __init__(self):
self.Token = scan.Scanner.__init__(self, 2.6)
-
+
def disassemble(self, co, classname=None):
'''
Disassemble a code object, returning a list of 'Token'.
-
The main part of this procedure is modelled after
dis.disassemble().
'''
@@ -98,14 +97,13 @@ class Scanner27(scan.Scanner):
extended_arg = 0
for offset in self.op_range(0, n):
-
if offset in cf:
k = 0
for j in cf[offset]:
rv.append(Token('COME_FROM', None, repr(j),
- offset="%s_%d" % (offset, k) ))
+ offset="%s_%d" % (offset, k)))
k += 1
-
+
op = code[offset]
op_name = opname[op]
oparg = None; pattr = None
@@ -274,7 +272,7 @@ class Scanner27(scan.Scanner):
last_stmt = s
slist += [s] * (s-i)
i = s
- slist += [len(code)] * (len(code)-len(slist))
+ slist += [end] * (end-len(slist))
def remove_mid_line_ifs(self, ifs):
filtered = []
@@ -341,13 +339,13 @@ class Scanner27(scan.Scanner):
start = pos+3
target = self.get_target(pos, op)
end = self.restrict_to_parent(target, parent)
-
+
if target != end:
self.fixed_jumps[pos] = end
-
(line_no, next_line_byte) = self.lines[pos]
jump_back = self.last_instr(start, end, JA,
next_line_byte, False)
+
if jump_back and jump_back != self.prev[end] and code[jump_back+3] in (JA, JF):
if code[self.prev[end]] == RETURN_VALUE or \
(code[self.prev[end]] == POP_BLOCK and code[self.prev[self.prev[end]]] == RETURN_VALUE):
@@ -365,8 +363,7 @@ class Scanner27(scan.Scanner):
end = jump_back + 3
else:
if self.get_target(jump_back) >= next_line_byte:
- jump_back = self.last_instr(start, end, JA,
- start, False)
+ jump_back = self.last_instr(start, end, JA, start, False)
if end > jump_back+4 and code[end] in (JF, JA):
if code[jump_back+4] in (JA, JF):
if self.get_target(jump_back+4) == self.get_target(end):
@@ -375,9 +372,8 @@ class Scanner27(scan.Scanner):
elif target < pos:
self.fixed_jumps[pos] = jump_back+4
end = jump_back+4
-
target = self.get_target(jump_back, JA)
-
+
if code[target] in (FOR_ITER, GET_ITER):
loop_type = 'for'
else:
@@ -385,7 +381,7 @@ class Scanner27(scan.Scanner):
test = self.prev[next_line_byte]
if test == pos:
loop_type = 'while 1'
- else:
+ elif self.code[test] in hasjabs+hasjrel:
self.ignore_if.add(test)
test_target = self.get_target(test)
if test_target > (jump_back+3):
@@ -455,7 +451,7 @@ class Scanner27(scan.Scanner):
#does this jump to right after another cond jump?
# if so, it's part of a larger conditional
if (code[pre[target]] in (JUMP_IF_FALSE_OR_POP, JUMP_IF_TRUE_OR_POP,
- PJIF, PJIT)) and (target > pos):
+ PJIF, PJIT)) and (target > pos):
self.fixed_jumps[pos] = pre[target]
self.structs.append({'type': 'and/or',
'start': start,