diff --git a/uncompyle6/deparser.py b/uncompyle6/deparser.py index a3a67b3c..3a96524d 100644 --- a/uncompyle6/deparser.py +++ b/uncompyle6/deparser.py @@ -1,12 +1,11 @@ +# Copyright (c) 1999 John Aycock +# Copyright (c) 2000-2002 by hartmut Goebel +# Copyright (c) 2005 by Dan Pascu +# Copyright (c) 2015 by Rocky Bernstein +# See LICENSE for license """ Deparsing saving text fragment information indexed by offset - Copyright (c) 1999 John Aycock - Copyright (c) 2000-2002 by hartmut Goebel - Copyright (c) 2005 by Dan Pascu - Copyright (c) 2015 by Rocky Bernstein - - See main module for license. Decompilation (walking AST) @@ -55,6 +54,7 @@ from uncompyle6.walker import AST, NONE, find_all_globals from uncompyle6.walker import find_globals, find_none, INDENT_PER_LEVEL from uncompyle6.walker import ParserError from uncompyle6 import parser +from uncompyle6.scanner import Token, Code, get_scanner try: from StringIO import StringIO @@ -70,13 +70,6 @@ from uncompyle6.spark import GenericASTTraversal from uncompyle6.spark import GenericASTTraversalPruningException from types import CodeType -try: - from uncompyle6.Scanner import Token, Code - older_uncompyle = True -except ImportError: - from uncompyle6.scanner import Token, Code - older_uncompyle = False - from collections import namedtuple NodeInfo = namedtuple("NodeInfo", "node start finish") ExtractInfo = namedtuple("ExtractInfo", @@ -1118,27 +1111,7 @@ def deparse(version, co, out=StringIO(), showasm=0, showast=0): assert inspect.iscode(co) # store final output stream for case of error __real_out = out or sys.stdout - try: - import uncompyle6.Scanner as scan - scanner = scan.Scanner(version) - except ImportError: - if version == 2.5: - import uncompyle6.scanners.scanner25 as scan - scanner = scan.Scanner25() - elif version == 2.6: - import uncompyle6.scanners.scanner26 as scan - scanner = scan.Scanner26() - elif version == 2.7: - import uncompyle6.scanners.scanner27 as scan - scanner = scan.Scanner27() - elif version == 3.2: - import uncompyle6.scanners.scanner34 as scan - scanner = scan.Scanner32() - elif version == 3.4: - import uncompyle6.scanners.scanner34 as scan - scanner = scan.Scanner34() - - scanner.setShowAsm(showasm, out) + scanner = get_scanner(version) tokens, customize = scanner.disassemble(co) # Build AST from disassembly. @@ -1146,10 +1119,7 @@ def deparse(version, co, out=StringIO(), showasm=0, showast=0): walk = Traverser(scanner, showast=showast) try: - if older_uncompyle: - walk.ast = walk.build_ast_d(tokens, customize) - else: - walk.ast = walk.build_ast_d(tokens, customize) + walk.ast = walk.build_ast_d(tokens, customize) except walker.ParserError as e : # parser failed, dump disassembly print(e, file=__real_out) raise diff --git a/uncompyle6/disas.py b/uncompyle6/disas.py index 77f905c8..9634245b 100644 --- a/uncompyle6/disas.py +++ b/uncompyle6/disas.py @@ -20,6 +20,7 @@ from __future__ import print_function import importlib, inspect, os, sys import uncompyle6 +from uncompyle6.scanner import get_scanner def check_object_path(path): if path.endswith(".py"): @@ -44,23 +45,7 @@ def disco(version, co, out=None): print('# Embedded file name: %s' % co.co_filename, file=real_out) - # Pick up appropriate scanner - if version == 2.7: - import uncompyle6.scanners.scanner27 as scan - scanner = scan.Scanner27() - elif version == 2.6: - import uncompyle6.scanners.scanner26 as scan - scanner = scan.Scanner26() - elif version == 2.5: - import uncompyle6.scanners.scanner25 as scan - scanner = scan.Scanner25() - elif version == 3.2: - import uncompyle6.scanners.scanner32 as scan - scanner = scan.Scanner32() - elif version == 3.4: - import uncompyle6.scanners.scanner34 as scan - scanner = scan.Scanner34() - scanner.setShowAsm(True, out) + scanner = get_scanner(version) tokens, customize = scanner.disassemble(co) for t in tokens: diff --git a/uncompyle6/parser.py b/uncompyle6/parser.py index aa6b1e43..b9266dc5 100644 --- a/uncompyle6/parser.py +++ b/uncompyle6/parser.py @@ -1,23 +1,16 @@ -from __future__ import print_function +# Copyright (c) 1999 John Aycock +# Copyright (c) 2000-2002 by hartmut Goebel +# Copyright (c) 2005 by Dan Pascu +# Copyright (c) 2015 Rocky Bernstein +""" +Spark parser for Python. +""" -''' - Copyright (c) 1999 John Aycock - Copyright (c) 2000-2002 by hartmut Goebel - Copyright (c) 2005 by Dan Pascu +from uncompyle6 import PYTHON3 +from uncompyle6.spark import GenericASTBuilder - See main module for license. -''' - -__all__ = ['parse', 'AST', 'ParserError', 'Parser'] - -try: - from spark import GenericASTBuilder -except ImportError: - from .spark import GenericASTBuilder - -import string, sys - -if (sys.version_info >= (3, 0)): +import sys +if PYTHON3: intern = sys.intern from collections import UserList else: @@ -794,3 +787,24 @@ def parse(tokens, customize): ast = p.parse(tokens) # p.cleanup() return ast + + +def parser(version, co, out=sys.stdout, showasm=False): + import inspect + assert inspect.iscode(co) + from uncompyle6.scanner import get_scanner + scanner = get_scanner(version) + tokens, customize = scanner.disassemble(co) + if showasm: + for t in tokens: + print(t) + + return parse(tokens, customize) + +if __name__ == '__main__': + def parse_test(co): + sys_version = sys.version_info.major + (sys.version_info.minor / 10.0) + ast = parser(sys_version, co, showasm=True) + print(ast) + return + parse_test(parse_test.__code__) diff --git a/uncompyle6/scanner.py b/uncompyle6/scanner.py index 543be669..cc553c7e 100755 --- a/uncompyle6/scanner.py +++ b/uncompyle6/scanner.py @@ -2,10 +2,10 @@ # Copyright (c) 2000-2002 by hartmut Goebel # Copyright (c) 2005 by Dan Pascu # -# See main module for license. +# See LICENSE # """ -scanner/disassembler module. From here we call various verison-specific +scanner/disassembler module. From here we call various version-specific scanners, e.g. for Python 2.7 or 3.4. This overlaps Python's dis module, but it can be run from Python 2 or @@ -16,8 +16,6 @@ for later use in deparsing. from __future__ import print_function -__all__ = ['Token', 'Scanner', 'Code'] - import sys from uncompyle6 import PYTHON3 @@ -328,10 +326,32 @@ class Scanner(object): target = parent['end'] return target +def get_scanner(version): + # Pick up appropriate scanner + if version == 2.7: + import uncompyle6.scanners.scanner27 as scan + scanner = scan.Scanner27() + elif version == 2.6: + import uncompyle6.scanners.scanner26 as scan + scanner = scan.Scanner26() + elif version == 2.5: + import uncompyle6.scanners.scanner25 as scan + scanner = scan.Scanner25() + elif version == 3.2: + import uncompyle6.scanners.scanner32 as scan + scanner = scan.Scanner32() + elif version == 3.4: + import uncompyle6.scanners.scanner34 as scan + scanner = scan.Scanner34() + else: + raise RuntimeError("Unsupported Python version %d" % version) + return scanner + if __name__ == "__main__": import inspect, uncompyle6 co = inspect.currentframe().f_code tokens, customize = Scanner(uncompyle6.PYTHON_VERSION).disassemble(co) + print('-' * 30) for t in tokens: print(t) pass diff --git a/uncompyle6/walker.py b/uncompyle6/walker.py index f3557b64..df0eba03 100644 --- a/uncompyle6/walker.py +++ b/uncompyle6/walker.py @@ -1,13 +1,8 @@ -from __future__ import print_function - -''' - Copyright (c) 1999 John Aycock - Copyright (c) 2000-2002 by hartmut Goebel - Copyright (c) 2005 by Dan Pascu - - See main module for license. - +# Copyright (c) 1999 John Aycock +# Copyright (c) 2000-2002 by hartmut Goebel +# Copyright (c) 2005 by Dan Pascu +""" Decompilation (walking AST) All table-driven. Step 1 determines a table (T) and a path to a @@ -41,17 +36,19 @@ from __future__ import print_function The '%' may optionally be followed by a number (C) in square brackets, which makes the engine walk down to N[C] before evaluating the escape code. -''' +""" -import sys, re +from __future__ import print_function +import inspect, sys, re + +from uncompyle6 import PYTHON3 from uncompyle6.spark import GenericASTTraversal from uncompyle6.parser import AST -from uncompyle6.scanner import Token, Code +from uncompyle6.scanner import Token, Code, get_scanner -if (sys.version_info >= (3, 0)): +if PYTHON3: from io import StringIO - import uncompyle6 minint = -sys.maxsize-1 maxint = sys.maxsize else: @@ -59,11 +56,8 @@ else: minint = -sys.maxint-1 maxint = sys.maxint -from types import CodeType - import uncompyle6.parser as dparser - # Some ASTs used for comparing code fragments (like 'return None' at # the end of functions). @@ -451,7 +445,7 @@ def find_none(node): class Walker(GenericASTTraversal, object): stacked_params = ('f', 'indent', 'isLambda', '_globals') - def __init__(self, out, scanner, showast=0): + def __init__(self, out, scanner, showast=False): GenericASTTraversal.__init__(self, ast=None) self.scanner = scanner params = { @@ -920,7 +914,7 @@ class Walker(GenericASTTraversal, object): self.prec = 27 code = node[-5].attr - assert type(code) == CodeType + assert inspect.iscode(code) code = Code(code, self.scanner, self.currentclass) # assert isinstance(code, Code) @@ -1283,7 +1277,7 @@ class Walker(GenericASTTraversal, object): defparams = node[:node[-1].attr] # node[-1] == MAKE_xxx_n code = node[-2].attr - assert type(code) == CodeType + assert inspect.iscode(code) code = Code(code, self.scanner, self.currentclass) # assert isinstance(code, Code) @@ -1349,7 +1343,7 @@ class Walker(GenericASTTraversal, object): def build_class(self, code): """Dump class definition, doc string and class body.""" - assert type(code) == CodeType + assert inspect.iscode(code) code = Code(code, self.scanner, self.currentclass) # assert isinstance(code, Code) @@ -1431,3 +1425,45 @@ class Walker(GenericASTTraversal, object): self.print_(repr(ast)) return ast + +def walker(version, co, out=sys.stdout, showasm=False, showast=False): + assert inspect.iscode(co) + # store final output stream for case of error + __real_out = out or sys.stdout + scanner = get_scanner(version) + + tokens, customize = scanner.disassemble(co) + if showasm: + for t in tokens: + print(t) + + # Build AST from disassembly. + walk = Walker(out, scanner, showast=showast) + + try: + walk.ast = walk.build_ast(tokens, customize) + except ParserError as e : # parser failed, dump disassembly + print(e, file=__real_out) + raise + + del tokens # save memory + + # convert leading '__doc__ = "..." into doc string + assert walk.ast == 'stmts' + walk.mod_globs = find_globals(walk.ast, set()) + walk.gen_source(walk.ast, customize) + + for g in walk.mod_globs: + walk.write('global %s ## Warning: Unused global' % g) + if walk.ERROR: + raise walk.ERROR + + return walk + +if __name__ == '__main__': + def walk_test(co): + sys_version = sys.version_info.major + (sys.version_info.minor / 10.0) + walker(sys_version, co, showasm=True, showast=True) + print() + return + walk_test(walk_test.__code__)