Textualize/textual · error · AttributeError

module {__name__!r} has no attribute {name!r}

Error message

module {__name__!r} has no attribute {name!r}

What it means

Textual defines a module-level __getattr__ in src/textual/__init__.py solely to lazily resolve __version__ from importlib.metadata. Any other attribute access on the textual module that isn't a real module attribute falls through to this hook and raises AttributeError. This is standard Python behavior for module-level __getattr__ (PEP 562).

Source

Thrown at src/textual/__init__.py:52


if TYPE_CHECKING:
    from importlib.metadata import version

    from textual.app import App as _App

    __version__ = version("textual")
    """The version of Textual."""

else:

    def __getattr__(name: str) -> str:
        """Lazily get the version."""
        if name == "__version__":
            from importlib.metadata import version

            return version("textual")
        raise AttributeError(f"module {__name__!r} has no attribute {name!r}")


class LoggerError(Exception):
    """Raised when the logger failed."""


@rich.repr.auto
class Logger:
    """A [logger class](/guide/devtools/#logging-handler) that logs to the Textual [console](/guide/devtools#console)."""

    def __init__(
        self,
        log_callable: LogCallable | None,
        group: LogGroup = LogGroup.INFO,
        verbosity: LogVerbosity = LogVerbosity.NORMAL,
        app: _App | None = None,
    ) -> None:
        self._log = log_callable

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Import the correct symbol explicitly: `from textual.app import App` instead of `textual.App`
  2. Ensure the submodule is imported: `import textual.app` before using `textual.app`
  3. Check spelling of the attribute against the textual API docs
  4. If introspecting the module, guard with getattr(textual, name, None) or try/except AttributeError

Example fix

# before
import textual
app = textual.App()  # AttributeError

# after
from textual.app import App
app = App()
Defensive patterns

Strategy: type-guard

Validate before calling

import textual
name = 'foo'
has_it = name == '__version__' or hasattr(textual, name)

Type guard

def textual_has(name: str) -> bool:
    import textual
    return name == '__version__' or hasattr(textual, name)

Prevention

When it happens

Trigger: Accessing any attribute on the textual module that does not exist, e.g. `textual.something` where `something` is neither an import in __init__.py nor '__version__'. Also triggered by hasattr/getattr fallbacks, mock.patch('textual.foo'), or copy/pickle introspecting module attributes.

Common situations: Typos in attribute names (textual.app vs textual.Application), forgetting to import the submodule first (import textual; textual.app without `import textual.app` on some setups), or tooling that walks all module attributes (autocomplete, sphinx, pickle).

Related errors


AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27). Data as JSON: /api/errors/f8f9e68fa99281b2. Report an issue: GitHub.