mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-21 02:05:45 +00:00
perf(sandbox): speed up should_ignore_name in glob/grep walks (#3657)
should_ignore_name runs once per directory entry during glob/grep tree walks and looped over ~57 IGNORE_PATTERNS calling fnmatch per pattern. Precompute the literal names into a frozenset (O(1)) and the few glob patterns into one combined compiled regex via fnmatch.translate; per name it's now one normcase + a set lookup + at most one regex match. os.path.normcase preserves fnmatch's platform case behavior, so results are identical (covered by an equivalence test against the old loop). Fixes #3655.
This commit is contained in:
@@ -67,11 +67,22 @@ class GrepMatch:
|
||||
line: str
|
||||
|
||||
|
||||
# ``should_ignore_name`` runs once per directory entry during glob/grep tree
|
||||
# walks, so we avoid ~50 ``fnmatch`` calls per name. Most ignore patterns are
|
||||
# literal names (O(1) set lookup after normcase); the few glob patterns are
|
||||
# pre-translated into a single combined regex. ``os.path.normcase`` keeps the
|
||||
# same case behavior ``fnmatch`` applies (case-sensitive on POSIX, folded on
|
||||
# Windows).
|
||||
_EXACT_IGNORE_NAMES = frozenset(os.path.normcase(p) for p in IGNORE_PATTERNS if not any(c in p for c in "*?["))
|
||||
_GLOB_IGNORE_PATTERNS = [p for p in IGNORE_PATTERNS if any(c in p for c in "*?[")]
|
||||
_GLOB_IGNORE_RE = re.compile("|".join(fnmatch.translate(os.path.normcase(p)) for p in _GLOB_IGNORE_PATTERNS)) if _GLOB_IGNORE_PATTERNS else None
|
||||
|
||||
|
||||
def should_ignore_name(name: str) -> bool:
|
||||
for pattern in IGNORE_PATTERNS:
|
||||
if fnmatch.fnmatch(name, pattern):
|
||||
return True
|
||||
return False
|
||||
normalized = os.path.normcase(name)
|
||||
if normalized in _EXACT_IGNORE_NAMES:
|
||||
return True
|
||||
return _GLOB_IGNORE_RE is not None and _GLOB_IGNORE_RE.match(normalized) is not None
|
||||
|
||||
|
||||
def should_ignore_path(path: str) -> bool:
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
"""should_ignore_name must stay behavior-identical to the original per-pattern
|
||||
fnmatch loop while doing O(1) set lookup + one combined glob regex instead of
|
||||
~50 fnmatch calls per directory entry.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import fnmatch
|
||||
|
||||
from deerflow.sandbox.search import IGNORE_PATTERNS, should_ignore_name, should_ignore_path
|
||||
|
||||
|
||||
def _reference(name: str) -> bool:
|
||||
"""Original implementation, kept here as the equivalence oracle."""
|
||||
return any(fnmatch.fnmatch(name, pattern) for pattern in IGNORE_PATTERNS)
|
||||
|
||||
|
||||
_SAMPLES = [
|
||||
# exact-name ignores
|
||||
".git",
|
||||
"node_modules",
|
||||
"__pycache__",
|
||||
".venv",
|
||||
"env",
|
||||
"logs",
|
||||
"coverage",
|
||||
".pytest_cache",
|
||||
".DS_Store",
|
||||
"Thumbs.db",
|
||||
"desktop.ini",
|
||||
# glob ignores
|
||||
"thing.egg-info",
|
||||
"x.swp",
|
||||
"y.swo",
|
||||
"z.log",
|
||||
"a.tmp",
|
||||
"b.temp",
|
||||
"c.bak",
|
||||
"d.cache",
|
||||
"core~",
|
||||
"shortcut.lnk",
|
||||
# kept (must NOT be ignored)
|
||||
"foo.py",
|
||||
"README.md",
|
||||
"src",
|
||||
"myenv",
|
||||
"node_modules_x",
|
||||
"x.git",
|
||||
"log",
|
||||
"main.c",
|
||||
]
|
||||
|
||||
|
||||
def test_matches_reference_for_all_samples():
|
||||
for name in _SAMPLES:
|
||||
assert should_ignore_name(name) == _reference(name), name
|
||||
|
||||
|
||||
def test_known_ignored_names():
|
||||
for name in [".git", "node_modules", "__pycache__", "x.swp", "z.log", "core~", "thing.egg-info"]:
|
||||
assert should_ignore_name(name) is True
|
||||
|
||||
|
||||
def test_known_kept_names():
|
||||
for name in ["foo.py", "README.md", "src", "myenv", "node_modules_x", "x.git"]:
|
||||
assert should_ignore_name(name) is False
|
||||
|
||||
|
||||
def test_should_ignore_path_segments():
|
||||
assert should_ignore_path("a/node_modules/b") is True
|
||||
assert should_ignore_path("proj/.git/config") is True
|
||||
assert should_ignore_path("a/b/c.py") is False
|
||||
Reference in New Issue
Block a user