Merge pull request #3720 from laloch/pos-only-args

Add support for PEP 570 positional-only parameters
This commit is contained in:
Anthony Scopatz 2020-08-26 23:20:02 -05:00 committed by GitHub
commit b30448b300
Failed to generate hash of commit
4 changed files with 139 additions and 17 deletions

23
news/pos-only-args.rst Normal file
View file

@ -0,0 +1,23 @@
**Added:**
* Support for PEP 570 positional-only parameters.
**Changed:**
* <news item>
**Deprecated:**
* <news item>
**Removed:**
* <news item>
**Fixed:**
* <news item>
**Security:**
* <news item>

View file

@ -11,7 +11,7 @@ from xonsh.ast import AST, With, Pass, Str, Call
from xonsh.parser import Parser
from xonsh.parsers.fstring_adaptor import FStringAdaptor
from tools import nodes_equal, skip_if_no_walrus, VER_MAJOR_MINOR
from tools import nodes_equal, skip_if_pre_3_8, VER_MAJOR_MINOR
@pytest.fixture(autouse=True)
@ -1041,6 +1041,11 @@ def test_lambda_x_star_y_kwargs():
check_ast("lambda x, *, y, **kwargs: 42")
@skip_if_pre_3_8
def test_lambda_x_divide_y_star_z_kwargs():
check_ast("lambda x, /, y, *, z, **kwargs: 42")
def test_call_range():
check_ast("range(6)")
@ -1209,12 +1214,12 @@ def test_rshift_op_three():
check_ast("42 >> 65 >> 1 >> 7")
@skip_if_no_walrus
@skip_if_pre_3_8
def test_named_expr():
check_ast("(x := 42)")
@skip_if_no_walrus
@skip_if_pre_3_8
def test_named_expr_list():
check_ast("[x := 42, x + 1, x + 2]")
@ -1560,10 +1565,7 @@ def test_yield_x_y():
check_stmts("yield x, y", False)
@pytest.mark.skipif(
VER_MAJOR_MINOR < (3, 8),
reason="Python 3.8 feature"
)
@skip_if_pre_3_8
def test_return_x_starexpr():
check_stmts("yield x, *[y, z]", False)
@ -1588,10 +1590,7 @@ def test_return_x_y():
check_stmts("return x, y", False)
@pytest.mark.skipif(
VER_MAJOR_MINOR < (3, 8),
reason="Python 3.8 feature"
)
@skip_if_pre_3_8
def test_return_x_starexpr():
check_stmts("return x, *[y, z]", False)
@ -1943,6 +1942,16 @@ def test_func_x_star_y_kwargs():
check_stmts("def f(x, *, y, **kwargs):\n return 42")
@skip_if_pre_3_8
def test_func_x_divide():
check_stmts("def f(x, /):\n return 42")
@skip_if_pre_3_8
def test_func_x_divide_y_star_z_kwargs():
check_stmts("def f(x, /, y, *, z, **kwargs):\n return 42")
def test_func_tx():
check_stmts("def f(x:int):\n return x")
@ -2049,22 +2058,22 @@ def test_async_await():
check_stmts("async def f():\n await fut\n", False)
@skip_if_no_walrus
@skip_if_pre_3_8
def test_named_expr_args():
check_stmts("id(x := 42)")
@skip_if_no_walrus
@skip_if_pre_3_8
def test_named_expr_if():
check_stmts("if (x := 42) > 0:\n x += 1")
@skip_if_no_walrus
@skip_if_pre_3_8
def test_named_expr_elif():
check_stmts("if False:\n pass\nelif x := 42:\n x += 1")
@skip_if_no_walrus
@skip_if_pre_3_8
def test_named_expr_while():
check_stmts("y = 42\nwhile (x := y) < 43:\n y += 1")
@ -3128,3 +3137,11 @@ def test_syntax_error_augassign_cmp(exp):
def test_syntax_error_bar_kwonlyargs():
with pytest.raises(SyntaxError):
PARSER.parse("def spam(*):\n pass\n", mode="exec")
def test_syntax_error_bar_posonlyargs():
with pytest.raises(SyntaxError):
PARSER.parse("def spam(/):\n pass\n", mode="exec")
def test_syntax_error_bar_posonlyargs_no_comma():
with pytest.raises(SyntaxError):
PARSER.parse("def spam(x /, y):\n pass\n", mode="exec")

View file

@ -30,7 +30,6 @@ ON_AZURE_PIPELINES = os.environ.get("TF_BUILD", "") == "True"
print("ON_AZURE_PIPELINES", repr(ON_AZURE_PIPELINES))
print("os.environ['TF_BUILD']", repr(os.environ.get("TF_BUILD", "")))
TEST_DIR = os.path.dirname(__file__)
HAS_WALRUS = VER_FULL > (3, 8)
# pytest skip decorators
skip_if_on_conda = pytest.mark.skipif(
@ -53,7 +52,7 @@ skip_if_on_darwin = pytest.mark.skipif(ON_DARWIN, reason="not Mac friendly")
skip_if_on_travis = pytest.mark.skipif(ON_TRAVIS, reason="not Travis CI friendly")
skip_if_no_walrus = pytest.mark.skipif(not HAS_WALRUS, reason="no assignment expr.")
skip_if_pre_3_8 = pytest.mark.skipif(VER_FULL < (3, 8), reason="Python >= 3.8 feature")
def sp(cmd):

View file

@ -51,6 +51,31 @@ class Parser(ThreeSixParser):
outputdir=outputdir,
)
def _set_posonly_args_def(self, argmts, vals):
for v in vals:
argmts.posonlyargs.append(v["arg"])
d = v["default"]
if d is not None:
argmts.defaults.append(d)
def _set_posonly_args(self, p0, p1, p2, p3):
if p2 is None and p3 is None:
# x
p0.posonlyargs.append(p1)
elif p2 is not None and p3 is None:
# x=42
p0.posonlyargs.append(p1)
p0.defaults.append(p2)
elif p2 is None and p3 is not None:
# x, y and x, y=42
p0.posonlyargs.append(p1)
self._set_posonly_args_def(p0, p3)
else:
# x=42, y=42
p0.posonlyargs.append(p1)
p0.defaults.append(p2)
self._set_posonly_args_def(p0, p3)
def p_parameters(self, p):
"""parameters : LPAREN typedargslist_opt RPAREN"""
p2 = p[2]
@ -214,6 +239,35 @@ class Parser(ThreeSixParser):
self._set_var_args(p0, p[6], p[7])
p[0] = p0
def p_typedargslist_t12(self, p):
"""typedargslist : posonlyargslist comma_opt
| posonlyargslist COMMA typedargslist
"""
if len(p) == 4:
p0 = p[3]
p0.posonlyargs = p[1].posonlyargs
else:
p0 = p[1]
p[0] = p0
def p_posonlyargslist(self, p):
"""posonlyargslist : tfpdef equals_test_opt COMMA DIVIDE
| tfpdef equals_test_opt comma_tfpdef_list COMMA DIVIDE"""
p0 = ast.arguments(
posonlyargs=[],
args=[],
vararg=None,
kwonlyargs=[],
kw_defaults=[],
kwarg=None,
defaults=[],
)
if p[3] == ",":
self._set_posonly_args(p0, p[1], p[2], None)
else:
self._set_posonly_args(p0, p[1], p[2], p[3])
p[0] = p0
def p_varargslist_kwargs(self, p):
"""varargslist : POW vfpdef"""
p[0] = ast.arguments(
@ -332,6 +386,35 @@ class Parser(ThreeSixParser):
self._set_var_args(p0, p[6], p[7])
p[0] = p0
def p_varargslist_t12(self, p):
"""varargslist : posonlyvarargslist comma_opt
| posonlyvarargslist COMMA varargslist
"""
if len(p) == 4:
p0 = p[3]
p0.posonlyargs = p[1].posonlyargs
else:
p0 = p[1]
p[0] = p0
def p_posonlyvarargslist(self, p):
"""posonlyvarargslist : vfpdef equals_test_opt COMMA DIVIDE
| vfpdef equals_test_opt comma_vfpdef_list COMMA DIVIDE"""
p0 = ast.arguments(
posonlyargs=[],
args=[],
vararg=None,
kwonlyargs=[],
kw_defaults=[],
kwarg=None,
defaults=[],
)
if p[3] == ",":
self._set_posonly_args(p0, p[1], p[2], None)
else:
self._set_posonly_args(p0, p[1], p[2], p[3])
p[0] = p0
def p_lambdef(self, p):
"""lambdef : lambda_tok varargslist_opt COLON test"""
p1, p2, p4 = p[1], p[2], p[4]