Skip to content

Commit 07a5ab6

Browse files
committed
short circuit using str.startswith() in compact_path() to cover the common case before using expensive relative_to
1 parent 3434815 commit 07a5ab6

2 files changed

Lines changed: 32 additions & 11 deletions

File tree

python_files/tests/pytestadapter/test_discovery.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ def test_compact_discovery_payload_keeps_paths_outside_base_absolute(tmp_path):
8282
base_path = tmp_path / "workspace"
8383
external_file = tmp_path / "external" / "test_external.py"
8484

85-
assert vscode_pytest.compact_path(external_file, base_path) == os.fspath(external_file)
85+
assert vscode_pytest.compact_path(external_file, base_path, str(base_path)) == os.fspath(external_file)
8686
assert (
87-
vscode_pytest.compact_test_id(f"{os.fspath(external_file)}::test_external", base_path)
87+
vscode_pytest.compact_test_id(f"{os.fspath(external_file)}::test_external", base_path, str(base_path))
8888
== f"{os.fspath(external_file)}::test_external"
8989
)
9090

python_files/vscode_pytest/__init__.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,9 +1005,26 @@ def cached_fsdecode(path: pathlib.Path) -> str:
10051005
return _path_to_str_cache[path]
10061006

10071007

1008-
def compact_path(path: pathlib.Path | str, path_base: pathlib.Path) -> str:
1008+
def compact_path(path: pathlib.Path | str, path_base: pathlib.Path, path_base_str: str) -> str:
10091009
"""Return path relative to path_base when possible without resolving symlinks."""
1010-
current_path = pathlib.Path(path)
1010+
1011+
if isinstance(path, str):
1012+
path_str = path
1013+
current_path = pathlib.Path(path)
1014+
else:
1015+
path_str = str(path)
1016+
current_path = path
1017+
1018+
# pathlib.Path.relative_to is an expensive operation,
1019+
# for common cases where path is nested in path_base and path_base is therefore a prefix
1020+
# we skip the expensive check and just chop off the prefix to yield relative path
1021+
if path_str.startswith(path_base_str):
1022+
# +1 because pathlib.Path never ends by a trailing separator which we also need to chop off:
1023+
# path_base_str= /some/prefix
1024+
# path_str= /some/prefix/tests/mytest.py
1025+
rel_str = path_str[(len(path_base_str) + 1):]
1026+
return "." if rel_str == "" else rel_str
1027+
10111028
if not current_path.is_absolute():
10121029
return os.fspath(current_path)
10131030

@@ -1020,17 +1037,19 @@ def compact_path(path: pathlib.Path | str, path_base: pathlib.Path) -> str:
10201037
return relative_path_str or "."
10211038

10221039

1023-
def compact_test_id(test_id: str, id_base: pathlib.Path) -> str:
1040+
def compact_test_id(test_id: str, id_base: pathlib.Path, id_base_str: str) -> str:
10241041
"""Compact the path prefix in a pytest node id while preserving pytest selectors."""
10251042
test_path, separator, selector = test_id.partition("::")
1026-
compact_test_path = compact_path(test_path, id_base)
1043+
compact_test_path = compact_path(test_path, id_base, id_base_str)
10271044
return f"{compact_test_path}{separator}{selector}" if separator else compact_test_path
10281045

10291046

10301047
def compact_test_node(
10311048
test_node: TestNode | TestItem | None,
10321049
path_base: pathlib.Path,
10331050
id_base: pathlib.Path,
1051+
path_base_str: str,
1052+
id_base_str: str,
10341053
) -> dict[str, Any] | None:
10351054
"""Create a compact copy of a discovery node for JSON serialization."""
10361055
if test_node is None:
@@ -1039,13 +1058,13 @@ def compact_test_node(
10391058
compact_node: dict[str, Any] = {}
10401059
for key, value in test_node.items():
10411060
if key == "path":
1042-
compact_node[key] = compact_path(cast("pathlib.Path", value), path_base)
1061+
compact_node[key] = compact_path(cast("pathlib.Path", value), path_base, path_base_str)
10431062
elif key in {"id_", "runID"}:
1044-
compact_node[key] = compact_test_id(cast("str", value), id_base)
1063+
compact_node[key] = compact_test_id(cast("str", value), id_base, id_base_str)
10451064
elif key == "children":
10461065
children_iter = value.values() if isinstance(value, Children) else value
10471066
compact_node[key] = [
1048-
compact_test_node(child, path_base, id_base)
1067+
compact_test_node(child, path_base, id_base, path_base_str, id_base_str)
10491068
for child in cast("list[TestNode | TestItem | None]", children_iter)
10501069
]
10511070
else:
@@ -1057,12 +1076,14 @@ def create_compact_discovery_payload(
10571076
cwd: str, session_node: TestNode
10581077
) -> CompactDiscoveryPayloadDict:
10591078
"""Create the compact wire payload after discovery has fully resolved the tree."""
1060-
path_base = pathlib.Path(session_node["path"])
1079+
path_base_str = str(session_node["path"])
1080+
path_base = pathlib.Path(path_base_str)
10611081
id_base = path_base
1082+
id_base_str = path_base_str
10621083
return CompactDiscoveryPayloadDict(
10631084
cwd=cwd,
10641085
status="success" if not ERRORS else "error",
1065-
tests=cast("TestNode", compact_test_node(session_node, path_base, id_base)),
1086+
tests=cast("TestNode", compact_test_node(session_node, path_base, id_base, path_base_str, id_base_str)),
10661087
error=ERRORS,
10671088
payloadVersion=2,
10681089
pathBase=os.fspath(path_base),

0 commit comments

Comments
 (0)