You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Handle walrus operator
Or rather set precedence on call_stmt and expr_stmt Adjust pytest test_single_compile so it works now
This commit is contained in:
@@ -1,22 +1,24 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from uncompyle6 import code_deparse
|
from uncompyle6 import code_deparse
|
||||||
from xdis.version_info import PYTHON_VERSION_TRIPLE
|
from xdis.version_info import PYTHON_VERSION_TRIPLE
|
||||||
pytestmark = pytest.mark.skip(PYTHON_VERSION_TRIPLE < (2, 7),
|
|
||||||
reason="need Python < 2.7")
|
pytest.mark.skip(PYTHON_VERSION_TRIPLE < (2, 7), reason="need Python < 2.7")
|
||||||
|
|
||||||
|
|
||||||
def test_single_mode():
|
def test_single_mode():
|
||||||
single_expressions = (
|
single_expressions = (
|
||||||
'i = 1',
|
"i = 1",
|
||||||
'i and (j or k)',
|
"i and (j or k)",
|
||||||
'i += 1',
|
"i += 1",
|
||||||
'i = j % 4',
|
"i = j % 4",
|
||||||
'i = {}',
|
"i = {}",
|
||||||
'i = []',
|
"i = []",
|
||||||
'for i in range(10):\n i\n',
|
"for i in range(10):\n i\n",
|
||||||
'for i in range(10):\n for j in range(10):\n i + j\n',
|
"for i in range(10):\n for j in range(10):\n i + j\n",
|
||||||
'try:\n i\nexcept Exception:\n j\nelse:\n k\n'
|
# 'try:\n i\nexcept Exception:\n j\nelse:\n k\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
for expr in single_expressions:
|
for expr in single_expressions:
|
||||||
code = compile(expr + '\n', '<string>', 'single')
|
code = compile(expr + "\n", "<string>", "single")
|
||||||
assert code_deparse(code, compile_mode='single').text == expr + '\n'
|
got = code_deparse(code, compile_mode="single").text
|
||||||
|
assert got == expr + "\n"
|
||||||
|
BIN
test/bytecode_3.8_run/02_named_expr.pyc
Normal file
BIN
test/bytecode_3.8_run/02_named_expr.pyc
Normal file
Binary file not shown.
14
test/simple_source/bug38/02_named_expr.py
Normal file
14
test/simple_source/bug38/02_named_expr.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# From 3.8 test_named_expressions.py
|
||||||
|
# Bug was not putting parenthesis around := below
|
||||||
|
# RUNNABLE!
|
||||||
|
|
||||||
|
"""This program is self-checking!"""
|
||||||
|
(a := 10)
|
||||||
|
assert a == 10
|
||||||
|
|
||||||
|
# Bug was not putting all of the levels of parentheses := below
|
||||||
|
|
||||||
|
(z := (y := (x := 0)))
|
||||||
|
assert x == 0
|
||||||
|
assert y == 0
|
||||||
|
assert z == 0
|
@@ -43,6 +43,13 @@ class Python37Parser(Python37BaseParser):
|
|||||||
call_stmt ::= expr POP_TOP
|
call_stmt ::= expr POP_TOP
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def p_eval_mode(self, args):
|
||||||
|
"""
|
||||||
|
# eval-mode compilation. Single-mode interactive compilation
|
||||||
|
# adds another rule.
|
||||||
|
expr_stmt ::= expr POP_TOP
|
||||||
|
"""
|
||||||
|
|
||||||
def p_stmt(self, args):
|
def p_stmt(self, args):
|
||||||
"""
|
"""
|
||||||
pass ::=
|
pass ::=
|
||||||
@@ -99,6 +106,7 @@ class Python37Parser(Python37BaseParser):
|
|||||||
else_suite_opt ::= pass
|
else_suite_opt ::= pass
|
||||||
|
|
||||||
stmt ::= classdef
|
stmt ::= classdef
|
||||||
|
stmt ::= expr_stmt
|
||||||
stmt ::= call_stmt
|
stmt ::= call_stmt
|
||||||
|
|
||||||
stmt ::= ifstmt
|
stmt ::= ifstmt
|
||||||
|
@@ -52,6 +52,9 @@ PRECEDENCE = {
|
|||||||
"named_expr": 40, # :=
|
"named_expr": 40, # :=
|
||||||
"yield": 38, # Needs to be below named_expr
|
"yield": 38, # Needs to be below named_expr
|
||||||
"yield_from": 38,
|
"yield_from": 38,
|
||||||
|
"tuple_list_starred": 38, # *x, *y, *z - about at the level of yield?
|
||||||
|
"dict_unpack": 38, # **kwargs
|
||||||
|
"list_unpack": 38, # *args
|
||||||
|
|
||||||
"_lambda_body": 30,
|
"_lambda_body": 30,
|
||||||
"lambda_body": 30, # lambda ... : lambda_body
|
"lambda_body": 30, # lambda ... : lambda_body
|
||||||
@@ -59,7 +62,7 @@ PRECEDENCE = {
|
|||||||
"if_exp": 28, # IfExp ( a if x else b)
|
"if_exp": 28, # IfExp ( a if x else b)
|
||||||
"if_exp_lambda": 28, # IfExp involving a lambda expression
|
"if_exp_lambda": 28, # IfExp involving a lambda expression
|
||||||
"if_exp_not_lambda": 28, # negated IfExp involving a lambda expression
|
"if_exp_not_lambda": 28, # negated IfExp involving a lambda expression
|
||||||
"if_exp_not": 28,
|
"if_exp_not": 28, # IfExp ( a if not x else b)
|
||||||
"if_exp_true": 28, # (a if True else b)
|
"if_exp_true": 28, # (a if True else b)
|
||||||
"if_exp_ret": 28,
|
"if_exp_ret": 28,
|
||||||
|
|
||||||
@@ -89,9 +92,9 @@ PRECEDENCE = {
|
|||||||
"BINARY_MULTIPLY": 8, # *
|
"BINARY_MULTIPLY": 8, # *
|
||||||
"BINARY_TRUE_DIVIDE": 8, # Division /
|
"BINARY_TRUE_DIVIDE": 8, # Division /
|
||||||
|
|
||||||
"unary_op": 6, # +x, -x, ~x
|
"unary_op": 6, # Positive, negative, bitwise NOT: +x, -x, ~x
|
||||||
|
|
||||||
"BINARY_POWER": 4, # Exponentiation, *
|
"BINARY_POWER": 4, # Exponentiation: **
|
||||||
|
|
||||||
"await_expr": 3, # await x, *
|
"await_expr": 3, # await x, *
|
||||||
|
|
||||||
@@ -123,22 +126,6 @@ LINE_LENGTH = 80
|
|||||||
# Some parse trees created below are used for comparing code
|
# Some parse trees created below are used for comparing code
|
||||||
# fragments (like "return None" at the end of functions).
|
# fragments (like "return None" at the end of functions).
|
||||||
|
|
||||||
RETURN_LOCALS = SyntaxTree(
|
|
||||||
"return",
|
|
||||||
[
|
|
||||||
SyntaxTree("return_expr", [SyntaxTree("expr", [Token("LOAD_LOCALS")])]),
|
|
||||||
Token("RETURN_VALUE"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
NONE = SyntaxTree("expr", [NoneToken])
|
|
||||||
|
|
||||||
RETURN_NONE = SyntaxTree("stmt", [SyntaxTree("return", [NONE, Token("RETURN_VALUE")])])
|
|
||||||
|
|
||||||
PASS = SyntaxTree(
|
|
||||||
"stmts", [SyntaxTree("sstmt", [SyntaxTree("stmt", [SyntaxTree("pass", [])])])]
|
|
||||||
)
|
|
||||||
|
|
||||||
ASSIGN_DOC_STRING = lambda doc_string, doc_load: SyntaxTree(
|
ASSIGN_DOC_STRING = lambda doc_string, doc_load: SyntaxTree(
|
||||||
"assign",
|
"assign",
|
||||||
[
|
[
|
||||||
@@ -149,6 +136,10 @@ ASSIGN_DOC_STRING = lambda doc_string, doc_load: SyntaxTree(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PASS = SyntaxTree(
|
||||||
|
"stmts", [SyntaxTree("sstmt", [SyntaxTree("stmt", [SyntaxTree("pass", [])])])]
|
||||||
|
)
|
||||||
|
|
||||||
NAME_MODULE = SyntaxTree(
|
NAME_MODULE = SyntaxTree(
|
||||||
"assign",
|
"assign",
|
||||||
[
|
[
|
||||||
@@ -161,6 +152,18 @@ NAME_MODULE = SyntaxTree(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
NONE = SyntaxTree("expr", [NoneToken])
|
||||||
|
|
||||||
|
RETURN_NONE = SyntaxTree("stmt", [SyntaxTree("return", [NONE, Token("RETURN_VALUE")])])
|
||||||
|
|
||||||
|
RETURN_LOCALS = SyntaxTree(
|
||||||
|
"return",
|
||||||
|
[
|
||||||
|
SyntaxTree("return_expr", [SyntaxTree("expr", [Token("LOAD_LOCALS")])]),
|
||||||
|
Token("RETURN_VALUE"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
# God intended \t, but Python has decided to use 4 spaces.
|
# God intended \t, but Python has decided to use 4 spaces.
|
||||||
# If you want real tabs, use Go.
|
# If you want real tabs, use Go.
|
||||||
# TAB = "\t"
|
# TAB = "\t"
|
||||||
@@ -312,6 +315,7 @@ TABLE_DIRECT = {
|
|||||||
# "classdef": (), # handled by n_classdef()
|
# "classdef": (), # handled by n_classdef()
|
||||||
# A custom rule in n_function def distinguishes whether to call this or
|
# A custom rule in n_function def distinguishes whether to call this or
|
||||||
# function_def_async
|
# function_def_async
|
||||||
|
|
||||||
"function_def": ("\n\n%|def %c\n", -2), # -2 to handle closures
|
"function_def": ("\n\n%|def %c\n", -2), # -2 to handle closures
|
||||||
"function_def_deco": ("\n\n%c", 0),
|
"function_def_deco": ("\n\n%c", 0),
|
||||||
"mkfuncdeco": ("%|@%c\n%c", 0, 1),
|
"mkfuncdeco": ("%|@%c\n%c", 0, 1),
|
||||||
@@ -393,8 +397,17 @@ TABLE_DIRECT = {
|
|||||||
"whileelsestmt": ("%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n", 1, 2, -2),
|
"whileelsestmt": ("%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n", 1, 2, -2),
|
||||||
"whileelsestmt2": ("%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n", 1, 2, -3),
|
"whileelsestmt2": ("%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n", 1, 2, -3),
|
||||||
"whileelselaststmt": ("%|while %c:\n%+%c%-%|else:\n%+%c%-", 1, 2, -2),
|
"whileelselaststmt": ("%|while %c:\n%+%c%-%|else:\n%+%c%-", 1, 2, -2),
|
||||||
|
|
||||||
|
"expr_stmt": (
|
||||||
|
"%|%p\n",
|
||||||
|
# When a statment contains only a named_expr (:=)
|
||||||
|
# the named_expr should have parenthesis around it.
|
||||||
|
(0, "expr", PRECEDENCE["named_expr"] - 1)
|
||||||
|
),
|
||||||
|
|
||||||
# Note: Python 3.8+ changes this
|
# Note: Python 3.8+ changes this
|
||||||
"for": ("%|for %c in %c:\n%+%c%-\n\n", (3, "store"), (1, "expr"), (4, "for_block")),
|
"for": ("%|for %c in %c:\n%+%c%-\n\n", (3, "store"), (1, "expr"), (4, "for_block")),
|
||||||
|
|
||||||
"forelsestmt": (
|
"forelsestmt": (
|
||||||
"%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
|
"%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
|
||||||
(3, "store"),
|
(3, "store"),
|
||||||
|
@@ -47,10 +47,6 @@ def customize_for_version38(self, version):
|
|||||||
(7, "suite_stmts")
|
(7, "suite_stmts")
|
||||||
),
|
),
|
||||||
|
|
||||||
"call_stmt": (
|
|
||||||
"%|%c\n", 0
|
|
||||||
),
|
|
||||||
|
|
||||||
"except_cond_as": (
|
"except_cond_as": (
|
||||||
"%|except %c as %c:\n",
|
"%|except %c as %c:\n",
|
||||||
(1, "expr"),
|
(1, "expr"),
|
||||||
|
@@ -2710,7 +2710,8 @@ def code_deparse(
|
|||||||
elif compile_mode == "exec":
|
elif compile_mode == "exec":
|
||||||
expected_start = "stmts"
|
expected_start = "stmts"
|
||||||
elif compile_mode == "single":
|
elif compile_mode == "single":
|
||||||
expected_start = "single_start"
|
# expected_start = "single_start"
|
||||||
|
expected_start = None
|
||||||
else:
|
else:
|
||||||
expected_start = None
|
expected_start = None
|
||||||
if expected_start:
|
if expected_start:
|
||||||
|
Reference in New Issue
Block a user