Merge branch 'master' into python-2.4

This commit is contained in:
rocky
2020-01-06 23:32:31 -05:00
7 changed files with 93 additions and 30 deletions

View File

@@ -231,7 +231,7 @@ See Also
* https://github.com/rocky/python-xdis : Cross Python version disassembler
* https://github.com/rocky/python-xasm : Cross Python version assembler
* https://github.com/rocky/python-uncompyle6/wiki : Wiki Documents which describe the code and aspects of it in more detail
* https://github.com/zrax/pycdc : The README for this C++ code syas it aims to support all versions of Python. It is best for Python versions around 2.7 and 3.3 when the code was initially developed. Accuracy for current versions of Python3 and early versions of Python is lacking. Without major effort, it is unlikely it can be made to support current Python 3. See its `issue tracker <https://github.com/zrax/pycdc/issues>`_ for details. Currently lightly maintained.
* https://github.com/zrax/pycdc : The README for this C++ code says it aims to support all versions of Python. It is best for Python versions around 2.7 and 3.3 when the code was initially developed. Accuracy for current versions of Python3 and early versions of Python is lacking. Without major effort, it is unlikely it can be made to support current Python 3. See its `issue tracker <https://github.com/zrax/pycdc/issues>`_ for details. Currently lightly maintained.
.. _trepan: https://pypi.python.org/pypi/trepan2g

View File

@@ -48,7 +48,7 @@ case $PYVERSION in
;;
2.5)
SKIP_TESTS=(
[test_dis.py]=1 # We change line numbers - duh!
[test_dis.py]=1 # We change line numbers - duh!
[test_grammar.py]=1 # Too many stmts. Handle large stmts
[test_grp.py]=1 # Long test - might work Control flow?
[test_pdb.py]=1 # Line-number specific
@@ -110,11 +110,9 @@ case $PYVERSION in
[test_capi.py]=1
[test_curses.py]=1 # Possibly fails on its own but not detected
[test_cmd_line.py]=1 # Takes too long, maybe hangs, or looking for interactive input?
[test_compilex.py]=1 # Probably complex literals again. Investigate
[test_dis.py]=1 # We change line numbers - duh!
[test_doctest.py]=1 # Fails on its own
[test_exceptions.py]=1
[test_format.py]=1 # control flow. uncompyle2 does not have problems here
[test_grammar.py]=1 # Too many stmts. Handle large stmts
[test_grp.py]=1 # test takes to long, works interactively though
[test_io.py]=1 # Test takes too long to run
@@ -137,7 +135,6 @@ case $PYVERSION in
[test_unicode.py]=1 # Too long to run 11 seconds
[test_xpickle.py]=1 # Runs ok but takes 72 seconds
[test_zipfile64.py]=1 # Runs ok but takes 204 seconds
[test_zipimport.py]=1 # FIXME: improper try from try/else ?
)
if (( batch )) ; then
# Fails in crontab environment?
@@ -232,9 +229,8 @@ case $PYVERSION in
3.5)
SKIP_TESTS=(
[test_ast.py]=1 # line 379, in test_literal_eval self.assertEqual(ast.literal_eval('b"hi"'), 'hi')
[test_atexit.py]=1 #
[test_atexit.py]=1
[test_builtin.py]=1 #
[test_compare.py]=1
[test_dis.py]=1 # We change line numbers - duh!
)
if (( batch )) ; then
@@ -247,11 +243,9 @@ case $PYVERSION in
3.6)
SKIP_TESTS=(
[test_ast.py]=1 #
[test_atexit.py]=1 #
[test_bdb.py]=1 #
[test_builtin.py]=1 #
[test_compare.py]=1
[test_builtin.py]=1 # Fails on its own
[test_compile.py]=1
[test_contains.py]=1 # Code "while False: yield None" is optimized away in compilation
[test_contextlib_async.py]=1 # Investigate
@@ -263,7 +257,7 @@ case $PYVERSION in
;;
3.7)
SKIP_TESTS=(
[test_ast.py]=1 #
[test_ast.py]=1 # test assertion error
[test_atexit.py]=1 #
[test_baseexception.py]=1 #
[test_bdb.py]=1 #
@@ -271,10 +265,9 @@ case $PYVERSION in
[test_builtin.py]=1 # parser error
[test_cmdline.py]=1 # Interactive?
[test_collections.py]=1 # Fixed I think in decompyle3 - pull from there
[test_compare.py]=1
[test_compare.py]=1 # test assert fail - investigate
[test_compile.py]=1
[test_configparser.py]=1
[test_contains.py]=1 # Code "while False: yield None" is optimized away in compilation
[test_contextlib_async.py]=1 # Investigate
[test_context.py]=1
[test_coroutines.py]=1 # Parse error
@@ -296,7 +289,6 @@ case $PYVERSION in
;;
3.8)
SKIP_TESTS=(
[test_contains.py]=1 # Code "while False: yield None" is optimized away in compilation
[test_collections.py]=1 # Investigate
[test_decorators.py]=1 # Control flow wrt "if elif"
[test_exceptions.py]=1 # parse error

View File

@@ -159,6 +159,17 @@ class Python2Parser(PythonParser):
except_handler ::= jmp_abs COME_FROM except_stmts
END_FINALLY
# except_handler_else is intended to be used only in a
# try_else. The disambiguation comes from reduction rule
# checking where we make sure that the JUMP_FORWARD mismatches
# the JUMP_FORWARD before the END_FINALLY
except_handler_else ::= JUMP_FORWARD COME_FROM except_stmts
END_FINALLY come_froms
except_handler_else ::= jmp_abs COME_FROM except_stmts
END_FINALLY
except_stmts ::= except_stmt+
except_stmt ::= except_cond1 except_suite
@@ -635,6 +646,8 @@ class Python2Parser(PythonParser):
self.addRule(rule, nop_func)
pass
self.check_reduce["except_handler"] = "tokens"
self.check_reduce["except_handler_else"] = "tokens"
self.check_reduce["raise_stmt1"] = "tokens"
self.check_reduce["assert_expr_and"] = "AST"
self.check_reduce["tryelsestmt"] = "AST"
@@ -666,6 +679,31 @@ class Python2Parser(PythonParser):
jmp_false = ast[1]
jump_target = jmp_false[0].attr
return jump_target > tokens[last].off2int()
elif lhs in ("except_handler, except_handler_else"):
# FIXME: expand this to other 2.x version
if self.version != 2.7: return False
if tokens[first] in ("JUMP_FORWARD", "JUMP_ABSOLUTE"):
first_jump_target = tokens[first].pattr
last = min(last, len(tokens)-1)
for i in range(last, first, -1):
if tokens[i] == "END_FINALLY":
i -= 1
second_jump = tokens[i]
if second_jump in ("JUMP_FORWARD", "JUMP_ABSOLUTE"):
second_jump_target = second_jump.pattr
equal_target = second_jump_target == first_jump_target
if equal_target:
return lhs != "except_handler"
else:
return lhs != "except_handler_else"
pass
else:
return False
pass
pass
pass
elif rule == ("ifstmt", ("testexpr", "_ifstmts_jump")):
for i in range(last-1, last-4, -1):
t = tokens[i]

View File

@@ -60,13 +60,13 @@ class Python27Parser(Python2Parser):
COME_FROM_FINALLY suite_stmts_opt END_FINALLY
tryelsestmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
except_handler else_suite COME_FROM
except_handler_else else_suite COME_FROM
tryelsestmtl ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
except_handler else_suitel JUMP_BACK COME_FROM
except_handler_else else_suitel JUMP_BACK COME_FROM
tryelsestmtl ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
except_handler else_suitel
except_handler_else else_suitel
except_stmt ::= except_cond2 except_suite
@@ -216,6 +216,10 @@ class Python27Parser(Python2Parser):
super(Python27Parser, self).customize_grammar_rules(tokens, customize)
self.check_reduce["and"] = "AST"
self.check_reduce["conditional"] = "AST"
self.check_reduce["except_handler"] = "tokens"
self.check_reduce["except_handler_else"] = "tokens"
# self.check_reduce["or"] = "AST"
self.check_reduce["raise_stmt1"] = "AST"
self.check_reduce["iflaststmtl"] = "AST"

View File

@@ -18,6 +18,7 @@ All the crazy things we have to do to handle Python functions in Python before 3
The saga of changes continues in 3.0 and above and in other files.
"""
from xdis.code import iscode, code_has_star_arg, code_has_star_star_arg
from xdis.util import CO_GENERATOR
from uncompyle6.scanner import Code
from uncompyle6.parsers.treenode import SyntaxTree
from uncompyle6 import PYTHON3
@@ -178,7 +179,6 @@ def make_function2(self, node, is_lambda, nested=1, code_node=None):
# docstring exists, dump it
print_docstring(self, indent, code.co_consts[0])
code._tokens = None # save memory
if not is_lambda:
assert ast == "stmts"
@@ -199,5 +199,6 @@ def make_function2(self, node, is_lambda, nested=1, code_node=None):
self.gen_source(
ast, code.co_name, code._customize, is_lambda=is_lambda, returnNone=rn
)
code._tokens = None
code._tokens = None # save memory
code._customize = None # save memory

View File

@@ -17,6 +17,7 @@ All the crazy things we have to do to handle Python functions in 3.0-3.5 or so.
The saga of changes before and after is in other files.
"""
from xdis.code import iscode, code_has_star_arg, code_has_star_star_arg
from xdis.util import CO_GENERATOR
from uncompyle6.scanner import Code
from uncompyle6.parsers.treenode import SyntaxTree
from uncompyle6.semantics.parser_error import ParserError
@@ -485,9 +486,8 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
kwargs = list(scanner_code.co_varnames[argc : argc + kwonlyargcount])
# defaults are for last n parameters when not in a lambda, thus reverse
if not is_lambda:
paramnames.reverse()
defparams.reverse()
paramnames.reverse()
defparams.reverse()
try:
ast = self.build_ast(scanner_code._tokens,
@@ -645,7 +645,6 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
# docstring exists, dump it
print_docstring(self, self.indent, code.co_consts[0])
scanner_code._tokens = None # save memory
assert ast == "stmts"
all_globals = find_all_globals(ast, set())
@@ -665,5 +664,21 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None):
self.gen_source(
ast, code.co_name, scanner_code._customize, is_lambda=is_lambda, returnNone=rn
)
scanner_code._tokens = None
# In obscure cases, a function may be a generator but the "yield"
# was optimized away. Here, we need to put in unreachable code to
# add in "yield" just so that the compiler will mark
# the GENERATOR bit of the function. See for example
# Python 3.x's test_generator.py test program.
if code.co_flags & CO_GENERATOR:
need_bogus_yield = True
for token in scanner_code._tokens:
if token in ("YIELD_VALUE", "YIELD_FROM"):
need_bogus_yield = False
break
pass
if need_bogus_yield:
self.template_engine(("%|if False:\n%+%|yield None%-",), node)
scanner_code._tokens = None # save memory
scanner_code._customize = None # save memory

View File

@@ -17,6 +17,7 @@ All the crazy things we have to do to handle Python functions in 3.6 and above.
The saga of changes before 3.6 is in other files.
"""
from xdis.code import iscode, code_has_star_arg, code_has_star_star_arg
from xdis.util import CO_GENERATOR, CO_ASYNC_GENERATOR
from uncompyle6.scanner import Code
from uncompyle6.parsers.treenode import SyntaxTree
from uncompyle6.semantics.parser_error import ParserError
@@ -197,10 +198,8 @@ def make_function36(self, node, is_lambda, nested=1, code_node=None):
paramnames = list(scanner_code.co_varnames[:argc])
kwargs = list(scanner_code.co_varnames[argc : argc + kwonlyargcount])
# defaults are for last n parameters when not in a lambda, thus reverse
if not is_lambda:
paramnames.reverse()
defparams.reverse()
paramnames.reverse()
defparams.reverse()
try:
ast = self.build_ast(
@@ -372,7 +371,6 @@ def make_function36(self, node, is_lambda, nested=1, code_node=None):
# docstring exists, dump it
self.println(self.traverse(node[-2]))
scanner_code._tokens = None # save memory
assert ast == "stmts"
all_globals = find_all_globals(ast, set())
@@ -393,5 +391,20 @@ def make_function36(self, node, is_lambda, nested=1, code_node=None):
ast, code.co_name, scanner_code._customize, is_lambda=is_lambda, returnNone=rn
)
scanner_code._tokens = None
# In obscure cases, a function may be a generator but the "yield"
# was optimized away. Here, we need to put in unreachable code to
# add in "yield" just so that the compiler will mark
# the GENERATOR bit of the function. See for example
# Python 3.x's test_generator.py test program.
if code.co_flags & (CO_GENERATOR | CO_ASYNC_GENERATOR):
need_bogus_yield = True
for token in scanner_code._tokens:
if token == "YIELD_VALUE":
need_bogus_yield = False
break
pass
if need_bogus_yield:
self.template_engine(("%|if False:\n%+%|yield None%-",), node)
scanner_code._tokens = None # save memory
scanner_code._customize = None # save memory