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

  1. Check __all__ / _LAZY_EXPORTS in deeptutor/tools/__init__.py for the exact exported names
  2. Import from the concrete submodule instead (e.g. from deeptutor.tools.builtin import X)
  3. If the name vanished after an upgrade, check the changelog for renames
  4. 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

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.