parse2.py, pysource.py: add buildclass nonterminal to structure tree

better and make more similar to Python3
load.py: handle magic errors better
main.py: correct use when passing a .py instead of a .pyc better and a message
err when file not found.
pysource.py: fix up main docstring; code moved from main.py
This commit is contained in:
rocky
2015-12-23 13:49:56 -05:00
parent 46828cd769
commit f630fe15fb
6 changed files with 62 additions and 44 deletions

Binary file not shown.

View File

@@ -8,8 +8,8 @@
# classdef ::= LOAD_CONST expr mkfunc CALL_FUNCTION_0 BUILD_CLASS designator # classdef ::= LOAD_CONST expr mkfunc CALL_FUNCTION_0 BUILD_CLASS designator
# mkfunc ::= LOAD_CONST MAKE_FUNCTION_0 # mkfunc ::= LOAD_CONST MAKE_FUNCTION_0
class A: # class A:
pass
# class B(Exception):
# pass # pass
class B(Exception):
pass

View File

@@ -70,8 +70,12 @@ def load_module(filename):
try: try:
version = float(magics.versions[magic]) version = float(magics.versions[magic])
except KeyError: except KeyError:
if len(magic) >= 2:
raise ImportError("Unknown magic number %s in %s" % raise ImportError("Unknown magic number %s in %s" %
(ord(magic[0])+256*ord(magic[1]), filename)) (ord(magic[0])+256*ord(magic[1]), filename))
else:
raise ImportError("Bad magic number: '%s'" % magic)
if not (2.5 <= version <= 2.7) and not (3.2 <= version <= 3.4): if not (2.5 <= version <= 2.7) and not (3.2 <= version <= 3.4):
raise ImportError("This is a Python %s file! Only " raise ImportError("This is a Python %s file! Only "
"Python 2.5 to 2.7 and 3.2 to 3.4 files are supported." "Python 2.5 to 2.7 and 3.2 to 3.4 files are supported."

View File

@@ -1,5 +1,5 @@
from __future__ import print_function from __future__ import print_function
import os, sys import inspect, os, sys
from uncompyle6.disas import check_object_path from uncompyle6.disas import check_object_path
from uncompyle6 import verify from uncompyle6 import verify
@@ -13,6 +13,8 @@ def uncompyle(version, co, out=None, showasm=False, showast=False,
disassembles and deparses a given code block 'co' disassembles and deparses a given code block '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 real_out = out or sys.stdout
print('# Python %s' % version, file=real_out) print('# Python %s' % version, file=real_out)
@@ -21,28 +23,18 @@ def uncompyle(version, co, out=None, showasm=False, showast=False,
file=real_out) file=real_out)
try: try:
deparsed = pysource.deparse_code(version, co, out, showasm, showast, showgrammar) 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
# FIXME: remove duplicate code from deparse_code
try:
if deparsed.ast[0][0] == pysource.ASSIGN_DOC_STRING(co.co_consts[0]):
deparsed.print_docstring('', co.co_consts[0])
del deparsed.ast[0]
if deparsed.ast[-1] == pysource.RETURN_NONE:
deparsed.ast.pop() # remove last node
# todo: if empty, add 'pass'
except:
pass
def uncompyle_file(filename, outstream=None, showasm=False, showast=False, def uncompyle_file(filename, outstream=None, showasm=False, showast=False,
showgrammar=False): showgrammar=False):
""" """
decompile Python byte-code file (.pyc) decompile Python byte-code file (.pyc)
""" """
check_object_path(filename)
filename = check_object_path(filename)
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:
@@ -119,7 +111,7 @@ def main(in_base, out_base, files, codes, outfile=None,
else: else:
sys.stderr.write("\n# %s" % sys.exc_info()[1]) 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 successful
if outfile: if outfile:
outstream.close() outstream.close()
if do_verify: if do_verify:
@@ -137,6 +129,13 @@ def main(in_base, out_base, files, codes, outfile=None,
if not outfile: if not outfile:
print("### Error Verifiying %s" % filename, file=sys.stderr) print("### Error Verifiying %s" % filename, file=sys.stderr)
print(e, file=sys.stderr) print(e, file=sys.stderr)
pass
pass
pass
elif do_verify:
print("\n### uncompile successful, but no file to compare against",
file=sys.stderr)
pass
else: else:
okay_files += 1 okay_files += 1
if not outfile: if not outfile:

View File

@@ -351,8 +351,10 @@ class Python2Parser(PythonParser):
kwarg ::= LOAD_CONST expr kwarg ::= LOAD_CONST expr
classdef ::= LOAD_CONST expr mkfunc classdef ::= buildclass designator
CALL_FUNCTION_0 BUILD_CLASS designator
buildclass ::= LOAD_CONST expr mkfunc
CALL_FUNCTION_0 BUILD_CLASS
stmt ::= classdefdeco stmt ::= classdefdeco
classdefdeco ::= classdefdeco1 designator classdefdeco ::= classdefdeco1 designator

View File

@@ -1054,22 +1054,25 @@ class Walker(GenericASTTraversal, object):
cclass = self.currentclass cclass = self.currentclass
if self.version > 3.0: if self.version > 3.0:
self.currentclass = str(node[1][1].pattr) buildclass = node[1]
self.currentclass = str(buildclass[1].pattr)
else: else:
self.currentclass = str(node[0].pattr) buildclass = node[0]
self.currentclass = str(buildclass[0].pattr)
self.write('\n\n') self.write('\n\n')
self.write(self.indent, 'class ', self.currentclass) self.write(self.indent, 'class ', self.currentclass)
self.print_super_classes(node)
self.print_super_classes(buildclass)
self.print_(':') self.print_(':')
# class body # class body
self.indentMore() self.indentMore()
if self.version > 3.0: if self.version > 3.0:
self.build_class(node[1][0].attr) self.build_class(buildclass[0].attr)
else: else:
self.build_class(node[2][-2].attr) self.build_class(buildclass[-3][0].attr)
self.indentLess() self.indentLess()
self.currentclass = cclass self.currentclass = cclass
@@ -1568,9 +1571,19 @@ def deparse_code(version, co, out=sys.stdout, showasm=False, showast=False,
del tokens # save memory del tokens # save memory
# convert leading '__doc__ = "..." into doc string
deparsed.mod_globs = find_globals(deparsed.ast, set()) deparsed.mod_globs = find_globals(deparsed.ast, set())
# convert leading '__doc__ = "..." into doc string
try:
if deparsed.ast[0][0] == ASSIGN_DOC_STRING(co.co_consts[0]):
deparsed.print_docstring('', co.co_consts[0])
del deparsed.ast[0]
if deparsed.ast[-1] == RETURN_NONE:
deparsed.ast.pop() # remove last node
# todo: if empty, add 'pass'
except:
pass
# What we've been waiting for: Generate source from AST! # What we've been waiting for: Generate source from AST!
deparsed.gen_source(deparsed.ast, customize) deparsed.gen_source(deparsed.ast, customize)