Start process of making python3 compatible

This commit is contained in:
rocky
2015-12-11 13:44:23 -05:00
parent 365cdfc941
commit d3c732298c
25 changed files with 282 additions and 391 deletions

15
.travis.yml Normal file
View File

@@ -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

95
Makefile Normal file
View File

@@ -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)

View File

@@ -4,12 +4,12 @@
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com> # Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# #
''' '''
Usage: uncompyle2 [OPTIONS]... [ FILE | DIR]... Usage: uncompyle6 [OPTIONS]... [ FILE | DIR]...
Examples: Examples:
uncompyle2 foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout uncompyle6 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 uncompyle6 -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 -o /tmp /usr/lib/python1.5 # decompile whole library
Options: Options:
-o <path> output decompiled files to this path: -o <path> output decompiled files to this path:
@@ -41,15 +41,15 @@ Extensions of generated files:
''' '''
Usage_short = \ Usage_short = \
"uncompyle2 [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..." "uncompyle6 [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..."
import sys, os, getopt import sys, os, getopt
import os.path import os.path
from uncompyle2 import main, verify from uncompyle6 import main, verify
import time import time
if sys.version[:3] != '2.7': 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) sys.exit(-1)
showasm = showast = do_verify = numproc = recurse_dirs = 0 showasm = showast = do_verify = numproc = recurse_dirs = 0

View File

@@ -4,12 +4,12 @@
from distutils.core import setup, Extension from distutils.core import setup, Extension
setup (name = "uncompyle2", setup (name = "uncompyle6",
version = "1.1", version = "2.0",
description = "Python byte-code to source-code converter", description = "Python byte-code to source-code converter",
author = "Mysterie", author = "Mysterie",
author_email = "kajusska@gmail.com", author_email = "kajusska@gmail.com",
url = "http://github.com/Mysterie/uncompyle2", url = "http://github.com/Mysterie/uncompyle2",
packages=['uncompyle2', 'uncompyle2.opcode'], packages=['uncompyle6', 'uncompyle6.opcode'],
scripts=['scripts/uncompyle2'] scripts=['scripts/uncompyle6']
) )

18
test_one Executable file
View File

@@ -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

19
tox.ini Normal file
View File

@@ -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

View File

@@ -3,6 +3,9 @@ from uncompyle2 import uncompyle, walker, verify, magics
from uncompyle2.spark import GenericASTTraversal, GenericASTTraversalPruningException from uncompyle2.spark import GenericASTTraversal, GenericASTTraversalPruningException
import sys, inspect, types, cStringIO import sys, inspect, types, cStringIO
from collections import namedtuple
NodeInfo = namedtuple("NodeInfo", "node start finish")
class FindWalker(walker.Walker, object): class FindWalker(walker.Walker, object):
stacked_params = ('f', 'indent', 'isLambda', '_globals') stacked_params = ('f', 'indent', 'isLambda', '_globals')
@@ -22,6 +25,10 @@ class FindWalker(walker.Walker, object):
self.currentclass = None self.currentclass = None
self.pending_newlines = 0 self.pending_newlines = 0
self.found_offset = False
self.offsets = {}
f = property(lambda s: s.__params['f'], f = property(lambda s: s.__params['f'],
lambda s, x: s.__params.__setitem__('f', x), lambda s, x: s.__params.__setitem__('f', x),
lambda s: s.__params.__delitem__('f'), lambda s: s.__params.__delitem__('f'),
@@ -47,10 +54,10 @@ class FindWalker(walker.Walker, object):
node = self.ast node = self.ast
if hasattr(node, 'offset'): 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: if node.offset == self.find_offset:
self.found_offset = True self.found_offset = True
print 'BINGO!' # print 'BINGO!'
try: try:
name = 'n_' + self.typestring(node) name = 'n_' + self.typestring(node)
@@ -60,6 +67,12 @@ class FindWalker(walker.Walker, object):
else: else:
self.default(node) self.default(node)
except GenericASTTraversalPruningException: 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 return
for kid in node: for kid in node:
@@ -70,6 +83,9 @@ class FindWalker(walker.Walker, object):
func = getattr(self, name) func = getattr(self, name)
func(node) func(node)
return
def find_source(self, offset, ast, customize, isLambda=0, returnNone=False): def find_source(self, offset, ast, customize, isLambda=0, returnNone=False):
"""convert AST to source code""" """convert AST to source code"""
@@ -86,10 +102,11 @@ class FindWalker(walker.Walker, object):
self.print_(self.indent, 'pass') self.print_(self.indent, 'pass')
else: else:
self.customize(customize) self.customize(customize)
result = self.traverse(ast, isLambda=isLambda)
if isLambda: if isLambda:
self.write(self.traverse(ast, isLambda=isLambda)) self.write(result)
else: else:
self.print_(self.traverse(ast, isLambda=isLambda)) self.print_(result)
self.return_none = rn self.return_none = rn
# FIXME; below duplicated the code, since we don't find self.__params # 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) uncompyle(2.7, co, sys.stdout, 1)
print print
print '------------------------' print '------------------------'
uncompyle_find(2.7, co, 24) uncompyle_find(2.7, co, 33)
finally: finally:
del frame del frame

View File

@@ -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()

View File

@@ -1,277 +0,0 @@
# Copyright (c) 1999 John Aycock
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
#
# 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 <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> 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 <target> 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 <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> 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 <target> 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 <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> 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 <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> 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

88
uncompyle6/magics.py Executable file
View File

@@ -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()

View File

@@ -26,12 +26,14 @@
%c evaluate N[A] recursively* %c evaluate N[A] recursively*
%C evaluate N[A[0]]..N[A[1]-1] recursively, separate by A[2]* %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) %, print ',' if last %C only printed one item (for tuples--unused)
%| tab to current indentation level %| tab to current indentation level
%+ increase current indentation level %+ increase current indentation level
%- decrease current indentation level %- decrease current indentation level
%{...} evaluate ... in context of N %{...} evaluate ... in context of N
%% literal '%' %% literal '%'
%p evaluate N setting precedence
* indicates an argument (A) required. * indicates an argument (A) required.