You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
main.py, pysource.py DRY deparse_code from main. Is better on showing
exception errrors such as when a pyc file is not found uncompyle6: Hook in --grammar option to showing grammar. rules. Default now does not show timestamp.
This commit is contained in:
@@ -24,7 +24,7 @@ Options:
|
|||||||
uncompyle6 -o /tmp /usr/lib/python1.5
|
uncompyle6 -o /tmp /usr/lib/python1.5
|
||||||
-> /tmp/smtplib.pyc_dis ... /tmp/lib-tk/FixTk.pyc_dis
|
-> /tmp/smtplib.pyc_dis ... /tmp/lib-tk/FixTk.pyc_dis
|
||||||
-c <file> attempts a disassembly after compiling <file>
|
-c <file> attempts a disassembly after compiling <file>
|
||||||
-d do not print timestamps
|
-d print timestamps
|
||||||
-p <integer> use <integer> number of processes
|
-p <integer> use <integer> number of processes
|
||||||
-r recurse directories looking for .pyc and .pyo files
|
-r recurse directories looking for .pyc and .pyo files
|
||||||
--verify compare generated source with input byte-code
|
--verify compare generated source with input byte-code
|
||||||
@@ -64,7 +64,7 @@ numproc = 0
|
|||||||
outfile = '-'
|
outfile = '-'
|
||||||
out_base = None
|
out_base = None
|
||||||
codes = []
|
codes = []
|
||||||
timestamp = True
|
timestamp = False
|
||||||
timestampfmt = "# %Y.%m.%d %H:%M:%S %Z"
|
timestampfmt = "# %Y.%m.%d %H:%M:%S %Z"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -93,7 +93,7 @@ for opt, val in opts:
|
|||||||
elif opt == '-o':
|
elif opt == '-o':
|
||||||
outfile = val
|
outfile = val
|
||||||
elif opt == ('--timestamp', '-d'):
|
elif opt == ('--timestamp', '-d'):
|
||||||
timestamp = False
|
timestamp = True
|
||||||
elif opt == '-c':
|
elif opt == '-c':
|
||||||
codes.append(val)
|
codes.append(val)
|
||||||
elif opt == '-p':
|
elif opt == '-p':
|
||||||
@@ -140,6 +140,7 @@ elif outfile and len(files) > 1:
|
|||||||
|
|
||||||
if timestamp:
|
if timestamp:
|
||||||
print(time.strftime(timestampfmt))
|
print(time.strftime(timestampfmt))
|
||||||
|
|
||||||
if numproc <= 1:
|
if numproc <= 1:
|
||||||
try:
|
try:
|
||||||
result = main(src_base, out_base, files, codes, outfile,
|
result = main(src_base, out_base, files, codes, outfile,
|
||||||
|
@@ -1,22 +1,18 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import os, sys, types
|
import os, sys
|
||||||
|
|
||||||
from uncompyle6.disas import check_object_path
|
from uncompyle6.disas import check_object_path
|
||||||
from uncompyle6 import verify
|
from uncompyle6 import verify
|
||||||
from uncompyle6.semantics import pysource
|
from uncompyle6.semantics import pysource
|
||||||
|
|
||||||
from uncompyle6.scanner import get_scanner
|
|
||||||
from uncompyle6.load import load_module
|
from uncompyle6.load import load_module
|
||||||
|
|
||||||
# FIXME: remove duplicate code from deparse_code
|
|
||||||
def uncompyle(version, co, out=None, showasm=False, showast=False,
|
def uncompyle(version, co, out=None, showasm=False, showast=False,
|
||||||
showgrammar=False):
|
showgrammar=False):
|
||||||
"""
|
"""
|
||||||
disassembles and deparses a given code block 'co'
|
disassembles and deparses a given code block 'co'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert isinstance(co, types.CodeType)
|
|
||||||
|
|
||||||
# store final output stream for case of error
|
# store final output stream for case of error
|
||||||
real_out = out or sys.stdout
|
real_out = out or sys.stdout
|
||||||
print('# Python %s' % version, file=real_out)
|
print('# Python %s' % version, file=real_out)
|
||||||
@@ -24,43 +20,25 @@ def uncompyle(version, co, out=None, showasm=False, showast=False,
|
|||||||
print('# Embedded file name: %s' % co.co_filename,
|
print('# Embedded file name: %s' % co.co_filename,
|
||||||
file=real_out)
|
file=real_out)
|
||||||
|
|
||||||
scanner = get_scanner(version)
|
|
||||||
tokens, customize = scanner.disassemble(co)
|
|
||||||
|
|
||||||
if showasm:
|
|
||||||
for t in tokens:
|
|
||||||
print(t, file=real_out)
|
|
||||||
print(file=out)
|
|
||||||
|
|
||||||
# Build AST from disassembly.
|
|
||||||
walk = pysource.Walker(version, out, scanner,
|
|
||||||
showast=showast, showgrammar=showgrammar)
|
|
||||||
try:
|
try:
|
||||||
ast = walk.build_ast(tokens, customize)
|
deparsed = pysource.deparse_code(version, co, out, showasm, showast, showgrammar)
|
||||||
except pysource.ParserError as e : # parser failed, dump disassembly
|
except pysource.ParserError as e : # parser failed, dump disassembly
|
||||||
print(e, file=real_out)
|
print(e, file=real_out)
|
||||||
raise
|
raise
|
||||||
del tokens # save memory
|
|
||||||
|
|
||||||
# convert leading '__doc__ = "..." into doc string
|
# FIXME: remove duplicate code from deparse_code
|
||||||
assert ast == 'stmts'
|
|
||||||
try:
|
try:
|
||||||
if ast[0][0] == pysource.ASSIGN_DOC_STRING(co.co_consts[0]):
|
if deparsed.ast[0][0] == pysource.ASSIGN_DOC_STRING(co.co_consts[0]):
|
||||||
walk.print_docstring('', co.co_consts[0])
|
deparsed.print_docstring('', co.co_consts[0])
|
||||||
del ast[0]
|
del deparsed.ast[0]
|
||||||
if ast[-1] == pysource.RETURN_NONE:
|
if deparsed.ast[-1] == pysource.RETURN_NONE:
|
||||||
ast.pop() # remove last node
|
deparsed.ast.pop() # remove last node
|
||||||
# todo: if empty, add 'pass'
|
# todo: if empty, add 'pass'
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
walk.mod_globs = pysource.find_globals(ast, set())
|
|
||||||
walk.gen_source(ast, customize)
|
|
||||||
for g in walk.mod_globs:
|
|
||||||
walk.write('global %s ## Warning: Unused global' % g)
|
|
||||||
if walk.ERROR:
|
|
||||||
raise walk.ERROR
|
|
||||||
|
|
||||||
def uncompyle_file(filename, outstream=None, showasm=False, showast=False):
|
def uncompyle_file(filename, outstream=None, showasm=False, showast=False,
|
||||||
|
showgrammar=False):
|
||||||
"""
|
"""
|
||||||
decompile Python byte-code file (.pyc)
|
decompile Python byte-code file (.pyc)
|
||||||
"""
|
"""
|
||||||
@@ -68,14 +46,15 @@ def uncompyle_file(filename, outstream=None, showasm=False, showast=False):
|
|||||||
version, magic_int, co = load_module(filename)
|
version, magic_int, co = load_module(filename)
|
||||||
if type(co) == list:
|
if type(co) == list:
|
||||||
for con in co:
|
for con in co:
|
||||||
uncompyle(version, con, outstream, showasm, showast)
|
uncompyle(version, con, outstream, showasm, showast, showgrammar)
|
||||||
else:
|
else:
|
||||||
uncompyle(version, co, outstream, showasm, showast)
|
uncompyle(version, co, outstream, showasm, showast, showgrammar)
|
||||||
co = None
|
co = None
|
||||||
|
|
||||||
def main(in_base, out_base, files, codes, outfile=None,
|
def main(in_base, out_base, files, codes, outfile=None,
|
||||||
showasm=False, showast=False, do_verify=False):
|
showasm=False, showast=False, do_verify=False,
|
||||||
'''
|
showgrammar=False):
|
||||||
|
"""
|
||||||
in_base base directory for input files
|
in_base base directory for input files
|
||||||
out_base base directory for output files (ignored when
|
out_base base directory for output files (ignored when
|
||||||
files list of filenames to be uncompyled (relative to src_base)
|
files list of filenames to be uncompyled (relative to src_base)
|
||||||
@@ -85,7 +64,7 @@ def main(in_base, out_base, files, codes, outfile=None,
|
|||||||
- <filename> outfile=<filename> (out_base is ignored)
|
- <filename> outfile=<filename> (out_base is ignored)
|
||||||
- files below out_base out_base=...
|
- files below out_base out_base=...
|
||||||
- stdout out_base=None, outfile=None
|
- stdout out_base=None, outfile=None
|
||||||
'''
|
"""
|
||||||
def _get_outstream(outfile):
|
def _get_outstream(outfile):
|
||||||
dir = os.path.dirname(outfile)
|
dir = os.path.dirname(outfile)
|
||||||
failed_file = outfile + '_failed'
|
failed_file = outfile + '_failed'
|
||||||
@@ -121,7 +100,7 @@ def main(in_base, out_base, files, codes, outfile=None,
|
|||||||
|
|
||||||
# Try to uncmpile the input file
|
# Try to uncmpile the input file
|
||||||
try:
|
try:
|
||||||
uncompyle_file(infile, outstream, showasm, showast)
|
uncompyle_file(infile, outstream, showasm, showast, showgrammar)
|
||||||
tot_files += 1
|
tot_files += 1
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
sys.stderr.write("\n# %s" % e)
|
sys.stderr.write("\n# %s" % e)
|
||||||
@@ -138,6 +117,7 @@ def main(in_base, out_base, files, codes, outfile=None,
|
|||||||
outstream.close()
|
outstream.close()
|
||||||
os.rename(outfile, outfile + '_failed')
|
os.rename(outfile, outfile + '_failed')
|
||||||
else:
|
else:
|
||||||
|
sys.stderr.write("\n# %s" % sys.exc_info()[1])
|
||||||
sys.stderr.write("\n# Can't uncompile %s\n" % infile)
|
sys.stderr.write("\n# Can't uncompile %s\n" % infile)
|
||||||
else: # uncompile successfull
|
else: # uncompile successfull
|
||||||
if outfile:
|
if outfile:
|
||||||
|
@@ -10,7 +10,7 @@ from __future__ import print_function
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from uncompyle6.parsers.spark import GenericASTBuilder
|
from uncompyle6.parsers.spark import GenericASTBuilder, DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||||
|
|
||||||
class ParserError(Exception):
|
class ParserError(Exception):
|
||||||
def __init__(self, token, offset):
|
def __init__(self, token, offset):
|
||||||
@@ -75,19 +75,20 @@ def parse(p, tokens, customize):
|
|||||||
return ast
|
return ast
|
||||||
|
|
||||||
|
|
||||||
def get_python_parser(version):
|
def get_python_parser(version, debug_parser):
|
||||||
"""
|
"""
|
||||||
Returns parser object for Python version 2 or 3
|
Returns parser object for Python version 2 or 3
|
||||||
depending on the parameter passed.
|
depending on the parameter passed.
|
||||||
"""
|
"""
|
||||||
if version < 3.0:
|
if version < 3.0:
|
||||||
import uncompyle6.parsers.parse2 as parse2
|
import uncompyle6.parsers.parse2 as parse2
|
||||||
return parse2.Python2Parser()
|
return parse2.Python2Parser(debug_parser)
|
||||||
else:
|
else:
|
||||||
import uncompyle6.parsers.parse3 as parse3
|
import uncompyle6.parsers.parse3 as parse3
|
||||||
return parse3.Python3Parser()
|
return parse3.Python3Parser(debug_parser)
|
||||||
|
|
||||||
def python_parser(version, co, out=sys.stdout, showasm=False):
|
def python_parser(version, co, out=sys.stdout, showasm=False,
|
||||||
|
parser_debug=PARSER_DEFAULT_DEBUG):
|
||||||
import inspect
|
import inspect
|
||||||
assert inspect.iscode(co)
|
assert inspect.iscode(co)
|
||||||
from uncompyle6.scanner import get_scanner
|
from uncompyle6.scanner import get_scanner
|
||||||
@@ -97,7 +98,7 @@ def python_parser(version, co, out=sys.stdout, showasm=False):
|
|||||||
# for t in tokens:
|
# for t in tokens:
|
||||||
# print(t)
|
# print(t)
|
||||||
|
|
||||||
p = get_python_parser(version)
|
p = get_python_parser(version, parser_debug)
|
||||||
return parse(p, tokens, customize)
|
return parse(p, tokens, customize)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@@ -19,12 +19,12 @@ from __future__ import print_function
|
|||||||
|
|
||||||
from uncompyle6.parser import PythonParser, nop_func
|
from uncompyle6.parser import PythonParser, nop_func
|
||||||
from uncompyle6.parsers.astnode import AST
|
from uncompyle6.parsers.astnode import AST
|
||||||
from uncompyle6.parsers.spark import GenericASTBuilder
|
from uncompyle6.parsers.spark import GenericASTBuilder, DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||||
|
|
||||||
class Python2Parser(PythonParser):
|
class Python2Parser(PythonParser):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
|
||||||
GenericASTBuilder.__init__(self, AST, 'stmts')
|
GenericASTBuilder.__init__(self, AST, 'stmts', debug=debug_parser)
|
||||||
self.customized = {}
|
self.customized = {}
|
||||||
|
|
||||||
def p_funcdef(self, args):
|
def p_funcdef(self, args):
|
||||||
|
@@ -643,7 +643,7 @@ class GenericParser:
|
|||||||
|
|
||||||
class GenericASTBuilder(GenericParser):
|
class GenericASTBuilder(GenericParser):
|
||||||
def __init__(self, AST, start, debug=DEFAULT_DEBUG):
|
def __init__(self, AST, start, debug=DEFAULT_DEBUG):
|
||||||
GenericParser.__init__(self, start)
|
GenericParser.__init__(self, start, debug=debug)
|
||||||
self.AST = AST
|
self.AST = AST
|
||||||
|
|
||||||
def preprocess(self, rule, func):
|
def preprocess(self, rule, func):
|
||||||
|
@@ -470,7 +470,8 @@ def find_none(node):
|
|||||||
class Walker(GenericASTTraversal, object):
|
class Walker(GenericASTTraversal, object):
|
||||||
stacked_params = ('f', 'indent', 'isLambda', '_globals')
|
stacked_params = ('f', 'indent', 'isLambda', '_globals')
|
||||||
|
|
||||||
def __init__(self, version, out, scanner, showast=False, showgrammar=False):
|
def __init__(self, version, out, scanner, showast=False,
|
||||||
|
debug_parser=PARSER_DEFAULT_DEBUG):
|
||||||
GenericASTTraversal.__init__(self, ast=None)
|
GenericASTTraversal.__init__(self, ast=None)
|
||||||
self.scanner = scanner
|
self.scanner = scanner
|
||||||
params = {
|
params = {
|
||||||
@@ -478,7 +479,8 @@ class Walker(GenericASTTraversal, object):
|
|||||||
'indent': '',
|
'indent': '',
|
||||||
}
|
}
|
||||||
self.version = version
|
self.version = version
|
||||||
self.p = get_python_parser(version)
|
self.p = get_python_parser(version, debug_parser=debug_parser)
|
||||||
|
self.debug_parser = dict(debug_parser)
|
||||||
self.showast = showast
|
self.showast = showast
|
||||||
self.params = params
|
self.params = params
|
||||||
self.param_stack = []
|
self.param_stack = []
|
||||||
@@ -1540,7 +1542,6 @@ def deparse_code(version, co, out=sys.stdout, showasm=False, showast=False,
|
|||||||
|
|
||||||
assert inspect.iscode(co)
|
assert inspect.iscode(co)
|
||||||
# store final output stream for case of error
|
# store final output stream for case of error
|
||||||
__real_out = out or sys.stdout
|
|
||||||
scanner = get_scanner(version)
|
scanner = get_scanner(version)
|
||||||
|
|
||||||
tokens, customize = scanner.disassemble(co)
|
tokens, customize = scanner.disassemble(co)
|
||||||
@@ -1548,37 +1549,37 @@ def deparse_code(version, co, out=sys.stdout, showasm=False, showast=False,
|
|||||||
for t in tokens:
|
for t in tokens:
|
||||||
print(t)
|
print(t)
|
||||||
|
|
||||||
parser_debug = dict(PARSER_DEFAULT_DEBUG)
|
debug_parser = dict(PARSER_DEFAULT_DEBUG)
|
||||||
parser_debug['reduce'] = showgrammar
|
debug_parser['reduce'] = showgrammar
|
||||||
|
|
||||||
|
|
||||||
# Build AST from disassembly.
|
# Build AST from disassembly.
|
||||||
walk = Walker(version, out, scanner, showast=showast)
|
deparsed = Walker(version, out, scanner, showast=showast, debug_parser=debug_parser)
|
||||||
|
|
||||||
try:
|
deparsed.ast = deparsed.build_ast(tokens, customize)
|
||||||
walk.ast = walk.build_ast(tokens, customize)
|
|
||||||
except ParserError as e : # parser failed, dump disassembly
|
assert deparsed.ast == 'stmts', 'Should have parsed grammar start'
|
||||||
print(e, file=__real_out)
|
|
||||||
raise
|
|
||||||
|
|
||||||
del tokens # save memory
|
del tokens # save memory
|
||||||
|
|
||||||
# convert leading '__doc__ = "..." into doc string
|
# convert leading '__doc__ = "..." into doc string
|
||||||
assert walk.ast == 'stmts'
|
deparsed.mod_globs = find_globals(deparsed.ast, set())
|
||||||
walk.mod_globs = find_globals(walk.ast, set())
|
|
||||||
walk.gen_source(walk.ast, customize)
|
|
||||||
|
|
||||||
for g in walk.mod_globs:
|
# What we've been waiting for: Generate source from AST!
|
||||||
walk.write('global %s ## Warning: Unused global' % g)
|
deparsed.gen_source(deparsed.ast, customize)
|
||||||
if walk.ERROR:
|
|
||||||
raise walk.ERROR
|
|
||||||
|
|
||||||
return walk
|
for g in deparsed.mod_globs:
|
||||||
|
deparsed.write('# global %s ## Warning: Unused global' % g)
|
||||||
|
|
||||||
|
return deparsed
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
def deparse_test(co):
|
def deparse_test(co):
|
||||||
|
"This is a docstring"
|
||||||
sys_version = sys.version_info.major + (sys.version_info.minor / 10.0)
|
sys_version = sys.version_info.major + (sys.version_info.minor / 10.0)
|
||||||
deparsed = deparse_code(sys_version, co, showasm=False, showast=False)
|
deparsed = deparse_code(sys_version, co, showasm=False, showast=False)
|
||||||
|
# deparsed = deparse_code(sys_version, co, showasm=False, showast=False,
|
||||||
|
# showgrammar=True)
|
||||||
print(deparsed.text)
|
print(deparsed.text)
|
||||||
return
|
return
|
||||||
deparse_test(deparse_test.__code__)
|
deparse_test(deparse_test.__code__)
|
||||||
|
Reference in New Issue
Block a user