optimize getting of line number in pytest discovery - #26017
Conversation
…xpensive call before falling back to location query
There was a problem hiding this comment.
Pull request overview
This PR speeds up pytest test discovery by avoiding the expensive pytest.Item.location path computation when only the line number is needed, using pytest’s internal Code.from_function(...).firstlineno as a fast-path with a fallback to the public API.
Changes:
- Use
_pytest._code.code.Code.from_function(...).firstlinenoto obtain the test item’s line number without computing the path. - Add a fallback to
test_case.location[1]when the internal API cannot be used.
Show a summary per file
| File | Description |
|---|---|
| python_files/vscode_pytest/init.py | Optimizes line-number retrieval during pytest discovery by using an internal pytest API fast-path with fallback. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 2
- Review effort level: Low
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
eleanorjboyd
left a comment
There was a problem hiding this comment.
The common-path optimization works for ordinary tests (4 targeted discovery cases pass; Ruff passes), but decorated tests can now navigate to the wrapper rather than the test definition. Please also add focused coverage proving a valid obj avoids location, plus deterministic fallback coverage for unavailable/failing internal APIs and None line numbers. The dedicated test-verification pass found no changed tests and confirmed these gaps.
| obj = getattr(test_case, "obj", None) | ||
| if obj is not None: | ||
| try: | ||
| lineno0 = Code.from_function(obj).firstlineno |
There was a problem hiding this comment.
pytest unwraps decorated functions before computing their source line, but this direct call inspects the wrapper. I reproduced this with functools.wraps: Code.from_function(obj).firstlineno was 3 while pytest's getfslineno(obj)[1] was 7, and create_test_node() emitted line 5 instead of the fallback/location line 8. Please unwrap using pytest-compatible behavior before Code.from_function, and add an exact-line regression for a decorated test.
The discovery for pytest is still quite a bit slower than running plain
-m pytest --collect-onlyso I investigated a bit more and the next most significant (at least for our test suite) slowdown is caused by thepytest.Item.locationcall used to get line number of the test:This is because the
.locationreturns tuple of path and line number, of which only the line number is used by the discovery, but computing the path is rather expensive. This PR proposes an optimization which uses internal pytest API in exactly the same way as thelocationfunction itself, but without also computing the path.In particular, the
locationcall hierarchy is:This PR calls
Code.from_functiondirectly and only gets.firstlinenofrom it.Both profiles are captured with this PR cherry-picked to main branch prior to the discovery result compaction introduced in 5389068 which causes significant performance hit that overshadows the optimization done here, for that I created a separate PR #26016 with proposed fix.
This change makes the discovery about 20% faster for our suite, now almost on par with the raw
pytestcall