python/cpython · error · AttributeError

module 'ast' has no attribute

Error message

module 'ast' has no attribute {attr!r}

What it means

ast's module-level __getattr__ serves the deprecated node classes removed from the main namespace (Index, ExtSlice, Suite, AugLoad, AugStore, Param, and peers kept in _deprecated). A lookup that misses _deprecated entirely raises AttributeError; names present in the mapping emit a DeprecationWarning (removal targeted for 3.21) and return the shim class. This pattern replaced the old hard ImportError after these classes were made subclasses of their replacements.

Solutions

  1. Replace deprecated classes: use the wrapped node directly (e.g. x instead of ast.Index(x) for subscripts; ast.Tuple for ExtSlice; plain Load/Store for AugLoad/AugStore; drop ast.Param in favor of plain arg).
  2. Pin or upgrade the tooling — many codemod libraries (e.g. libcst, astroid) already shipped 3.9+ fixes.
  3. For dynamic lookups use getattr(ast, name, None) and check sys.version_info before touching removed names.

Example fix

# before
slc = ast.Index(value=ast.Name(id='i'))  # deprecation + removal in 3.21

# after
slc = ast.Name(id='i', ctx=ast.Load())  # bare expression as index (3.9+)
Defensive patterns

Strategy: type-guard

Validate before calling

DEPRECATED_AST = {'Index', 'ExtSlice', 'Suite', 'AugLoad', 'AugStore', 'Param'}

def ast_attr(name, default=None):
    if name in DEPRECATED_AST:
        return default  # do not touch the deprecation shims
    return getattr(ast, name, default)

Type guard

def ast_has(name) -> bool:
    """True only for non-deprecated attributes."""
    import warnings
    DEPRECATED = {'Index', 'ExtSlice', 'Suite', 'AugLoad', 'AugStore', 'Param'}
    return name not in DEPRECATED and hasattr(ast, name)

Try / catch

try:
    cls = getattr(ast, name)
except AttributeError:
    raise RuntimeError(
        f'ast.{name} does not exist on Python {sys.version.split()[0]}; '
        f'use 3.9+ node classes') from None

Prevention

When it happens

Trigger: ast.Index / ast.ExtSlice / ast.Suite / ast.AugLoad / ast.AugStore / ast.Param accessed directly (deprecation path); any other misspelled or nonexistent ast name such as ast.AstParser raising the AttributeError; getattr(ast, name) probes in version-adaptive code.

Common situations: Old AST-rewriting tools (codemods, macros, optimizers) written before 3.9 when Index/SExtSlice were real node classes; code that constructs ast.Index(value) while targeting multiple Python versions; hasattr-based feature detection against ast.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/04c59be28c01f626. Report an issue: GitHub.

Appendix: source

Thrown at Lib/ast.py:733

    print(dump(tree, include_attributes=args.include_attributes,
               color=can_colorize(file=sys.stdout),
               indent=args.indent, show_empty=args.show_empty))

_deprecated = {
        'slice': globals().pop("slice"),
        'Index': globals().pop("Index"),
        'ExtSlice': globals().pop("ExtSlice"),
        'Suite': globals().pop("Suite"),
        'AugLoad': globals().pop("AugLoad"),
        'AugStore': globals().pop("AugStore"),
        'Param': globals().pop("Param")
}

def __getattr__(attr):
    try:
        val = _deprecated[attr]
    except KeyError:
        raise AttributeError(f"module 'ast' has no attribute {attr!r}") from None
    warnings._deprecated(f"ast.{attr}", remove=(3, 21))
    return val

if __name__ == '__main__':
    main()

View on GitHub (pinned to bc6749cc3b)