Private
Public Access
refactor(consumers): migrate 85 'from src.models import' sites to direct subsystem imports
Per post_module_taxonomy_de_cruft_20260627 Phase 2 (FR7). Each
'from src.models import X' for a moved class is rewritten to
'from src.<destination> import X':
Ticket, Track, WorkerContext, TrackState, TrackMetadata,
ThinkingSegment, EMPTY_TRACK_STATE -> src.mma
ProjectContext, ProjectMeta, ProjectOutput, ProjectFiles,
ProjectScreenshots, ProjectDiscussion, EMPTY_PROJECT_CONTEXT -> src.project
FileItem, Preset, ContextPreset, ContextFileEntry,
NamedViewPreset -> src.project_files
Tool, ToolPreset -> src.tool_presets
BiasProfile -> src.tool_bias
TextEditorConfig, ExternalEditorConfig,
EMPTY_TEXT_EDITOR_CONFIG -> src.external_editor
Persona -> src.personas
WorkspaceProfile -> src.workspace_manager
MCPServerConfig, MCPConfiguration, VectorStoreConfig,
RAGConfig, load_mcp_config -> src.mcp_client
NOT touched (kept on src.models; Phase 3 or Phase 4 will move them):
GenerateRequest, ConfirmRequest, DEFAULT_TOOL_CATEGORIES, Metadata, PROVIDERS
Migration was performed by the one-time script
scripts/tier2/artifacts/post_module_taxonomy_de_cruft_20260627/migrate_imports.py
which uses a class-to-module map and re.sub() to rewrite each
'from src.models import X' line.
Total: 85 import lines rewritten across 71 files.
Note: this commit depends on the v2 SHIPPED work
(origin/tier2/module_taxonomy_refactor_20260627) being merged into
this branch NEXT. On master (without the v2 SHIPPED commits), the
destination modules do not exist and these imports would fail.
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
"""One-time migration script: src.models import -> direct subsystem imports.
|
||||
|
||||
Per post_module_taxonomy_de_cruft_20260627 Phase 2. Updates 95 consumer
|
||||
sites that use 'from src.models import X' to use the direct subsystem
|
||||
import path. Each 'from src.models import X' is rewritten based on the
|
||||
class mapping:
|
||||
|
||||
Ticket, Track, WorkerContext, TrackState, TrackMetadata,
|
||||
ThinkingSegment, EMPTY_TRACK_STATE -> src.mma
|
||||
ProjectContext, ProjectMeta, ProjectOutput, ProjectFiles,
|
||||
ProjectScreenshots, ProjectDiscussion, EMPTY_PROJECT_CONTEXT -> src.project
|
||||
FileItem, Preset, ContextPreset, ContextFileEntry, NamedViewPreset -> src.project_files
|
||||
Tool, ToolPreset -> src.tool_presets
|
||||
BiasProfile -> src.tool_bias
|
||||
TextEditorConfig, ExternalEditorConfig,
|
||||
EMPTY_TEXT_EDITOR_CONFIG -> src.external_editor
|
||||
Persona -> src.personas
|
||||
WorkspaceProfile -> src.workspace_manager
|
||||
MCPServerConfig, MCPConfiguration, VectorStoreConfig,
|
||||
RAGConfig, load_mcp_config -> src.mcp_client
|
||||
|
||||
NOT touched (kept on src.models):
|
||||
GenerateRequest, ConfirmRequest -> Phase 4 (api_hooks.py)
|
||||
DEFAULT_TOOL_CATEGORIES -> Phase 3 (ai_client.py)
|
||||
Metadata (the legacy alias) -> kept (re-exported at module level)
|
||||
PROVIDERS -> kept (lazy __getattr__)
|
||||
|
||||
Usage:
|
||||
uv run python scripts/tier2/artifacts/post_module_taxonomy_de_cruft_20260627/migrate_imports.py
|
||||
|
||||
This is a one-time script; it does not run as part of the test suite.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
CLASS_TO_MODULE: dict[str, str] = {
|
||||
"Ticket": "mma",
|
||||
"Track": "mma",
|
||||
"WorkerContext": "mma",
|
||||
"TrackState": "mma",
|
||||
"TrackMetadata": "mma",
|
||||
"ThinkingSegment": "mma",
|
||||
"EMPTY_TRACK_STATE": "mma",
|
||||
"ProjectContext": "project",
|
||||
"ProjectMeta": "project",
|
||||
"ProjectOutput": "project",
|
||||
"ProjectFiles": "project",
|
||||
"ProjectScreenshots": "project",
|
||||
"ProjectDiscussion": "project",
|
||||
"EMPTY_PROJECT_CONTEXT": "project",
|
||||
"FileItem": "project_files",
|
||||
"Preset": "project_files",
|
||||
"ContextPreset": "project_files",
|
||||
"ContextFileEntry": "project_files",
|
||||
"NamedViewPreset": "project_files",
|
||||
"Tool": "tool_presets",
|
||||
"ToolPreset": "tool_presets",
|
||||
"BiasProfile": "tool_bias",
|
||||
"TextEditorConfig": "external_editor",
|
||||
"ExternalEditorConfig": "external_editor",
|
||||
"EMPTY_TEXT_EDITOR_CONFIG": "external_editor",
|
||||
"Persona": "personas",
|
||||
"WorkspaceProfile": "workspace_manager",
|
||||
"MCPServerConfig": "mcp_client",
|
||||
"MCPConfiguration": "mcp_client",
|
||||
"VectorStoreConfig": "mcp_client",
|
||||
"RAGConfig": "mcp_client",
|
||||
"load_mcp_config": "mcp_client",
|
||||
}
|
||||
|
||||
KEEP_ON_MODELS: set[str] = {
|
||||
"GenerateRequest",
|
||||
"ConfirmRequest",
|
||||
"DEFAULT_TOOL_CATEGORIES",
|
||||
"Metadata",
|
||||
"PROVIDERS",
|
||||
}
|
||||
|
||||
|
||||
def migrate_file(path: Path) -> tuple[int, list[str]]:
|
||||
"""Rewrite 'from src.models import X' lines in path. Returns (count, errors)."""
|
||||
try:
|
||||
content = path.read_text(encoding="utf-8")
|
||||
except (OSError, UnicodeDecodeError) as e:
|
||||
return 0, [f" {path}: cannot read: {e}"]
|
||||
original = content
|
||||
errors: list[str] = []
|
||||
|
||||
pattern = re.compile(r"^(\s*)from\s+src\.models\s+import\s+(.+?)$", re.MULTILINE)
|
||||
|
||||
def replace(m: re.Match[str]) -> str:
|
||||
indent = m.group(1)
|
||||
names_str = m.group(2)
|
||||
names = [n.strip() for n in names_str.split(",")]
|
||||
kept: list[str] = []
|
||||
moved: dict[str, list[str]] = {}
|
||||
for name in names:
|
||||
if not name:
|
||||
continue
|
||||
if name in KEEP_ON_MODELS:
|
||||
kept.append(name)
|
||||
continue
|
||||
if " as " in name:
|
||||
orig, alias = [s.strip() for s in name.split(" as ", 1)]
|
||||
if orig in KEEP_ON_MODELS:
|
||||
kept.append(name)
|
||||
continue
|
||||
if orig in CLASS_TO_MODULE:
|
||||
target_mod = CLASS_TO_MODULE[orig]
|
||||
moved.setdefault(target_mod, []).append(name)
|
||||
else:
|
||||
errors.append(f" {path}: unknown alias '{name}' (orig={orig})")
|
||||
kept.append(name)
|
||||
continue
|
||||
if name in CLASS_TO_MODULE:
|
||||
target_mod = CLASS_TO_MODULE[name]
|
||||
moved.setdefault(target_mod, []).append(name)
|
||||
else:
|
||||
errors.append(f" {path}: unknown class '{name}'")
|
||||
kept.append(name)
|
||||
if not moved and kept == names:
|
||||
return m.group(0)
|
||||
lines: list[str] = []
|
||||
for mod, names_in_mod in sorted(moved.items()):
|
||||
lines.append(f"{indent}from src.{mod} import {', '.join(names_in_mod)}")
|
||||
if kept:
|
||||
lines.append(f"{indent}from src.models import {', '.join(kept)}")
|
||||
return "\n".join(lines)
|
||||
|
||||
new_content = pattern.sub(replace, content)
|
||||
if new_content != original:
|
||||
try:
|
||||
path.write_text(new_content, encoding="utf-8", newline="")
|
||||
except OSError as e:
|
||||
return 0, [f" {path}: cannot write: {e}"]
|
||||
return len(pattern.findall(original)), []
|
||||
return 0, []
|
||||
|
||||
|
||||
def main() -> int:
|
||||
root = Path(".")
|
||||
src_files = sorted(root.glob("src/*.py")) + sorted(root.glob("tests/*.py"))
|
||||
total_changed = 0
|
||||
files_changed = 0
|
||||
all_errors: list[str] = []
|
||||
for path in src_files:
|
||||
count, errors = migrate_file(path)
|
||||
all_errors.extend(errors)
|
||||
if count > 0:
|
||||
files_changed += 1
|
||||
total_changed += count
|
||||
print(f" {path}: {count} import line(s) rewritten")
|
||||
print(f"\nTotal: {total_changed} import line(s) rewritten in {files_changed} file(s)")
|
||||
if all_errors:
|
||||
print("\nWarnings:")
|
||||
for err in all_errors:
|
||||
print(err)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user