Skip to content

perf(collection): collect a parent level once for explicit file arguments - #15228

Open
ShahabAhmed01 wants to merge 1 commit into
pytest-dev:mainfrom
ShahabAhmed01:contrib/issue-15221-collect-parent-cache-b6ebdc41
Open

ShahabAhmed01 wants to merge 1 commit into
pytest-dev:mainfrom
ShahabAhmed01:contrib/issue-15221-collect-parent-cache-b6ebdc41

Conversation

@ShahabAhmed01

@ShahabAhmed01 ShahabAhmed01 commented Oct 20, 2026 •

Copy link
Copy Markdown

Fixes #15221

Summary

Explicit file arguments used to re-collect their parent level for every file argument: a file at the end of a path re-collects its level fresh (files given directly multiple times on the command line are not deduplicated), so pytest a/test_1.py a/test_2.py … a/test_N.py scanned the directory that holds the arguments once per argument and pruned all but one of the collected siblings each time.

This collection now reuses the parent level's collection report for file arguments whose path occurs in exactly one initial argument, so the level is scanned once and the remaining arguments reuse it. Node ids and their order are unchanged.

The case where the same file path is given more than once keeps its previous behaviour: with --keepduplicates, or when the same file is selected again with different :: parts, each occurrence is still collected fresh per occurrence, so a directly given file is never deduplicated. A cached failing report for such a level is also still re-collected per file argument, so collection failures stay reported as before.

Implementation

  • Session.perform_collect now computes, once per collection, the set of file-argument paths that occur in more than one initial argument.
  • Session.collect keeps a fresh collection (handle_dupes=False) for those paths and reuses the level's cached report for file arguments collected exactly once.
  • The path_cache on Session (used for the top-level arguments) and the per-level _collection_cache already cover the rest of the walk, which is why directory arguments were unaffected by this slowdown.

Testing

  • testing/test_collection.py gains four tests:

    • test_file_arguments_scan_parent_level_once asserts the parent level is scanned once for several explicit file arguments and that the node ids and order are unchanged.
    • test_keepduplicates_runs_the_same_file_twice asserts the same explicitly given file still collects twice with --keepduplicates.
    • test_same_file_with_different_parts_is_collected_twice asserts items selected with :: parts from the same file are still collected per argument.
    • test_file_arguments_recollect_after_failed_parent_collection asserts a file argument whose parent level failed is re-collected per argument, matching the previous failure reporting.
  • With the report's minimal reproduction (100 file arguments of a 150-entry directory, --collect-only, one round each side, Python 3.14.8 / Linux):

    run directory argument 100 file arguments
    before 0.64s 2.31s / 2.34s
    after 0.39–0.41s 0.39–0.41s

    The collected items are identical before and after (the test asserts exact node id lists; the issue's own runs check 9,608 and 38,514 items).

  • Full results in this environment (Python 3.14.8, Linux): tox -e py314 is not run; the relevant test files pass in full:

    pytest testing/test_collection.py testing/test_main.py  -> 143 passed, 2 skipped, 1 xfailed
    pytest testing/acceptance_test.py                       -> 103 passed, 2 skipped, 1 xfailed
    pytest testing/test_pytester.py testing/test_collect_imported_tests.py testing/test_nodes.py -> 68 passed, 1 skipped, 1 xfailed
    pytest testing/python/                                  -> 733 passed, 2 skipped, 3 xfailed
    

    (pre-commit.ci is green on this head; re-verified locally after opening: ruff check and ruff format --check clean on the changed files with the pre-commit-pinned ruff 0.17.1, and mypy clean on src/_pytest/main.py + testing/test_collection.py under the repo's own config — mypy 2.4.0, the pre-commit pin.)

Scope

The change affects only the collection walk of explicit file arguments in Session.collect and preserves the deduplication guarantees of files given directly multiple times on the command line. Directory arguments, ::-part selections of now-reused levels, --pyargs, --keepduplicates, and the collection cache's Directory reconciliation from #14635 are untouched.

Shahab Ahmed added to AUTHORS; changelog entry changelog/15221.improvement.rst included.

AI assistance disclosure

Per CONTRIBUTING.rst's "AI/LLM-Assisted Contributions Policy": this change was
prepared with AI-assisted tooling as part of the author's contribution workflow —
the duplication sweep over existing issues/PRs, the reproduction and measurements,
the implementation iterations, and the test runs above. The author takes
responsibility for the diff, has reviewed it, and will engage with review feedback
from this account (never an automated reply loop). The commit carries no
Co-authored-by trailer yet; if reviewers prefer one, I'll add it in a follow-up
commit rather than force-pushing this head.

…ents

Explicit file arguments used to re-collect their parent level for every
file argument: file arguments re-collect their level fresh, so N file
arguments of one directory scanned that directory N times (and pruned all
but one of the collected siblings each time), which scales with the
number of file arguments times the number of their parent's entries.

File argument paths that occur in more than one initial argument
(--keepduplicates, or the same file selected with different :: parts)
keep getting a fresh collection per occurrence, so directly given files
are still not deduplicated. File arguments collected exactly once reuse
the parent level's collection report instead, leaving node ids and their
order unchanged. A cached failing report for such a level is still
re-collected per file argument, as before, to keep collection failures
reported as they were.

Measured with the report's minimal reproduction (100 file arguments of a
150-entry directory, --collect-only, no caching plugin): 2.3-2.3s
before, 0.4-0.4s after, equal to the directory-argument run. Collected
items were equal before and after.

Fixes pytest-dev#15221
Copilot AI balanced review requested due to automatic review settings October 20, 2026 16:12
@pyba-chronographer pyba-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Oct 20, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

This branch has not been deployed

No deployments
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

2 participants