mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-13 15:40:05 +00:00
_source_key and _resolve_cached key their lru_cache on the path and the
cwd, and call os.getcwd() on every lookup, hit or miss. On a 900-file
repo an extract makes about 215k of those calls. os.getcwd() is a
syscall that costs 30 to 40us on macOS, so building the key cost more
than the resolution the memo saved.
An absolute path resolves the same from any cwd, so it now keys with an
empty cwd. A relative path still keys on the cwd, so a chdir still misses
the cache. Path.is_absolute() applies host rules, which is what local
resolution needs: a driveless path on Windows depends on the drive.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 2228f80804)
90 lines
3.2 KiB
Python
90 lines
3.2 KiB
Python
"""_resolve_cached: Path.resolve() with a per-(path, cwd) memo (#perf).
|
|
|
|
The symbol-resolution passes call Path.resolve() once per import/export/use
|
|
fact and per node — tens of thousands of calls over a few hundred distinct
|
|
corpus paths, each walking nt._getfinalpathname / readlink. Memoizing on the
|
|
(path, cwd) pair collapses that to one syscall per distinct path; on a
|
|
364-file self-corpus a sequential extract dropped from ~27s to ~14s, with a
|
|
byte-identical graph (verified separately).
|
|
"""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
try:
|
|
from graphify.extractors.resolution import _resolve_cached, _cached_realpath
|
|
except ImportError: # pre-fix tree
|
|
_resolve_cached = None
|
|
_cached_realpath = None
|
|
|
|
needs_cache = pytest.mark.skipif(
|
|
_resolve_cached is None, reason="resolve memo not present"
|
|
)
|
|
|
|
|
|
@needs_cache
|
|
def test_matches_path_resolve(tmp_path, monkeypatch):
|
|
monkeypatch.chdir(tmp_path)
|
|
(tmp_path / "pkg").mkdir()
|
|
(tmp_path / "pkg" / "mod.py").write_text("x = 1\n", encoding="utf-8")
|
|
for arg in ("pkg/mod.py", str(tmp_path / "pkg" / "mod.py"), "missing.py"):
|
|
assert _resolve_cached(arg) == Path(arg).resolve()
|
|
assert _resolve_cached(Path(arg)) == Path(arg).resolve()
|
|
|
|
|
|
@needs_cache
|
|
def test_resolves_once_per_distinct_path(tmp_path, monkeypatch):
|
|
monkeypatch.chdir(tmp_path)
|
|
(tmp_path / "a.py").write_text("x = 1\n", encoding="utf-8")
|
|
_cached_realpath.cache_clear()
|
|
for _ in range(50):
|
|
_resolve_cached("a.py")
|
|
_resolve_cached(Path("a.py")) # str/Path collapse to one key
|
|
info = _cached_realpath.cache_info()
|
|
assert info.misses == 1 and info.hits == 99, info
|
|
|
|
|
|
@needs_cache
|
|
def test_cache_is_cwd_sensitive(tmp_path, monkeypatch):
|
|
d1, d2 = tmp_path / "one", tmp_path / "two"
|
|
for d in (d1, d2):
|
|
d.mkdir()
|
|
(d / "m.py").write_text("x = 1\n", encoding="utf-8")
|
|
monkeypatch.chdir(d1)
|
|
r1 = _resolve_cached("m.py")
|
|
monkeypatch.chdir(d2)
|
|
r2 = _resolve_cached("m.py")
|
|
assert r1 == d1.resolve() / "m.py"
|
|
assert r2 == d2.resolve() / "m.py"
|
|
|
|
|
|
@needs_cache
|
|
def test_exception_paths_match_resolve(tmp_path, monkeypatch):
|
|
"""A path that resolve() can handle returns the same as a direct call;
|
|
the helper never swallows what resolve() would raise (callers keep their
|
|
own try/except, unchanged)."""
|
|
monkeypatch.chdir(tmp_path)
|
|
weird = "a/../b/./c.py"
|
|
assert _resolve_cached(weird) == Path(weird).resolve()
|
|
|
|
|
|
@needs_cache
|
|
def test_absolute_path_lookup_skips_getcwd(tmp_path, monkeypatch):
|
|
"""An absolute path resolves the same from any cwd, so its key leaves the
|
|
cwd out and a lookup never calls os.getcwd() (a syscall per call)."""
|
|
target = tmp_path / "a.py"
|
|
expected = target.resolve()
|
|
assert _resolve_cached is not None
|
|
calls = []
|
|
real_getcwd = os.getcwd
|
|
monkeypatch.chdir(tmp_path)
|
|
assert _resolve_cached("a.py") == expected # warm the memo
|
|
monkeypatch.setattr(os, "getcwd", lambda: calls.append(1) or real_getcwd())
|
|
assert _resolve_cached(target) == expected
|
|
assert _resolve_cached(str(target)) == expected
|
|
assert calls == []
|
|
assert _resolve_cached("a.py") == expected
|
|
assert calls, "a relative path keys on the cwd, even on a memo hit"
|