You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 16:59:52 +08:00
Merge branch 'master' into python-2.4
This commit is contained in:
@@ -57,6 +57,19 @@ disassembler called `pydisasm`.
|
|||||||
|
|
||||||
### Semantic equivalence vs. exact source code
|
### Semantic equivalence vs. exact source code
|
||||||
|
|
||||||
|
Consider how Python compiles something like "(x*y) + 5". Early on
|
||||||
|
Python creates an "abstract syntax tree" (AST) for this. And this is
|
||||||
|
"abstract" in the sense that unimportant, redundant or unnecceary
|
||||||
|
items have been removed. Here, this means that any notion that you
|
||||||
|
wrote "x+y" in parenthesis is lost, since in this context they are
|
||||||
|
unneeded. Also lost is the fact that the multiplication didn't have
|
||||||
|
spaces around it while the addition did. It should not come as a
|
||||||
|
surprise then that the bytecode which is derived from the AST also has
|
||||||
|
no notion of such possible variation. Generally this kind of thing
|
||||||
|
isn't noticed since the Python community has laid out a very rigid set
|
||||||
|
of formatting guidelines; and it has largely beaten the community into
|
||||||
|
compliance.
|
||||||
|
|
||||||
Almost all versions of Python can perform some sort of code
|
Almost all versions of Python can perform some sort of code
|
||||||
improvement that can't be undone. In earlier versions of Python it is
|
improvement that can't be undone. In earlier versions of Python it is
|
||||||
rare; in later Python versions, it is more common.
|
rare; in later Python versions, it is more common.
|
||||||
@@ -66,7 +79,7 @@ If the code emitted is semantically equivalent, then this isn't a bug.
|
|||||||
|
|
||||||
For example the code might be
|
For example the code might be
|
||||||
|
|
||||||
```
|
```python
|
||||||
if a:
|
if a:
|
||||||
if b:
|
if b:
|
||||||
x = 1
|
x = 1
|
||||||
@@ -74,7 +87,7 @@ if a:
|
|||||||
|
|
||||||
and we might produce:
|
and we might produce:
|
||||||
|
|
||||||
```
|
```python
|
||||||
if a and b:
|
if a and b:
|
||||||
x = 1
|
x = 1
|
||||||
```
|
```
|
||||||
@@ -87,24 +100,35 @@ else:
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
may come out as `elif`.
|
may come out as `elif` or vice versa.
|
||||||
|
|
||||||
|
|
||||||
As mentioned in the README, It is possible that Python changes what
|
As mentioned in the README, It is possible that Python changes what
|
||||||
you write to be more efficient. For example, for:
|
you write to be more efficient. For example, for:
|
||||||
|
|
||||||
|
|
||||||
```
|
```python
|
||||||
if True:
|
if True:
|
||||||
x = 5
|
x = 5
|
||||||
```
|
```
|
||||||
|
|
||||||
Python will generate code like:
|
Python will generate code like:
|
||||||
|
|
||||||
```
|
```python
|
||||||
x = 5
|
x = 5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Even more extreme, if your code is:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if False:
|
||||||
|
x = 1
|
||||||
|
y = 2
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Python will eliminate the entire "if" statement.
|
||||||
|
|
||||||
So just because the text isn't the same, does not
|
So just because the text isn't the same, does not
|
||||||
necessarily mean there's a bug.
|
necessarily mean there's a bug.
|
||||||
|
|
||||||
|
@@ -5,4 +5,4 @@ if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
|
|||||||
echo "This script should be *sourced* rather than run directly through bash"
|
echo "This script should be *sourced* rather than run directly through bash"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
export PYVERSIONS='3.5.3 3.6.3 2.6.9 3.3.6 2.7.14 3.4.2'
|
export PYVERSIONS='3.5.5 3.6.4 2.6.9 3.3.7 2.7.14 3.4.8'
|
||||||
|
BIN
test/bytecode_2.5/08_if_while_else.pyc
Normal file
BIN
test/bytecode_2.5/08_if_while_else.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
13
test/simple_source/bug25/08_if_while_else.py
Normal file
13
test/simple_source/bug25/08_if_while_else.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# From 2.3 Queue.py
|
||||||
|
# Bug was adding COME_FROM from while
|
||||||
|
# confusing the else
|
||||||
|
def put(item, block=True, timeout=None):
|
||||||
|
if block:
|
||||||
|
if timeout:
|
||||||
|
while True:
|
||||||
|
if item:
|
||||||
|
block = 1
|
||||||
|
else:
|
||||||
|
block = 5
|
||||||
|
elif item:
|
||||||
|
block = False
|
@@ -4,3 +4,14 @@ def __new__(cls, encode, decode, streamreader=None, streamwriter=None,
|
|||||||
incrementalencoder=None, incrementaldecoder=None, name=None,
|
incrementalencoder=None, incrementaldecoder=None, name=None,
|
||||||
*, _is_text_encoding=None):
|
*, _is_text_encoding=None):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# From 3.3 _pyio.py. A closure is created here.
|
||||||
|
# This changes how the default params are found
|
||||||
|
class StringIO(object):
|
||||||
|
def __init__(self, initial_value="", newline="\n"):
|
||||||
|
super(StringIO, self).__init__()
|
||||||
|
|
||||||
|
# No closure created here
|
||||||
|
class StringIO2(object):
|
||||||
|
def __init__(self, initial_value="", newline="\n"):
|
||||||
|
return 5
|
||||||
|
@@ -1,49 +0,0 @@
|
|||||||
# Bug was found in 3.6 _osx_support.py in if/elif needing
|
|
||||||
# EXTENDED_ARGS which are the targets of jumps.
|
|
||||||
def get_platform_osx(_config_vars, osname, release, machine, sys, re):
|
|
||||||
"""Filter values for get_platform()"""
|
|
||||||
|
|
||||||
macver = _config_vars.get('MACOSX_DEPLOYMENT_TARGET', '')
|
|
||||||
macrelease = release or 10
|
|
||||||
macver = macver or macrelease
|
|
||||||
|
|
||||||
if macver:
|
|
||||||
release = macver
|
|
||||||
osname = "macosx"
|
|
||||||
|
|
||||||
cflags = _config_vars.get('CFLAGS', _config_vars.get('CFLAGS', ''))
|
|
||||||
if macrelease:
|
|
||||||
try:
|
|
||||||
macrelease = tuple(int(i) for i in macrelease.split('.')[0:2])
|
|
||||||
except ValueError:
|
|
||||||
macrelease = (10, 0)
|
|
||||||
else:
|
|
||||||
macrelease = (10, 0)
|
|
||||||
|
|
||||||
if (macrelease >= (10, 4)) and '-arch' in cflags.strip():
|
|
||||||
machine = 'fat'
|
|
||||||
|
|
||||||
archs = re.findall(r'-arch\s+(\S+)', cflags)
|
|
||||||
archs = tuple(sorted(set(archs)))
|
|
||||||
|
|
||||||
if len(archs) == 1:
|
|
||||||
machine = archs[0]
|
|
||||||
elif archs == ('i386', 'ppc'):
|
|
||||||
machine = 'fat'
|
|
||||||
elif archs == ('i386', 'x86_64'):
|
|
||||||
machine = 'intel'
|
|
||||||
elif archs == ('i386', 'ppc', 'x86_64'):
|
|
||||||
machine = 'fat3'
|
|
||||||
elif archs == ('ppc64', 'x86_64'):
|
|
||||||
machine = 'fat64'
|
|
||||||
elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'):
|
|
||||||
machine = 'universal'
|
|
||||||
else:
|
|
||||||
raise ValueError(
|
|
||||||
"Don't know machine value for archs=%r" % (archs,))
|
|
||||||
|
|
||||||
elif machine == 'i386':
|
|
||||||
if sys.maxsize >= 2**32:
|
|
||||||
machine = 'x86_64'
|
|
||||||
|
|
||||||
return (osname, release, machine)
|
|
@@ -1,5 +1,30 @@
|
|||||||
# Python 3.6's changes for calling functions.
|
# Python 3.6's changes for calling functions.
|
||||||
# See https://github.com/rocky/python-uncompyle6/issues/58
|
# See https://github.com/rocky/python-uncompyle6/issues/58
|
||||||
# CALL_FUNCTION_EX takes 2 to 3 arguments on the stack: the function, the tuple of positional arguments,
|
|
||||||
# and optionally the dict of keyword arguments if bit 0 of oparg is 1.
|
# CALL_FUNCTION_EX takes 2 to 3 arguments on the stack:
|
||||||
a(*[])
|
# * the function,
|
||||||
|
# * the tuple of positional arguments, and optionally
|
||||||
|
# * the dict of keyword arguments if bit 0 of oparg is 1.
|
||||||
|
from foo import f, dialect, args, kwds, reader
|
||||||
|
|
||||||
|
f(*[])
|
||||||
|
|
||||||
|
# From Python 3.6 csv.py
|
||||||
|
# (f, dialect) are positional arg tuples, *args, is by itself, i.e.
|
||||||
|
# no tuple.
|
||||||
|
x = reader(f, dialect, *args, **kwds)
|
||||||
|
|
||||||
|
# From 3.6 functools.py
|
||||||
|
# Below there is a load_closure instruction added
|
||||||
|
def cmp_to_key(mycmp):
|
||||||
|
class K(object):
|
||||||
|
def __ge__():
|
||||||
|
return mycmp()
|
||||||
|
return
|
||||||
|
|
||||||
|
# In this situation though, there is no load_closure
|
||||||
|
def cmp2_to_key(mycmp):
|
||||||
|
class K2(object):
|
||||||
|
def __ge__():
|
||||||
|
return 5
|
||||||
|
return
|
||||||
|
@@ -6,3 +6,12 @@ def a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\v'):
|
|||||||
_UNSET = object()
|
_UNSET = object()
|
||||||
def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
|
def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# From 3.6 compileall.py Bug is making default values are in quotes
|
||||||
|
def compile_command(source, filename="<input>", symbol="single"):
|
||||||
|
return
|
||||||
|
|
||||||
|
# From 3.6 _pyio.py. Bug was in getting order of metaclass=abc.ABCMeta right
|
||||||
|
import abc
|
||||||
|
class IOBase(metaclass=abc.ABCMeta):
|
||||||
|
pass
|
||||||
|
@@ -115,8 +115,7 @@ class Python26Parser(Python2Parser):
|
|||||||
|
|
||||||
# Semantic actions want else_suitel to be at index 3
|
# Semantic actions want else_suitel to be at index 3
|
||||||
ifelsestmtl ::= testexpr c_stmts_opt cf_jb_cf_pop else_suitel
|
ifelsestmtl ::= testexpr c_stmts_opt cf_jb_cf_pop else_suitel
|
||||||
|
ifelsestmtc ::= testexpr c_stmts_opt ja_cf_pop else_suitec
|
||||||
ifelsestmtc ::= testexpr c_stmts_opt ja_cf_pop else_suitec
|
|
||||||
|
|
||||||
# Semantic actions want suite_stmts_opt to be at index 3
|
# Semantic actions want suite_stmts_opt to be at index 3
|
||||||
withstmt ::= expr setupwith SETUP_FINALLY suite_stmts_opt
|
withstmt ::= expr setupwith SETUP_FINALLY suite_stmts_opt
|
||||||
|
@@ -19,6 +19,7 @@ spark grammar differences over Python 3.5 for Python 3.6.
|
|||||||
from uncompyle6.parser import PythonParserSingle, nop_func
|
from uncompyle6.parser import PythonParserSingle, nop_func
|
||||||
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||||
from uncompyle6.parsers.parse35 import Python35Parser
|
from uncompyle6.parsers.parse35 import Python35Parser
|
||||||
|
from uncompyle6.scanners.tok import Token
|
||||||
|
|
||||||
class Python36Parser(Python35Parser):
|
class Python36Parser(Python35Parser):
|
||||||
|
|
||||||
@@ -77,6 +78,8 @@ class Python36Parser(Python35Parser):
|
|||||||
|
|
||||||
def customize_grammar_rules(self, tokens, customize):
|
def customize_grammar_rules(self, tokens, customize):
|
||||||
super(Python36Parser, self).customize_grammar_rules(tokens, customize)
|
super(Python36Parser, self).customize_grammar_rules(tokens, customize)
|
||||||
|
self.check_reduce['call_kw'] = 'AST'
|
||||||
|
|
||||||
for i, token in enumerate(tokens):
|
for i, token in enumerate(tokens):
|
||||||
opname = token.kind
|
opname = token.kind
|
||||||
|
|
||||||
@@ -186,6 +189,8 @@ class Python36Parser(Python35Parser):
|
|||||||
self.addRule("""expr ::= call_ex_kw
|
self.addRule("""expr ::= call_ex_kw
|
||||||
expr ::= call_ex_kw2
|
expr ::= call_ex_kw2
|
||||||
expr ::= call_ex_kw3
|
expr ::= call_ex_kw3
|
||||||
|
expr ::= call_ex_kw4
|
||||||
|
|
||||||
call_ex_kw ::= expr expr build_map_unpack_with_call
|
call_ex_kw ::= expr expr build_map_unpack_with_call
|
||||||
CALL_FUNCTION_EX_KW
|
CALL_FUNCTION_EX_KW
|
||||||
call_ex_kw2 ::= expr
|
call_ex_kw2 ::= expr
|
||||||
@@ -193,6 +198,10 @@ class Python36Parser(Python35Parser):
|
|||||||
build_map_unpack_with_call
|
build_map_unpack_with_call
|
||||||
CALL_FUNCTION_EX_KW
|
CALL_FUNCTION_EX_KW
|
||||||
call_ex_kw3 ::= expr
|
call_ex_kw3 ::= expr
|
||||||
|
build_tuple_unpack_with_call
|
||||||
|
expr
|
||||||
|
CALL_FUNCTION_EX_KW
|
||||||
|
call_ex_kw4 ::= expr
|
||||||
expr
|
expr
|
||||||
expr
|
expr
|
||||||
CALL_FUNCTION_EX_KW
|
CALL_FUNCTION_EX_KW
|
||||||
@@ -212,6 +221,21 @@ class Python36Parser(Python35Parser):
|
|||||||
seen_GET_AWAITABLE_YIELD_FROM,
|
seen_GET_AWAITABLE_YIELD_FROM,
|
||||||
next_token)
|
next_token)
|
||||||
|
|
||||||
|
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||||
|
invalid = super(Python36Parser,
|
||||||
|
self).reduce_is_invalid(rule, ast,
|
||||||
|
tokens, first, last)
|
||||||
|
if invalid:
|
||||||
|
return invalid
|
||||||
|
if rule[0] == 'call_kw':
|
||||||
|
# Make sure we don't derive call_kw
|
||||||
|
nt = ast[0]
|
||||||
|
while not isinstance(nt, Token):
|
||||||
|
if nt[0] == 'call_kw':
|
||||||
|
return True
|
||||||
|
nt = nt[0]
|
||||||
|
|
||||||
|
return False
|
||||||
class Python36ParserSingle(Python36Parser, PythonParserSingle):
|
class Python36ParserSingle(Python36Parser, PythonParserSingle):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@@ -1131,7 +1131,13 @@ class Scanner2(Scanner):
|
|||||||
source = self.setup_loops[label]
|
source = self.setup_loops[label]
|
||||||
else:
|
else:
|
||||||
source = offset
|
source = offset
|
||||||
targets[label] = targets.get(label, []) + [source]
|
# FIXME: The grammar for 2.6 and before doesn't
|
||||||
|
# handle COME_FROM's from a loop inside if's
|
||||||
|
# It probably should.
|
||||||
|
if (self.version > 2.6 or
|
||||||
|
self.code[source] != self.opc.SETUP_LOOP or
|
||||||
|
self.code[label] != self.opc.JUMP_FORWARD):
|
||||||
|
targets[label] = targets.get(label, []) + [source]
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
|
@@ -455,6 +455,33 @@ def customize_for_version(self, is_pypy, version):
|
|||||||
self.n_call_ex_kw2 = call_ex_kw2
|
self.n_call_ex_kw2 = call_ex_kw2
|
||||||
|
|
||||||
def call_ex_kw3(node):
|
def call_ex_kw3(node):
|
||||||
|
"""Handle CALL_FUNCTION_EX 1 (have KW) but without
|
||||||
|
BUILD_MAP_UNPACK_WITH_CALL"""
|
||||||
|
self.preorder(node[0])
|
||||||
|
self.write('(')
|
||||||
|
args = node[1][0]
|
||||||
|
if args == 'expr':
|
||||||
|
args = args[0]
|
||||||
|
if args == 'tuple':
|
||||||
|
if self.call36_tuple(args) > 0:
|
||||||
|
self.write(', ')
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.write('*')
|
||||||
|
self.preorder(node[1][1])
|
||||||
|
self.write(', ')
|
||||||
|
|
||||||
|
kwargs = node[2]
|
||||||
|
if kwargs == 'expr':
|
||||||
|
kwargs = kwargs[0]
|
||||||
|
self.write('**')
|
||||||
|
self.preorder(kwargs)
|
||||||
|
self.write(')')
|
||||||
|
self.prune()
|
||||||
|
self.n_call_ex_kw3 = call_ex_kw3
|
||||||
|
|
||||||
|
def call_ex_kw4(node):
|
||||||
"""Handle CALL_FUNCTION_EX 2 (have KW) but without
|
"""Handle CALL_FUNCTION_EX 2 (have KW) but without
|
||||||
BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL"""
|
BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL"""
|
||||||
self.preorder(node[0])
|
self.preorder(node[0])
|
||||||
@@ -478,8 +505,7 @@ def customize_for_version(self, is_pypy, version):
|
|||||||
self.preorder(kwargs)
|
self.preorder(kwargs)
|
||||||
self.write(')')
|
self.write(')')
|
||||||
self.prune()
|
self.prune()
|
||||||
self.n_call_ex_kw3 = call_ex_kw3
|
self.n_call_ex_kw4 = call_ex_kw4
|
||||||
|
|
||||||
|
|
||||||
def call36_tuple(node):
|
def call36_tuple(node):
|
||||||
"""
|
"""
|
||||||
@@ -610,7 +636,7 @@ def customize_for_version(self, is_pypy, version):
|
|||||||
num_posargs = len(node) - (num_kwargs + 1)
|
num_posargs = len(node) - (num_kwargs + 1)
|
||||||
n = len(node)
|
n = len(node)
|
||||||
assert n >= len(keys)+1, \
|
assert n >= len(keys)+1, \
|
||||||
'not enough parameters keyword-tuple values'
|
'not enough parameters keyword-tuple values'
|
||||||
# try:
|
# try:
|
||||||
# assert n >= len(keys)+1, \
|
# assert n >= len(keys)+1, \
|
||||||
# 'not enough parameters keyword-tuple values'
|
# 'not enough parameters keyword-tuple values'
|
||||||
@@ -665,7 +691,7 @@ def customize_for_version(self, is_pypy, version):
|
|||||||
self.prune()
|
self.prune()
|
||||||
return
|
return
|
||||||
self.n_return_closure = return_closure
|
self.n_return_closure = return_closure
|
||||||
pass # version > 3.6
|
pass # version >= 3.6
|
||||||
pass # version > 3.4
|
pass # version >= 3.4
|
||||||
pass # version > 3.0
|
pass # version >= 3.0
|
||||||
return
|
return
|
||||||
|
@@ -456,8 +456,11 @@ def make_function3(self, node, is_lambda, nested=1, codeNode=None):
|
|||||||
|
|
||||||
# MAKE_CLOSURE adds an additional closure slot
|
# MAKE_CLOSURE adds an additional closure slot
|
||||||
|
|
||||||
# Thank you, Python: such a well-thought out system that has
|
# In Python 3.6 stack entries change again. I understand
|
||||||
# changed and continues to change many times.
|
# 3.7 changes some of those changes. Yes, it is hard to follow
|
||||||
|
# and I am sure I haven't been able to keep up.
|
||||||
|
|
||||||
|
# Thank you, Python.
|
||||||
|
|
||||||
def build_param(ast, name, default):
|
def build_param(ast, name, default):
|
||||||
"""build parameters:
|
"""build parameters:
|
||||||
@@ -482,10 +485,23 @@ def make_function3(self, node, is_lambda, nested=1, codeNode=None):
|
|||||||
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
|
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
|
||||||
assert node[-1].kind.startswith('MAKE_')
|
assert node[-1].kind.startswith('MAKE_')
|
||||||
|
|
||||||
|
# Python 3.3+ adds a qualified name at TOS (-1)
|
||||||
|
# moving down the LOAD_LAMBDA instruction
|
||||||
|
if 3.0 <= self.version <= 3.2:
|
||||||
|
lambda_index = -2
|
||||||
|
elif 3.03 <= self.version:
|
||||||
|
lambda_index = -3
|
||||||
|
else:
|
||||||
|
lambda_index = None
|
||||||
|
|
||||||
args_node = node[-1]
|
args_node = node[-1]
|
||||||
if isinstance(args_node.attr, tuple):
|
if isinstance(args_node.attr, tuple):
|
||||||
if self.version <= 3.3 and len(node) > 2 and node[-3] != 'LOAD_LAMBDA':
|
pos_args, kw_args, annotate_argc = args_node.attr
|
||||||
# positional args are after kwargs
|
# FIXME: there is probably a better way to classify this.
|
||||||
|
if (self.version <= 3.3 and len(node) > 2 and
|
||||||
|
node[lambda_index] != 'LOAD_LAMBDA' and
|
||||||
|
(node[0].kind.startswith('kwarg') or node[-4].kind != 'load_closure')):
|
||||||
|
# args are after kwargs; kwargs are bundled as one node
|
||||||
defparams = node[1:args_node.attr[0]+1]
|
defparams = node[1:args_node.attr[0]+1]
|
||||||
else:
|
else:
|
||||||
# args are before kwargs; kwags as bundled as one node
|
# args are before kwargs; kwags as bundled as one node
|
||||||
@@ -502,7 +518,7 @@ def make_function3(self, node, is_lambda, nested=1, codeNode=None):
|
|||||||
expr_node = node[0]
|
expr_node = node[0]
|
||||||
if (expr_node[0] == 'LOAD_CONST' and
|
if (expr_node[0] == 'LOAD_CONST' and
|
||||||
isinstance(expr_node[0].attr, tuple)):
|
isinstance(expr_node[0].attr, tuple)):
|
||||||
defparams = list(expr_node[0].attr)
|
defparams = [repr(a) for a in expr_node[0].attr]
|
||||||
elif expr_node[0] in frozenset(('list', 'tuple', 'dict', 'set')):
|
elif expr_node[0] in frozenset(('list', 'tuple', 'dict', 'set')):
|
||||||
defparams = [self.traverse(n, indent='') for n in expr_node[0][:-1]]
|
defparams = [self.traverse(n, indent='') for n in expr_node[0][:-1]]
|
||||||
else:
|
else:
|
||||||
|
@@ -536,278 +536,8 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
pass
|
pass
|
||||||
self.n_unmapexpr = unmapexpr
|
self.n_unmapexpr = unmapexpr
|
||||||
|
|
||||||
if version >= 3.6:
|
pass # version >= 3.4
|
||||||
########################
|
pass # version >= 3.0
|
||||||
# Python 3.6+ Additions
|
|
||||||
#######################
|
|
||||||
|
|
||||||
TABLE_DIRECT.update({
|
|
||||||
'fstring_expr': ( "{%c%{conversion}}", 0),
|
|
||||||
'fstring_single': ( "f'{%c%{conversion}}'", 0),
|
|
||||||
'fstring_multi': ( "f'%c'", 0),
|
|
||||||
'func_args36': ( "%c(**", 0),
|
|
||||||
'try_except36': ( '%|try:\n%+%c%-%c\n\n', 1, 2 ),
|
|
||||||
'unpack_list': ( '*%c', (0, 'list') ),
|
|
||||||
'starred': ( '*%c', (0, 'expr') ),
|
|
||||||
'call_ex' : (
|
|
||||||
'%c(%c)',
|
|
||||||
(0, 'expr'), 1),
|
|
||||||
'call_ex_kw' : (
|
|
||||||
'%c(%c)',
|
|
||||||
(0, 'expr'), 2),
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
TABLE_R.update({
|
|
||||||
'CALL_FUNCTION_EX': ('%c(*%P)', 0, (1, 2, ', ', 100)),
|
|
||||||
# Not quite right
|
|
||||||
'CALL_FUNCTION_EX_KW': ('%c(**%C)', 0, (2, 3, ',')),
|
|
||||||
})
|
|
||||||
|
|
||||||
def build_unpack_tuple_with_call(node):
|
|
||||||
|
|
||||||
if node[0] == 'expr':
|
|
||||||
tup = node[0][0]
|
|
||||||
else:
|
|
||||||
tup = node[0]
|
|
||||||
pass
|
|
||||||
assert tup == 'tuple'
|
|
||||||
self.call36_tuple(tup)
|
|
||||||
|
|
||||||
buwc = node[-1]
|
|
||||||
assert buwc.kind.startswith('BUILD_TUPLE_UNPACK_WITH_CALL')
|
|
||||||
for n in node[1:-1]:
|
|
||||||
self.f.write(', *')
|
|
||||||
self.preorder(n)
|
|
||||||
pass
|
|
||||||
self.prune()
|
|
||||||
return
|
|
||||||
self.n_build_tuple_unpack_with_call = build_unpack_tuple_with_call
|
|
||||||
|
|
||||||
def build_unpack_map_with_call(node):
|
|
||||||
n = node[0]
|
|
||||||
if n == 'expr':
|
|
||||||
n = n[0]
|
|
||||||
if n == 'dict':
|
|
||||||
self.call36_dict(n)
|
|
||||||
first = 1
|
|
||||||
sep = ', **'
|
|
||||||
else:
|
|
||||||
first = 0
|
|
||||||
sep = '**'
|
|
||||||
for n in node[first:-1]:
|
|
||||||
self.f.write(sep)
|
|
||||||
self.preorder(n)
|
|
||||||
sep = ', **'
|
|
||||||
pass
|
|
||||||
self.prune()
|
|
||||||
return
|
|
||||||
self.n_build_map_unpack_with_call = build_unpack_map_with_call
|
|
||||||
|
|
||||||
def call_ex_kw2(node):
|
|
||||||
"""Handle CALL_FUNCTION_EX 2 (have KW) but with
|
|
||||||
BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL"""
|
|
||||||
|
|
||||||
# This is weird shit. Thanks Python!
|
|
||||||
self.preorder(node[0])
|
|
||||||
self.write('(')
|
|
||||||
|
|
||||||
assert node[1] == 'build_tuple_unpack_with_call'
|
|
||||||
btuwc = node[1]
|
|
||||||
tup = btuwc[0]
|
|
||||||
if tup == 'expr':
|
|
||||||
tup = tup[0]
|
|
||||||
assert tup == 'tuple'
|
|
||||||
self.call36_tuple(tup)
|
|
||||||
assert node[2] == 'build_map_unpack_with_call'
|
|
||||||
|
|
||||||
self.write(', ')
|
|
||||||
d = node[2][0]
|
|
||||||
if d == 'expr':
|
|
||||||
d = d[0]
|
|
||||||
assert d == 'dict'
|
|
||||||
self.call36_dict(d)
|
|
||||||
|
|
||||||
args = btuwc[1]
|
|
||||||
self.write(', *')
|
|
||||||
self.preorder(args)
|
|
||||||
|
|
||||||
self.write(', **')
|
|
||||||
star_star_args = node[2][1]
|
|
||||||
if star_star_args == 'expr':
|
|
||||||
star_star_args = star_star_args[0]
|
|
||||||
self.preorder(star_star_args)
|
|
||||||
self.write(')')
|
|
||||||
self.prune()
|
|
||||||
self.n_call_ex_kw2 = call_ex_kw2
|
|
||||||
|
|
||||||
def call_ex_kw3(node):
|
|
||||||
"""Handle CALL_FUNCTION_EX 2 (have KW) but without
|
|
||||||
BUILD_{MAP,TUPLE}_UNPACK_WITH_CALL"""
|
|
||||||
self.preorder(node[0])
|
|
||||||
self.write('(')
|
|
||||||
args = node[1][0]
|
|
||||||
if args == 'tuple':
|
|
||||||
if self.call36_tuple(args) > 0:
|
|
||||||
self.write(', ')
|
|
||||||
pass
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.write('*')
|
|
||||||
self.preorder(args)
|
|
||||||
self.write(', ')
|
|
||||||
pass
|
|
||||||
|
|
||||||
kwargs = node[2]
|
|
||||||
if kwargs == 'expr':
|
|
||||||
kwargs = kwargs[0]
|
|
||||||
self.write('**')
|
|
||||||
self.preorder(kwargs)
|
|
||||||
self.write(')')
|
|
||||||
self.prune()
|
|
||||||
self.n_call_ex_kw3 = call_ex_kw3
|
|
||||||
|
|
||||||
|
|
||||||
def call36_tuple(node):
|
|
||||||
"""
|
|
||||||
A tuple used in a call, these are like normal tuples but they
|
|
||||||
don't have the enclosing parenthesis.
|
|
||||||
"""
|
|
||||||
assert node == 'tuple'
|
|
||||||
# Note: don't iterate over last element which is a
|
|
||||||
# BUILD_TUPLE...
|
|
||||||
flat_elems = flatten_list(node[:-1])
|
|
||||||
|
|
||||||
self.indent_more(INDENT_PER_LEVEL)
|
|
||||||
sep = ''
|
|
||||||
|
|
||||||
for elem in flat_elems:
|
|
||||||
if elem in ('ROT_THREE', 'EXTENDED_ARG'):
|
|
||||||
continue
|
|
||||||
assert elem == 'expr'
|
|
||||||
line_number = self.line_number
|
|
||||||
value = self.traverse(elem)
|
|
||||||
if line_number != self.line_number:
|
|
||||||
sep += '\n' + self.indent + INDENT_PER_LEVEL[:-1]
|
|
||||||
self.write(sep, value)
|
|
||||||
sep = ', '
|
|
||||||
|
|
||||||
self.indent_less(INDENT_PER_LEVEL)
|
|
||||||
return len(flat_elems)
|
|
||||||
self.call36_tuple = call36_tuple
|
|
||||||
|
|
||||||
def call36_dict(node):
|
|
||||||
"""
|
|
||||||
A dict used in a call_ex_kw2, which are a dictionary items expressed
|
|
||||||
in a call. This should format to:
|
|
||||||
a=1, b=2
|
|
||||||
In other words, no braces, no quotes around keys and ":" becomes
|
|
||||||
"=".
|
|
||||||
|
|
||||||
We will source-code use line breaks to guide us when to break.
|
|
||||||
"""
|
|
||||||
p = self.prec
|
|
||||||
self.prec = 100
|
|
||||||
|
|
||||||
self.indent_more(INDENT_PER_LEVEL)
|
|
||||||
sep = INDENT_PER_LEVEL[:-1]
|
|
||||||
line_number = self.line_number
|
|
||||||
|
|
||||||
assert node[0].kind.startswith('kvlist')
|
|
||||||
# Python 3.5+ style key/value list in dict
|
|
||||||
kv_node = node[0]
|
|
||||||
l = list(kv_node)
|
|
||||||
i = 0
|
|
||||||
# Respect line breaks from source
|
|
||||||
while i < len(l):
|
|
||||||
self.write(sep)
|
|
||||||
name = self.traverse(l[i], indent='')
|
|
||||||
# Strip off beginning and trailing quotes in name
|
|
||||||
name = name[1:-1]
|
|
||||||
if i > 0:
|
|
||||||
line_number = self.indent_if_source_nl(line_number,
|
|
||||||
self.indent + INDENT_PER_LEVEL[:-1])
|
|
||||||
line_number = self.line_number
|
|
||||||
self.write(name, '=')
|
|
||||||
value = self.traverse(l[i+1], indent=self.indent+(len(name)+2)*' ')
|
|
||||||
self.write(value)
|
|
||||||
sep = ","
|
|
||||||
if line_number != self.line_number:
|
|
||||||
sep += "\n" + self.indent + INDENT_PER_LEVEL[:-1]
|
|
||||||
line_number = self.line_number
|
|
||||||
i += 2
|
|
||||||
pass
|
|
||||||
self.prec = p
|
|
||||||
self.indent_less(INDENT_PER_LEVEL)
|
|
||||||
return
|
|
||||||
self.call36_dict = call36_dict
|
|
||||||
|
|
||||||
|
|
||||||
FSTRING_CONVERSION_MAP = {1: '!s', 2: '!r', 3: '!a'}
|
|
||||||
|
|
||||||
def f_conversion(node):
|
|
||||||
node.conversion = FSTRING_CONVERSION_MAP.get(node.data[1].attr, '')
|
|
||||||
|
|
||||||
def fstring_expr(node):
|
|
||||||
f_conversion(node)
|
|
||||||
self.default(node)
|
|
||||||
self.n_fstring_expr = fstring_expr
|
|
||||||
|
|
||||||
def fstring_single(node):
|
|
||||||
f_conversion(node)
|
|
||||||
self.default(node)
|
|
||||||
self.n_fstring_single = fstring_single
|
|
||||||
|
|
||||||
# def kwargs_only_36(node):
|
|
||||||
# keys = node[-1].attr
|
|
||||||
# num_kwargs = len(keys)
|
|
||||||
# values = node[:num_kwargs]
|
|
||||||
# for i, (key, value) in enumerate(zip(keys, values)):
|
|
||||||
# self.write(key + '=')
|
|
||||||
# self.preorder(value)
|
|
||||||
# if i < num_kwargs:
|
|
||||||
# self.write(',')
|
|
||||||
# self.prune()
|
|
||||||
# return
|
|
||||||
# self.n_kwargs_only_36 = kwargs_only_36
|
|
||||||
|
|
||||||
def kwargs_36(node):
|
|
||||||
self.write('(')
|
|
||||||
keys = node[-1].attr
|
|
||||||
num_kwargs = len(keys)
|
|
||||||
num_posargs = len(node) - (num_kwargs + 1)
|
|
||||||
n = len(node)
|
|
||||||
assert n >= len(keys)+2
|
|
||||||
sep = ''
|
|
||||||
# FIXME: adjust output for line breaks?
|
|
||||||
for i in range(num_posargs):
|
|
||||||
self.write(sep)
|
|
||||||
self.preorder(node[i])
|
|
||||||
sep = ', '
|
|
||||||
|
|
||||||
i = num_posargs
|
|
||||||
j = 0
|
|
||||||
# FIXME: adjust output for line breaks?
|
|
||||||
while i < n-1:
|
|
||||||
self.write(sep)
|
|
||||||
self.write(keys[j] + '=')
|
|
||||||
self.preorder(node[i])
|
|
||||||
i += 1
|
|
||||||
j += 1
|
|
||||||
self.write(')')
|
|
||||||
self.prune()
|
|
||||||
return
|
|
||||||
self.n_kwargs_36 = kwargs_36
|
|
||||||
|
|
||||||
|
|
||||||
def return_closure(node):
|
|
||||||
# Nothing should be output here
|
|
||||||
self.prune()
|
|
||||||
return
|
|
||||||
self.n_return_closure = return_closure
|
|
||||||
pass # version > 3.6
|
|
||||||
pass # version > 3.4
|
|
||||||
pass # version > 3.0
|
|
||||||
return
|
return
|
||||||
|
|
||||||
f = property(lambda s: s.params['f'],
|
f = property(lambda s: s.params['f'],
|
||||||
@@ -1801,17 +1531,21 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
class_name = node[2][0].pattr
|
class_name = node[2][0].pattr
|
||||||
else:
|
else:
|
||||||
class_name = node[1][2].pattr
|
class_name = node[1][2].pattr
|
||||||
buildclass = node
|
build_class = node
|
||||||
else:
|
else:
|
||||||
|
build_class = node[0]
|
||||||
if self.version >= 3.6:
|
if self.version >= 3.6:
|
||||||
class_name = node[0][1][0].attr.co_name
|
if build_class[1][0] == 'load_closure':
|
||||||
buildclass = node[0]
|
code_node = build_class[1][1]
|
||||||
|
else:
|
||||||
|
code_node = build_class[1][0]
|
||||||
|
class_name = code_node.attr.co_name
|
||||||
else:
|
else:
|
||||||
class_name = node[1][0].pattr
|
class_name = node[1][0].pattr
|
||||||
buildclass = node[0]
|
build_class = node[0]
|
||||||
|
|
||||||
assert 'mkfunc' == buildclass[1]
|
assert 'mkfunc' == build_class[1]
|
||||||
mkfunc = buildclass[1]
|
mkfunc = build_class[1]
|
||||||
if mkfunc[0] == 'kwargs':
|
if mkfunc[0] == 'kwargs':
|
||||||
if 3.0 <= self.version <= 3.2:
|
if 3.0 <= self.version <= 3.2:
|
||||||
for n in mkfunc:
|
for n in mkfunc:
|
||||||
@@ -1833,9 +1567,9 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
subclass_info = node
|
subclass_info = node
|
||||||
else:
|
else:
|
||||||
subclass_info = node[0]
|
subclass_info = node[0]
|
||||||
elif buildclass[1][0] == 'load_closure':
|
elif build_class[1][0] == 'load_closure':
|
||||||
# Python 3 with closures not functions
|
# Python 3 with closures not functions
|
||||||
load_closure = buildclass[1]
|
load_closure = build_class[1]
|
||||||
if hasattr(load_closure[-3], 'attr'):
|
if hasattr(load_closure[-3], 'attr'):
|
||||||
# Python 3.3 classes with closures work like this.
|
# Python 3.3 classes with closures work like this.
|
||||||
# Note have to test before 3.2 case because
|
# Note have to test before 3.2 case because
|
||||||
@@ -1846,34 +1580,34 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
subclass_code = load_closure[-2].attr
|
subclass_code = load_closure[-2].attr
|
||||||
else:
|
else:
|
||||||
raise 'Internal Error n_classdef: cannot find class body'
|
raise 'Internal Error n_classdef: cannot find class body'
|
||||||
if hasattr(buildclass[3], '__len__'):
|
if hasattr(build_class[3], '__len__'):
|
||||||
subclass_info = buildclass[3]
|
subclass_info = build_class[3]
|
||||||
elif hasattr(buildclass[2], '__len__'):
|
elif hasattr(build_class[2], '__len__'):
|
||||||
subclass_info = buildclass[2]
|
subclass_info = build_class[2]
|
||||||
else:
|
else:
|
||||||
raise 'Internal Error n_classdef: cannot superclass name'
|
raise 'Internal Error n_classdef: cannot superclass name'
|
||||||
elif self.version >= 3.6 and node == 'classdefdeco2':
|
elif self.version >= 3.6 and node == 'classdefdeco2':
|
||||||
subclass_info = node
|
subclass_info = node
|
||||||
subclass_code = buildclass[1][0].attr
|
subclass_code = build_class[1][0].attr
|
||||||
else:
|
else:
|
||||||
subclass_code = buildclass[1][0].attr
|
subclass_code = build_class[1][0].attr
|
||||||
subclass_info = node[0]
|
subclass_info = node[0]
|
||||||
else:
|
else:
|
||||||
if node == 'classdefdeco2':
|
if node == 'classdefdeco2':
|
||||||
buildclass = node
|
build_class = node
|
||||||
else:
|
else:
|
||||||
buildclass = node[0]
|
build_class = node[0]
|
||||||
build_list = buildclass[1][0]
|
build_list = build_class[1][0]
|
||||||
if hasattr(buildclass[-3][0], 'attr'):
|
if hasattr(build_class[-3][0], 'attr'):
|
||||||
subclass_code = buildclass[-3][0].attr
|
subclass_code = build_class[-3][0].attr
|
||||||
class_name = buildclass[0].pattr
|
class_name = build_class[0].pattr
|
||||||
elif (buildclass[-3] == 'mkfunc' and
|
elif (build_class[-3] == 'mkfunc' and
|
||||||
node == 'classdefdeco2' and
|
node == 'classdefdeco2' and
|
||||||
buildclass[-3][0] == 'load_closure'):
|
build_class[-3][0] == 'load_closure'):
|
||||||
subclass_code = buildclass[-3][1].attr
|
subclass_code = build_class[-3][1].attr
|
||||||
class_name = buildclass[-3][0][0].pattr
|
class_name = build_class[-3][0][0].pattr
|
||||||
elif hasattr(node[0][0], 'pattr'):
|
elif hasattr(node[0][0], 'pattr'):
|
||||||
subclass_code = buildclass[-3][1].attr
|
subclass_code = build_class[-3][1].attr
|
||||||
class_name = node[0][0].pattr
|
class_name = node[0][0].pattr
|
||||||
else:
|
else:
|
||||||
raise 'Internal Error n_classdef: cannot find class name'
|
raise 'Internal Error n_classdef: cannot find class name'
|
||||||
@@ -1954,13 +1688,13 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
else:
|
else:
|
||||||
l = n
|
l = n
|
||||||
while i < l:
|
while i < l:
|
||||||
|
# 3.6+ may have this
|
||||||
|
if kwargs:
|
||||||
|
self.write("%s=" % kwargs[j])
|
||||||
|
j += 1
|
||||||
value = self.traverse(node[i])
|
value = self.traverse(node[i])
|
||||||
i += 1
|
i += 1
|
||||||
self.write(sep, value)
|
self.write(sep, value)
|
||||||
# 3.6+ may have this
|
|
||||||
if kwargs:
|
|
||||||
self.write("=%s" % kwargs[j])
|
|
||||||
j += 1
|
|
||||||
sep = line_separator
|
sep = line_separator
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
|
Reference in New Issue
Block a user