Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,32 @@ def test_vararg_after_star(self):
"""
self.expect_failure(block, err, lineno=6)

def test_double_star_after_var_keyword(self):
err = "Function 'my_test_func' has an invalid parameter declaration (**kwargs?): '**kwds: dict'"
block = """
/*[clinic input]
my_test_func

pos_arg: object
**kwds: dict
**
[clinic start generated code]*/
"""
self.expect_failure(block, err, lineno=5)

def test_var_keyword_after_star(self):
err = "Function 'my_test_func' has an invalid parameter declaration: '**'"
block = """
/*[clinic input]
my_test_func

pos_arg: object
**
**kwds: dict
[clinic start generated code]*/
"""
self.expect_failure(block, err, lineno=5)

def test_module_already_got_one(self):
err = "Already defined module 'm'!"
block = """
Expand Down Expand Up @@ -748,6 +774,16 @@ def test_ignore_preprocessor_in_comments(self):
""")
self.clinic.parse(raw)

def test_var_keyword_non_dict(self):
err = "'var_keyword_object' is not a valid converter"
block = """
/*[clinic input]
my_test_func

**kwds: object
[clinic start generated code]*/
"""
self.expect_failure(block, err, lineno=4)

class ParseFileUnitTest(TestCase):
def expect_parsing_failure(
Expand Down Expand Up @@ -1608,6 +1644,11 @@ def test_disallowed_grouping__must_be_position_only(self):
[
a: object
]
""", """
with_kwds
[
**kwds: dict
]
Comment on lines +1649 to +1651
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we are here, could you also add a case with a var-positional parameter inside an optional group?

And I think that it would be worth to add a test for different kinds of parameters (keyword-or-positional, keyword-only, var-positional, var-keyword) after a valid optional group (if there are no such tests yet). This may be a separate test method. Some of these cases can be supported in principle in future. We want to know if we enabled them accidentally.

""")
err = (
"You cannot use optional groups ('[' and ']') unless all "
Expand Down Expand Up @@ -1991,6 +2032,44 @@ def test_slash_after_vararg(self):
err = "Function 'bar': '/' must precede '*'"
self.expect_failure(block, err)

def test_slash_after_var_keyword(self):
block = """
module foo
foo.bar
x: int
y: int
**kwds: dict
z: int
/
"""
err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'"
self.expect_failure(block, err)

def test_star_after_var_keyword(self):
block = """
module foo
foo.bar
x: int
y: int
**kwds: dict
z: int
*
"""
err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'"
self.expect_failure(block, err)

def test_parameter_after_var_keyword(self):
block = """
module foo
foo.bar
x: int
y: int
**kwds: dict
z: int
"""
err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'"
self.expect_failure(block, err)

def test_depr_star_must_come_after_slash(self):
block = """
module foo
Expand Down Expand Up @@ -2079,6 +2158,16 @@ def test_parameters_no_more_than_one_vararg(self):
"""
self.expect_failure(block, err, lineno=3)

def test_parameters_no_more_than_one_var_keyword(self):
err = "Encountered parameter line when not expecting parameters: **var_keyword_2: dict"
block = """
module foo
foo.bar
**var_keyword_1: dict
**var_keyword_2: dict
"""
self.expect_failure(block, err, lineno=3)

def test_function_not_at_column_0(self):
function = self.parse_function("""
module foo
Expand Down Expand Up @@ -2513,6 +2602,14 @@ def test_vararg_cannot_take_default_value(self):
"""
self.expect_failure(block, err, lineno=1)

def test_var_keyword_cannot_take_default_value(self):
err = "Function 'fn' has an invalid parameter declaration:"
block = """
fn
**kwds: dict = None
"""
self.expect_failure(block, err, lineno=1)

def test_default_is_not_of_correct_type(self):
err = ("int_converter: default value 2.5 for field 'a' "
"is not of type 'int'")
Expand Down Expand Up @@ -2610,6 +2707,43 @@ def test_disallow_defining_class_at_module_level(self):
"""
self.expect_failure(block, err, lineno=2)

def test_var_keyword_with_pos_or_kw(self):
block = """
module foo
foo.bar
x: int
**kwds: dict
"""
err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'"
self.expect_failure(block, err)

def test_var_keyword_with_kw_only(self):
block = """
module foo
foo.bar
x: int
/
*
y: int
**kwds: dict
"""
err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'"
self.expect_failure(block, err)

def test_var_keyword_with_pos_or_kw_and_kw_only(self):
block = """
module foo
foo.bar
x: int
/
y: int
*
z: int
**kwds: dict
"""
err = "Function 'bar' has an invalid parameter declaration (**kwargs?): '**kwds: dict'"
self.expect_failure(block, err)

def test_allow_negative_accepted_by_py_ssize_t_converter_only(self):
errmsg = re.escape("converter_init() got an unexpected keyword argument 'allow_negative'")
unsupported_converters = [converter_name for converter_name in converters.keys()
Expand Down Expand Up @@ -3954,6 +4088,49 @@ def test_depr_multi(self):
check("a", b="b", c="c", d="d", e="e", f="f", g="g")
self.assertRaises(TypeError, fn, a="a", b="b", c="c", d="d", e="e", f="f", g="g")

def test_lone_kwds(self):
with self.assertRaises(TypeError):
ac_tester.lone_kwds(1, 2)
self.assertEqual(ac_tester.lone_kwds(), ({},))
self.assertEqual(ac_tester.lone_kwds(y='y'), ({'y': 'y'},))
kwds = {'y': 'y', 'z': 'z'}
self.assertEqual(ac_tester.lone_kwds(y='y', z='z'), (kwds,))
self.assertEqual(ac_tester.lone_kwds(**kwds), (kwds,))

def test_kwds_with_pos_only(self):
with self.assertRaises(TypeError):
ac_tester.kwds_with_pos_only()
with self.assertRaises(TypeError):
ac_tester.kwds_with_pos_only(y='y')
with self.assertRaises(TypeError):
ac_tester.kwds_with_pos_only(1, y='y')
self.assertEqual(ac_tester.kwds_with_pos_only(1, 2), (1, 2, {}))
self.assertEqual(ac_tester.kwds_with_pos_only(1, 2, y='y'), (1, 2, {'y': 'y'}))
kwds = {'y': 'y', 'z': 'z'}
self.assertEqual(ac_tester.kwds_with_pos_only(1, 2, y='y', z='z'), (1, 2, kwds))
self.assertEqual(ac_tester.kwds_with_pos_only(1, 2, **kwds), (1, 2, kwds))

def test_kwds_with_stararg(self):
self.assertEqual(ac_tester.kwds_with_stararg(), ((), {}))
self.assertEqual(ac_tester.kwds_with_stararg(1, 2), ((1, 2), {}))
self.assertEqual(ac_tester.kwds_with_stararg(y='y'), ((), {'y': 'y'}))
args = (1, 2)
kwds = {'y': 'y', 'z': 'z'}
self.assertEqual(ac_tester.kwds_with_stararg(1, 2, y='y', z='z'), (args, kwds))
self.assertEqual(ac_tester.kwds_with_stararg(*args, **kwds), (args, kwds))

def test_kwds_with_pos_only_and_stararg(self):
with self.assertRaises(TypeError):
ac_tester.kwds_with_pos_only_and_stararg()
with self.assertRaises(TypeError):
ac_tester.kwds_with_pos_only_and_stararg(y='y')
self.assertEqual(ac_tester.kwds_with_pos_only_and_stararg(1, 2), (1, 2, (), {}))
self.assertEqual(ac_tester.kwds_with_pos_only_and_stararg(1, 2, y='y'), (1, 2, (), {'y': 'y'}))
args = ('lobster', 'thermidor')
kwds = {'y': 'y', 'z': 'z'}
self.assertEqual(ac_tester.kwds_with_pos_only_and_stararg(1, 2, 'lobster', 'thermidor', y='y', z='z'), (1, 2, args, kwds))
self.assertEqual(ac_tester.kwds_with_pos_only_and_stararg(1, 2, *args, **kwds), (1, 2, args, kwds))


class LimitedCAPIOutputTests(unittest.TestCase):

Expand Down
88 changes: 88 additions & 0 deletions Modules/_testclinic.c
Original file line number Diff line number Diff line change
Expand Up @@ -2308,6 +2308,88 @@ depr_multi_impl(PyObject *module, PyObject *a, PyObject *b, PyObject *c,
#undef _SAVED_PY_VERSION


/*[clinic input]
output pop
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e7c7c42daced52b0]*/


/*[clinic input]
output push
destination kwarg new file '{dirname}/clinic/_testclinic_kwds.c.h'
output everything kwarg
output docstring_prototype suppress
output parser_prototype suppress
output impl_definition block
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=02965b54b3981cc4]*/

#include "clinic/_testclinic_kwds.c.h"


/*[clinic input]
lone_kwds
**kwds: dict
[clinic start generated code]*/

static PyObject *
lone_kwds_impl(PyObject *module, PyObject *kwds)
/*[clinic end generated code: output=572549c687a0432e input=6ef338b913ecae17]*/
{
return pack_arguments_newref(1, kwds);
}


/*[clinic input]
kwds_with_pos_only
a: object
b: object
/
**kwds: dict
[clinic start generated code]*/

static PyObject *
kwds_with_pos_only_impl(PyObject *module, PyObject *a, PyObject *b,
PyObject *kwds)
/*[clinic end generated code: output=573096d3a7efcce5 input=da081a5d9ae8878a]*/
{
return pack_arguments_newref(3, a, b, kwds);
}


/*[clinic input]
kwds_with_stararg
*args: tuple
**kwds: dict
[clinic start generated code]*/

static PyObject *
kwds_with_stararg_impl(PyObject *module, PyObject *args, PyObject *kwds)
/*[clinic end generated code: output=d4b0064626a25208 input=1be404572d685859]*/
{
return pack_arguments_newref(2, args, kwds);
}


/*[clinic input]
kwds_with_pos_only_and_stararg
a: object
b: object
/
*args: tuple
**kwds: dict
[clinic start generated code]*/

static PyObject *
kwds_with_pos_only_and_stararg_impl(PyObject *module, PyObject *a,
PyObject *b, PyObject *args,
PyObject *kwds)
/*[clinic end generated code: output=af7df7640c792246 input=2fe330c7981f0829]*/
{
return pack_arguments_newref(4, a, b, args, kwds);
}


/*[clinic input]
output pop
[clinic start generated code]*/
Expand Down Expand Up @@ -2404,6 +2486,12 @@ static PyMethodDef tester_methods[] = {
DEPR_KWD_NOINLINE_METHODDEF
DEPR_KWD_MULTI_METHODDEF
DEPR_MULTI_METHODDEF

LONE_KWDS_METHODDEF
KWDS_WITH_POS_ONLY_METHODDEF
KWDS_WITH_STARARG_METHODDEF
KWDS_WITH_POS_ONLY_AND_STARARG_METHODDEF

{NULL, NULL}
};

Expand Down
Loading
Loading