| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
…ommon case before using expensive relative_to
|
Hello Eleanor Boyd (@eleanorjboyd) should I create a separate issue for this? |
Sorry, something went wrong.
There was a problem hiding this comment.
This PR optimizes pytest discovery payload compaction by avoiding the relatively expensive pathlib.Path.relative_to() for the common case where a node’s absolute path is nested under the discovery root, using a cheaper string-prefix check and substring instead.
Changes:
| File | Description |
|---|---|
| python_files/vscode_pytest/__init__.py | Introduces the startswith/substring optimization and threads precomputed base-path strings through the compacting functions. |
| python_files/tests/pytestadapter/test_discovery.py | Updates existing assertions to call the new signatures for compact_path() / compact_test_id(). |
Sorry, something went wrong.
| # pathlib.Path.relative_to is an expensive operation, | ||
| # for common cases where path is nested in path_base and path_base is therefore a prefix | ||
| # we skip the expensive check and just chop off the prefix to yield relative path | ||
| if path_str.startswith(path_base_str): | ||
| # +1 because pathlib.Path never ends by a trailing separator which we also need to chop off: | ||
| # path_base_str= /some/prefix | ||
| # path_str= /some/prefix/tests/mytest.py | ||
| rel_str = path_str[(len(path_base_str) + 1):] | ||
| return "." if rel_str == "" else rel_str |
There was a problem hiding this comment.
Confirmed locally on the current head. compact_path(Path('/tmp/workspace-other/test.py'), Path('/tmp/workspace'), '/tmp/workspace') returns other/test.py instead of preserving the absolute path, and a filesystem-root base corrupts /tmp/test.py to mp/test.py. The existing three compact-payload tests still pass, so please add prefix-collision and root-base regressions along with a boundary-aware fast path.
Sorry, something went wrong.
| def test_compact_discovery_payload_keeps_paths_outside_base_absolute(tmp_path): | ||
| base_path = tmp_path / "workspace" | ||
| external_file = tmp_path / "external" / "test_external.py" | ||
|
|
||
| assert vscode_pytest.compact_path(external_file, base_path) == os.fspath(external_file) | ||
| assert vscode_pytest.compact_path(external_file, base_path, str(base_path)) == os.fspath(external_file) | ||
| assert ( | ||
| vscode_pytest.compact_test_id(f"{os.fspath(external_file)}::test_external", base_path) | ||
| vscode_pytest.compact_test_id(f"{os.fspath(external_file)}::test_external", base_path, str(base_path)) | ||
| == f"{os.fspath(external_file)}::test_external" | ||
| ) |
|
Hello Eleanor Boyd (@eleanorjboyd) , I have now looked at the actual data that gets sent in the discovery payload, and I wonder if, rather than applying this band-aid, we could perhaps remove some unnecessary data completely and recalculate it once the message is received in the TestDiscoveryHandler. In particular, I think there is no need to include path and full id in each class, function and test object since this can easily be "inherited" from parent node once traversing the node tree. Also there seems to be duplicate runId field which has same value as id so it can be dropped completely. I have done a quick manual test and after this simple change, the payload for this parameterized discovery repro https://github.com/vaclavHala/pytest-discovery-perf went from ~35MB to ~5MB. In particular:
Do you see some problem with this approach or should I try to implement it? The experiment I did shows for large parameterized suites there will be significant reduction in data that has to be transferred, and I expect computation time may also be lower as we will have to go through the code that deals with computing the relative paths fewer times |
Sorry, something went wrong.
|
Hi! Sorry for the delay. So I implemented a similar idea in PR #25982 but only as prefix compression. compact_test_node still sends every node’s path, id_, and runID; it makes them relative, and expandCompactDiscoveryPayload restores them. So it was paths and IDs, but not field removal/inheritance. The proposed follow-up is sound for pytest, with two caveats:
Otherwise the proposal makes sense and feel free to implement and ill follow up with a review from there. Thanks!! |
Sorry, something went wrong.
There was a problem hiding this comment.
The optimization is worthwhile (local microbenchmark: ~0.20 µs/call versus ~7.91 µs/call for the current common path), but the fast path is not path-boundary-safe and changes relative-input semantics. Please require exact equality or a separator-delimited prefix, preserve the existing relative-path guard, and add prefix-collision/root/relative regression cases. The changed files also currently fail local Ruff on D202 and W291. Targeted compact tests pass (3/3), and the dedicated test-verification pass confirmed the missing boundary coverage.
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
I tested the latest changes to discovery where absolute paths get compacted to global prefix + relative path in each test node, and this change has made the discovery 2 to 3 times slower for us.
The problem is mainly the pathlib.Path.relative_to operation which is relatively expensive:
This PR adds optimization which first checks using simple str.startswith if the test is in some subfolder of the root, in which case creating of the relative path is done as trivial (and cheap) substring:
I pass the base paths as both str and pathlib.Path.relative_to so each operation can use whichever form of the base path is needed without having to convert to the other, i.e. str(pathlibPath), which in my testing also adds noticeable overhead.