Skip to content

Commit b777774

Browse files
authored
fix: keyword only non default argument (#1290)
1 parent 0f8445e commit b777774

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

src/validators/arguments.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ impl BuildValidator for ArgumentsValidator {
5151

5252
let mut positional_params_count = 0;
5353
let mut had_default_arg = false;
54+
let mut had_keyword_only = false;
5455

5556
for (arg_index, arg) in arguments_schema.iter().enumerate() {
5657
let arg = arg.downcast::<PyDict>()?;
@@ -68,6 +69,10 @@ impl BuildValidator for ArgumentsValidator {
6869
positional_params_count = arg_index + 1;
6970
}
7071

72+
if mode == "keyword_only" {
73+
had_keyword_only = true;
74+
}
75+
7176
let mut kw_lookup_key = None;
7277
let mut kwarg_key = None;
7378
if mode == "keyword_only" || mode == "positional_or_keyword" {
@@ -98,7 +103,7 @@ impl BuildValidator for ArgumentsValidator {
98103
_ => false,
99104
};
100105

101-
if had_default_arg && !has_default {
106+
if had_default_arg && !has_default && !had_keyword_only {
102107
return py_schema_err!("Non-default argument '{}' follows default argument", name);
103108
} else if has_default {
104109
had_default_arg = true;

tests/validators/test_arguments.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,83 @@ def test_positional_or_keyword(py_and_json: PyAndJson, input_value, expected):
371371
assert v.validate_test(input_value) == expected
372372

373373

374+
@pytest.mark.parametrize(
375+
'input_value,expected,arguments_schema',
376+
[
377+
(
378+
{'a': 1, 'b': 2, 'e': 3.14},
379+
((), {'a': 1, 'b': 2, 'c': 5, 'd': 'default', 'e': 3.14}),
380+
[
381+
{'name': 'a', 'mode': 'positional_or_keyword', 'schema': {'type': 'int'}},
382+
{'name': 'b', 'mode': 'positional_or_keyword', 'schema': {'type': 'int'}},
383+
{
384+
'name': 'c',
385+
'mode': 'keyword_only',
386+
'schema': {'type': 'default', 'schema': {'type': 'int'}, 'default': 5},
387+
},
388+
{
389+
'name': 'd',
390+
'mode': 'keyword_only',
391+
'schema': {'type': 'default', 'schema': {'type': 'str'}, 'default': 'default'},
392+
},
393+
{'name': 'e', 'mode': 'keyword_only', 'schema': {'type': 'float'}},
394+
],
395+
),
396+
(
397+
{'y': 'test'},
398+
((), {'x': 1, 'y': 'test'}),
399+
[
400+
{
401+
'name': 'x',
402+
'mode': 'keyword_only',
403+
'schema': {'type': 'default', 'schema': {'type': 'int'}, 'default': 1},
404+
},
405+
{'name': 'y', 'mode': 'keyword_only', 'schema': {'type': 'str'}},
406+
],
407+
),
408+
(
409+
{'a': 1, 'd': 3.14},
410+
((), {'a': 1, 'b': 10, 'c': 'hello', 'd': 3.14}),
411+
[
412+
{'name': 'a', 'mode': 'positional_or_keyword', 'schema': {'type': 'int'}},
413+
{
414+
'name': 'b',
415+
'mode': 'positional_or_keyword',
416+
'schema': {'type': 'default', 'schema': {'type': 'int'}, 'default': 10},
417+
},
418+
{
419+
'name': 'c',
420+
'mode': 'keyword_only',
421+
'schema': {'type': 'default', 'schema': {'type': 'str'}, 'default': 'hello'},
422+
},
423+
{'name': 'd', 'mode': 'keyword_only', 'schema': {'type': 'float'}},
424+
],
425+
),
426+
(
427+
{'x': 3, 'y': 'custom', 'z': 4},
428+
((), {'x': 3, 'y': 'custom', 'z': 4}),
429+
[
430+
{'name': 'x', 'mode': 'positional_or_keyword', 'schema': {'type': 'int'}},
431+
{
432+
'name': 'y',
433+
'mode': 'keyword_only',
434+
'schema': {'type': 'default', 'schema': {'type': 'str'}, 'default': 'default'},
435+
},
436+
{'name': 'z', 'mode': 'keyword_only', 'schema': {'type': 'int'}},
437+
],
438+
),
439+
],
440+
)
441+
def test_keyword_only_non_default(py_and_json: PyAndJson, input_value, expected, arguments_schema):
442+
v = py_and_json(
443+
{
444+
'type': 'arguments',
445+
'arguments_schema': arguments_schema,
446+
}
447+
)
448+
assert v.validate_test(input_value) == expected
449+
450+
374451
@pytest.mark.parametrize('input_value,expected', [[(1,), ((1,), {})], [(), ((42,), {})]], ids=repr)
375452
def test_positional_optional(py_and_json: PyAndJson, input_value, expected):
376453
v = py_and_json(

0 commit comments

Comments
 (0)