Skip to content

Commit 8ecb2a1

Browse files
yoneymeta-codesync[bot]
authored andcommitted
Refactor positional arguments handling
Summary: Refactor positional default argument filling in `JITRT_BindKeywordArgs`: rename variables `(n, m)` for clarity, simplify the missing-arg check to return early, and add tests covering the keyword call path. Reviewed By: alexmalyshev Differential Revision: D96811731 fbshipit-source-id: f5c971a5c5711d0c70f373847af60096b0106bcc
1 parent 17dd01c commit 8ecb2a1

File tree

2 files changed

+36
-20
lines changed

2 files changed

+36
-20
lines changed

cinderx/Jit/jit_rt.cpp

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -144,30 +144,26 @@ static int JITRT_BindKeywordArgs(
144144

145145
// Add missing positional arguments (copy default values from defs)
146146
if (argcount < co->co_argcount) {
147-
Py_ssize_t defcount;
148-
if (func->func_defaults != nullptr) {
149-
defcount = PyTuple_Size(func->func_defaults);
150-
} else {
151-
defcount = 0;
152-
}
153-
Py_ssize_t m = co->co_argcount - defcount;
154-
Py_ssize_t missing = 0;
155-
for (Py_ssize_t i = argcount; i < m; i++) {
147+
PyObject* defaults = func->func_defaults;
148+
Py_ssize_t defcount = defaults == nullptr ? 0 : PyTuple_Size(defaults);
149+
Py_ssize_t first_default_arg = co->co_argcount - defcount;
150+
151+
// Any unset slot before the defaults region means we are missing
152+
// a required positional argument.
153+
for (Py_ssize_t i = argcount; i < first_default_arg; i++) {
156154
if (arg_space[i] == nullptr) {
157-
missing++;
155+
return 0;
158156
}
159157
}
160-
if (missing) {
161-
return 0;
162-
}
163158

164-
if (defcount) {
165-
PyObject* const* defs =
166-
&((PyTupleObject*)func->func_defaults)->ob_item[0];
167-
for (Py_ssize_t i = std::max<Py_ssize_t>(n - m, 0); i < defcount; i++) {
168-
if (arg_space[m + i] == nullptr) {
169-
PyObject* def = defs[i];
170-
arg_space[m + i] = def;
159+
if (defaults != nullptr) {
160+
PyObject* const* defs = &((PyTupleObject*)defaults)->ob_item[0];
161+
// Only slots in the defaults-covered tail can still need filling.
162+
Py_ssize_t arg_index = std::max(argcount, first_default_arg);
163+
for (; arg_index < co->co_argcount; arg_index++) {
164+
if (arg_space[arg_index] == nullptr) {
165+
Py_ssize_t def_index = arg_index - first_default_arg;
166+
arg_space[arg_index] = defs[def_index];
171167
}
172168
}
173169
}

cinderx/PythonLib/test_cinderx/test_cinderjit.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,6 +1458,26 @@ def test_kwonly_args_and_varkwargs(self) -> None:
14581458
self.assertEqual(self.f4(2, y=10, z=30, a=40, b=50), {"a": 40, "b": 50})
14591459

14601460

1461+
class PositionalDefaultsWithKeywordCallTest(unittest.TestCase):
1462+
"""Test that positional args with defaults are filled correctly
1463+
when the function is called with keyword arguments."""
1464+
1465+
@cinder_support.failUnlessJITCompiled
1466+
def f(self, a, b=2, c=3):
1467+
return (a, b, c)
1468+
1469+
def test_skip_default_with_kwarg(self) -> None:
1470+
self.assertEqual(self.f(1, b=20), (1, 20, 3))
1471+
self.assertEqual(self.f(1, c=30), (1, 2, 30))
1472+
1473+
def test_override_defaults_with_kwargs(self) -> None:
1474+
self.assertEqual(self.f(1, b=20, c=30), (1, 20, 30))
1475+
1476+
def test_missing_required_arg_with_kwargs(self) -> None:
1477+
with self.assertRaises(TypeError):
1478+
self.f(b=20, c=30)
1479+
1480+
14611481
class ClassA:
14621482
z = 100
14631483
x = 41

0 commit comments

Comments
 (0)