ITADN

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

#14492ClosedEternalRights 创建于 2026-05-17
E
EternalRightscommented
Was debugging a traceback and noticed the argument list had extra entries that shouldn't be there. Traced it to `Code.getargs()` in `src/_pytest/_code/code.py` lines 123-125: ```python if var: argcount += raw.co_flags & CO_VARARGS argcount += raw.co_flags & CO_VARKEYWORDS ``` `CO_VARARGS` is 4 and `CO_VARKEYWORDS` is 8. The bitwise AND against `co_flags` gives 4 or 0, 8 or 0 -- but the intent is to add 1 for each flag, since `co_varnames` has one slot each for `*args` and `**kwargs`. So when a function has both `*args` and `**kwargs`, `argcount` gets bumped by 12 instead of 2. The `co_varnames[:argcount]` slice overshoots into local variables: ```python def f(a, b, *args, **kwargs): x = 1 y = 2 raise ValueError('test') # Code(f).getargs(var=True) returns: # ('a', 'b', 'args', 'kwargs', 'x', 'y') <- x, y are NOT arguments # should be: # ('a', 'b', 'args', 'kwargs') ``` This feeds into `repr_args()` which shows function arguments in tracebacks, so local variables end up displayed as if they were parameters. Bit misleading. The fix is just wrapping the flags in `bool()`: ```python if var: argcount += bool(raw.co_flags & CO_VARARGS) argcount += bool(raw.co_flags & CO_VARKEYWORDS) ``` Been there since the initial commit in 2015. The existing tests don't catch it because none of the test functions used there have both `*args`/`**kwargs` and local variables at the same time.
关闭于 2026-05-30 1 条评论