Skip to content
This repository has been archived by the owner on Jul 7, 2021. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
gstarnberger committed Oct 1, 2016
2 parents e516b53 + dffbdc4 commit 0118c8d
Show file tree
Hide file tree
Showing 40 changed files with 1,520 additions and 1,124 deletions.
2 changes: 1 addition & 1 deletion PKG-INFO
Expand Up @@ -4,7 +4,7 @@ Version: 1.1
Summary: Python byte-code to source-code converter
Home-page: http://github.com/sysfrog/uncompyle
Author: Hartmut Goebel
Author-email: hartmut@oberon.noris.de
Author-email: h.goebel@crazy-compilers.com
License: GPLv3
Description: UNKNOWN
Platform: UNKNOWN
21 changes: 12 additions & 9 deletions README
@@ -1,13 +1,15 @@

uncompyle -- A Python 2.7 byte-code decompiler
0.12
2012-1-23
uncompyle
A Python 2.7 byte-code decompiler, written in Python 2.7
0.13
2012-2-22

Introduction
------------

'uncompyle' converts Python byte-code back into equivalent Python
source. It accepts byte-code from Python version 2.7 only.
source. It accepts byte-code from Python version 2.7 only. Additionally,
it will only run on Python 2.7.

The generated source is very readable: docstrings, lists, tuples and
hashes get pretty-printed.
Expand All @@ -17,13 +19,14 @@ by compiling it and comparing both byte-codes.

'uncompyle' is based on John Aycock's generic small languages compiler
'spark' (http://www.csr.uvic.ca/~aycock/python/) and his prior work on
'decompyle'.
a tool called 'decompyle'. This tool has been vastly improved by
Hartmut Goebel `http://www.crazy-compilers.com/`_

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.
This software is no longer available from the original website. 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
Expand Down Expand Up @@ -51,7 +54,7 @@ Features
Requirements
------------

'decompile' requires Python 2.2 or later.
uncompyle requires Python 2.7


Installation
Expand Down
145 changes: 81 additions & 64 deletions scripts/uncompyler.py
@@ -1,31 +1,35 @@
#!/usr/bin/env python2.7
# Mode: -*- python -*-
#
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
"""
Usage: uncompyler [OPTIONS]... [ FILE | DIR]...
Examples:
uncompyler foo.pyc bar.pyc # uncompyle foo.pyc, bar.pyc to stdout
uncompyler -o . foo.pyc bar.pyc # uncompyle to ./foo.dis and ./bar.dis
uncompyler -o /tmp /usr/lib/python1.5 # uncompyle whole library
uncompyler foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout
uncompyler -o . foo.pyc bar.pyc # decompile to ./foo.dis and ./bar.dis
uncompyler -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>
uncompyler -o /tmp bla/fasel.pyc bla/foo.pyc
uncompyle -o /tmp bla/fasel.pyc bla/foo.pyc
-> /tmp/fasel.dis, /tmp/foo.dis
uncompyler -o /tmp bla/fasel.pyc bar/foo.pyc
uncompyle -o /tmp bla/fasel.pyc bar/foo.pyc
-> /tmp/bla/fasel.dis, /tmp/bar/foo.dis
uncompyler -o /tmp /usr/lib/python1.5
-s if multiple input files are decompiled, the common prefix
is stripped from these names and the remainder appended to
<path>
uncompyle -o /tmp /usr/lib/python1.5
-> /tmp/smtplib.dis ... /tmp/lib-tk/FixTk.dis
-c <file> attempts a disassembly after compiling <file>
-d do not print timestamps
-p <integer> use <integer> number of processes
-r recurse directories looking for .pyc and .pyo files
-m use multiprocessing
--py use '.py' extension for generated files
--norecur don't recurse directories looking for .pyc and .pyo files
--verify compare generated source with input byte-code
(requires -o)
--help show this message
Expand All @@ -35,51 +39,54 @@
--showast -t include AST (abstract syntax tree) (disables --verify)
Extensions of generated files:
'.dis' successfully decompiled (and verified if --verify)
'.dis_unverified' successfully decompile but --verify failed
'.nodis' uncompyle failed (contact author for enhancement)
'.pyc_dis' '.pyo_dis' successfully decompiled (and verified if --verify)
'.py' with --py option
+ '_unverified' successfully decompile but --verify failed
+ '_failed' uncompyle failed (contact author for enhancement)
"""
from threading import Thread
from multiprocessing import Process, Queue
from Queue import Empty
from uncompyle import main, verify

def process_func(src_base, out_base, codes, outfile, showasm, showast, do_verify, fqueue, rqueue):
try:
(tot_files, okay_files, failed_files, verify_failed_files) = (0,0,0,0)
while 1:
f = fqueue.get()
if f == None:
break
(t, o, f, v) = \
main(src_base, out_base, [f], codes, outfile, showasm, showast, do_verify)
tot_files += t
okay_files += o
failed_files += f
verify_failed_files += v
except (Empty, KeyboardInterrupt, OSError):
pass
rqueue.put((tot_files, okay_files, failed_files, verify_failed_files))
rqueue.close()
Usage_short = \
"uncompyler [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..."

if __name__ == '__main__':
Usage_short = \
"decomyple [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..."
import sys, os, getopt
if sys.version[:3] != '2.7':
print >>sys.stderr, 'Error: uncompyler requires Python 2.7.'
sys.exit(-1)
from uncompyler import main, verify
import time
from multiprocessing import Process, Queue, cpu_count
from Queue import Empty

import sys, os, getopt
import os.path
import time
def process_func(fq, rq, src_base, out_base, codes, outfile, showasm, showast, do_verify, py, deob):
try:
(tot_files, okay_files, failed_files, verify_failed_files) = (0,0,0,0)
while 1:
f = fq.get()
if f == None:
break
(t, o, f, v) = \
main(src_base, out_base, [f], codes, outfile, showasm, showast, do_verify, py, deob)
tot_files += t
okay_files += o
failed_files += f
verify_failed_files += v
except (Empty, KeyboardInterrupt):
pass
rq.put((tot_files, okay_files, failed_files, verify_failed_files))
rq.close()

showasm = showast = do_verify = numproc = recurse_dirs = 0
if __name__ == '__main__': ## for Windows multiprocessing

showasm = showast = do_verify = multi = norecur = strip_common_path = py = deob = 0
outfile = '-'
out_base = None
codes = []
timestamp = True
timestampfmt = "# %Y.%m.%d %H:%M:%S %Z"

try:
opts, files = getopt.getopt(sys.argv[1:], 'hatdro:c:p:',
['help', 'verify', 'showast', 'showasm'])
opts, files = getopt.getopt(sys.argv[1:], 'hatdrmso:c:',
['help', 'verify', 'showast', 'showasm', 'norecur', 'py', 'deob'])
except getopt.GetoptError, e:
print >>sys.stderr, '%s: %s' % (os.path.basename(sys.argv[0]), e)
sys.exit(-1)
Expand All @@ -102,36 +109,47 @@ def process_func(src_base, out_base, codes, outfile, showasm, showast, do_verify
timestamp = False
elif opt == '-c':
codes.append(val)
elif opt == '-p':
numproc = int(val)
elif opt == '-r':
recurse_dirs = 1
elif opt == '-m':
multi = 1
elif opt == '--norecur':
norecur = 1
elif opt == '-s':
strip_common_path = 1
elif opt == '--py':
py = 1
elif opt == '--deob':
deob = 1
else:
print opt
print Usage_short
sys.exit(1)

# expand directory if specified
if recurse_dirs:
if not norecur:
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))
else:
expanded_files.append(f)
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 = map(lambda f: f[sb_len:], files)
del sb_len
if strip_common_path:
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 = map(lambda f: f[sb_len:], files)
del sb_len
else:
src_base = ''

if outfile == '-':
outfile = None # use stdout
Expand All @@ -142,21 +160,17 @@ def process_func(src_base, out_base, codes, outfile, showasm, showast, do_verify

if timestamp:
print time.strftime(timestampfmt)
if numproc <= 1:
if not multi:
try:
result = main(src_base, out_base, files, codes, outfile, showasm, showast, do_verify)
result = main(src_base, out_base, files, codes, outfile,
showasm, showast, do_verify, py, deob)
print '# decompiled %i files: %i okay, %i failed, %i verify failed' % result
except (KeyboardInterrupt, OSError):
except (KeyboardInterrupt):
pass
except verify.VerifyCmpError:
raise
else:
# create directories beforehand
for f in files:
try:
os.makedirs(os.path.join(out_base, os.path.dirname(f)))
except OSError:
pass
numproc = cpu_count()
fqueue = Queue(len(files)+numproc)
for f in files:
fqueue.put(f)
Expand All @@ -166,7 +180,10 @@ def process_func(src_base, out_base, codes, outfile, showasm, showast, do_verify
rqueue = Queue(numproc)

try:
procs = [Process(target=process_func, args=(src_base, out_base, codes, outfile, showasm, showast, do_verify, fqueue, rqueue)) for i in range(numproc)]
procs = [Process(target=process_func,
args=(fqueue, rqueue, src_base, out_base, codes, outfile,
showasm, showast, do_verify, py, deob))
for i in range(numproc)]
for p in procs:
p.start()
for p in procs:
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
@@ -1,6 +1,6 @@
[bdist_rpm]
release = 1
packager = Hartmut Goebel <hartmut.goebel@noris.net>
packager = Hartmut Goebel <h.goebel@crazy-compilers.com>
doc_files = README
# CHANGES.txt
# USAGE.txt
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -8,7 +8,7 @@
version = "1.1",
description = "Python byte-code to source-code converter",
author = "Hartmut Goebel",
author_email = "hartmut@oberon.noris.de",
author_email = "h.goebel@crazy-compilers.com",
url = "http://github.com/gstarnberger/uncompyle",
packages=['uncompyle'],
scripts=['scripts/uncompyler.py'],
Expand Down
2 changes: 1 addition & 1 deletion test/compile_tests
Expand Up @@ -5,7 +5,7 @@ compile_tests -- compile test patterns for the decompyle test suite
This source is part of the decompyle test suite.
decompyle is a Python byte-code decompiler
See http://www.goebel-consult.de/decompyle/ for download and
See http://www.crazy-compilers.com/decompyle/ for
for further information
"""

Expand Down
2 changes: 1 addition & 1 deletion test/test_applyEquiv.py
Expand Up @@ -3,7 +3,7 @@
# This simple program is part of the decompyle test suite.
#
# decompyle is a Python byte-code decompiler
# See http://www.goebel-consult.de/decompyle/ for download and
# See http://www.crazy-compilers.com/decompyle/ for
# for further information

def kwfunc(**kwargs):
Expand Down
2 changes: 1 addition & 1 deletion test/test_augmentedAssign.py
Expand Up @@ -4,7 +4,7 @@
This source is part of the decompyle test suite.
decompyle is a Python byte-code decompiler
See http://www.goebel-consult.de/decompyle/ for download and
See http://www.crazy-compilers.com/decompyle/ for
for further information
"""

Expand Down
2 changes: 1 addition & 1 deletion test/test_class.py
Expand Up @@ -4,7 +4,7 @@
This source is part of the decompyle test suite.
decompyle is a Python byte-code decompiler
See http://www.goebel-consult.de/decompyle/ for download and
See http://www.crazy-compilers.com/decompyle/ for
for further information
"""

Expand Down
2 changes: 1 addition & 1 deletion test/test_del.py
Expand Up @@ -5,7 +5,7 @@
Snippet taken from python libs's test_class.py
decompyle is a Python byte-code decompiler
See http://www.goebel-consult.de/decompyle/ for download and
See http://www.crazy-compilers.com/decompyle/ for
for further information
"""

Expand Down
2 changes: 1 addition & 1 deletion test/test_docstring.py
Expand Up @@ -3,7 +3,7 @@
# This simple program is part of the decompyle test suite.
#
# decompyle is a Python byte-code decompiler
# See http://www.goebel-consult.de/decompyle/ for download and
# See http://www.crazy-compilers.com/decompyle/ for
# for further information

'''
Expand Down
2 changes: 1 addition & 1 deletion test/test_exec.py
Expand Up @@ -3,7 +3,7 @@
# This simple program is part of the decompyle test suite.
#
# decompyle is a Python byte-code decompiler
# See http://www.goebel-consult.de/decompyle/ for download and
# See http://www.crazy-compilers.com/decompyle/ for
# for further information

testcode = 'a = 12'
Expand Down
2 changes: 1 addition & 1 deletion test/test_expressions.py
Expand Up @@ -3,7 +3,7 @@
# This simple program is part of the decompyle test suite.
#
# decompyle is a Python byte-code decompiler
# See http://www.goebel-consult.de/decompyle/ for download and
# See http://www.crazy-compilers.com/decompyle/ for
# for further information

def _lsbStrToInt(str):
Expand Down
2 changes: 1 addition & 1 deletion test/test_extendedImport.py
Expand Up @@ -3,7 +3,7 @@
# This simple program is part of the decompyle test suite.
#
# decompyle is a Python byte-code decompiler
# See http://www.goebel-consult.de/decompyle/ for download and
# See http://www.crazy-compilers.com/decompyle/ for
# for further information

import os, sys as System, time
Expand Down
2 changes: 1 addition & 1 deletion test/test_extendedPrint.py
Expand Up @@ -3,7 +3,7 @@
# This simple program is part of the decompyle test suite.
#
# decompyle is a Python byte-code decompiler
# See http://www.goebel-consult.de/decompyle/ for download and
# See http://www.crazy-compilers.com/decompyle/ for
# for further information

import sys
Expand Down

0 comments on commit 0118c8d

Please sign in to comment.