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
79 changes: 79 additions & 0 deletions Lib/test/clinic.test.c
Original file line number Diff line number Diff line change
Expand Up @@ -5830,6 +5830,85 @@ group_and_optional_parameter_impl(PyObject *module, int group_left_1,
/*[clinic end generated code: output=3faea69eafd5bbbe input=7f0fbb6124f5a972]*/


/*[clinic input]
two_groups_on_the_same_level
[
a: object
b: object
]
[
c: object
]
d: object
/
Groups on the same level are independent of each other.
[clinic start generated code]*/

PyDoc_STRVAR(two_groups_on_the_same_level__doc__,
"two_groups_on_the_same_level([a, b,] [c,] d)\n"
"Groups on the same level are independent of each other.");

#define TWO_GROUPS_ON_THE_SAME_LEVEL_METHODDEF \
{"two_groups_on_the_same_level", (PyCFunction)two_groups_on_the_same_level, METH_VARARGS, two_groups_on_the_same_level__doc__},

static PyObject *
two_groups_on_the_same_level_impl(PyObject *module, int group_left_1,
PyObject *a, PyObject *b, int group_left_2,
PyObject *c, PyObject *d);

static PyObject *
two_groups_on_the_same_level(PyObject *module, PyObject *args)
{
PyObject *return_value = NULL;
int group_left_1 = 0;
PyObject *a = NULL;
PyObject *b = NULL;
int group_left_2 = 0;
PyObject *c = NULL;
PyObject *d;

switch (PyTuple_GET_SIZE(args)) {
case 1:
if (!PyArg_ParseTuple(args, "O:two_groups_on_the_same_level", &d)) {
goto exit;
}
break;
case 2:
if (!PyArg_ParseTuple(args, "OO:two_groups_on_the_same_level", &c, &d)) {
goto exit;
}
group_left_2 = 1;
break;
case 3:
if (!PyArg_ParseTuple(args, "OOO:two_groups_on_the_same_level", &a, &b, &d)) {
goto exit;
}
group_left_1 = 1;
break;
case 4:
if (!PyArg_ParseTuple(args, "OOOO:two_groups_on_the_same_level", &a, &b, &c, &d)) {
goto exit;
}
group_left_1 = 1;
group_left_2 = 1;
break;
default:
PyErr_SetString(PyExc_TypeError, "two_groups_on_the_same_level requires 1 to 4 arguments");
goto exit;
}
return_value = two_groups_on_the_same_level_impl(module, group_left_1, a, b, group_left_2, c, d);

exit:
return return_value;
}

static PyObject *
two_groups_on_the_same_level_impl(PyObject *module, int group_left_1,
PyObject *a, PyObject *b, int group_left_2,
PyObject *c, PyObject *d)
/*[clinic end generated code: output=508a61ee582da21e input=1b45d9b675b32d1a]*/


/*[clinic input]
Test._pyarg_parsestackandkeywords
cls: defining_class
Expand Down
160 changes: 125 additions & 35 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -836,15 +836,15 @@ def _test(self, l, m, r, output):
self.assertEqual(output, computed)

def test_range(self):
self._test([['start']], ['stop'], [['step']],
self._test([[['start']]], ['stop'], [[['step']]],
(
('stop',),
('start', 'stop',),
('start', 'stop', 'step',),
))

def test_add_window(self):
self._test([['x', 'y']], ['ch'], [['attr']],
self._test([[['x', 'y']]], ['ch'], [[['attr']]],
(
('ch',),
('ch', 'attr'),
Expand All @@ -853,7 +853,8 @@ def test_add_window(self):
))

def test_ludicrous(self):
self._test([['a1', 'a2', 'a3'], ['b1', 'b2']], ['c1'], [['d1', 'd2'], ['e1', 'e2', 'e3']],
self._test([[['a1', 'a2', 'a3'], ['b1', 'b2']]], ['c1'],
[[['d1', 'd2'], ['e1', 'e2', 'e3']]],
(
('c1',),
('b1', 'b2', 'c1'),
Expand All @@ -864,17 +865,36 @@ def test_ludicrous(self):
))

def test_right_only(self):
self._test([], [], [['a'],['b'],['c']],
self._test([], [], [[['a'],['b'],['c']]],
(
(),
('a',),
('a', 'b'),
('a', 'b', 'c')
))

def test_chgat(self):
# Two independent groups on the left.
self._test([[['y', 'x']], [['n']]], ['attr'], [],
(
('attr',),
('n', 'attr'),
('y', 'x', 'attr'),
('y', 'x', 'n', 'attr'),
))

def test_independent_groups_on_the_right(self):
self._test([], ['a'], [[['b']], [['c', 'd']]],
(
('a',),
('a', 'b'),
('a', 'c', 'd'),
('a', 'b', 'c', 'd'),
))

def test_have_left_options_but_required_is_empty(self):
def fn():
permute_optional_groups(['a'], [], [])
permute_optional_groups([[['a']]], [], [])
self.assertRaises(ValueError, fn)


Expand Down Expand Up @@ -1715,41 +1735,74 @@ def test_nested_groups(self):
Attributes for the character.
""")

def test_disallowed_grouping__two_top_groups_on_left(self):
err = (
"Function 'two_top_groups_on_left' has an unsupported group "
"configuration. (Unexpected state 2.b)"
)
block = """
module foo
foo.two_top_groups_on_left
def test_two_top_groups_on_left(self):
function = self.parse_function("""
module curses
curses.chgat
[
group1 : int
y: int
Y-coordinate.
x: int
X-coordinate.
]
[
group2 : int
num: int
Number of characters.
]
param: int
"""
self.expect_failure(block, err, lineno=5)
attr: long
Attributes for the characters.
/
""")
dataset = (
('y', -1), ('x', -1),
('num', -2),
('attr', 0),
)
for name, group in dataset:
with self.subTest(name=name, group=group):
p = function.parameters[name]
self.assertEqual(p.group, group)
self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
self.checkDocstring(function, """
chgat([y, x,] [num,] attr)

def test_disallowed_grouping__two_top_groups_on_right(self):
block = """

y
Y-coordinate.
x
X-coordinate.
num
Number of characters.
attr
Attributes for the characters.
""")

def test_two_top_groups_on_right(self):
function = self.parse_function("""
module foo
foo.two_top_groups_on_right
param: int
[
group1 : int
group1: int
]
[
group2 : int
group2: int
]
"""
err = (
"Function 'two_top_groups_on_right' has an unsupported group "
"configuration. (Unexpected state 6.b)"
/
""")
dataset = (
('param', 0),
('group1', 1),
('group2', 2),
)
self.expect_failure(block, err)
for name, group in dataset:
with self.subTest(name=name, group=group):
p = function.parameters[name]
self.assertEqual(p.group, group)
self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
self.checkDocstring(function, """
two_top_groups_on_right(param, [group1,] [group2])
""")

def test_disallowed_grouping__parameter_after_group_on_right(self):
block = """
Expand Down Expand Up @@ -4047,6 +4100,26 @@ def test_group_and_two_opt(self):
self.assertEqual(fn(1, 2, 3, 4, 5), (True, 1, 2, 3, 4, 5))
self.assertRaises(TypeError, fn, 1, 2, 3, 4, 5, 6)

def test_two_groups_on_left(self):
# fn([a, b,] [c,] d)
fn = ac_tester.two_groups_on_left
self.assertRaises(TypeError, fn)
self.assertEqual(fn(1), (False, None, None, False, None, 1))
self.assertEqual(fn(1, 2), (False, None, None, True, 1, 2))
self.assertEqual(fn(1, 2, 3), (True, 1, 2, False, None, 3))
self.assertEqual(fn(1, 2, 3, 4), (True, 1, 2, True, 3, 4))
self.assertRaises(TypeError, fn, 1, 2, 3, 4, 5)

def test_two_groups_on_right(self):
# fn(a, [b,] [c, d])
fn = ac_tester.two_groups_on_right
self.assertRaises(TypeError, fn)
self.assertEqual(fn(1), (1, False, None, False, None, None))
self.assertEqual(fn(1, 2), (1, True, 2, False, None, None))
self.assertEqual(fn(1, 2, 3), (1, False, None, True, 2, 3))
self.assertEqual(fn(1, 2, 3, 4), (1, True, 2, True, 3, 4))
self.assertRaises(TypeError, fn, 1, 2, 3, 4, 5)

def test_gh_32092_oob(self):
ac_tester.gh_32092_oob(1, 2, 3, 4, kw1=5, kw2=6)

Expand Down Expand Up @@ -4649,59 +4722,59 @@ def test_permute_optional_groups(self):
"expected": ((),),
}
noleft1 = {
"left": (), "required": ("b",), "right": ("c",),
"left": (), "required": ("b",), "right": (("c",),),
"expected": (
("b",),
("b", "c"),
),
}
noleft2 = {
"left": (), "required": ("b", "c",), "right": ("d",),
"left": (), "required": ("b", "c",), "right": (("d",),),
"expected": (
("b", "c"),
("b", "c", "d"),
),
}
noleft3 = {
"left": (), "required": ("b", "c",), "right": ("d", "e"),
"left": (), "required": ("b", "c",), "right": (("d", "e"),),
"expected": (
("b", "c"),
("b", "c", "d"),
("b", "c", "d", "e"),
),
}
noright1 = {
"left": ("a",), "required": ("b",), "right": (),
"left": (("a",),), "required": ("b",), "right": (),
"expected": (
("b",),
("a", "b"),
),
}
noright2 = {
"left": ("a",), "required": ("b", "c"), "right": (),
"left": (("a",),), "required": ("b", "c"), "right": (),
"expected": (
("b", "c"),
("a", "b", "c"),
),
}
noright3 = {
"left": ("a", "b"), "required": ("c",), "right": (),
"left": (("a", "b"),), "required": ("c",), "right": (),
"expected": (
("c",),
("b", "c"),
("a", "b", "c"),
),
}
leftandright1 = {
"left": ("a",), "required": ("b",), "right": ("c",),
"left": (("a",),), "required": ("b",), "right": (("c",),),
"expected": (
("b",),
("a", "b"), # Prefer left.
("a", "b", "c"),
),
}
leftandright2 = {
"left": ("a", "b"), "required": ("c", "d"), "right": ("e", "f"),
"left": (("a", "b"),), "required": ("c", "d"), "right": (("e", "f"),),
"expected": (
("c", "d"),
("b", "c", "d"), # Prefer left.
Expand All @@ -4710,11 +4783,28 @@ def test_permute_optional_groups(self):
("a", "b", "c", "d", "e", "f"),
),
}
independentleft = {
"left": (("a",), ("b",)), "required": ("c",), "right": (),
"expected": (
("c",),
("b", "c"),
("a", "b", "c"),
),
}
independentright = {
"left": (), "required": ("a",), "right": (("b",), ("c",)),
"expected": (
("a",),
("a", "b"),
("a", "b", "c"),
),
}
dataset = (
empty,
noleft1, noleft2, noleft3,
noright1, noright2, noright3,
leftandright1, leftandright2,
independentleft, independentright,
)
for params in dataset:
with self.subTest(**params):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Argument Clinic now supports several optional groups on the same nesting
level, like in ``[y, x,] [n,] attr``.
Such groups can be omitted independently of each other.
Loading
Loading