|
| 1 | +import importlib.util |
1 | 2 | import inspect |
2 | 3 | import logging |
3 | 4 | import os |
4 | | -import sys |
5 | 5 | from functools import cached_property |
6 | 6 | from pathlib import Path |
7 | 7 | from typing import Any, Self |
@@ -138,56 +138,26 @@ class AgentDefinition(AgentConfig): |
138 | 138 |
|
139 | 139 | @field_validator("base_class", mode="before") |
140 | 140 | def base_class_import_points_to_file(cls, v: Any) -> Any: |
141 | | - """Ensure ImportString based base_class points to an existing file or |
142 | | - package directory to catch a FileError and not interpret it as str |
143 | | - class_name. |
| 141 | + """Ensure ImportString based base_class points to an existing file to |
| 142 | + catch a FileError and not interpret it as str class_name. |
144 | 143 |
|
145 | 144 | A dotted path indicates an import string (e.g., |
146 | | - my_pkg.module.Class). We map the module portion to either: |
147 | | - - A .py file (e.g., my_module.py) |
148 | | - - A package directory with __init__.py (e.g., my_module/__init__.py) |
149 | | -
|
150 | | - Checks relative to current working directory and sys.path entries |
151 | | - (including config file directory). |
| 145 | + dir.agent.MyAgent). We use importlib to automatically search for |
| 146 | + the module in sys.path. |
152 | 147 | """ |
153 | 148 | if isinstance(v, str) and "." in v: |
154 | 149 | module_parts = v.split(".") |
155 | 150 | if len(module_parts) >= 2: |
156 | | - module_path = os.sep.join(module_parts[:-1]) |
157 | | - |
158 | | - def check_module_exists(base_path: Path) -> bool: |
159 | | - """Check if module exists as file or package directory.""" |
160 | | - # Check for .py file |
161 | | - file_candidate = base_path / (module_path + ".py") |
162 | | - if file_candidate.exists(): |
163 | | - return True |
164 | | - |
165 | | - # Check for package directory with __init__.py |
166 | | - package_candidate = base_path / module_path / "__init__.py" |
167 | | - if package_candidate.exists(): |
168 | | - return True |
169 | | - |
170 | | - return False |
171 | | - |
172 | | - # Try to find relative to current working directory first |
173 | | - if check_module_exists(Path.cwd()): |
174 | | - return v |
175 | | - |
176 | | - # Try to find relative to sys.path entries (including config directory) |
177 | | - for sys_path in sys.path: |
178 | | - if sys_path: |
179 | | - base_path = Path(sys_path) |
180 | | - if check_module_exists(base_path): |
181 | | - return v |
182 | | - |
183 | | - # If not found, raise error with helpful message |
184 | | - file_candidate = Path(os.sep.join(module_parts[:-1]) + ".py") |
185 | | - package_candidate = Path(os.sep.join(module_parts[:-1])) / "__init__.py" |
186 | | - raise FileNotFoundError( |
187 | | - f"base_class import '{v}' points to module '{module_path}', but neither " |
188 | | - f"'{file_candidate}' nor '{package_candidate}' exists. " |
189 | | - f"Checked relative to current directory and sys.path entries." |
190 | | - ) |
| 151 | + # Get module path (everything except the class name) |
| 152 | + module_path = ".".join(module_parts[:-1]) |
| 153 | + # Use importlib to find module in sys.path automatically |
| 154 | + spec = importlib.util.find_spec(module_path) |
| 155 | + if spec is None or spec.origin is None: |
| 156 | + file_path = Path(*module_parts[:-1]).with_suffix(".py") |
| 157 | + raise FileNotFoundError( |
| 158 | + f"base_class import '{v}' points to '{file_path}', " |
| 159 | + f"but the file could not be found in sys.path" |
| 160 | + ) |
191 | 161 | return v |
192 | 162 |
|
193 | 163 | @model_validator(mode="before") |
|
0 commit comments