From fa59a08760ddee5393a9bd75d598e0e7daa67e51 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 30 May 2026 09:27:09 -0300 Subject: [PATCH] Use inspect module in Code class instead of direct co_* attribute access As a follow-up to #14493, replace direct co_* accesses in Code.path and Code.getargs with inspect.getfile and inspect.getfullargspec respectively, delegating argument-decoding logic to well-tested stdlib APIs rather than re-implementing it with raw bitmask arithmetic. Code.firstlineno and Code.name still access co_firstlineno and co_name directly: the inspect module has no equivalent for these that does not require reading the source file from disk, which would regress correctness for compile()'d code and performance for every traceback formatting path. Co-Authored-By: Claude Sonnet 4.6 --- src/_pytest/_code/code.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 4fcce0427ef..268417b6134 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -8,8 +8,6 @@ from collections.abc import Sequence import dataclasses import inspect -from inspect import CO_VARARGS -from inspect import CO_VARKEYWORDS from io import StringIO import os from pathlib import Path @@ -87,10 +85,11 @@ def name(self) -> str: def path(self) -> Path | str: """Return a path object pointing to source code, or an ``str`` in case of ``OSError`` / non-existing file.""" - if not self.raw.co_filename: + filename = inspect.getfile(self.raw) + if not filename: return "" try: - p = absolutepath(self.raw.co_filename) + p = absolutepath(filename) # maybe don't try this checking if not p.exists(): raise OSError("path check failed.") @@ -98,7 +97,7 @@ def path(self) -> Path | str: except OSError: # XXX maybe try harder like the weird logic # in the standard lib [linecache.updatecache] does? - return self.raw.co_filename + return filename @property def fullsource(self) -> Source | None: @@ -117,14 +116,17 @@ def getargs(self, var: bool = False) -> tuple[str, ...]: If 'var' is set True also return the names of the variable and keyword arguments when present. """ - # Handy shortcut for getting args. - raw = self.raw - argcount = raw.co_argcount - if var: - argcount += raw.co_kwonlyargcount - argcount += bool(raw.co_flags & CO_VARARGS) - argcount += bool(raw.co_flags & CO_VARKEYWORDS) - return raw.co_varnames[:argcount] + # inspect.getargs merges positional and kwonly into a single list; + # co_argcount is needed to exclude kwonly when var=False. + args, varargs, varkw = inspect.getargs(self.raw) + if not var: + return tuple(args[: self.raw.co_argcount]) + result = list(args) + if varargs is not None: + result.append(varargs) + if varkw is not None: + result.append(varkw) + return tuple(result) class Frame: