diff --git a/changelog/14492.bugfix.rst b/changelog/14492.bugfix.rst new file mode 100644 index 00000000000..301958d6400 --- /dev/null +++ b/changelog/14492.bugfix.rst @@ -0,0 +1 @@ +Fixed ``Code.getargs()`` incorrectly including local variable names in the returned argument tuple for functions with ``*args`` and/or ``**kwargs``. The method was using ``co_flags`` bitmask values (``4`` and ``8``) directly as counts instead of converting them to ``1`` via ``bool()``. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 4cf99a77340..4fcce0427ef 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -121,8 +121,9 @@ def getargs(self, var: bool = False) -> tuple[str, ...]: raw = self.raw argcount = raw.co_argcount if var: - argcount += raw.co_flags & CO_VARARGS - argcount += raw.co_flags & CO_VARKEYWORDS + argcount += raw.co_kwonlyargcount + argcount += bool(raw.co_flags & CO_VARARGS) + argcount += bool(raw.co_flags & CO_VARKEYWORDS) return raw.co_varnames[:argcount] diff --git a/testing/code/test_code.py b/testing/code/test_code.py index e10139dee50..6947320a9ce 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -115,6 +115,20 @@ def f4(x, *y, **z): c4 = Code.from_function(f4) assert c4.getargs(var=True) == ("x", "y", "z") + def f5(x, *y, **z): + a1 = a2 = a3 = a4 = a5 = a6 = 1 # noqa: F841 + + c5 = Code.from_function(f5) + f5(1, 2, 3, z=4) # cover function body + assert c5.getargs(var=True) == ("x", "y", "z") + + def f6(x, *y, kw=1, **z): + a1 = a2 = a3 = a4 = a5 = a6 = 1 # noqa: F841 + + c6 = Code.from_function(f6) + f6(1, 2, kw=3, z=4) # cover function body + assert c6.getargs(var=True) == ("x", "kw", "y", "z") + def test_frame_getargs() -> None: def f1(x) -> FrameType: