You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-02 16:44:46 +08:00
new dis - Python compisons involving tuples
This commit is contained in:
@@ -14,7 +14,7 @@ matrix:
|
|||||||
|
|
||||||
install:
|
install:
|
||||||
# Remove the next line when xdis 6.0.0 is released
|
# Remove the next line when xdis 6.0.0 is released
|
||||||
- pip install git://github.com/rocky/python-xdis.git#egg=xdis
|
# - pip install git://github.com/rocky/python-xdis.git#egg=xdis
|
||||||
- pip install -e .
|
- pip install -e .
|
||||||
- pip install -r requirements-dev.txt
|
- pip install -r requirements-dev.txt
|
||||||
|
|
||||||
|
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
# Things that change more often go here.
|
# Things that change more often go here.
|
||||||
copyright = """
|
copyright = """
|
||||||
Copyright (C) 2015-2020 Rocky Bernstein <rb@dustyfeet.com>.
|
Copyright (C) 2015-2021 Rocky Bernstein <rb@dustyfeet.com>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classifiers = [
|
classifiers = [
|
||||||
@@ -70,7 +70,7 @@ entry_points = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
ftp_url = None
|
ftp_url = None
|
||||||
install_requires = ["spark-parser >= 1.8.9, < 1.9.0", "xdis >= 5.9.0, <= 6.0.1"]
|
install_requires = ["spark-parser >= 1.8.9, < 1.9.0", "xdis >= 6.0.0, < 6.1.0"]
|
||||||
|
|
||||||
license = "GPL3"
|
license = "GPL3"
|
||||||
mailing_list = "python-debugger@googlegroups.com"
|
mailing_list = "python-debugger@googlegroups.com"
|
||||||
|
@@ -649,30 +649,30 @@ def get_python_parser(
|
|||||||
# in import all of the parsers all of the time. Perhaps there is
|
# in import all of the parsers all of the time. Perhaps there is
|
||||||
# a lazy way of doing the import?
|
# a lazy way of doing the import?
|
||||||
|
|
||||||
if version < 3.0:
|
if version < (3, 0):
|
||||||
if version < 2.2:
|
if version < (2, 2):
|
||||||
if version == 1.0:
|
if version[:2] == (1, 0):
|
||||||
import uncompyle6.parsers.parse10 as parse10
|
import uncompyle6.parsers.parse10 as parse10
|
||||||
|
|
||||||
if compile_mode == "exec":
|
if compile_mode == "exec":
|
||||||
p = parse10.Python10Parser(debug_parser)
|
p = parse10.Python10Parser(debug_parser)
|
||||||
else:
|
else:
|
||||||
p = parse10.Python01ParserSingle(debug_parser)
|
p = parse10.Python01ParserSingle(debug_parser)
|
||||||
elif version == 1.1:
|
elif version[:2] == (1, 1):
|
||||||
import uncompyle6.parsers.parse11 as parse11
|
import uncompyle6.parsers.parse11 as parse11
|
||||||
|
|
||||||
if compile_mode == "exec":
|
if compile_mode == "exec":
|
||||||
p = parse11.Python11Parser(debug_parser)
|
p = parse11.Python11Parser(debug_parser)
|
||||||
else:
|
else:
|
||||||
p = parse11.Python11ParserSingle(debug_parser)
|
p = parse11.Python11ParserSingle(debug_parser)
|
||||||
if version == 1.2:
|
if version[:2] == (1, 2):
|
||||||
import uncompyle6.parsers.parse12 as parse12
|
import uncompyle6.parsers.parse12 as parse12
|
||||||
|
|
||||||
if compile_mode == "exec":
|
if compile_mode == "exec":
|
||||||
p = parse12.Python12Parser(debug_parser)
|
p = parse12.Python12Parser(debug_parser)
|
||||||
else:
|
else:
|
||||||
p = parse12.Python12ParserSingle(debug_parser)
|
p = parse12.Python12ParserSingle(debug_parser)
|
||||||
if version == 1.3:
|
if version[:2] == (1, 3):
|
||||||
import uncompyle6.parsers.parse13 as parse13
|
import uncompyle6.parsers.parse13 as parse13
|
||||||
|
|
||||||
if compile_mode == "exec":
|
if compile_mode == "exec":
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2015-2020 Rocky Bernstein
|
# Copyright (c) 2015-2021 Rocky Bernstein
|
||||||
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
||||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||||
# Copyright (c) 1999 John Aycock
|
# Copyright (c) 1999 John Aycock
|
||||||
@@ -536,7 +536,7 @@ class Python3Parser(PythonParser):
|
|||||||
# FIXME: What's the deal with the two rules? Different Python versions?
|
# FIXME: What's the deal with the two rules? Different Python versions?
|
||||||
# Different situations? Note that the above rule is based on the CALL_FUNCTION
|
# Different situations? Note that the above rule is based on the CALL_FUNCTION
|
||||||
# token found, while this one doesn't.
|
# token found, while this one doesn't.
|
||||||
if self.version < 3.6:
|
if self.version < (3, 6):
|
||||||
call_function = self.call_fn_name(call_fn_tok)
|
call_function = self.call_fn_name(call_fn_tok)
|
||||||
args_pos, args_kw = self.get_pos_kw(call_fn_tok)
|
args_pos, args_kw = self.get_pos_kw(call_fn_tok)
|
||||||
rule = "build_class ::= LOAD_BUILD_CLASS mkfunc %s" "%s" % (
|
rule = "build_class ::= LOAD_BUILD_CLASS mkfunc %s" "%s" % (
|
||||||
@@ -591,7 +591,7 @@ class Python3Parser(PythonParser):
|
|||||||
|
|
||||||
# Note: 3.5+ have subclassed this method; so we don't handle
|
# Note: 3.5+ have subclassed this method; so we don't handle
|
||||||
# 'CALL_FUNCTION_VAR' or 'CALL_FUNCTION_EX' here.
|
# 'CALL_FUNCTION_VAR' or 'CALL_FUNCTION_EX' here.
|
||||||
if is_pypy and self.version >= 3.6:
|
if is_pypy and self.version >= (3, 6):
|
||||||
if token == "CALL_FUNCTION":
|
if token == "CALL_FUNCTION":
|
||||||
token.kind = self.call_fn_name(token)
|
token.kind = self.call_fn_name(token)
|
||||||
rule = (
|
rule = (
|
||||||
@@ -625,7 +625,7 @@ class Python3Parser(PythonParser):
|
|||||||
"""Python 3.3 added a an addtional LOAD_STR before MAKE_FUNCTION and
|
"""Python 3.3 added a an addtional LOAD_STR before MAKE_FUNCTION and
|
||||||
this has an effect on many rules.
|
this has an effect on many rules.
|
||||||
"""
|
"""
|
||||||
if self.version >= 3.3:
|
if self.version >= (3, 3):
|
||||||
if PYTHON3 or not self.is_pypy:
|
if PYTHON3 or not self.is_pypy:
|
||||||
load_op = "LOAD_STR "
|
load_op = "LOAD_STR "
|
||||||
else:
|
else:
|
||||||
@@ -774,7 +774,7 @@ class Python3Parser(PythonParser):
|
|||||||
rule = "kvlist_n ::="
|
rule = "kvlist_n ::="
|
||||||
self.add_unique_rule(rule, "kvlist_n", 1, customize)
|
self.add_unique_rule(rule, "kvlist_n", 1, customize)
|
||||||
rule = "dict ::= BUILD_MAP_n kvlist_n"
|
rule = "dict ::= BUILD_MAP_n kvlist_n"
|
||||||
elif self.version >= 3.5:
|
elif self.version >= (3, 5):
|
||||||
if not opname.startswith("BUILD_MAP_WITH_CALL"):
|
if not opname.startswith("BUILD_MAP_WITH_CALL"):
|
||||||
# FIXME: Use the attr
|
# FIXME: Use the attr
|
||||||
# so this doesn't run into exponential parsing time.
|
# so this doesn't run into exponential parsing time.
|
||||||
@@ -1059,7 +1059,7 @@ class Python3Parser(PythonParser):
|
|||||||
args_pos, args_kw, annotate_args = token.attr
|
args_pos, args_kw, annotate_args = token.attr
|
||||||
|
|
||||||
# FIXME: Fold test into add_make_function_rule
|
# FIXME: Fold test into add_make_function_rule
|
||||||
if self.version < 3.3:
|
if self.version < (3, 3):
|
||||||
j = 1
|
j = 1
|
||||||
else:
|
else:
|
||||||
j = 2
|
j = 2
|
||||||
@@ -1121,7 +1121,7 @@ class Python3Parser(PythonParser):
|
|||||||
kwargs_str = ""
|
kwargs_str = ""
|
||||||
|
|
||||||
# Note order of kwargs and pos args changed between 3.3-3.4
|
# Note order of kwargs and pos args changed between 3.3-3.4
|
||||||
if self.version <= 3.2:
|
if self.version <= (3, 2):
|
||||||
if annotate_args > 0:
|
if annotate_args > 0:
|
||||||
rule = (
|
rule = (
|
||||||
"mkfunc_annotate ::= %s%s%sannotate_tuple load_closure LOAD_CODE %s"
|
"mkfunc_annotate ::= %s%s%sannotate_tuple load_closure LOAD_CODE %s"
|
||||||
@@ -1138,7 +1138,7 @@ class Python3Parser(PythonParser):
|
|||||||
"pos_arg " * args_pos,
|
"pos_arg " * args_pos,
|
||||||
opname,
|
opname,
|
||||||
)
|
)
|
||||||
elif self.version == 3.3:
|
elif self.version == (3, 3):
|
||||||
if annotate_args > 0:
|
if annotate_args > 0:
|
||||||
rule = (
|
rule = (
|
||||||
"mkfunc_annotate ::= %s%s%sannotate_tuple load_closure LOAD_CODE LOAD_STR %s"
|
"mkfunc_annotate ::= %s%s%sannotate_tuple load_closure LOAD_CODE LOAD_STR %s"
|
||||||
@@ -1156,7 +1156,7 @@ class Python3Parser(PythonParser):
|
|||||||
opname,
|
opname,
|
||||||
)
|
)
|
||||||
|
|
||||||
elif self.version >= 3.4:
|
elif self.version >= (3, 4):
|
||||||
if PYTHON3 or not self.is_pypy:
|
if PYTHON3 or not self.is_pypy:
|
||||||
load_op = "LOAD_STR"
|
load_op = "LOAD_STR"
|
||||||
else:
|
else:
|
||||||
@@ -1190,7 +1190,7 @@ class Python3Parser(PythonParser):
|
|||||||
)
|
)
|
||||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||||
|
|
||||||
if self.version < 3.4:
|
if self.version < (3, 4):
|
||||||
rule = "mkfunc ::= %sload_closure LOAD_CODE %s" % (
|
rule = "mkfunc ::= %sload_closure LOAD_CODE %s" % (
|
||||||
"expr " * args_pos,
|
"expr " * args_pos,
|
||||||
opname,
|
opname,
|
||||||
@@ -1200,7 +1200,7 @@ class Python3Parser(PythonParser):
|
|||||||
pass
|
pass
|
||||||
elif opname_base.startswith("MAKE_FUNCTION"):
|
elif opname_base.startswith("MAKE_FUNCTION"):
|
||||||
# DRY with MAKE_CLOSURE
|
# DRY with MAKE_CLOSURE
|
||||||
if self.version >= 3.6:
|
if self.version >= (3, 6):
|
||||||
# The semantics of MAKE_FUNCTION in 3.6 are totally different from
|
# The semantics of MAKE_FUNCTION in 3.6 are totally different from
|
||||||
# before.
|
# before.
|
||||||
args_pos, args_kw, annotate_args, closure = token.attr
|
args_pos, args_kw, annotate_args, closure = token.attr
|
||||||
@@ -1262,7 +1262,7 @@ class Python3Parser(PythonParser):
|
|||||||
if self.is_pypy or (
|
if self.is_pypy or (
|
||||||
i >= 2 and tokens[i - 2] == "LOAD_LISTCOMP"
|
i >= 2 and tokens[i - 2] == "LOAD_LISTCOMP"
|
||||||
):
|
):
|
||||||
if self.version >= 3.6:
|
if self.version >= (3, 6):
|
||||||
# 3.6+ sometimes bundles all of the
|
# 3.6+ sometimes bundles all of the
|
||||||
# 'exprs' in the rule above into a
|
# 'exprs' in the rule above into a
|
||||||
# tuple.
|
# tuple.
|
||||||
@@ -1293,12 +1293,12 @@ class Python3Parser(PythonParser):
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self.version < 3.6:
|
if self.version < (3, 6):
|
||||||
args_pos, args_kw, annotate_args = token.attr
|
args_pos, args_kw, annotate_args = token.attr
|
||||||
else:
|
else:
|
||||||
args_pos, args_kw, annotate_args, closure = token.attr
|
args_pos, args_kw, annotate_args, closure = token.attr
|
||||||
|
|
||||||
if self.version < 3.3:
|
if self.version < (3, 3):
|
||||||
j = 1
|
j = 1
|
||||||
else:
|
else:
|
||||||
j = 2
|
j = 2
|
||||||
@@ -1339,7 +1339,7 @@ class Python3Parser(PythonParser):
|
|||||||
else:
|
else:
|
||||||
kwargs = "kwargs"
|
kwargs = "kwargs"
|
||||||
|
|
||||||
if self.version < 3.3:
|
if self.version < (3, 3):
|
||||||
# positional args after keyword args
|
# positional args after keyword args
|
||||||
rule = "mkfunc ::= %s %s%s%s" % (
|
rule = "mkfunc ::= %s %s%s%s" % (
|
||||||
kwargs,
|
kwargs,
|
||||||
@@ -1353,7 +1353,7 @@ class Python3Parser(PythonParser):
|
|||||||
"LOAD_CODE ",
|
"LOAD_CODE ",
|
||||||
opname,
|
opname,
|
||||||
)
|
)
|
||||||
elif self.version == 3.3:
|
elif self.version == (3, 3):
|
||||||
# positional args after keyword args
|
# positional args after keyword args
|
||||||
rule = "mkfunc ::= %s %s%s%s" % (
|
rule = "mkfunc ::= %s %s%s%s" % (
|
||||||
kwargs,
|
kwargs,
|
||||||
@@ -1361,7 +1361,7 @@ class Python3Parser(PythonParser):
|
|||||||
"LOAD_CODE LOAD_STR ",
|
"LOAD_CODE LOAD_STR ",
|
||||||
opname,
|
opname,
|
||||||
)
|
)
|
||||||
elif self.version > 3.5:
|
elif self.version > (3, 5):
|
||||||
# positional args before keyword args
|
# positional args before keyword args
|
||||||
rule = "mkfunc ::= %s%s %s%s" % (
|
rule = "mkfunc ::= %s%s %s%s" % (
|
||||||
"pos_arg " * args_pos,
|
"pos_arg " * args_pos,
|
||||||
@@ -1369,7 +1369,7 @@ class Python3Parser(PythonParser):
|
|||||||
"LOAD_CODE LOAD_STR ",
|
"LOAD_CODE LOAD_STR ",
|
||||||
opname,
|
opname,
|
||||||
)
|
)
|
||||||
elif self.version > 3.3:
|
elif self.version > (3, 3):
|
||||||
# positional args before keyword args
|
# positional args before keyword args
|
||||||
rule = "mkfunc ::= %s%s %s%s" % (
|
rule = "mkfunc ::= %s%s %s%s" % (
|
||||||
"pos_arg " * args_pos,
|
"pos_arg " * args_pos,
|
||||||
@@ -1386,7 +1386,7 @@ class Python3Parser(PythonParser):
|
|||||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||||
|
|
||||||
if re.search("^MAKE_FUNCTION.*_A", opname):
|
if re.search("^MAKE_FUNCTION.*_A", opname):
|
||||||
if self.version >= 3.6:
|
if self.version >= (3, 6):
|
||||||
rule = (
|
rule = (
|
||||||
"mkfunc_annotate ::= %s%sannotate_tuple LOAD_CODE LOAD_STR %s"
|
"mkfunc_annotate ::= %s%sannotate_tuple LOAD_CODE LOAD_STR %s"
|
||||||
% (
|
% (
|
||||||
@@ -1404,11 +1404,11 @@ class Python3Parser(PythonParser):
|
|||||||
opname,
|
opname,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if self.version >= 3.3:
|
if self.version >= (3, 3):
|
||||||
# Normally we remove EXTENDED_ARG from the opcodes, but in the case of
|
# Normally we remove EXTENDED_ARG from the opcodes, but in the case of
|
||||||
# annotated functions can use the EXTENDED_ARG tuple to signal we have an annotated function.
|
# annotated functions can use the EXTENDED_ARG tuple to signal we have an annotated function.
|
||||||
# Yes this is a little hacky
|
# Yes this is a little hacky
|
||||||
if self.version == 3.3:
|
if self.version == (3, 3):
|
||||||
# 3.3 puts kwargs before pos_arg
|
# 3.3 puts kwargs before pos_arg
|
||||||
pos_kw_tuple = (
|
pos_kw_tuple = (
|
||||||
("kwargs " * args_kw),
|
("kwargs " * args_kw),
|
||||||
@@ -1548,7 +1548,7 @@ class Python3Parser(PythonParser):
|
|||||||
"try_except": tryexcept,
|
"try_except": tryexcept,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.version == 3.6:
|
if self.version == (3, 6):
|
||||||
self.reduce_check_table["and"] = and_check
|
self.reduce_check_table["and"] = and_check
|
||||||
self.check_reduce["and"] = "AST"
|
self.check_reduce["and"] = "AST"
|
||||||
|
|
||||||
@@ -1560,7 +1560,7 @@ class Python3Parser(PythonParser):
|
|||||||
self.check_reduce["ifelsestmtc"] = "AST"
|
self.check_reduce["ifelsestmtc"] = "AST"
|
||||||
self.check_reduce["ifstmt"] = "AST"
|
self.check_reduce["ifstmt"] = "AST"
|
||||||
self.check_reduce["ifstmtl"] = "AST"
|
self.check_reduce["ifstmtl"] = "AST"
|
||||||
if self.version == 3.6:
|
if self.version == (3, 6):
|
||||||
self.reduce_check_table["iflaststmtl"] = iflaststmt
|
self.reduce_check_table["iflaststmtl"] = iflaststmt
|
||||||
self.check_reduce["iflaststmt"] = "AST"
|
self.check_reduce["iflaststmt"] = "AST"
|
||||||
self.check_reduce["iflaststmtl"] = "AST"
|
self.check_reduce["iflaststmtl"] = "AST"
|
||||||
@@ -1568,7 +1568,7 @@ class Python3Parser(PythonParser):
|
|||||||
self.check_reduce["testtrue"] = "tokens"
|
self.check_reduce["testtrue"] = "tokens"
|
||||||
if not PYTHON3:
|
if not PYTHON3:
|
||||||
self.check_reduce["kwarg"] = "noAST"
|
self.check_reduce["kwarg"] = "noAST"
|
||||||
if self.version < 3.6 and not self.is_pypy:
|
if self.version < (3, 6) and not self.is_pypy:
|
||||||
# 3.6+ can remove a JUMP_FORWARD which messes up our testing here
|
# 3.6+ can remove a JUMP_FORWARD which messes up our testing here
|
||||||
# Pypy we need to go over in better detail
|
# Pypy we need to go over in better detail
|
||||||
self.check_reduce["try_except"] = "AST"
|
self.check_reduce["try_except"] = "AST"
|
||||||
@@ -1595,11 +1595,11 @@ class Python3Parser(PythonParser):
|
|||||||
elif lhs == "kwarg":
|
elif lhs == "kwarg":
|
||||||
arg = tokens[first].attr
|
arg = tokens[first].attr
|
||||||
return not (isinstance(arg, str) or isinstance(arg, unicode))
|
return not (isinstance(arg, str) or isinstance(arg, unicode))
|
||||||
elif lhs in ("iflaststmt", "iflaststmtl") and self.version == 3.6:
|
elif lhs in ("iflaststmt", "iflaststmtl") and self.version == (3, 6):
|
||||||
return ifstmt(self, lhs, n, rule, ast, tokens, first, last)
|
return ifstmt(self, lhs, n, rule, ast, tokens, first, last)
|
||||||
elif rule == ("ifstmt", ("testexpr", "_ifstmts_jump")):
|
elif rule == ("ifstmt", ("testexpr", "_ifstmts_jump")):
|
||||||
# FIXME: go over what's up with 3.0. Evetually I'd like to remove RETURN_END_IF
|
# FIXME: go over what's up with 3.0. Evetually I'd like to remove RETURN_END_IF
|
||||||
if self.version <= 3.0 or tokens[last] == "RETURN_END_IF":
|
if self.version <= (3, 0) or tokens[last] == "RETURN_END_IF":
|
||||||
return False
|
return False
|
||||||
if ifstmt(self, lhs, n, rule, ast, tokens, first, last):
|
if ifstmt(self, lhs, n, rule, ast, tokens, first, last):
|
||||||
return True
|
return True
|
||||||
@@ -1641,7 +1641,7 @@ class Python3Parser(PythonParser):
|
|||||||
if while1stmt(self, lhs, n, rule, ast, tokens, first, last):
|
if while1stmt(self, lhs, n, rule, ast, tokens, first, last):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if self.version == 3.0:
|
if self.version == (3, 0):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if 0 <= last < len(tokens) and tokens[last] in (
|
if 0 <= last < len(tokens) and tokens[last] in (
|
||||||
@@ -1683,7 +1683,7 @@ class Python3Parser(PythonParser):
|
|||||||
if last == n:
|
if last == n:
|
||||||
return False
|
return False
|
||||||
# 3.8+ Doesn't have SETUP_LOOP
|
# 3.8+ Doesn't have SETUP_LOOP
|
||||||
return self.version < 3.8 and tokens[first].attr > tokens[last].offset
|
return self.version < (3, 8) and tokens[first].attr > tokens[last].offset
|
||||||
elif rule == (
|
elif rule == (
|
||||||
"ifelsestmt",
|
"ifelsestmt",
|
||||||
(
|
(
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2020 Rocky Bernstein
|
# Copyright (c) 2020-2021 Rocky Bernstein
|
||||||
|
|
||||||
from uncompyle6.scanners.tok import Token
|
from uncompyle6.scanners.tok import Token
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ def ifelsestmt(self, lhs, n, rule, ast, tokens, first, last):
|
|||||||
# just the last one.
|
# just the last one.
|
||||||
if len(ast) == 5:
|
if len(ast) == 5:
|
||||||
end_come_froms = ast[-1]
|
end_come_froms = ast[-1]
|
||||||
if end_come_froms.kind != "else_suite" and self.version >= 3.0:
|
if end_come_froms.kind != "else_suite" and self.version >= (3, 0):
|
||||||
if end_come_froms == "opt_come_from_except" and len(end_come_froms) > 0:
|
if end_come_froms == "opt_come_from_except" and len(end_come_froms) > 0:
|
||||||
end_come_froms = end_come_froms[0]
|
end_come_froms = end_come_froms[0]
|
||||||
if not isinstance(end_come_froms, Token):
|
if not isinstance(end_come_froms, Token):
|
||||||
@@ -169,12 +169,12 @@ def ifelsestmt(self, lhs, n, rule, ast, tokens, first, last):
|
|||||||
|
|
||||||
# FIXME: There is weirdness in the grammar we need to work around.
|
# FIXME: There is weirdness in the grammar we need to work around.
|
||||||
# we need to clean up the grammar.
|
# we need to clean up the grammar.
|
||||||
if self.version < 3.0:
|
if self.version < (3, 0):
|
||||||
last_token = ast[-1]
|
last_token = ast[-1]
|
||||||
else:
|
else:
|
||||||
last_token = tokens[last]
|
last_token = tokens[last]
|
||||||
if last_token == "COME_FROM" and tokens[first].offset > last_token.attr:
|
if last_token == "COME_FROM" and tokens[first].offset > last_token.attr:
|
||||||
if self.version < 3.0 and self.insts[self.offset2inst_index[last_token.attr]].opname != "SETUP_LOOP":
|
if self.version < (3, 0) and self.insts[self.offset2inst_index[last_token.attr]].opname != "SETUP_LOOP":
|
||||||
return True
|
return True
|
||||||
|
|
||||||
testexpr = ast[0]
|
testexpr = ast[0]
|
||||||
@@ -191,7 +191,7 @@ def ifelsestmt(self, lhs, n, rule, ast, tokens, first, last):
|
|||||||
if last == n:
|
if last == n:
|
||||||
last -= 1
|
last -= 1
|
||||||
jmp = if_condition[1]
|
jmp = if_condition[1]
|
||||||
if self.version > 2.6:
|
if self.version > (2, 6):
|
||||||
jmp_target = jmp[0].attr
|
jmp_target = jmp[0].attr
|
||||||
else:
|
else:
|
||||||
jmp_target = int(jmp[0].pattr)
|
jmp_target = int(jmp[0].pattr)
|
||||||
|
@@ -33,36 +33,38 @@ import xdis
|
|||||||
from xdis import Bytecode, canonic_python_version, code2num, instruction_size, extended_arg_val, next_offset
|
from xdis import Bytecode, canonic_python_version, code2num, instruction_size, extended_arg_val, next_offset
|
||||||
|
|
||||||
# The byte code versions we support.
|
# The byte code versions we support.
|
||||||
# Note: these all have to be floats
|
# Note: these all have to be tuples of 2 ints
|
||||||
PYTHON_VERSIONS = frozenset(
|
PYTHON_VERSIONS = frozenset(
|
||||||
(
|
(
|
||||||
1.0,
|
(1, 0),
|
||||||
1.1,
|
(1, 1),
|
||||||
1.3,
|
(1, 3),
|
||||||
1.4,
|
(1, 4),
|
||||||
1.5,
|
(1, 5),
|
||||||
1.6,
|
(1, 6),
|
||||||
2.1,
|
(2, 1),
|
||||||
2.2,
|
(2, 2),
|
||||||
2.3,
|
(2, 3),
|
||||||
2.4,
|
(2, 4),
|
||||||
2.5,
|
(2, 5),
|
||||||
2.6,
|
(2, 6),
|
||||||
2.7,
|
(2, 7),
|
||||||
3.0,
|
(3, 0),
|
||||||
3.1,
|
(3, 1),
|
||||||
3.2,
|
(3, 2),
|
||||||
3.3,
|
(3, 3),
|
||||||
3.4,
|
(3, 4),
|
||||||
3.5,
|
(3, 5),
|
||||||
3.6,
|
(3, 6),
|
||||||
3.7,
|
(3, 7),
|
||||||
3.8,
|
(3, 8),
|
||||||
3.9,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
CANONIC2VERSION = dict((canonic_python_version[str(v)], v) for v in PYTHON_VERSIONS)
|
CANONIC2VERSION = dict(
|
||||||
|
(canonic_python_version[".".join(str(v) for v in python_version)], python_version)
|
||||||
|
for python_version in PYTHON_VERSIONS
|
||||||
|
)
|
||||||
|
|
||||||
# Magic changed mid version for Python 3.5.2. Compatibility was added for
|
# Magic changed mid version for Python 3.5.2. Compatibility was added for
|
||||||
# the older 3.5 interpreter magic.
|
# the older 3.5 interpreter magic.
|
||||||
@@ -105,9 +107,9 @@ class Scanner(object):
|
|||||||
|
|
||||||
if version in PYTHON_VERSIONS:
|
if version in PYTHON_VERSIONS:
|
||||||
if is_pypy:
|
if is_pypy:
|
||||||
v_str = "opcode_%spypy" % (int(version * 10))
|
v_str = "opcode_%spypy" % ("".join([str(v) for v in version]))
|
||||||
else:
|
else:
|
||||||
v_str = "opcode_%s" % (int(version * 10))
|
v_str = "opcode_%s" % ("".join([str(v) for v in version]))
|
||||||
exec("from xdis.opcodes import %s" % v_str)
|
exec("from xdis.opcodes import %s" % v_str)
|
||||||
exec("self.opc = %s" % v_str)
|
exec("self.opc = %s" % v_str)
|
||||||
else:
|
else:
|
||||||
@@ -144,7 +146,7 @@ class Scanner(object):
|
|||||||
|
|
||||||
# Offset: lineno pairs, only for offsets which start line.
|
# Offset: lineno pairs, only for offsets which start line.
|
||||||
# Locally we use list for more convenient iteration using indices
|
# Locally we use list for more convenient iteration using indices
|
||||||
if self.version > 1.4:
|
if self.version > (1, 4):
|
||||||
linestarts = list(self.opc.findlinestarts(code_obj))
|
linestarts = list(self.opc.findlinestarts(code_obj))
|
||||||
else:
|
else:
|
||||||
linestarts = [[0, 1]]
|
linestarts = [[0, 1]]
|
||||||
@@ -533,8 +535,8 @@ def get_scanner(version, is_pypy=False, show_asm=None):
|
|||||||
version = CANONIC2VERSION[canonic_version]
|
version = CANONIC2VERSION[canonic_version]
|
||||||
|
|
||||||
# Pick up appropriate scanner
|
# Pick up appropriate scanner
|
||||||
if version in PYTHON_VERSIONS:
|
if version[:2] in PYTHON_VERSIONS:
|
||||||
v_str = "%s" % (int(version * 10))
|
v_str = "".join([str(v) for v in version[:2]])
|
||||||
try:
|
try:
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2015-2019 by Rocky Bernstein
|
# Copyright (c) 2015-2019, 2021 by Rocky Bernstein
|
||||||
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
||||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||||
#
|
#
|
||||||
@@ -66,7 +66,7 @@ class Scanner3(Scanner):
|
|||||||
# at the their targets.
|
# at the their targets.
|
||||||
# Some blocks and END_ statements. And they can start
|
# Some blocks and END_ statements. And they can start
|
||||||
# a new statement
|
# a new statement
|
||||||
if self.version < 3.8:
|
if self.version < (3, 8):
|
||||||
setup_ops = [
|
setup_ops = [
|
||||||
self.opc.SETUP_LOOP,
|
self.opc.SETUP_LOOP,
|
||||||
self.opc.SETUP_EXCEPT,
|
self.opc.SETUP_EXCEPT,
|
||||||
@@ -79,11 +79,11 @@ class Scanner3(Scanner):
|
|||||||
setup_ops = [self.opc.SETUP_FINALLY]
|
setup_ops = [self.opc.SETUP_FINALLY]
|
||||||
self.setup_ops_no_loop = frozenset(setup_ops)
|
self.setup_ops_no_loop = frozenset(setup_ops)
|
||||||
|
|
||||||
if self.version >= 3.2:
|
if self.version >= (3, 2):
|
||||||
setup_ops.append(self.opc.SETUP_WITH)
|
setup_ops.append(self.opc.SETUP_WITH)
|
||||||
self.setup_ops = frozenset(setup_ops)
|
self.setup_ops = frozenset(setup_ops)
|
||||||
|
|
||||||
if self.version == 3.0:
|
if self.version[:2] == (3, 0):
|
||||||
self.pop_jump_tf = frozenset(
|
self.pop_jump_tf = frozenset(
|
||||||
[self.opc.JUMP_IF_FALSE, self.opc.JUMP_IF_TRUE]
|
[self.opc.JUMP_IF_FALSE, self.opc.JUMP_IF_TRUE]
|
||||||
)
|
)
|
||||||
@@ -114,7 +114,7 @@ class Scanner3(Scanner):
|
|||||||
self.opc.JUMP_ABSOLUTE,
|
self.opc.JUMP_ABSOLUTE,
|
||||||
]
|
]
|
||||||
|
|
||||||
if self.version < 3.8:
|
if self.version < (3, 8):
|
||||||
statement_opcodes += [self.opc.BREAK_LOOP, self.opc.CONTINUE_LOOP]
|
statement_opcodes += [self.opc.BREAK_LOOP, self.opc.CONTINUE_LOOP]
|
||||||
|
|
||||||
self.statement_opcodes = frozenset(statement_opcodes) | self.setup_ops_no_loop
|
self.statement_opcodes = frozenset(statement_opcodes) | self.setup_ops_no_loop
|
||||||
@@ -135,7 +135,7 @@ class Scanner3(Scanner):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.version > 3.0:
|
if self.version > (3, 0):
|
||||||
self.jump_if_pop = frozenset(
|
self.jump_if_pop = frozenset(
|
||||||
[self.opc.JUMP_IF_FALSE_OR_POP, self.opc.JUMP_IF_TRUE_OR_POP]
|
[self.opc.JUMP_IF_FALSE_OR_POP, self.opc.JUMP_IF_TRUE_OR_POP]
|
||||||
)
|
)
|
||||||
@@ -182,9 +182,9 @@ class Scanner3(Scanner):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
if is_pypy or self.version >= 3.7:
|
if is_pypy or self.version >= (3, 7):
|
||||||
varargs_ops.add(self.opc.CALL_METHOD)
|
varargs_ops.add(self.opc.CALL_METHOD)
|
||||||
if self.version >= 3.5:
|
if self.version >= (3, 5):
|
||||||
varargs_ops |= set(
|
varargs_ops |= set(
|
||||||
[
|
[
|
||||||
self.opc.BUILD_SET_UNPACK,
|
self.opc.BUILD_SET_UNPACK,
|
||||||
@@ -193,7 +193,7 @@ class Scanner3(Scanner):
|
|||||||
self.opc.BUILD_TUPLE_UNPACK,
|
self.opc.BUILD_TUPLE_UNPACK,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
if self.version >= 3.6:
|
if self.version >= (3, 6):
|
||||||
varargs_ops.add(self.opc.BUILD_CONST_KEY_MAP)
|
varargs_ops.add(self.opc.BUILD_CONST_KEY_MAP)
|
||||||
# Below is in bit order, "default = bit 0, closure = bit 3
|
# Below is in bit order, "default = bit 0, closure = bit 3
|
||||||
self.MAKE_FUNCTION_FLAGS = tuple(
|
self.MAKE_FUNCTION_FLAGS = tuple(
|
||||||
@@ -257,7 +257,7 @@ class Scanner3(Scanner):
|
|||||||
# If we have a JUMP_FORWARD after the
|
# If we have a JUMP_FORWARD after the
|
||||||
# RAISE_VARARGS then we have a "raise" statement
|
# RAISE_VARARGS then we have a "raise" statement
|
||||||
# else we have an "assert" statement.
|
# else we have an "assert" statement.
|
||||||
if self.version == 3.0:
|
if self.version[:2] == (3, 0):
|
||||||
# Like 2.6, 3.0 doesn't have POP_JUMP_IF... so we have
|
# Like 2.6, 3.0 doesn't have POP_JUMP_IF... so we have
|
||||||
# to go through more machinations
|
# to go through more machinations
|
||||||
assert_can_follow = inst.opname == "POP_TOP" and i + 1 < n
|
assert_can_follow = inst.opname == "POP_TOP" and i + 1 < n
|
||||||
@@ -390,7 +390,7 @@ class Scanner3(Scanner):
|
|||||||
pattr = const
|
pattr = const
|
||||||
pass
|
pass
|
||||||
elif opname in ("MAKE_FUNCTION", "MAKE_CLOSURE"):
|
elif opname in ("MAKE_FUNCTION", "MAKE_CLOSURE"):
|
||||||
if self.version >= 3.6:
|
if self.version >= (3, 6):
|
||||||
# 3.6+ doesn't have MAKE_CLOSURE, so opname == 'MAKE_FUNCTION'
|
# 3.6+ doesn't have MAKE_CLOSURE, so opname == 'MAKE_FUNCTION'
|
||||||
flags = argval
|
flags = argval
|
||||||
opname = "MAKE_FUNCTION_%d" % (flags)
|
opname = "MAKE_FUNCTION_%d" % (flags)
|
||||||
@@ -444,7 +444,7 @@ class Scanner3(Scanner):
|
|||||||
# as JUMP_IF_NOT_DEBUG. The value is not used in these cases, so we put
|
# as JUMP_IF_NOT_DEBUG. The value is not used in these cases, so we put
|
||||||
# in arbitrary value 0.
|
# in arbitrary value 0.
|
||||||
customize[opname] = 0
|
customize[opname] = 0
|
||||||
elif self.version >= 3.6 and argval > 255:
|
elif self.version >= (3, 6) and argval > 255:
|
||||||
opname = "CALL_FUNCTION_KW"
|
opname = "CALL_FUNCTION_KW"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -483,7 +483,7 @@ class Scanner3(Scanner):
|
|||||||
and self.insts[i + 1].opname == "JUMP_FORWARD"
|
and self.insts[i + 1].opname == "JUMP_FORWARD"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (self.version == 3.0 and self.insts[i + 1].opname == "JUMP_FORWARD"
|
if (self.version[:2] == (3, 0) and self.insts[i + 1].opname == "JUMP_FORWARD"
|
||||||
and not is_continue):
|
and not is_continue):
|
||||||
target_prev = self.offset2inst_index[self.prev_op[target]]
|
target_prev = self.offset2inst_index[self.prev_op[target]]
|
||||||
is_continue = (
|
is_continue = (
|
||||||
@@ -585,7 +585,7 @@ class Scanner3(Scanner):
|
|||||||
if inst.has_arg:
|
if inst.has_arg:
|
||||||
label = self.fixed_jumps.get(offset)
|
label = self.fixed_jumps.get(offset)
|
||||||
oparg = inst.arg
|
oparg = inst.arg
|
||||||
if self.version >= 3.6 and self.code[offset] == self.opc.EXTENDED_ARG:
|
if self.version >= (3, 6) and self.code[offset] == self.opc.EXTENDED_ARG:
|
||||||
j = xdis.next_offset(op, self.opc, offset)
|
j = xdis.next_offset(op, self.opc, offset)
|
||||||
next_offset = xdis.next_offset(op, self.opc, j)
|
next_offset = xdis.next_offset(op, self.opc, j)
|
||||||
else:
|
else:
|
||||||
@@ -732,7 +732,7 @@ class Scanner3(Scanner):
|
|||||||
end = current_end
|
end = current_end
|
||||||
parent = struct
|
parent = struct
|
||||||
|
|
||||||
if self.version < 3.8 and op == self.opc.SETUP_LOOP:
|
if self.version < (3, 8) and op == self.opc.SETUP_LOOP:
|
||||||
# We categorize loop types: 'for', 'while', 'while 1' with
|
# We categorize loop types: 'for', 'while', 'while 1' with
|
||||||
# possibly suffixes '-loop' and '-else'
|
# possibly suffixes '-loop' and '-else'
|
||||||
# Try to find the jump_back instruction of the loop.
|
# Try to find the jump_back instruction of the loop.
|
||||||
@@ -863,7 +863,7 @@ class Scanner3(Scanner):
|
|||||||
# In some cases the pretarget can be a jump to the next instruction
|
# In some cases the pretarget can be a jump to the next instruction
|
||||||
# and these aren't and/or's either. We limit to 3.5+ since we experienced there
|
# and these aren't and/or's either. We limit to 3.5+ since we experienced there
|
||||||
# but it might be earlier versions, or might be a general principle.
|
# but it might be earlier versions, or might be a general principle.
|
||||||
if self.version < 3.5 or pretarget.argval != target:
|
if self.version < (3, 5) or pretarget.argval != target:
|
||||||
# FIXME: this is not accurate The commented out below
|
# FIXME: this is not accurate The commented out below
|
||||||
# is what it should be. However grammar rules right now
|
# is what it should be. However grammar rules right now
|
||||||
# assume the incorrect offsets.
|
# assume the incorrect offsets.
|
||||||
@@ -957,7 +957,7 @@ class Scanner3(Scanner):
|
|||||||
)
|
)
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
elif self.version <= 3.2:
|
elif self.version <= (3, 2):
|
||||||
fix = None
|
fix = None
|
||||||
jump_ifs = self.inst_matches(
|
jump_ifs = self.inst_matches(
|
||||||
start,
|
start,
|
||||||
@@ -976,7 +976,7 @@ class Scanner3(Scanner):
|
|||||||
self.fixed_jumps[offset] = fix or match[-1]
|
self.fixed_jumps[offset] = fix or match[-1]
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if self.version < 3.6:
|
if self.version < (3, 6):
|
||||||
# FIXME: this is putting in COME_FROMs in the wrong place.
|
# FIXME: this is putting in COME_FROMs in the wrong place.
|
||||||
# Fix up grammar so we don't need to do this.
|
# Fix up grammar so we don't need to do this.
|
||||||
# See cf_for_iter use in parser36.py
|
# See cf_for_iter use in parser36.py
|
||||||
@@ -1044,20 +1044,20 @@ class Scanner3(Scanner):
|
|||||||
# if the condition jump is to a forward location.
|
# if the condition jump is to a forward location.
|
||||||
# Also the existence of a jump to the instruction after "END_FINALLY"
|
# Also the existence of a jump to the instruction after "END_FINALLY"
|
||||||
# will distinguish "try/else" from "try".
|
# will distinguish "try/else" from "try".
|
||||||
if self.version < 3.8:
|
if self.version < (3, 8):
|
||||||
rtarget_break = (self.opc.RETURN_VALUE, self.opc.BREAK_LOOP)
|
rtarget_break = (self.opc.RETURN_VALUE, self.opc.BREAK_LOOP)
|
||||||
else:
|
else:
|
||||||
rtarget_break = (self.opc.RETURN_VALUE,)
|
rtarget_break = (self.opc.RETURN_VALUE,)
|
||||||
|
|
||||||
if self.is_jump_forward(pre_rtarget) or (
|
if self.is_jump_forward(pre_rtarget) or (
|
||||||
rtarget_is_ja and self.version >= 3.5
|
rtarget_is_ja and self.version >= (3, 5)
|
||||||
):
|
):
|
||||||
if_end = self.get_target(pre_rtarget)
|
if_end = self.get_target(pre_rtarget)
|
||||||
|
|
||||||
# If the jump target is back, we are looping
|
# If the jump target is back, we are looping
|
||||||
if (
|
if (
|
||||||
if_end < pre_rtarget
|
if_end < pre_rtarget
|
||||||
and self.version < 3.8
|
and self.version < (3, 8)
|
||||||
and (code[prev_op[if_end]] == self.opc.SETUP_LOOP)
|
and (code[prev_op[if_end]] == self.opc.SETUP_LOOP)
|
||||||
):
|
):
|
||||||
if if_end > start:
|
if if_end > start:
|
||||||
@@ -1094,11 +1094,11 @@ class Scanner3(Scanner):
|
|||||||
if self.is_pypy and code[jump_prev] == self.opc.COMPARE_OP:
|
if self.is_pypy and code[jump_prev] == self.opc.COMPARE_OP:
|
||||||
if self.opc.cmp_op[code[jump_prev + 1]] == "exception-match":
|
if self.opc.cmp_op[code[jump_prev + 1]] == "exception-match":
|
||||||
return
|
return
|
||||||
if self.version >= 3.5:
|
if self.version >= (3, 5):
|
||||||
# Python 3.5 may remove as dead code a JUMP
|
# Python 3.5 may remove as dead code a JUMP
|
||||||
# instruction after a RETURN_VALUE. So we check
|
# instruction after a RETURN_VALUE. So we check
|
||||||
# based on seeing SETUP_EXCEPT various places.
|
# based on seeing SETUP_EXCEPT various places.
|
||||||
if self.version < 3.6 and code[rtarget] == self.opc.SETUP_EXCEPT:
|
if self.version < (3, 6) and code[rtarget] == self.opc.SETUP_EXCEPT:
|
||||||
return
|
return
|
||||||
# Check that next instruction after pops and jump is
|
# Check that next instruction after pops and jump is
|
||||||
# not from SETUP_EXCEPT
|
# not from SETUP_EXCEPT
|
||||||
@@ -1111,14 +1111,14 @@ class Scanner3(Scanner):
|
|||||||
for try_op in targets[next_op]:
|
for try_op in targets[next_op]:
|
||||||
come_from_op = code[try_op]
|
come_from_op = code[try_op]
|
||||||
if (
|
if (
|
||||||
self.version < 3.8
|
self.version < (3, 8)
|
||||||
and come_from_op == self.opc.SETUP_EXCEPT
|
and come_from_op == self.opc.SETUP_EXCEPT
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if self.version >= 3.4:
|
if self.version >= (3, 4):
|
||||||
self.fixed_jumps[offset] = rtarget
|
self.fixed_jumps[offset] = rtarget
|
||||||
|
|
||||||
if code[pre_rtarget] == self.opc.RETURN_VALUE:
|
if code[pre_rtarget] == self.opc.RETURN_VALUE:
|
||||||
@@ -1137,8 +1137,8 @@ class Scanner3(Scanner):
|
|||||||
# FIXME: this is very convoluted and based on rather hacky
|
# FIXME: this is very convoluted and based on rather hacky
|
||||||
# empirical evidence. It should go a way when
|
# empirical evidence. It should go a way when
|
||||||
# we have better control-flow analysis
|
# we have better control-flow analysis
|
||||||
normal_jump = self.version >= 3.6
|
normal_jump = self.version >= (3, 6)
|
||||||
if self.version == 3.5:
|
if self.version[:2] == (3, 5):
|
||||||
j = self.offset2inst_index[target]
|
j = self.offset2inst_index[target]
|
||||||
if j + 2 < len(self.insts) and self.insts[j + 2].is_jump_target:
|
if j + 2 < len(self.insts) and self.insts[j + 2].is_jump_target:
|
||||||
normal_jump = self.insts[j + 1].opname == "POP_BLOCK"
|
normal_jump = self.insts[j + 1].opname == "POP_BLOCK"
|
||||||
@@ -1155,7 +1155,7 @@ class Scanner3(Scanner):
|
|||||||
if rtarget > offset:
|
if rtarget > offset:
|
||||||
self.fixed_jumps[offset] = rtarget
|
self.fixed_jumps[offset] = rtarget
|
||||||
|
|
||||||
elif self.version < 3.8 and op == self.opc.SETUP_EXCEPT:
|
elif self.version < (3, 8) and op == self.opc.SETUP_EXCEPT:
|
||||||
target = self.get_target(offset)
|
target = self.get_target(offset)
|
||||||
end = self.restrict_to_parent(target, parent)
|
end = self.restrict_to_parent(target, parent)
|
||||||
self.fixed_jumps[offset] = end
|
self.fixed_jumps[offset] = end
|
||||||
@@ -1188,7 +1188,7 @@ class Scanner3(Scanner):
|
|||||||
self.fixed_jumps[offset] = self.restrict_to_parent(target, parent)
|
self.fixed_jumps[offset] = self.restrict_to_parent(target, parent)
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
elif self.version >= 3.5:
|
elif self.version >= (3, 5):
|
||||||
# 3.5+ has Jump optimization which too often causes RETURN_VALUE to get
|
# 3.5+ has Jump optimization which too often causes RETURN_VALUE to get
|
||||||
# misclassified as RETURN_END_IF. Handle that here.
|
# misclassified as RETURN_END_IF. Handle that here.
|
||||||
# In RETURN_VALUE, JUMP_ABSOLUTE, RETURN_VALUE is never RETURN_END_IF
|
# In RETURN_VALUE, JUMP_ABSOLUTE, RETURN_VALUE is never RETURN_END_IF
|
||||||
@@ -1278,7 +1278,7 @@ class Scanner3(Scanner):
|
|||||||
start, end, instr, target, include_beyond_target
|
start, end, instr, target, include_beyond_target
|
||||||
)
|
)
|
||||||
# Get all POP_JUMP_IF_TRUE (or) offsets
|
# Get all POP_JUMP_IF_TRUE (or) offsets
|
||||||
if self.version == 3.0:
|
if self.version[:2] == (3, 0):
|
||||||
jump_true_op = self.opc.JUMP_IF_TRUE
|
jump_true_op = self.opc.JUMP_IF_TRUE
|
||||||
else:
|
else:
|
||||||
jump_true_op = self.opc.POP_JUMP_IF_TRUE
|
jump_true_op = self.opc.POP_JUMP_IF_TRUE
|
||||||
@@ -1295,17 +1295,16 @@ class Scanner3(Scanner):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from uncompyle6 import PYTHON_VERSION
|
from xdis.version_info import PYTHON_VERSION_TRIPLE
|
||||||
|
|
||||||
if PYTHON_VERSION >= 3.2:
|
if PYTHON_VERSION_TRIPLE >= (3, 2):
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
co = inspect.currentframe().f_code
|
co = inspect.currentframe().f_code
|
||||||
from uncompyle6 import PYTHON_VERSION
|
|
||||||
|
|
||||||
tokens, customize = Scanner3(PYTHON_VERSION).ingest(co)
|
tokens, customize = Scanner3(PYTHON_VERSION_TRIPLE).ingest(co)
|
||||||
for t in tokens:
|
for t in tokens:
|
||||||
print(t)
|
print(t)
|
||||||
else:
|
else:
|
||||||
print("Need to be Python 3.2 or greater to demo; I am %s." % PYTHON_VERSION)
|
print("Need to be Python 3.2 or greater to demo; I am %s." % sys.version)
|
||||||
pass
|
pass
|
||||||
|
@@ -34,7 +34,7 @@ from uncompyle6.scanners.scanner3 import Scanner3
|
|||||||
class Scanner34(Scanner3):
|
class Scanner34(Scanner3):
|
||||||
|
|
||||||
def __init__(self, show_asm=None):
|
def __init__(self, show_asm=None):
|
||||||
Scanner3.__init__(self, 3.4, show_asm)
|
Scanner3.__init__(self, (3, 4), show_asm)
|
||||||
return
|
return
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@@ -60,8 +60,8 @@ def customize_for_version(self, is_pypy, version):
|
|||||||
"assign3": ("%|%c, %c, %c = %c, %c, %c\n", 5, 6, 7, 0, 1, 2),
|
"assign3": ("%|%c, %c, %c = %c, %c, %c\n", 5, 6, 7, 0, 1, 2),
|
||||||
"try_except": ("%|try:\n%+%c%-%c\n\n", 1, 3),
|
"try_except": ("%|try:\n%+%c%-%c\n\n", 1, 3),
|
||||||
})
|
})
|
||||||
if version >= 3.0:
|
if version >= (3, 0):
|
||||||
if version >= 3.2:
|
if version >= (3, 2):
|
||||||
TABLE_DIRECT.update(
|
TABLE_DIRECT.update(
|
||||||
{"del_deref_stmt": ("%|del %c\n", 0), "DELETE_DEREF": ("%{pattr}", 0)}
|
{"del_deref_stmt": ("%|del %c\n", 0), "DELETE_DEREF": ("%{pattr}", 0)}
|
||||||
)
|
)
|
||||||
@@ -72,20 +72,20 @@ def customize_for_version(self, is_pypy, version):
|
|||||||
TABLE_DIRECT.update(
|
TABLE_DIRECT.update(
|
||||||
{"except_cond3": ("%|except %c, %c:\n", (1, "expr"), (-2, "store"))}
|
{"except_cond3": ("%|except %c, %c:\n", (1, "expr"), (-2, "store"))}
|
||||||
)
|
)
|
||||||
if version <= 2.6:
|
if version <= (2, 6):
|
||||||
TABLE_DIRECT["testtrue_then"] = TABLE_DIRECT["testtrue"]
|
TABLE_DIRECT["testtrue_then"] = TABLE_DIRECT["testtrue"]
|
||||||
|
|
||||||
if 2.4 <= version <= 2.6:
|
if (2, 4) <= version <= (2, 6):
|
||||||
TABLE_DIRECT.update({"comp_for": (" for %c in %c", 3, 1)})
|
TABLE_DIRECT.update({"comp_for": (" for %c in %c", 3, 1)})
|
||||||
else:
|
else:
|
||||||
TABLE_DIRECT.update({"comp_for": (" for %c in %c%c", 2, 0, 3)})
|
TABLE_DIRECT.update({"comp_for": (" for %c in %c%c", 2, 0, 3)})
|
||||||
|
|
||||||
if version >= 2.5:
|
if version >= (2, 5):
|
||||||
from uncompyle6.semantics.customize25 import customize_for_version25
|
from uncompyle6.semantics.customize25 import customize_for_version25
|
||||||
|
|
||||||
customize_for_version25(self, version)
|
customize_for_version25(self, version)
|
||||||
|
|
||||||
if version >= 2.6:
|
if version >= (2, 6):
|
||||||
from uncompyle6.semantics.customize26_27 import (
|
from uncompyle6.semantics.customize26_27 import (
|
||||||
customize_for_version26_27,
|
customize_for_version26_27,
|
||||||
)
|
)
|
||||||
@@ -137,7 +137,7 @@ def customize_for_version(self, is_pypy, version):
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if version == 2.4:
|
if version == (2, 4):
|
||||||
def n_iftrue_stmt24(node):
|
def n_iftrue_stmt24(node):
|
||||||
self.template_engine(("%c", 0), node)
|
self.template_engine(("%c", 0), node)
|
||||||
self.default(node)
|
self.default(node)
|
||||||
@@ -146,7 +146,7 @@ def customize_for_version(self, is_pypy, version):
|
|||||||
self.n_iftrue_stmt24 = n_iftrue_stmt24
|
self.n_iftrue_stmt24 = n_iftrue_stmt24
|
||||||
else: # version <= 2.3:
|
else: # version <= 2.3:
|
||||||
TABLE_DIRECT.update({"if1_stmt": ("%|if 1\n%+%c%-", 5)})
|
TABLE_DIRECT.update({"if1_stmt": ("%|if 1\n%+%c%-", 5)})
|
||||||
if version <= 2.1:
|
if version <= (2, 1):
|
||||||
TABLE_DIRECT.update(
|
TABLE_DIRECT.update(
|
||||||
{
|
{
|
||||||
"importmultiple": ("%c", 2),
|
"importmultiple": ("%c", 2),
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2018-2020 by Rocky Bernstein
|
# Copyright (c) 2018-2021 by Rocky Bernstein
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -58,7 +58,7 @@ def customize_for_version3(self, version):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert version >= 3.0
|
assert version >= (3, 0)
|
||||||
|
|
||||||
# In 2.5+ and 3.0+ "except" handlers and the "finally" can appear in one
|
# In 2.5+ and 3.0+ "except" handlers and the "finally" can appear in one
|
||||||
# "try" statement. So the below has the effect of combining the
|
# "try" statement. So the below has the effect of combining the
|
||||||
@@ -107,7 +107,7 @@ def customize_for_version3(self, version):
|
|||||||
collections = [node[-3]]
|
collections = [node[-3]]
|
||||||
list_ifs = []
|
list_ifs = []
|
||||||
|
|
||||||
if self.version == 3.0 and n != "list_iter":
|
if self.version == (3, 0) and n != "list_iter":
|
||||||
# FIXME 3.0 is a snowflake here. We need
|
# FIXME 3.0 is a snowflake here. We need
|
||||||
# special code for this. Not sure if this is totally
|
# special code for this. Not sure if this is totally
|
||||||
# correct.
|
# correct.
|
||||||
@@ -207,7 +207,7 @@ def customize_for_version3(self, version):
|
|||||||
"""Handle "classdef" nonterminal for 3.0 >= version 3.0 <= 3.5
|
"""Handle "classdef" nonterminal for 3.0 >= version 3.0 <= 3.5
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert 3.0 <= self.version <= 3.5
|
assert (3, 0) <= self.version <= (3, 5)
|
||||||
|
|
||||||
# class definition ('class X(A,B,C):')
|
# class definition ('class X(A,B,C):')
|
||||||
cclass = self.currentclass
|
cclass = self.currentclass
|
||||||
@@ -220,7 +220,7 @@ def customize_for_version3(self, version):
|
|||||||
# * subclass_code - the code for the subclass body
|
# * subclass_code - the code for the subclass body
|
||||||
subclass_info = None
|
subclass_info = None
|
||||||
if node == "classdefdeco2":
|
if node == "classdefdeco2":
|
||||||
if self.version <= 3.3:
|
if self.version <= (3, 3):
|
||||||
class_name = node[2][0].attr
|
class_name = node[2][0].attr
|
||||||
else:
|
else:
|
||||||
class_name = node[1][2].attr
|
class_name = node[1][2].attr
|
||||||
@@ -233,7 +233,7 @@ def customize_for_version3(self, version):
|
|||||||
assert "mkfunc" == build_class[1]
|
assert "mkfunc" == build_class[1]
|
||||||
mkfunc = build_class[1]
|
mkfunc = build_class[1]
|
||||||
if mkfunc[0] in ("kwargs", "no_kwargs"):
|
if mkfunc[0] in ("kwargs", "no_kwargs"):
|
||||||
if 3.0 <= self.version <= 3.2:
|
if (3, 0) <= self.version <= (3, 2):
|
||||||
for n in mkfunc:
|
for n in mkfunc:
|
||||||
if hasattr(n, "attr") and iscode(n.attr):
|
if hasattr(n, "attr") and iscode(n.attr):
|
||||||
subclass_code = n.attr
|
subclass_code = n.attr
|
||||||
@@ -355,7 +355,7 @@ def customize_for_version3(self, version):
|
|||||||
|
|
||||||
self.n_yield_from = n_yield_from
|
self.n_yield_from = n_yield_from
|
||||||
|
|
||||||
if 3.2 <= version <= 3.4:
|
if (3, 2) <= version <= (3, 4):
|
||||||
|
|
||||||
def n_call(node):
|
def n_call(node):
|
||||||
|
|
||||||
@@ -416,9 +416,9 @@ def customize_for_version3(self, version):
|
|||||||
# Handling EXTENDED_ARG before MAKE_FUNCTION ...
|
# Handling EXTENDED_ARG before MAKE_FUNCTION ...
|
||||||
i = -1 if node[-2] == "EXTENDED_ARG" else 0
|
i = -1 if node[-2] == "EXTENDED_ARG" else 0
|
||||||
|
|
||||||
if self.version <= 3.2:
|
if self.version <= (3, 2):
|
||||||
code = node[-2 + i]
|
code = node[-2 + i]
|
||||||
elif self.version >= 3.3 or node[-2] == "kwargs":
|
elif self.version >= (3, 3) or node[-2] == "kwargs":
|
||||||
# LOAD_CONST code object ..
|
# LOAD_CONST code object ..
|
||||||
# LOAD_CONST 'x0' if >= 3.3
|
# LOAD_CONST 'x0' if >= 3.3
|
||||||
# EXTENDED_ARG
|
# EXTENDED_ARG
|
||||||
@@ -468,7 +468,7 @@ def customize_for_version3(self, version):
|
|||||||
"LOAD_CLASSDEREF": ("%{pattr}",),
|
"LOAD_CLASSDEREF": ("%{pattr}",),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if version >= 3.4:
|
if version >= (3, 4):
|
||||||
#######################
|
#######################
|
||||||
# Python 3.4+ Changes #
|
# Python 3.4+ Changes #
|
||||||
#######################
|
#######################
|
||||||
@@ -478,13 +478,13 @@ def customize_for_version3(self, version):
|
|||||||
"yield_from": ("yield from %c", (0, "expr")),
|
"yield_from": ("yield from %c", (0, "expr")),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if version >= 3.5:
|
if version >= (3, 5):
|
||||||
customize_for_version35(self, version)
|
customize_for_version35(self, version)
|
||||||
if version >= 3.6:
|
if version >= (3, 6):
|
||||||
customize_for_version36(self, version)
|
customize_for_version36(self, version)
|
||||||
if version >= 3.7:
|
if version >= (3, 7):
|
||||||
customize_for_version37(self, version)
|
customize_for_version37(self, version)
|
||||||
if version >= 3.8:
|
if version >= (3, 8):
|
||||||
customize_for_version38(self, version)
|
customize_for_version38(self, version)
|
||||||
pass # version >= 3.8
|
pass # version >= 3.8
|
||||||
pass # 3.7
|
pass # 3.7
|
||||||
|
@@ -78,7 +78,7 @@ def find_globals_and_nonlocals(node, globs, nonlocals, code, version):
|
|||||||
code, version)
|
code, version)
|
||||||
elif n.kind in read_global_ops:
|
elif n.kind in read_global_ops:
|
||||||
globs.add(n.pattr)
|
globs.add(n.pattr)
|
||||||
elif (version >= 3.0
|
elif (version >= (3, 0)
|
||||||
and n.kind in nonglobal_ops
|
and n.kind in nonglobal_ops
|
||||||
and n.pattr in code.co_freevars
|
and n.pattr in code.co_freevars
|
||||||
and n.pattr != code.co_name
|
and n.pattr != code.co_name
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2015-2020 by Rocky Bernstein
|
# Copyright (c) 2015-2021 by Rocky Bernstein
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -366,9 +366,9 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
|
|||||||
|
|
||||||
# Python 3.3+ adds a qualified name at TOS (-1)
|
# Python 3.3+ adds a qualified name at TOS (-1)
|
||||||
# moving down the LOAD_LAMBDA instruction
|
# moving down the LOAD_LAMBDA instruction
|
||||||
if 3.0 <= self.version <= 3.2:
|
if (3, 0) <= self.version <= (3, 2):
|
||||||
lambda_index = -2
|
lambda_index = -2
|
||||||
elif 3.03 <= self.version:
|
elif (3, 3) <= self.version:
|
||||||
lambda_index = -3
|
lambda_index = -3
|
||||||
else:
|
else:
|
||||||
lambda_index = None
|
lambda_index = None
|
||||||
@@ -590,7 +590,7 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
|
|||||||
ends_in_comma = True
|
ends_in_comma = True
|
||||||
|
|
||||||
kw_args = [None] * kwonlyargcount
|
kw_args = [None] * kwonlyargcount
|
||||||
if self.version <= 3.3:
|
if self.version <= (3, 3):
|
||||||
kw_nodes = node[0]
|
kw_nodes = node[0]
|
||||||
else:
|
else:
|
||||||
kw_nodes = node[args_node.attr[0]]
|
kw_nodes = node[args_node.attr[0]]
|
||||||
|
@@ -483,7 +483,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
and node[0][0][0] == "LOAD_CONST"
|
and node[0][0][0] == "LOAD_CONST"
|
||||||
and node[0][0][0].pattr is None
|
and node[0][0][0].pattr is None
|
||||||
)
|
)
|
||||||
if self.version <= 2.6:
|
if self.version <= (2, 6):
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
# FIXME: should the SyntaxTree expression be folded into
|
# FIXME: should the SyntaxTree expression be folded into
|
||||||
@@ -537,7 +537,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
def n_yield(self, node):
|
def n_yield(self, node):
|
||||||
if node != SyntaxTree("yield", [NONE, Token("YIELD_VALUE")]):
|
if node != SyntaxTree("yield", [NONE, Token("YIELD_VALUE")]):
|
||||||
self.template_engine(("yield %c", 0), node)
|
self.template_engine(("yield %c", 0), node)
|
||||||
elif self.version <= 2.4:
|
elif self.version <= (2, 4):
|
||||||
# Early versions of Python don't allow a plain "yield"
|
# Early versions of Python don't allow a plain "yield"
|
||||||
self.write("yield None")
|
self.write("yield None")
|
||||||
else:
|
else:
|
||||||
@@ -823,7 +823,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
self.prune()
|
self.prune()
|
||||||
|
|
||||||
def n_alias(self, node):
|
def n_alias(self, node):
|
||||||
if self.version <= 2.1:
|
if self.version <= (2, 1):
|
||||||
if len(node) == 2:
|
if len(node) == 2:
|
||||||
store = node[1]
|
store = node[1]
|
||||||
assert store == "store"
|
assert store == "store"
|
||||||
@@ -848,10 +848,10 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
|
|
||||||
def n_import_from(self, node):
|
def n_import_from(self, node):
|
||||||
relative_path_index = 0
|
relative_path_index = 0
|
||||||
if self.version >= 2.5:
|
if self.version >= (2, 5):
|
||||||
if node[relative_path_index].pattr > 0:
|
if node[relative_path_index].pattr > 0:
|
||||||
node[2].pattr = ("." * node[relative_path_index].pattr) + node[2].pattr
|
node[2].pattr = ("." * node[relative_path_index].pattr) + node[2].pattr
|
||||||
if self.version > 2.7:
|
if self.version > (2, 7):
|
||||||
if isinstance(node[1].pattr, tuple):
|
if isinstance(node[1].pattr, tuple):
|
||||||
imports = node[1].pattr
|
imports = node[1].pattr
|
||||||
for pattr in imports:
|
for pattr in imports:
|
||||||
@@ -882,11 +882,11 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
# Python changes make function this much that we need at least 3 different routines,
|
# Python changes make function this much that we need at least 3 different routines,
|
||||||
# and probably more in the future.
|
# and probably more in the future.
|
||||||
def make_function(self, node, is_lambda, nested=1, code_node=None, annotate=None):
|
def make_function(self, node, is_lambda, nested=1, code_node=None, annotate=None):
|
||||||
if self.version <= 2.7:
|
if self.version <= (2, 7):
|
||||||
make_function2(self, node, is_lambda, nested, code_node)
|
make_function2(self, node, is_lambda, nested, code_node)
|
||||||
elif 3.0 <= self.version <= 3.5:
|
elif (3, 0) <= self.version <= (3, 5):
|
||||||
make_function3(self, node, is_lambda, nested, code_node)
|
make_function3(self, node, is_lambda, nested, code_node)
|
||||||
elif self.version >= 3.6:
|
elif self.version >= (3, 6):
|
||||||
make_function36(self, node, is_lambda, nested, code_node)
|
make_function36(self, node, is_lambda, nested, code_node)
|
||||||
|
|
||||||
def n_docstring(self, node):
|
def n_docstring(self, node):
|
||||||
@@ -978,7 +978,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
"""List comprehensions"""
|
"""List comprehensions"""
|
||||||
p = self.prec
|
p = self.prec
|
||||||
self.prec = 100
|
self.prec = 100
|
||||||
if self.version >= 2.7:
|
if self.version >= (2, 7):
|
||||||
if self.is_pypy:
|
if self.is_pypy:
|
||||||
self.n_list_comp_pypy27(node)
|
self.n_list_comp_pypy27(node)
|
||||||
return
|
return
|
||||||
@@ -1005,7 +1005,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
assert n == "lc_body"
|
assert n == "lc_body"
|
||||||
self.write("[ ")
|
self.write("[ ")
|
||||||
|
|
||||||
if self.version >= 2.7:
|
if self.version >= (2, 7):
|
||||||
expr = n[0]
|
expr = n[0]
|
||||||
list_iter = node[-1]
|
list_iter = node[-1]
|
||||||
else:
|
else:
|
||||||
@@ -1090,15 +1090,15 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
self.prec = 27
|
self.prec = 27
|
||||||
|
|
||||||
# FIXME: clean this up
|
# FIXME: clean this up
|
||||||
if self.version >= 3.0 and node == "dict_comp":
|
if self.version >= (3, 0) and node == "dict_comp":
|
||||||
cn = node[1]
|
cn = node[1]
|
||||||
elif self.version <= 2.7 and node == "generator_exp":
|
elif self.version <= (2, 7) and node == "generator_exp":
|
||||||
if node[0] == "LOAD_GENEXPR":
|
if node[0] == "LOAD_GENEXPR":
|
||||||
cn = node[0]
|
cn = node[0]
|
||||||
elif node[0] == "load_closure":
|
elif node[0] == "load_closure":
|
||||||
cn = node[1]
|
cn = node[1]
|
||||||
|
|
||||||
elif self.version >= 3.0 and node in ("generator_exp", "generator_exp_async"):
|
elif self.version >= (3, 0) and node in ("generator_exp", "generator_exp_async"):
|
||||||
if node[0] == "load_genexpr":
|
if node[0] == "load_genexpr":
|
||||||
load_genexpr = node[0]
|
load_genexpr = node[0]
|
||||||
elif node[1] == "load_genexpr":
|
elif node[1] == "load_genexpr":
|
||||||
@@ -1167,9 +1167,9 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
def n_generator_exp(self, node):
|
def n_generator_exp(self, node):
|
||||||
self.write("(")
|
self.write("(")
|
||||||
iter_index = 3
|
iter_index = 3
|
||||||
if self.version > 3.2:
|
if self.version > (3, 2):
|
||||||
code_index = -6
|
code_index = -6
|
||||||
if self.version > 3.6:
|
if self.version > (3, 6):
|
||||||
# Python 3.7+ adds optional "come_froms" at node[0]
|
# Python 3.7+ adds optional "come_froms" at node[0]
|
||||||
iter_index = 4
|
iter_index = 4
|
||||||
else:
|
else:
|
||||||
@@ -1184,7 +1184,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
self.write("{")
|
self.write("{")
|
||||||
if node[0] in ["LOAD_SETCOMP", "LOAD_DICTCOMP"]:
|
if node[0] in ["LOAD_SETCOMP", "LOAD_DICTCOMP"]:
|
||||||
self.comprehension_walk_newer(node, 1, 0)
|
self.comprehension_walk_newer(node, 1, 0)
|
||||||
elif node[0].kind == "load_closure" and self.version >= 3.0:
|
elif node[0].kind == "load_closure" and self.version >= (3, 0):
|
||||||
self.setcomprehension_walk3(node, collection_index=4)
|
self.setcomprehension_walk3(node, collection_index=4)
|
||||||
else:
|
else:
|
||||||
self.comprehension_walk(node, iter_index=4)
|
self.comprehension_walk(node, iter_index=4)
|
||||||
@@ -1241,7 +1241,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
elif ast in ("dict_comp", "set_comp"):
|
elif ast in ("dict_comp", "set_comp"):
|
||||||
assert self.version == 3.0
|
assert self.version == (3, 0)
|
||||||
for k in ast:
|
for k in ast:
|
||||||
if k in ("dict_comp_header", "set_comp_header"):
|
if k in ("dict_comp_header", "set_comp_header"):
|
||||||
n = k
|
n = k
|
||||||
@@ -1313,7 +1313,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
# Python 2.7+ starts including set_comp_body
|
# Python 2.7+ starts including set_comp_body
|
||||||
# Python 3.5+ starts including set_comp_func
|
# Python 3.5+ starts including set_comp_func
|
||||||
# Python 3.0 is yet another snowflake
|
# Python 3.0 is yet another snowflake
|
||||||
if self.version != 3.0 and self.version < 3.7:
|
if self.version != (3, 0) and self.version < (3, 7):
|
||||||
assert n.kind in (
|
assert n.kind in (
|
||||||
"lc_body",
|
"lc_body",
|
||||||
"list_if37",
|
"list_if37",
|
||||||
@@ -1356,7 +1356,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
self.preorder(node[in_node_index])
|
self.preorder(node[in_node_index])
|
||||||
|
|
||||||
# Here is where we handle nested list iterations.
|
# Here is where we handle nested list iterations.
|
||||||
if ast == "list_comp" and self.version != 3.0:
|
if ast == "list_comp" and self.version != (3, 0):
|
||||||
list_iter = ast[1]
|
list_iter = ast[1]
|
||||||
assert list_iter == "list_iter"
|
assert list_iter == "list_iter"
|
||||||
if list_iter[0] == "list_for":
|
if list_iter[0] == "list_for":
|
||||||
@@ -1379,7 +1379,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
def n_listcomp(self, node):
|
def n_listcomp(self, node):
|
||||||
self.write("[")
|
self.write("[")
|
||||||
if node[0].kind == "load_closure":
|
if node[0].kind == "load_closure":
|
||||||
assert self.version >= 3.0
|
assert self.version >= (3, 0)
|
||||||
self.listcomp_closure3(node)
|
self.listcomp_closure3(node)
|
||||||
else:
|
else:
|
||||||
if node == "listcomp_async":
|
if node == "listcomp_async":
|
||||||
@@ -1447,9 +1447,9 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
|
|
||||||
def n_classdef(self, node):
|
def n_classdef(self, node):
|
||||||
|
|
||||||
if self.version >= 3.6:
|
if self.version >= (3, 6):
|
||||||
self.n_classdef36(node)
|
self.n_classdef36(node)
|
||||||
elif self.version >= 3.0:
|
elif self.version >= (3, 0):
|
||||||
self.n_classdef3(node)
|
self.n_classdef3(node)
|
||||||
|
|
||||||
# class definition ('class X(A,B,C):')
|
# class definition ('class X(A,B,C):')
|
||||||
@@ -1514,7 +1514,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
n_subclasses = len(node[:-1])
|
n_subclasses = len(node[:-1])
|
||||||
if n_subclasses > 0 or self.version > 2.4:
|
if n_subclasses > 0 or self.version > (2, 4):
|
||||||
# Not an old-style pre-2.2 class
|
# Not an old-style pre-2.2 class
|
||||||
self.write("(")
|
self.write("(")
|
||||||
|
|
||||||
@@ -1525,7 +1525,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
self.write(sep, value)
|
self.write(sep, value)
|
||||||
sep = line_separator
|
sep = line_separator
|
||||||
|
|
||||||
if n_subclasses > 0 or self.version > 2.4:
|
if n_subclasses > 0 or self.version > (2, 4):
|
||||||
# Not an old-style pre-2.2 class
|
# Not an old-style pre-2.2 class
|
||||||
self.write(")")
|
self.write(")")
|
||||||
|
|
||||||
@@ -1674,7 +1674,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
self.write("{")
|
self.write("{")
|
||||||
line_number = self.line_number
|
line_number = self.line_number
|
||||||
|
|
||||||
if self.version >= 3.0 and not self.is_pypy:
|
if self.version >= (3, 0) and not self.is_pypy:
|
||||||
if node[0].kind.startswith("kvlist"):
|
if node[0].kind.startswith("kvlist"):
|
||||||
# Python 3.5+ style key/value list in dict
|
# Python 3.5+ style key/value list in dict
|
||||||
kv_node = node[0]
|
kv_node = node[0]
|
||||||
@@ -1760,14 +1760,14 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
self.write(sep[1:])
|
self.write(sep[1:])
|
||||||
pass
|
pass
|
||||||
elif node[0].kind.startswith("dict_entry"):
|
elif node[0].kind.startswith("dict_entry"):
|
||||||
assert self.version >= 3.5
|
assert self.version >= (3, 5)
|
||||||
template = ("%C", (0, len(node[0]), ", **"))
|
template = ("%C", (0, len(node[0]), ", **"))
|
||||||
self.template_engine(template, node[0])
|
self.template_engine(template, node[0])
|
||||||
sep = ""
|
sep = ""
|
||||||
elif node[-1].kind.startswith("BUILD_MAP_UNPACK") or node[
|
elif node[-1].kind.startswith("BUILD_MAP_UNPACK") or node[
|
||||||
-1
|
-1
|
||||||
].kind.startswith("dict_entry"):
|
].kind.startswith("dict_entry"):
|
||||||
assert self.version >= 3.5
|
assert self.version >= (3, 5)
|
||||||
# FIXME: I think we can intermingle dict_comp's with other
|
# FIXME: I think we can intermingle dict_comp's with other
|
||||||
# dictionary kinds of things. The most common though is
|
# dictionary kinds of things. The most common though is
|
||||||
# a sequence of dict_comp's
|
# a sequence of dict_comp's
|
||||||
@@ -1790,7 +1790,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
else:
|
else:
|
||||||
sep = ""
|
sep = ""
|
||||||
opname = node[-1].kind
|
opname = node[-1].kind
|
||||||
if self.is_pypy and self.version >= 3.5:
|
if self.is_pypy and self.version >= (3, 5):
|
||||||
if opname.startswith("BUILD_CONST_KEY_MAP"):
|
if opname.startswith("BUILD_CONST_KEY_MAP"):
|
||||||
keys = node[-2].attr
|
keys = node[-2].attr
|
||||||
# FIXME: DRY this and the above
|
# FIXME: DRY this and the above
|
||||||
@@ -1966,7 +1966,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
|
|
||||||
# In Python 2.4, unpack is used in (a, b, c) of:
|
# In Python 2.4, unpack is used in (a, b, c) of:
|
||||||
# except RuntimeError, (a, b, c):
|
# except RuntimeError, (a, b, c):
|
||||||
if self.version < 2.7:
|
if self.version < (2, 7):
|
||||||
node.kind = "unpack_w_parens"
|
node.kind = "unpack_w_parens"
|
||||||
self.default(node)
|
self.default(node)
|
||||||
|
|
||||||
@@ -1984,7 +1984,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
|
|
||||||
def n_assign(self, node):
|
def n_assign(self, node):
|
||||||
# A horrible hack for Python 3.0 .. 3.2
|
# A horrible hack for Python 3.0 .. 3.2
|
||||||
if 3.0 <= self.version <= 3.2 and len(node) == 2:
|
if (3, 0) <= self.version <= (3, 2) and len(node) == 2:
|
||||||
if (
|
if (
|
||||||
node[0][0] == "LOAD_FAST"
|
node[0][0] == "LOAD_FAST"
|
||||||
and node[0][0].pattr == "__locals__"
|
and node[0][0].pattr == "__locals__"
|
||||||
@@ -2197,7 +2197,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
if k.startswith("CALL_METHOD"):
|
if k.startswith("CALL_METHOD"):
|
||||||
# This happens in PyPy and Python 3.7+
|
# This happens in PyPy and Python 3.7+
|
||||||
TABLE_R[k] = ("%c(%P)", 0, (1, -1, ", ", 100))
|
TABLE_R[k] = ("%c(%P)", 0, (1, -1, ", ", 100))
|
||||||
elif self.version >= 3.6 and k.startswith("CALL_FUNCTION_KW"):
|
elif self.version >= (3, 6) and k.startswith("CALL_FUNCTION_KW"):
|
||||||
TABLE_R[k] = ("%c(%P)", 0, (1, -1, ", ", 100))
|
TABLE_R[k] = ("%c(%P)", 0, (1, -1, ", ", 100))
|
||||||
elif op == "CALL_FUNCTION":
|
elif op == "CALL_FUNCTION":
|
||||||
TABLE_R[k] = ("%c(%P)", (0, "expr"), (1, -1, ", ", PRECEDENCE["yield"]-1))
|
TABLE_R[k] = ("%c(%P)", (0, "expr"), (1, -1, ", ", PRECEDENCE["yield"]-1))
|
||||||
@@ -2219,12 +2219,12 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
if op == "CALL_FUNCTION_VAR":
|
if op == "CALL_FUNCTION_VAR":
|
||||||
# Python 3.5 only puts optional args (the VAR part)
|
# Python 3.5 only puts optional args (the VAR part)
|
||||||
# lowest down the stack
|
# lowest down the stack
|
||||||
if self.version == 3.5:
|
if self.version == (3, 5):
|
||||||
if str == "%c(%C, ":
|
if str == "%c(%C, ":
|
||||||
entry = ("%c(*%C, %c)", 0, p2, -2)
|
entry = ("%c(*%C, %c)", 0, p2, -2)
|
||||||
elif str == "%c(%C":
|
elif str == "%c(%C":
|
||||||
entry = ("%c(*%C)", 0, (1, 100, ""))
|
entry = ("%c(*%C)", 0, (1, 100, ""))
|
||||||
elif self.version == 3.4:
|
elif self.version == (3, 4):
|
||||||
# CALL_FUNCTION_VAR's top element of the stack contains
|
# CALL_FUNCTION_VAR's top element of the stack contains
|
||||||
# the variable argument list
|
# the variable argument list
|
||||||
if v == 0:
|
if v == 0:
|
||||||
@@ -2244,7 +2244,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
# Python 3.5 only puts optional args (the VAR part)
|
# Python 3.5 only puts optional args (the VAR part)
|
||||||
# lowest down the stack
|
# lowest down the stack
|
||||||
na = v & 0xFF # positional parameters
|
na = v & 0xFF # positional parameters
|
||||||
if self.version == 3.5 and na == 0:
|
if self.version == (3, 5) and na == 0:
|
||||||
if p2[2]:
|
if p2[2]:
|
||||||
p2 = (2, -2, ", ")
|
p2 = (2, -2, ", ")
|
||||||
entry = (str, 0, p2, 1, -2)
|
entry = (str, 0, p2, 1, -2)
|
||||||
@@ -2326,7 +2326,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
del ast[0]
|
del ast[0]
|
||||||
first_stmt = ast[0]
|
first_stmt = ast[0]
|
||||||
|
|
||||||
if 3.0 <= self.version <= 3.3:
|
if (3, 0) <= self.version <= (3, 3):
|
||||||
try:
|
try:
|
||||||
if first_stmt == "store_locals":
|
if first_stmt == "store_locals":
|
||||||
if self.hide_internal:
|
if self.hide_internal:
|
||||||
@@ -2352,7 +2352,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
ast[0] = ast[0][0]
|
ast[0] = ast[0][0]
|
||||||
first_stmt = ast[0]
|
first_stmt = ast[0]
|
||||||
|
|
||||||
if self.version < 3.0:
|
if self.version < (3, 0):
|
||||||
# Should we ditch this in favor of the "else" case?
|
# Should we ditch this in favor of the "else" case?
|
||||||
qualname = ".".join(self.classes)
|
qualname = ".".join(self.classes)
|
||||||
QUAL_NAME = SyntaxTree(
|
QUAL_NAME = SyntaxTree(
|
||||||
@@ -2612,7 +2612,7 @@ def code_deparse(
|
|||||||
|
|
||||||
assert not nonlocals
|
assert not nonlocals
|
||||||
|
|
||||||
if version >= 3.0:
|
if version >= (3, 0):
|
||||||
load_op = "LOAD_STR"
|
load_op = "LOAD_STR"
|
||||||
else:
|
else:
|
||||||
load_op = "LOAD_CONST"
|
load_op = "LOAD_CONST"
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2019-2020 by Rocky Bernstein
|
# Copyright (c) 2019-2021 by Rocky Bernstein
|
||||||
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -48,7 +48,7 @@ def is_docstring(node, version, co_consts):
|
|||||||
# return node.kind == "assign" and node[1][0].pattr == "__doc__"
|
# return node.kind == "assign" and node[1][0].pattr == "__doc__"
|
||||||
# except:
|
# except:
|
||||||
# return False
|
# return False
|
||||||
if version <= 2.7:
|
if version <= (2, 7):
|
||||||
doc_load = "LOAD_CONST"
|
doc_load = "LOAD_CONST"
|
||||||
else:
|
else:
|
||||||
doc_load = "LOAD_STR"
|
doc_load = "LOAD_STR"
|
||||||
@@ -110,7 +110,7 @@ class TreeTransform(GenericASTTraversal, object):
|
|||||||
than the code field is seen and used.
|
than the code field is seen and used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.version >= 3.7:
|
if self.version >= (3, 7):
|
||||||
code_index = -3
|
code_index = -3
|
||||||
else:
|
else:
|
||||||
code_index = -2
|
code_index = -2
|
||||||
|
Reference in New Issue
Block a user