Skip to content

Fix Code.getargs() treating co_flags bitmask values as counts#14493

Open
EternalRights wants to merge 5 commits into
pytest-dev:mainfrom
EternalRights:fix-code-getargs-bit-flags
Open

Fix Code.getargs() treating co_flags bitmask values as counts#14493
EternalRights wants to merge 5 commits into
pytest-dev:mainfrom
EternalRights:fix-code-getargs-bit-flags

Conversation

@EternalRights
Copy link
Copy Markdown
Contributor

CO_VARARGS (4) and CO_VARKEYWORDS (8) are bitmask flags, but they were used directly as increment values for the argument count via &. This caused co_varnames[:argcount] to slice beyond the actual arguments when the function body contained enough local variables.

Fix by wrapping the bitmask results in bool(), so they contribute at most 1 to the argcount.

Closes #14492

CO_VARARGS (4) and CO_VARKEYWORDS (8) are bitmask flags, but they were
used directly as increment values for the argument count via `&`.
This caused co_varnames[:argcount] to slice beyond the actual arguments
when the function body contained enough local variables.

Fix by wrapping the bitmask results in bool(), so they contribute
at most 1 to the argcount.

Closes pytest-dev#14492
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label May 17, 2026
Comment thread src/_pytest/_code/code.py
if var:
argcount += raw.co_flags & CO_VARARGS
argcount += raw.co_flags & CO_VARKEYWORDS
argcount += bool(raw.co_flags & CO_VARARGS)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I fear this may get confused by mixing in kwonly args

@EternalRights
Copy link
Copy Markdown
Contributor Author

You were right about kwonly args.

The layout of co_varnames in Python 3.8+ puts kwonly args between *args and **kwargs, so without accounting for co_kwonlyargcount the slice grabs the kwonly name before hitting z.

Pushed a fix that adds raw.co_kwonlyargcount to argcount when var=True, and added a test case with a kwonly arg (f6) to cover it.

ptal.

@RonnyPfannschmidt
Copy link
Copy Markdown
Member

man, this one is a messy piece with a history - i wonder if signature could be used in more modern code to just use the names from the signature

that being said - the remenants of py.code is one of the painful legacies that evolved back in 2005 when it was modern code growing out of the testing utilities of the pypy project - now its 20 years later and python changed quite a bit

@EternalRights
Copy link
Copy Markdown
Contributor Author

man, this one is a messy piece with a history - i wonder if signature could be used in more modern code to just use the names from the signature

that being said - the remenants of py.code is one of the painful legacies that evolved back in 2005 when it was modern code growing out of the testing utilities of the pypy project - now its 20 years later and python changed quite a bit

yeah this needs more thought. I’ll come back to it tomorrow.

co_varnames layout is: positional args, kwonly args, *args, **kwargs, locals.
The f6 test expected the wrong order. Also suppress ruff F841 false
positives on locals deliberately placed to test co_varnames slicing.
Codecov reports coverage for testing/ files since the project config
includes them. The function bodies of f5/f6 (local var assignments)
were never executed because the test only used Code.from_function(),
never called the functions. Replace raise NotImplementedError() with
actual calls to cover all diff lines.
@EternalRights
Copy link
Copy Markdown
Contributor Author

kwonly args are now accounted for and CI is all green. The inspect.signature idea makes sense for a future cleanup — the current co_varnames slicing works but it's fragile with every Python release adding new co_* fields.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Code.getargs: CO_VARARGS/CO_VARKEYWORDS bit flags treated as counts, local vars leak into argument list

2 participants