quaint-telephone-89068
05/05/2026, 8:53 PM❯ rm -f src/python/pants/util/strutil_test.py
❯ ./pants --changed-since=HEAD test
...
14:51:39.93 [INFO] Completed: Run Pytest - src/python/pants/util/docutil_test.py:tests succeeded.
✓ src/python/pants/util/collections_test.py:tests succeeded.
✓ src/python/pants/util/contextutil_test.py:tests succeeded.
✓ src/python/pants/util/dirutil_test.py:tests succeeded.
✓ src/python/pants/util/docutil_test.py:tests succeeded.
✓ src/python/pants/util/enums_test.py:tests succeeded.
✓ src/python/pants/util/eval_test.py:tests succeeded.
✓ src/python/pants/util/filtering_test.py:tests succeeded.
✓ src/python/pants/util/frozendict_test.py:tests succeeded.
✓ src/python/pants/util/memo_test.py:tests succeeded.
✓ src/python/pants/util/meta_test.py:tests succeeded.
✓ src/python/pants/util/ordered_set_test.py:tests succeeded.
✓ src/python/pants/util/osutil_test.py:tests succeeded.
✓ src/python/pants/util/socket_test.py:tests succeeded.
This is very intentional. For deleted files, we look at target generators rather than generated targets:
pants/src/python/pants/engine/internals/graph.py
Lines 472 to 486 in</pantsbuild/pants/commit/84310d680eb7b8c14b56430efb7d3154cbd56b4a|84310d6>
| live_files = FrozenOrderedSet(sources_paths.files) |
| --------------------------------------------------------------------------------------------- |
| deleted_files = FrozenOrderedSet(s for s in owners_request.sources if s not in live_files) |
| live_dirs = FrozenOrderedSet(os.path.dirname(s) for s in live_files) |
| deleted_dirs = FrozenOrderedSet(os.path.dirname(s) for s in deleted_files) |
| |
| # Walk up the buildroot looking for targets that would conceivably claim changed sources. |
| # For live files, we use Targets, which causes more precise, often file-level, targets |
| # to be created. For deleted files we use UnexpandedTargets, which have the original declared |
| # glob. |
| live_candidate_specs = tuple(AscendantAddresses(directory=d) for d in live_dirs) |
| deleted_candidate_specs = tuple(AscendantAddresses(directory=d) for d in deleted_dirs) |
| live_candidate_tgts, deleted_candidate_tgts = await MultiGet( |
| Get(Targets, AddressSpecs(live_candidate_specs)), |
| Get(UnexpandedTargets, AddressSpecs(deleted_candidate_specs)), |
| ) |
Why do this? The other generated targets did not change! When you change the contents of strutil_test.py, we don't claim that dirutil_test.py was also changed. So why would deleting strutil_test.py claim that dirutil_test.py was changed?
I think this relates to #12953. The target generator python_tests doesn't actually "own" the files in its sources field! It only uses them to trigger generation of python_test targets, which are the actual owners.
pantsbuild/pantsquaint-telephone-89068
05/05/2026, 8:53 PM