HKUDS/DeepTutor · error · AttributeError
module {__name__!r} has no attribute {name!r}
Error message
module {__name__!r} has no attribute {name!r} What it means
deeptutor/tools/__init__.py implements PEP 562 lazy exports via module-level __getattr__: attribute access on the package triggers on-demand import of the real class. Accessing any attribute not listed in _LAZY_EXPORTS raises a plain Python AttributeError naming the module and attribute. This is normal Python behavior, not a library-specific error class.
Source
Thrown at deeptutor/tools/__init__.py:32
"TexDownloader": (".tex_downloader", "TexDownloader"),
"read_tex_file": (".tex_downloader", "read_tex_file"),
"BrainstormTool": (".builtin", "BrainstormTool"),
"CodeExecutionTool": (".builtin", "CodeExecutionTool"),
"GeoGebraAnalysisTool": (".builtin", "GeoGebraAnalysisTool"),
"PaperSearchToolWrapper": (".builtin", "PaperSearchToolWrapper"),
"RAGTool": (".builtin", "RAGTool"),
"ReasonTool": (".builtin", "ReasonTool"),
"WebSearchTool": (".builtin", "WebSearchTool"),
"ToolPromptComposer": (".prompting", "ToolPromptComposer"),
"load_prompt_hints": (".prompting", "load_prompt_hints"),
}
__all__ = sorted(_LAZY_EXPORTS)
def __getattr__(name: str):
if name not in _LAZY_EXPORTS:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
module_name, attr_name = _LAZY_EXPORTS[name]
module = importlib.import_module(module_name, __name__)
value = getattr(module, attr_name)
globals()[name] = value
return value
# Question generation tools (lazy import to avoid circular dependencies)
# Access via: from deeptutor.tools.question import parse_pdf_with_mineru, etc.
View on GitHub (pinned to 3e82f13042)
Solutions
- Check __all__ / _LAZY_EXPORTS in deeptutor/tools/__init__.py for the exact exported names
- Import from the concrete submodule instead (e.g. from deeptutor.tools.builtin import X)
- If the name vanished after an upgrade, check the changelog for renames
- Fix typos — names are case-sensitive
Example fix
# before from deeptutor.tools import WebSerach # typo # after from deeptutor.tools import WebSearch
Defensive patterns
Strategy: type-guard
Validate before calling
import deeptutor.tools as dt
if name not in dt.__all__:
raise ImportError(f"deeptutor.tools does not export {name!r}; see dt.__all__") Type guard
import deeptutor.tools as dt
def is_lazy_export(name: str) -> bool:
"""Type guard: True if deeptutor.tools exports `name` (PEP 562 lazy attr)."""
return hasattr(dt, name) Try / catch
try:
Tool = getattr(deeptutor.tools, name)
except AttributeError:
raise ImportError(f"Unknown tool export {name!r}; available: {deeptutor.tools.__all__}") from None Prevention
- Import from concrete submodules (deeptutor.tools.builtin) for stability
- Check __all__ when unsure of an export name
- Run a quick import smoke test after upgrading deeptutor
When it happens
Trigger: from deeptutor.tools import SomethingNotExported, or deeptutor.tools.Foo where Foo is not a key in _LAZY_EXPORTS (misspelled tool class, removed export after upgrade, or a private name).
Common situations: Typos in tool class names, referencing a tool that was renamed/removed in a newer version, or assuming every module in deeptutor.tools.* is re-exported at package level.
Related errors
AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27).
Data as JSON: /api/errors/8266796934dcf430.
Report an issue: GitHub.