mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-22 20:01:23 +00:00
`extractors/markdown.py` read the scan root of the extraction in flight with a
function-local `import graphify.extract` + `getattr(_extract,
"_XAML_ACTIVE_EXTRACT_ROOT", None)`. That violates MIGRATION.md invariant #4
("never import from graphify.extract inside this package; direction is
extract.py -> extractors/"), and the `getattr(..., None)` meant a rename of the
module global would silently disable Markdown wikilink (Obsidian vault)
resolution rather than fail loudly.
Move the ambient scan root onto `extractors/base.py` — where the extractors
legitimately live — as `set_active_scan_root()` / `active_scan_root()`.
`_safe_extract_with_xaml_root` now publishes the root there (still saving and
restoring for nested extraction), the two XAML readers and the Markdown vault
lookup read it through the accessor, and `markdown.py` imports it in the correct
direction. The `_XAML_ACTIVE_EXTRACT_ROOT` module global in extract.py is gone;
nothing else referenced it.
Behavior is unchanged — the same process-global root is set around each
extraction and read by the same sites. Tests assert the extractor no longer
imports graphify.extract (fails on the old code), that the set/get/restore
contract nests correctly, and that markdown's accessor IS the base accessor.
The markdown, XAML/dotnet, and language suites stay green; the only failures on
this machine are the pre-existing v8 baseline set (c-include determinism,
wikilink-unicode, two watch cwd-deleted cases), which fail identically on clean
v8.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJbLfztSxm2tH5cJwBbe9q
(cherry picked from commit 86721f765d)
74 lines
2.9 KiB
Python
74 lines
2.9 KiB
Python
"""The in-flight scan root is shared through extractors.base, not by reaching
|
|
back into graphify.extract — MIGRATION.md invariant #4 (#3666)."""
|
|
from __future__ import annotations
|
|
|
|
import ast
|
|
from pathlib import Path
|
|
|
|
from graphify.extractors import base as base_mod
|
|
from graphify.extractors import markdown as markdown_mod
|
|
|
|
|
|
def _imports_graphify_extract(module) -> bool:
|
|
"""True if the module's source imports graphify.extract anywhere — a
|
|
top-level or a function-local `import graphify.extract` / `from
|
|
graphify.extract import ...`."""
|
|
source = Path(module.__file__).read_text(encoding="utf-8")
|
|
for node in ast.walk(ast.parse(source)):
|
|
if isinstance(node, ast.Import):
|
|
if any(a.name == "graphify.extract" or a.name.startswith("graphify.extract.")
|
|
for a in node.names):
|
|
return True
|
|
elif isinstance(node, ast.ImportFrom):
|
|
if node.module == "graphify.extract" or (node.module or "").startswith("graphify.extract."):
|
|
return True
|
|
return False
|
|
|
|
|
|
def test_markdown_extractor_does_not_import_graphify_extract():
|
|
"""The Markdown extractor read the in-flight scan root via a function-local
|
|
`import graphify.extract` + getattr, a package->parent dependency that
|
|
violated invariant #4 and degraded silently on a rename (#3666). It must now
|
|
reach the shared context through extractors.base instead."""
|
|
assert not _imports_graphify_extract(markdown_mod), (
|
|
"extractors/markdown.py must not import graphify.extract — read the scan "
|
|
"root via extractors.base.active_scan_root() (MIGRATION.md invariant #4)"
|
|
)
|
|
|
|
|
|
def test_active_scan_root_defaults_to_none():
|
|
# A fresh process (no extraction in flight) has no active root.
|
|
assert base_mod.active_scan_root() is None
|
|
|
|
|
|
def test_active_scan_root_set_get_restore_nested():
|
|
"""set_active_scan_root returns the previous value so nested extraction can
|
|
restore it; active_scan_root reflects the current top of that stack."""
|
|
assert base_mod.active_scan_root() is None
|
|
outer = Path("/repo").resolve()
|
|
inner = Path("/repo/vendored").resolve()
|
|
|
|
prev_outer = base_mod.set_active_scan_root(outer)
|
|
try:
|
|
assert prev_outer is None
|
|
assert base_mod.active_scan_root() == outer
|
|
|
|
prev_inner = base_mod.set_active_scan_root(inner)
|
|
try:
|
|
assert prev_inner == outer
|
|
assert base_mod.active_scan_root() == inner
|
|
finally:
|
|
base_mod.set_active_scan_root(prev_inner)
|
|
|
|
assert base_mod.active_scan_root() == outer
|
|
finally:
|
|
base_mod.set_active_scan_root(prev_outer)
|
|
|
|
assert base_mod.active_scan_root() is None
|
|
|
|
|
|
def test_markdown_active_scan_root_alias_is_base_accessor():
|
|
"""The Markdown extractor's _active_scan_root is the base accessor, so the
|
|
scan root extract() publishes is exactly what link resolution reads."""
|
|
assert markdown_mod._active_scan_root is base_mod.active_scan_root
|