Private
Public Access
refactor(tool_bias): merge BiasProfile from models.py into tool_bias.py
Per the 4-criteria decision rule: BiasProfile fails C1 (only used by
tool_presets + tool_bias), fails C2 (no state machine), fails C3 (no
dedicated test file), borderline C4. MERGE into the existing
src/tool_bias.py which already has ToolBiasEngine.
This commit:
1. Adds BiasProfile class definition to src/tool_bias.py at the top
(after the dataclass + typing imports).
2. Removes BiasProfile from src/models.py.
3. Adds lazy re-export via the existing __getattr__ in src/models.py
(EAGER would deadlock: tool_presets needs BiasProfile + tool_bias
needs Tool/ToolPreset, and both want models re-exports).
4. Updates src/tool_presets.py to use the local-import pattern for
BiasProfile (in load_all_bias_profiles) + adds
'from __future__ import annotations' so the 'BiasProfile' type
annotation is a string. This breaks the cycle.
5. Updates src/tool_bias.py to import Tool + ToolPreset from
src.tool_presets directly (no longer through models) + adds
'from __future__ import annotations'.
Verification: VC8 (BiasProfile)
from src.tool_bias import BiasProfile # OK
from src.tool_presets import Tool, ToolPreset # OK
from src.models import Tool, ToolPreset, BiasProfile # OK (lazy)
Tool is Tool returns True
ToolPreset is ToolPreset returns True
BiasProfile is BiasProfile returns True
Tests verified (10/10 PASS):
tests/test_tool_preset_manager.py (4 tests)
tests/test_bias_models.py (3 tests)
tests/test_tool_bias.py (3 tests)
Cycle resolution:
models -> tool_presets (lazy via __getattr__)
tool_presets -> tool_bias (local import in function body, only at call time)
tool_bias -> tool_presets (eager; OK because tool_presets is fully
loaded by the time tool_bias's class
definitions need Tool/ToolPreset)
The eager load of tool_bias from tool_presets is what made the
'from __future__ import annotations' necessary in both files (for
Tool/ToolPreset string annotations in tool_bias method signatures).
This commit is contained in:
+8
-25
@@ -276,6 +276,10 @@ def __getattr__(name: str) -> Any:
|
|||||||
val = _Tool if name == "Tool" else _ToolPreset
|
val = _Tool if name == "Tool" else _ToolPreset
|
||||||
globals()[name] = val
|
globals()[name] = val
|
||||||
return val
|
return val
|
||||||
|
if name == "BiasProfile":
|
||||||
|
from src.tool_bias import BiasProfile as _BiasProfile
|
||||||
|
globals()[name] = _BiasProfile
|
||||||
|
return _BiasProfile
|
||||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||||
|
|
||||||
# MMA Core dataclasses (ThinkingSegment, Ticket, Track, WorkerContext, TrackMetadata)
|
# MMA Core dataclasses (ThinkingSegment, Ticket, Track, WorkerContext, TrackMetadata)
|
||||||
@@ -291,31 +295,10 @@ def __getattr__(name: str) -> Any:
|
|||||||
# src.project_files directly.
|
# src.project_files directly.
|
||||||
|
|
||||||
#region: Tool Models
|
#region: Tool Models
|
||||||
# Tool + ToolPreset moved to src/tool_presets.py in
|
# Tool + ToolPreset moved to src/tool_presets.py in Phase 3d. BiasProfile
|
||||||
# module_taxonomy_refactor_20260627 Phase 3d. The re-exports at the top of
|
# moved to src/tool_bias.py in Phase 3e. All three are re-exported lazily
|
||||||
# this module keep 'from src.models import Tool' (and ToolPreset) working for
|
# via the __getattr__ below to avoid the circular import (tool_presets and
|
||||||
# legacy callers. BiasProfile stays here until Phase 3e.
|
# tool_bias both want to import from each other via models).
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class BiasProfile:
|
|
||||||
name: str
|
|
||||||
tool_weights: Dict[str, int] = field(default_factory=dict)
|
|
||||||
category_multipliers: Dict[str, float] = field(default_factory=dict)
|
|
||||||
|
|
||||||
def to_dict(self) -> Metadata:
|
|
||||||
return {
|
|
||||||
"name": self.name,
|
|
||||||
"tool_weights": self.tool_weights,
|
|
||||||
"category_multipliers": self.category_multipliers,
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, data: Metadata) -> "BiasProfile":
|
|
||||||
return cls(
|
|
||||||
name = data["name"],
|
|
||||||
tool_weights = data.get("tool_weights", {}),
|
|
||||||
category_multipliers = data.get("category_multipliers", {}),
|
|
||||||
)
|
|
||||||
|
|
||||||
#region: UI/Editor
|
#region: UI/Editor
|
||||||
|
|
||||||
|
|||||||
+28
-2
@@ -1,6 +1,32 @@
|
|||||||
from typing import List, Dict, Any, Optional
|
from __future__ import annotations
|
||||||
|
|
||||||
from src.models import Tool, ToolPreset, BiasProfile
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
from src.tool_presets import Tool, ToolPreset
|
||||||
|
from src.type_aliases import Metadata
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BiasProfile:
|
||||||
|
name: str
|
||||||
|
tool_weights: Dict[str, int] = field(default_factory=dict)
|
||||||
|
category_multipliers: Dict[str, float] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def to_dict(self) -> Metadata:
|
||||||
|
return {
|
||||||
|
"name": self.name,
|
||||||
|
"tool_weights": self.tool_weights,
|
||||||
|
"category_multipliers": self.category_multipliers,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: Metadata) -> "BiasProfile":
|
||||||
|
return cls(
|
||||||
|
name = data["name"],
|
||||||
|
tool_weights = data.get("tool_weights", {}),
|
||||||
|
category_multipliers = data.get("category_multipliers", {}),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ToolBiasEngine:
|
class ToolBiasEngine:
|
||||||
|
|||||||
+4
-2
@@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import tomllib
|
import tomllib
|
||||||
import tomli_w
|
import tomli_w
|
||||||
|
|
||||||
@@ -6,7 +8,6 @@ from pathlib import Path
|
|||||||
from typing import Dict, List, Optional, Union, Any
|
from typing import Dict, List, Optional, Union, Any
|
||||||
|
|
||||||
from src import paths
|
from src import paths
|
||||||
from src.models import BiasProfile
|
|
||||||
from src.type_aliases import Metadata
|
from src.type_aliases import Metadata
|
||||||
|
|
||||||
|
|
||||||
@@ -135,10 +136,11 @@ class ToolPresetManager:
|
|||||||
del data["presets"][name]
|
del data["presets"][name]
|
||||||
self._write_raw(path, data)
|
self._write_raw(path, data)
|
||||||
|
|
||||||
def load_all_bias_profiles(self) -> Dict[str, BiasProfile]:
|
def load_all_bias_profiles(self) -> Dict[str, "BiasProfile"]:
|
||||||
"""
|
"""
|
||||||
[C: tests/test_tool_preset_manager.py:test_bias_profiles_merged, tests/test_tool_preset_manager.py:test_delete_bias_profile, tests/test_tool_preset_manager.py:test_save_bias_profile]
|
[C: tests/test_tool_preset_manager.py:test_bias_profiles_merged, tests/test_tool_preset_manager.py:test_delete_bias_profile, tests/test_tool_preset_manager.py:test_save_bias_profile]
|
||||||
"""
|
"""
|
||||||
|
from src.tool_bias import BiasProfile
|
||||||
global_path = paths.get_global_tool_presets_path()
|
global_path = paths.get_global_tool_presets_path()
|
||||||
global_data = self._read_raw(global_path).get("bias_profiles", {})
|
global_data = self._read_raw(global_path).get("bias_profiles", {})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user