diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..f25ba308 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: python + +sudo: false + +python: + - '2.7' + +install: + - pip install -r requirements.txt + - pip install -r requirements-dev.txt + - pip install . + - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi + +script: +- make test diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..23cea64e --- /dev/null +++ b/Makefile @@ -0,0 +1,95 @@ +# Compatibility for us old-timers. + +# Note: This makefile include remake-style target comments. +# These comments before the targets start with #: +# remake --tasks to shows the targets and the comments + +GIT2CL ?= git2cl +PYTHON ?= python +PYTHON3 ?= python3 +RM ?= rm +LINT = flake8 + +#EXTRA_DIST=ipython/ipy_trepan.py trepan +PHONY=check clean dist distclean test test-unit test-functional rmChangeLog clean_pyc nosetests + +#: Default target - same as "check" +all: check + +#: Make HTML docs +html: + cd docs && $(MAKE) html + +#: Same as "check" +test: check + +#: Same as "check" +nosetests: check + +#: Run all tests +check-short: test-unit-short + +#: Run all tests: unit, functional and integration verbosely +check: test-unit lint + +#: Run unit (white-box) tests +test-unit: + $(PYTHON) ./setup.py nosetests + +#: Run unit (white-box) tests +test-unit-short: + $(PYTHON) ./setup.py nosetests --quiet 2>&1 | \ + $(PYTHON) ./test/make-check-filter.py + +#: Clean up temporary files and .pyc files +clean: clean_pyc + $(PYTHON) ./setup.py $@ + +#: Create source (tarball) and binary (egg) distribution +dist: + $(PYTHON) ./setup.py sdist bdist_egg + +#: Remove .pyc files +clean_pyc: + $(RM) -f */*.pyc */*/*.pyc */*/*/*.pyc */*/*/*/*.pyc + +#: Create source tarball +sdist: + $(PYTHON) ./setup.py sdist + + +#: Style check. Set env var LINT to pyflakes, flake, or flake8 +lint: + $(LINT) trepan_deparse/deparser.py + +#: Create binary egg distribution +bdist_egg: + $(PYTHON) ./setup.py bdist_egg + + +# It is too much work to figure out how to add a new command to distutils +# to do the following. I'm sure distutils will someday get there. +DISTCLEAN_FILES = build dist *.pyc + +#: Remove ALL derived files +distclean: clean + -rm -fr $(DISTCLEAN_FILES) || true + -find . -name \*.pyc -exec rm -v {} \; + -find . -name \*.egg-info -exec rm -vr {} \; + +#: Install package locally +verbose-install: + $(PYTHON) ./setup.py install + +#: Install package locally without the verbiage +install: + $(PYTHON) ./setup.py install >/dev/null + +rmChangeLog: + rm ChangeLog || true + +#: Create a ChangeLog from git via git log and git2cl +ChangeLog: rmChangeLog + git log --pretty --numstat --summary | $(GIT2CL) >$@ + +.PHONY: $(PHONY) diff --git a/scripts/uncompyle2 b/scripts/uncompyle6 similarity index 92% rename from scripts/uncompyle2 rename to scripts/uncompyle6 index a503c4e8..409198c2 100755 --- a/scripts/uncompyle2 +++ b/scripts/uncompyle6 @@ -4,12 +4,12 @@ # Copyright (c) 2000-2002 by hartmut Goebel # ''' -Usage: uncompyle2 [OPTIONS]... [ FILE | DIR]... +Usage: uncompyle6 [OPTIONS]... [ FILE | DIR]... Examples: - uncompyle2 foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout - uncompyle2 -o . foo.pyc bar.pyc # decompile to ./foo.pyc_dis and ./bar.pyc_dis - uncompyle2 -o /tmp /usr/lib/python1.5 # decompile whole library + uncompyle6 foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout + uncompyle6 -o . foo.pyc bar.pyc # decompile to ./foo.pyc_dis and ./bar.pyc_dis + uncompyle6 -o /tmp /usr/lib/python1.5 # decompile whole library Options: -o output decompiled files to this path: @@ -41,17 +41,17 @@ Extensions of generated files: ''' Usage_short = \ -"uncompyle2 [--help] [--verify] [--showasm] [--showast] [-o ] FILE|DIR..." +"uncompyle6 [--help] [--verify] [--showasm] [--showast] [-o ] FILE|DIR..." import sys, os, getopt import os.path -from uncompyle2 import main, verify +from uncompyle6 import main, verify import time if sys.version[:3] != '2.7': - print >>sys.stderr, 'Error: uncompyle2 requires Python 2.7.' + print >>sys.stderr, 'Error: uncompyle6 requires Python 2.7.' sys.exit(-1) - + showasm = showast = do_verify = numproc = recurse_dirs = 0 outfile = '-' out_base = None @@ -64,7 +64,7 @@ try: ['help', 'verify', 'showast', 'showasm']) except getopt.GetoptError, e: print >>sys.stderr, '%s: %s' % (os.path.basename(sys.argv[0]), e) - sys.exit(-1) + sys.exit(-1) for opt, val in opts: if opt in ('-h', '--help'): @@ -114,7 +114,7 @@ if src_base: sb_len = len( os.path.join(src_base, '') ) files = map(lambda f: f[sb_len:], files) del sb_len - + if outfile == '-': outfile = None # use stdout elif outfile and os.path.isdir(outfile): @@ -140,9 +140,9 @@ else: fqueue.put(f) for i in range(numproc): fqueue.put(None) - + rqueue = Queue(numproc) - + def process_func(): try: (tot_files, okay_files, failed_files, verify_failed_files) = (0,0,0,0) @@ -181,7 +181,7 @@ else: (tot_files, okay_files, failed_files, verify_failed_files) except (KeyboardInterrupt, OSError): pass - + if timestamp: print time.strftime(timestampfmt) diff --git a/setup.py b/setup.py index 870563f4..c541c0d0 100755 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ from distutils.core import setup, Extension -setup (name = "uncompyle2", - version = "1.1", +setup (name = "uncompyle6", + version = "2.0", description = "Python byte-code to source-code converter", author = "Mysterie", author_email = "kajusska@gmail.com", url = "http://github.com/Mysterie/uncompyle2", - packages=['uncompyle2', 'uncompyle2.opcode'], - scripts=['scripts/uncompyle2'] + packages=['uncompyle6', 'uncompyle6.opcode'], + scripts=['scripts/uncompyle6'] ) diff --git a/test_one b/test_one new file mode 100755 index 00000000..aef2bdac --- /dev/null +++ b/test_one @@ -0,0 +1,18 @@ +#!/bin/bash + +PAGER=${PAGER:-less} +file=$1 +shift +options=$@ + +#BASEDIR=test/bytecode_1.5 +#BASEDIR=test/bytecode_2.0 +#BASEDIR=test/bytecode_2.1 +#BASEDIR=test/bytecode_2.2 +BASEDIR=test/bytecode_2.7 + +if [[ `dirname $file` == '.' ]] ; then + file=$BASEDIR/test_$file.pyc +fi + +python -u ./scripts/uncompyle6 $options $file 2>&1 | $PAGER diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..85dcde79 --- /dev/null +++ b/tox.ini @@ -0,0 +1,19 @@ +; Settings file for flake8: +; http://flake8.readthedocs.org/en/latest/config.html#settings +[flake8] +exclude = .tox,./build,./trepan/processor/command/tmp +filename = *.py +ignore = C901,E113,E121,E122,E123,E125,E126,E127,E128,E129,E201,E202,E203,E221,E222,E225,E226,E241,E242,E251,E261,E271,E272,E302,E401,E501,F401,E701,E702 + +[tox] +envlist = py26, py27, pypy + +[testenv] +deps = + requests>=0.8.8 + mock>=1.0.1 +commands = python -W always setup.py nosetests {posargs} + +[testenv:py27] +deps = + flake8 diff --git a/uncompyle-code.py b/uncompyle-code.py index d2cfb85d..90f53198 100644 --- a/uncompyle-code.py +++ b/uncompyle-code.py @@ -3,6 +3,9 @@ from uncompyle2 import uncompyle, walker, verify, magics from uncompyle2.spark import GenericASTTraversal, GenericASTTraversalPruningException import sys, inspect, types, cStringIO +from collections import namedtuple +NodeInfo = namedtuple("NodeInfo", "node start finish") + class FindWalker(walker.Walker, object): stacked_params = ('f', 'indent', 'isLambda', '_globals') @@ -22,6 +25,10 @@ class FindWalker(walker.Walker, object): self.currentclass = None self.pending_newlines = 0 + self.found_offset = False + self.offsets = {} + + f = property(lambda s: s.__params['f'], lambda s, x: s.__params.__setitem__('f', x), lambda s: s.__params.__delitem__('f'), @@ -47,10 +54,10 @@ class FindWalker(walker.Walker, object): node = self.ast if hasattr(node, 'offset'): - print "Name %s has an offset %d" % (self.typestring(node), node.offset) + start = len(self.f.getvalue()) if node.offset == self.find_offset: self.found_offset = True - print 'BINGO!' + # print 'BINGO!' try: name = 'n_' + self.typestring(node) @@ -60,6 +67,12 @@ class FindWalker(walker.Walker, object): else: self.default(node) except GenericASTTraversalPruningException: + if hasattr(node, 'offset'): + self.offsets[node.offset] = NodeInfo(node = node, + start = start, + finish = len(self.f.getvalue())) + # print self.offsets[node.offset] + # print self.f.getvalue()[start:] return for kid in node: @@ -70,6 +83,9 @@ class FindWalker(walker.Walker, object): func = getattr(self, name) func(node) + return + + def find_source(self, offset, ast, customize, isLambda=0, returnNone=False): """convert AST to source code""" @@ -86,10 +102,11 @@ class FindWalker(walker.Walker, object): self.print_(self.indent, 'pass') else: self.customize(customize) + result = self.traverse(ast, isLambda=isLambda) if isLambda: - self.write(self.traverse(ast, isLambda=isLambda)) + self.write(result) else: - self.print_(self.traverse(ast, isLambda=isLambda)) + self.print_(result) self.return_none = rn # FIXME; below duplicated the code, since we don't find self.__params @@ -172,7 +189,7 @@ def uncompyle_test(): uncompyle(2.7, co, sys.stdout, 1) print print '------------------------' - uncompyle_find(2.7, co, 24) + uncompyle_find(2.7, co, 33) finally: del frame diff --git a/uncompyle2/magics.py b/uncompyle2/magics.py deleted file mode 100755 index cd6493c1..00000000 --- a/uncompyle2/magics.py +++ /dev/null @@ -1,86 +0,0 @@ -import struct - -__all__ = ['magics', 'versions'] - -def __build_magic(magic): - return struct.pack('Hcc', magic, '\r', '\n') - -def __by_version(magics): - by_version = {} - for m, v in magics.items(): - by_version[v] = m - return by_version - -versions = { - # taken from from Python/import.c - # magic, version - __build_magic(20121): '1.5', #1.5, 1.5.1, 1.5.2 - __build_magic(50428): '1.6', #1.6 - __build_magic(50823): '2.0', #2.0, 2.0.1 - __build_magic(60202): '2.1', #2.1, 2.1.1, 2.1.2 - __build_magic(60717): '2.2', #2.2 - __build_magic(62011): '2.3', #2.3a0 - __build_magic(62021): '2.3', #2.3a0 - __build_magic(62041): '2.4', #2.4a0 - __build_magic(62051): '2.4', #2.4a3 - __build_magic(62061): '2.4', #2.4b1 - __build_magic(62071): '2.5', #2.5a0 - __build_magic(62081): '2.5', #2.5a0 (ast-branch) - __build_magic(62091): '2.5', #2.5a0 (with) - __build_magic(62092): '2.5', #2.5a0 (changed WITH_CLEANUP opcode) - __build_magic(62101): '2.5', #2.5b3 (fix wrong code: for x, in ...) - __build_magic(62111): '2.5', #2.5b3 (fix wrong code: x += yield) - __build_magic(62121): '2.5', #2.5c1 (fix wrong lnotab with for loops and - # storing constants that should have been removed - __build_magic(62131): '2.5', #2.5c2 (fix wrong code: for x, in ... in listcomp/genexp) - __build_magic(62151): '2.6', #2.6a0 (peephole optimizations & STORE_MAP) - __build_magic(62161): '2.6', #2.6a1 (WITH_CLEANUP optimization) - __build_magic(62171): '2.7', #2.7a0 (optimize list comprehensions/change LIST_APPEND) - __build_magic(62181): '2.7', #2.7a0 (optimize conditional branches: - # introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE) - __build_magic(62191): '2.7', #2.7a0 (introduce SETUP_WITH) - __build_magic(62201): '2.7', #2.7a0 (introduce BUILD_SET) - __build_magic(62211): '2.7', #2.7a0 (introduce MAP_ADD and SET_ADD) - __build_magic(3000): '3.0', #3.000 - __build_magic(3010): '3.0', #3.000 (removed UNARY_CONVERT) - __build_magic(3020): '3.0', #3.000 (added BUILD_SET) - __build_magic(3030): '3.0', #3.000 (added keyword-only parameters) - __build_magic(3040): '3.0', #3.000 (added signature annotations) - __build_magic(3050): '3.0', #3.000 (print becomes a function) - __build_magic(3060): '3.0', #3.000 (PEP 3115 metaclass syntax) - __build_magic(3061): '3.0', #3.000 (string literals become unicode) - __build_magic(3071): '3.0', #3.000 (PEP 3109 raise changes) - __build_magic(3081): '3.0', #3.000 (PEP 3137 make __file__ and __name__ unicode) - __build_magic(3091): '3.0', #3.000 (kill str8 interning) - __build_magic(3101): '3.0', #3.000 (merge from 2.6a0, see 62151) - __build_magic(3103): '3.0', #3.000 (__file__ points to source file) - __build_magic(3111): '3.0', #3.0a4 (WITH_CLEANUP optimization). - __build_magic(3131): '3.0', #3.0a5 (lexical exception stacking, including POP_EXCEPT) - __build_magic(3141): '3.1', #3.1a0 (optimize list, set and dict comprehensions) - __build_magic(3151): '3.1', #3.1a0 (optimize conditional branches) - __build_magic(3160): '3.2', #3.2a0 (add SETUP_WITH) - __build_magic(3170): '3.2', #3.2a1 (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR) - __build_magic(3180): '3.2', #3.2a2 (add DELETE_DEREF) -} - -magics = __by_version(versions) - -def __show(text, magic): - print text, struct.unpack('BBBB', magic), \ - struct.unpack('HBB', magic) - -def test(): - import imp - magic_20 = by_version['2.0'] - current = imp.get_magic() - current_version = magics[current] - magic_current = by_version[ current_version ] - print type(magic_20), len(magic_20), repr(magic_20) - print - print 'This Python interpreter has version', current_version - __show('imp.get_magic():\t', current), - __show('magic[current_version]:\t', magic_current) - __show('magic_20:\t\t', magic_20) - -if __name__ == '__main__': - test() diff --git a/uncompyle2/scanner.py b/uncompyle2/scanner.py deleted file mode 100755 index 55a20710..00000000 --- a/uncompyle2/scanner.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (c) 1999 John Aycock -# Copyright (c) 2000-2002 by hartmut Goebel -# Copyright (c) 2005 by Dan Pascu -# -# See main module for license. -# - -__all__ = ['Token', 'Scanner', 'Code'] - -import types -from collections import namedtuple -from array import array -from operator import itemgetter - -from uncompyle2.opcode import opcode_25, opcode_26, opcode_27 - -class Token: - ''' - Class representing a byte-code token. - - A byte-code token is equivalent to the contents of one line - as output by dis.dis(). - ''' - def __init__(self, type_, attr=None, pattr=None, offset=-1, linestart=False): - self.type = intern(type_) - self.attr = attr - self.pattr = pattr - self.offset = offset - self.linestart = linestart - - def __cmp__(self, o): - if isinstance(o, Token): - # both are tokens: compare type and pattr - return cmp(self.type, o.type) or cmp(self.pattr, o.pattr) - else: - return cmp(self.type, o) - - def __repr__(self): return str(self.type) - def __str__(self): - pattr = self.pattr - if self.linestart: - return '\n%s\t%-17s %r' % (self.offset, self.type, pattr) - else: - return '%s\t%-17s %r' % (self.offset, self.type, pattr) - - def __hash__(self): return hash(self.type) - def __getitem__(self, i): raise IndexError - -class Code: - ''' - Class for representing code-objects. - - This is similar to the original code object, but additionally - the diassembled code is stored in the attribute '_tokens'. - ''' - def __init__(self, co, scanner, classname=None): - for i in dir(co): - if i.startswith('co_'): - setattr(self, i, getattr(co, i)) - self._tokens, self._customize = scanner.disassemble(co, classname) - -class Scanner(object): - opc = None # opcode module - - def __init__(self, version): - if version == 2.7: - self.opc = opcode_27 - elif version == 2.6: - self.opc = opcode_26 - elif version == 2.5: - self.opc = opcode_25 - - return self.resetTokenClass() - - def setShowAsm(self, showasm, out=None): - self.showasm = showasm - self.out = out - - def setTokenClass(self, tokenClass): - assert type(tokenClass) == types.ClassType - self.Token = tokenClass - return self.Token - - def resetTokenClass(self): - return self.setTokenClass(Token) - - def get_target(self, pos, op=None): - if op is None: - op = self.code[pos] - target = self.get_argument(pos) - if op in self.opc.hasjrel: - target += pos + 3 - return target - - def get_argument(self, pos): - arg = self.code[pos+1] + self.code[pos+2] * 256 - return arg - - def print_bytecode(self): - for i in self.op_range(0, len(self.code)): - op = self.code[i] - if op in self.opc.hasjabs+self.opc.hasjrel: - dest = self.get_target(i, op) - print '%i\t%s\t%i' % (i, self.opc.opname[op], dest) - else: - print '%i\t%s\t' % (i, self.opc.opname[op]) - - def first_instr(self, start, end, instr, target=None, exact=True): - ''' - Find the first in the block from start to end. - is any python bytecode instruction or a list of opcodes - If is an opcode with a target (like a jump), a target - destination can be specified which must match precisely if exact - is True, or if exact is False, the instruction which has a target - closest to will be returned. - - Return index to it or None if not found. - ''' - code = self.code - assert(start>=0 and end<=len(code)) - - try: None in instr - except: instr = [instr] - - pos = None - distance = len(code) - for i in self.op_range(start, end): - op = code[i] - if op in instr: - if target is None: - return i - dest = self.get_target(i, op) - if dest == target: - return i - elif not exact: - _distance = abs(target - dest) - if _distance < distance: - distance = _distance - pos = i - return pos - - def last_instr(self, start, end, instr, target=None, exact=True): - ''' - Find the last in the block from start to end. - is any python bytecode instruction or a list of opcodes - If is an opcode with a target (like a jump), a target - destination can be specified which must match precisely if exact - is True, or if exact is False, the instruction which has a target - closest to will be returned. - - Return index to it or None if not found. - ''' - - code = self.code - if not (start>=0 and end<=len(code)): - return None - - try: None in instr - except: instr = [instr] - - pos = None - distance = len(code) - for i in self.op_range(start, end): - op = code[i] - if op in instr: - if target is None: - pos = i - else: - dest = self.get_target(i, op) - if dest == target: - distance = 0 - pos = i - elif not exact: - _distance = abs(target - dest) - if _distance <= distance: - distance = _distance - pos = i - return pos - - def all_instr(self, start, end, instr, target=None, include_beyond_target=False): - ''' - Find all in the block from start to end. - is any python bytecode instruction or a list of opcodes - If is an opcode with a target (like a jump), a target - destination can be specified which must match precisely. - - Return a list with indexes to them or [] if none found. - ''' - - code = self.code - assert(start>=0 and end<=len(code)) - - try: None in instr - except: instr = [instr] - - result = [] - for i in self.op_range(start, end): - op = code[i] - if op in instr: - if target is None: - result.append(i) - else: - t = self.get_target(i, op) - if include_beyond_target and t >= target: - result.append(i) - elif t == target: - result.append(i) - return result - - def op_size(self, op): - if op < self.opc.HAVE_ARGUMENT and op not in self.opc.hasArgumentExtended: - return 1 - else: - return 3 - - def op_hasArgument(self, op): - return self.op_size(op) > 1 - - def op_range(self, start, end): - while start < end: - yield start - start += self.op_size(self.code[start]) - - def remove_mid_line_ifs(self, ifs): - filtered = [] - for i in ifs: - if self.lines[i].l_no == self.lines[i+3].l_no: - if self.code[self.prev[self.lines[i].next]] in (self.opc.PJIT, self.opc.PJIF): - continue - filtered.append(i) - return filtered - - def rem_or(self, start, end, instr, target=None, include_beyond_target=False): - ''' - Find all in the block from start to end. - is any python bytecode instruction or a list of opcodes - If is an opcode with a target (like a jump), a target - destination can be specified which must match precisely. - - Return a list with indexes to them or [] if none found. - ''' - - code = self.code - assert(start>=0 and end<=len(code)) - - try: None in instr - except: instr = [instr] - - result = [] - for i in self.op_range(start, end): - op = code[i] - if op in instr: - if target is None: - result.append(i) - else: - t = self.get_target(i, op) - if include_beyond_target and t >= target: - result.append(i) - elif t == target: - result.append(i) - - pjits = self.all_instr(start, end, self.opc.PJIT) - filtered = [] - for pjit in pjits: - tgt = self.get_target(pjit)-3 - for i in result: - if i <= pjit or i >= tgt: - filtered.append(i) - result = filtered - filtered = [] - return result - - def restrict_to_parent(self, target, parent): - '''Restrict pos to parent boundaries.''' - if not (parent['start'] < target < parent['end']): - target = parent['end'] - return target \ No newline at end of file diff --git a/uncompyle2/__init__.py b/uncompyle6/__init__.py similarity index 98% rename from uncompyle2/__init__.py rename to uncompyle6/__init__.py index 710e10db..17113c1b 100755 --- a/uncompyle2/__init__.py +++ b/uncompyle6/__init__.py @@ -9,10 +9,10 @@ distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. @@ -62,7 +62,7 @@ def _load_module(filename): (normally a .pyc) code_object: code_object from this file ''' - + fp = open(filename, 'rb') magic = fp.read(4) try: @@ -168,7 +168,7 @@ def main(in_base, out_base, files, codes, outfile=None, def _get_outstream(outfile): dir = os.path.dirname(outfile) failed_file = outfile + '_failed' - if os.path.exists(failed_file): + if os.path.exists(failed_file): os.remove(failed_file) try: os.makedirs(dir) @@ -196,7 +196,7 @@ def main(in_base, out_base, files, codes, outfile=None, else: outfile = os.path.join(out_base, file) + '_dis' outstream = _get_outstream(outfile) - #print >>sys.stderr, outfile + #print >>sys.stderr, outfile # try to decomyple the input file try: @@ -228,7 +228,7 @@ def main(in_base, out_base, files, codes, outfile=None, except verify.VerifyCmpError, e: verify_failed_files += 1 os.rename(outfile, outfile + '_unverified') - if not outfile: + if not outfile: print >>sys.stderr, "### Error Verifiying", file print >>sys.stderr, e else: diff --git a/uncompyle2/disas.py b/uncompyle6/disas.py similarity index 100% rename from uncompyle2/disas.py rename to uncompyle6/disas.py diff --git a/uncompyle6/magics.py b/uncompyle6/magics.py new file mode 100755 index 00000000..0d3f53f2 --- /dev/null +++ b/uncompyle6/magics.py @@ -0,0 +1,88 @@ +from __future__ import print_function +import struct + +__all__ = ['magics', 'versions'] + +def __build_magic(magic): + return struct.pack('Hcc', magic, '\r', '\n') + +by_magic = {} +by_version = {} + +def __by_version(magics): + for m, v in magics.items(): + by_magic[m] = v + by_version[v] = m + return by_version + +versions = { + # taken from from Python/import.c + # magic, version + __build_magic(20121): '1.5', # 1.5, 1.5.1, 1.5.2 + __build_magic(50428): '1.6', # 1.6 + __build_magic(50823): '2.0', # 2.0, 2.0.1 + __build_magic(60202): '2.1', # 2.1, 2.1.1, 2.1.2 + __build_magic(60717): '2.2', # 2.2 + __build_magic(62011): '2.3', # 2.3a0 + __build_magic(62021): '2.3', # 2.3a0 + __build_magic(62041): '2.4', # 2.4a0 + __build_magic(62051): '2.4', # 2.4a3 + __build_magic(62061): '2.4', # 2.4b1 + __build_magic(62071): '2.5', # 2.5a0 + __build_magic(62081): '2.5', # 2.5a0 (ast-branch) + __build_magic(62091): '2.5', # 2.5a0 (with) + __build_magic(62092): '2.5', # 2.5a0 (changed WITH_CLEANUP opcode) + __build_magic(62101): '2.5', # 2.5b3 (fix wrong code: for x, in ...) + __build_magic(62111): '2.5', # 2.5b3 (fix wrong code: x += yield) + __build_magic(62121): '2.5', # 2.5c1 (fix wrong lnotab with for loops and + # storing constants that should have been removed + __build_magic(62131): '2.5', # 2.5c2 (fix wrong code: for x, in ... in listcomp/genexp) + __build_magic(62151): '2.6', # 2.6a0 (peephole optimizations & STORE_MAP) + __build_magic(62161): '2.6', # 2.6a1 (WITH_CLEANUP optimization) + __build_magic(62171): '2.7', # 2.7a0 (optimize list comprehensions/change LIST_APPEND) + __build_magic(62181): '2.7', # 2.7a0 (optimize conditional branches: + # introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE) + __build_magic(62191): '2.7', # 2.7a0 (introduce SETUP_WITH) + __build_magic(62201): '2.7', # 2.7a0 (introduce BUILD_SET) + __build_magic(62211): '2.7', # 2.7a0 (introduce MAP_ADD and SET_ADD) + __build_magic(3000): '3.0', # 3.000 + __build_magic(3010): '3.0', # 3.000 (removed UNARY_CONVERT) + __build_magic(3020): '3.0', # 3.000 (added BUILD_SET) + __build_magic(3030): '3.0', # 3.000 (added keyword-only parameters) + __build_magic(3040): '3.0', # 3.000 (added signature annotations) + __build_magic(3050): '3.0', # 3.000 (print becomes a function) + __build_magic(3060): '3.0', # 3.000 (PEP 3115 metaclass syntax) + __build_magic(3061): '3.0', # 3.000 (string literals become unicode) + __build_magic(3071): '3.0', # 3.000 (PEP 3109 raise changes) + __build_magic(3081): '3.0', # 3.000 (PEP 3137 make __file__ and __name__ unicode) + __build_magic(3091): '3.0', # 3.000 (kill str8 interning) + __build_magic(3101): '3.0', # 3.000 (merge from 2.6a0, see 62151) + __build_magic(3103): '3.0', # 3.000 (__file__ points to source file) + __build_magic(3111): '3.0', # 3.0a4 (WITH_CLEANUP optimization). + __build_magic(3131): '3.0', # 3.0a5 (lexical exception stacking, including POP_EXCEPT) + __build_magic(3141): '3.1', # 3.1a0 (optimize list, set and dict comprehensions) + __build_magic(3151): '3.1', # 3.1a0 (optimize conditional branches) + __build_magic(3160): '3.2', # 3.2a0 (add SETUP_WITH) + __build_magic(3170): '3.2', # 3.2a1 (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR) + __build_magic(3180): '3.2', # 3.2a2 (add DELETE_DEREF) +} + +magics = __by_version(versions) + +def __show(text, magic): + print(text, struct.unpack('BBBB', magic), struct.unpack('HBB', magic)) + +def test(): + import imp + magic_20 = magics['2.0'] + current = imp.get_magic() + current_version = struct.unpack('HBB', current)[0] + magic_current = by_magic[ current ] + print(type(magic_20), len(magic_20), repr(magic_20)) + print() + print('This Python interpreter has version', magic_current) + print('Magic code: ', current_version) + print(type(magic_20), len(magic_20), repr(magic_20)) + +if __name__ == '__main__': + test() diff --git a/uncompyle2/opcode/__init__.py b/uncompyle6/opcode/__init__.py similarity index 100% rename from uncompyle2/opcode/__init__.py rename to uncompyle6/opcode/__init__.py diff --git a/uncompyle2/opcode/opcode_23.py b/uncompyle6/opcode/opcode_23.py similarity index 100% rename from uncompyle2/opcode/opcode_23.py rename to uncompyle6/opcode/opcode_23.py diff --git a/uncompyle2/opcode/opcode_24.py b/uncompyle6/opcode/opcode_24.py similarity index 100% rename from uncompyle2/opcode/opcode_24.py rename to uncompyle6/opcode/opcode_24.py diff --git a/uncompyle2/opcode/opcode_25.py b/uncompyle6/opcode/opcode_25.py similarity index 100% rename from uncompyle2/opcode/opcode_25.py rename to uncompyle6/opcode/opcode_25.py diff --git a/uncompyle2/opcode/opcode_26.py b/uncompyle6/opcode/opcode_26.py similarity index 100% rename from uncompyle2/opcode/opcode_26.py rename to uncompyle6/opcode/opcode_26.py diff --git a/uncompyle2/opcode/opcode_27.py b/uncompyle6/opcode/opcode_27.py similarity index 100% rename from uncompyle2/opcode/opcode_27.py rename to uncompyle6/opcode/opcode_27.py diff --git a/uncompyle2/parser.py b/uncompyle6/parser.py similarity index 100% rename from uncompyle2/parser.py rename to uncompyle6/parser.py diff --git a/uncompyle2/scanner25.py b/uncompyle6/scanner25.py similarity index 100% rename from uncompyle2/scanner25.py rename to uncompyle6/scanner25.py diff --git a/uncompyle2/scanner26.py b/uncompyle6/scanner26.py similarity index 100% rename from uncompyle2/scanner26.py rename to uncompyle6/scanner26.py diff --git a/uncompyle2/scanner27.py b/uncompyle6/scanner27.py similarity index 100% rename from uncompyle2/scanner27.py rename to uncompyle6/scanner27.py diff --git a/uncompyle2/spark.py b/uncompyle6/spark.py similarity index 100% rename from uncompyle2/spark.py rename to uncompyle6/spark.py diff --git a/uncompyle2/verify.py b/uncompyle6/verify.py similarity index 100% rename from uncompyle2/verify.py rename to uncompyle6/verify.py diff --git a/uncompyle2/walker.py b/uncompyle6/walker.py similarity index 99% rename from uncompyle2/walker.py rename to uncompyle6/walker.py index 576229c0..c0456c8d 100755 --- a/uncompyle2/walker.py +++ b/uncompyle6/walker.py @@ -26,12 +26,14 @@ %c evaluate N[A] recursively* %C evaluate N[A[0]]..N[A[1]-1] recursively, separate by A[2]* + %P same as %C but sets operator precedence %, print ',' if last %C only printed one item (for tuples--unused) %| tab to current indentation level %+ increase current indentation level %- decrease current indentation level %{...} evaluate ... in context of N %% literal '%' + %p evaluate N setting precedence * indicates an argument (A) required.