48c9649951
Phase 5C of startup_speedup_20260606 track.
src/markdown_helper.py imported src.markdown_table at module level:
from src.markdown_table import parse_tables, render_table
Both parse_tables and render_table are only used inside
MarkdownRenderer.render(). Removed the top-level import; the
MarkdownRenderer.render() method now does:
markdown_table = _require_warmed('src.markdown_table')
parse_tables = markdown_table.parse_tables
render_table = markdown_table.render_table
at the top of its body, before any other logic.
TESTS:
- tests/test_markdown_helper_no_top_level_table.py: 3/3 PASS (all RED -> GREEN)
- tests/test_markdown_table*.py (5 files) + test_markdown_helper_bullets.py +
test_markdown_render_robust.py: 24/24 PASS (no breakage)
EFFECTIVENESS: import src.markdown_helper no longer triggers src.markdown_table
(~250ms). For renderers that never hit a GFM table, the import is never
paid. For renderers that do, the warmup pre-loads it on _io_pool and the
render() lookup is O(1).
NEXT: Phase 5D - bulk refactor of src/gui_2.py feature-gated imports via
scripts/audit_gui2_imports.py.
92 lines
3.1 KiB
Python
92 lines
3.1 KiB
Python
"""Tests that src/markdown_helper.py has NO top-level src.markdown_table import.
|
|
|
|
Per spec.md:2.2 Layer 1, the main thread's import chain must not include
|
|
heavy feature-gated modules. src.markdown_table (~250ms) is warmed on
|
|
AppController's _io_pool and accessed via _require_warmed at use sites.
|
|
The GFM table-rendering branch is optional (default markdown rendering
|
|
falls through to imgui_md).
|
|
|
|
src/markdown_helper.py uses src.markdown_table only inside the
|
|
MarkdownRenderer.render() method (parse_tables at the top, render_table
|
|
in a nested code block). The lookup is deferred to the render() call.
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
import textwrap
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
|
|
def _run_in_subprocess(snippet: str) -> subprocess.CompletedProcess:
|
|
script = textwrap.dedent(snippet)
|
|
return subprocess.run(
|
|
[sys.executable, "-c", script],
|
|
capture_output=True,
|
|
text=True,
|
|
cwd=str(ROOT),
|
|
timeout=30,
|
|
)
|
|
|
|
|
|
def test_markdown_helper_does_not_import_markdown_table_at_module_level() -> None:
|
|
res = _run_in_subprocess("""
|
|
import sys
|
|
import src.markdown_helper
|
|
print('src.markdown_table' in sys.modules)
|
|
""")
|
|
assert res.returncode == 0, f"stderr: {res.stderr}"
|
|
assert res.stdout.strip() == "False", f"markdown_helper triggered src.markdown_table import: {res.stdout}"
|
|
|
|
|
|
def test_markdown_helper_render_method_lazy_lookups() -> None:
|
|
"""The render() method should resolve parse_tables and render_table
|
|
via _require_warmed at call time, not at module load."""
|
|
res = _run_in_subprocess("""
|
|
import ast
|
|
from pathlib import Path
|
|
root = Path('.').resolve()
|
|
helper_path = root / 'src' / 'markdown_helper.py'
|
|
tree = ast.parse(helper_path.read_text(encoding='utf-8'))
|
|
# Look for any top-level ImportFrom that imports parse_tables or render_table
|
|
has_top_level_table_import = False
|
|
for node in tree.body:
|
|
if isinstance(node, ast.ImportFrom):
|
|
for alias in node.names:
|
|
if alias.name in ('parse_tables', 'render_table'):
|
|
has_top_level_table_import = True
|
|
print('HAS_TOP_LEVEL_TABLE_IMPORT:', has_top_level_table_import)
|
|
""")
|
|
assert res.returncode == 0, f"stderr: {res.stderr}"
|
|
assert "HAS_TOP_LEVEL_TABLE_IMPORT: False" in res.stdout
|
|
|
|
|
|
def test_audit_main_thread_imports_sees_no_new_violation_from_markdown_helper() -> None:
|
|
"""Run the static audit and check that src/markdown_helper.py contributes no
|
|
new markdown_table violations.
|
|
"""
|
|
res = _run_in_subprocess("""
|
|
import ast
|
|
from pathlib import Path
|
|
root = Path('.').resolve()
|
|
helper_path = root / 'src' / 'markdown_helper.py'
|
|
tree = ast.parse(helper_path.read_text(encoding='utf-8'))
|
|
heavy = ['src.markdown_table', 'markdown_table']
|
|
for node in tree.body:
|
|
if isinstance(node, ast.Import):
|
|
for alias in node.names:
|
|
for h in heavy:
|
|
if alias.name == h or alias.name.startswith(h + '.'):
|
|
print('VIOLATION:', alias.name)
|
|
elif isinstance(node, ast.ImportFrom):
|
|
if node.module:
|
|
for h in heavy:
|
|
if node.module == h or node.module.startswith(h + '.'):
|
|
print('VIOLATION:', node.module)
|
|
print('OK')
|
|
""")
|
|
assert res.returncode == 0, f"stderr: {res.stderr}"
|
|
assert "OK" in res.stdout
|
|
assert "VIOLATION" not in res.stdout
|