diff --git a/test/bytecode_2.7/01_class.pyc b/test/bytecode_2.7/01_class.pyc index 909e0ad1..543da485 100644 Binary files a/test/bytecode_2.7/01_class.pyc and b/test/bytecode_2.7/01_class.pyc differ diff --git a/test/simple_source/def/01_class.py b/test/simple_source/def/01_class.py index 755e6a56..18f59c0d 100644 --- a/test/simple_source/def/01_class.py +++ b/test/simple_source/def/01_class.py @@ -8,8 +8,8 @@ # classdef ::= LOAD_CONST expr mkfunc CALL_FUNCTION_0 BUILD_CLASS designator # mkfunc ::= LOAD_CONST MAKE_FUNCTION_0 -class A: - pass - -# class B(Exception): +# class A: # pass + +class B(Exception): + pass diff --git a/uncompyle6/load.py b/uncompyle6/load.py index b08887fa..09d592f0 100644 --- a/uncompyle6/load.py +++ b/uncompyle6/load.py @@ -70,8 +70,12 @@ def load_module(filename): try: version = float(magics.versions[magic]) except KeyError: - raise ImportError("Unknown magic number %s in %s" % - (ord(magic[0])+256*ord(magic[1]), filename)) + if len(magic) >= 2: + raise ImportError("Unknown magic number %s in %s" % + (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): raise ImportError("This is a Python %s file! Only " "Python 2.5 to 2.7 and 3.2 to 3.4 files are supported." diff --git a/uncompyle6/main.py b/uncompyle6/main.py index 45cae2b9..5b70524f 100644 --- a/uncompyle6/main.py +++ b/uncompyle6/main.py @@ -1,5 +1,5 @@ from __future__ import print_function -import os, sys +import inspect, os, sys from uncompyle6.disas import check_object_path 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' """ + assert inspect.iscode(co) + # store final output stream for case of error real_out = out or sys.stdout print('# Python %s' % version, file=real_out) @@ -21,28 +23,18 @@ def uncompyle(version, co, out=None, showasm=False, showast=False, file=real_out) 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 print(e, file=real_out) 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, showgrammar=False): """ decompile Python byte-code file (.pyc) """ - check_object_path(filename) + + filename = check_object_path(filename) version, magic_int, co = load_module(filename) if type(co) == list: for con in co: @@ -119,24 +111,31 @@ def main(in_base, out_base, files, codes, outfile=None, else: sys.stderr.write("\n# %s" % sys.exc_info()[1]) sys.stderr.write("\n# Can't uncompile %s\n" % infile) - else: # uncompile successfull + else: # uncompile successful if outfile: outstream.close() - if do_verify: - try: - msg = verify.compare_code_with_srcfile(infile, outfile) - if not outfile: - if not msg: - print('\n# okay decompiling %s' % infile) - okay_files += 1 - else: - print('\n# %s\n\t%s', infile, msg) - except verify.VerifyCmpError as e: - verify_failed_files += 1 - os.rename(outfile, outfile + '_unverified') - if not outfile: - print("### Error Verifiying %s" % filename, file=sys.stderr) - print(e, file=sys.stderr) + if do_verify: + try: + msg = verify.compare_code_with_srcfile(infile, outfile) + if not outfile: + if not msg: + print('\n# okay decompiling %s' % infile) + okay_files += 1 + else: + print('\n# %s\n\t%s', infile, msg) + except verify.VerifyCmpError as e: + verify_failed_files += 1 + os.rename(outfile, outfile + '_unverified') + if not outfile: + print("### Error Verifiying %s" % filename, 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: okay_files += 1 if not outfile: diff --git a/uncompyle6/parsers/parse2.py b/uncompyle6/parsers/parse2.py index 99fb1c9b..f018de31 100644 --- a/uncompyle6/parsers/parse2.py +++ b/uncompyle6/parsers/parse2.py @@ -351,8 +351,10 @@ class Python2Parser(PythonParser): kwarg ::= LOAD_CONST expr - classdef ::= LOAD_CONST expr mkfunc - CALL_FUNCTION_0 BUILD_CLASS designator + classdef ::= buildclass designator + + buildclass ::= LOAD_CONST expr mkfunc + CALL_FUNCTION_0 BUILD_CLASS stmt ::= classdefdeco classdefdeco ::= classdefdeco1 designator diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index 85562428..edf8475b 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -1054,22 +1054,25 @@ class Walker(GenericASTTraversal, object): cclass = self.currentclass if self.version > 3.0: - self.currentclass = str(node[1][1].pattr) + buildclass = node[1] + self.currentclass = str(buildclass[1].pattr) else: - self.currentclass = str(node[0].pattr) + buildclass = node[0] + self.currentclass = str(buildclass[0].pattr) self.write('\n\n') self.write(self.indent, 'class ', self.currentclass) - self.print_super_classes(node) + + self.print_super_classes(buildclass) self.print_(':') # class body self.indentMore() if self.version > 3.0: - self.build_class(node[1][0].attr) + self.build_class(buildclass[0].attr) else: - self.build_class(node[2][-2].attr) + self.build_class(buildclass[-3][0].attr) self.indentLess() self.currentclass = cclass @@ -1568,9 +1571,19 @@ def deparse_code(version, co, out=sys.stdout, showasm=False, showast=False, del tokens # save memory - # convert leading '__doc__ = "..." into doc string 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! deparsed.gen_source(deparsed.ast, customize)