Skip to content

← Modules, Packaging and Tooling step 6 of 41

Easy Framework

List a Module's Functions with ast

Before you change a module, you often want to know what it defines. Importing it would run its top-level code, which might open a database connection or start a thread. The ast module reads the source without running it. ast.parse turns source text into an AST, or abstract syntax tree. It is a tree of node objects, one for each statement or expression.

import ast

module = ast.parse("import json\n\ndef load(path): ...\n\nclass Store:\n    def get(self, key): ...\n")
for node in module.body:
    print(type(node).__name__)
# Import
# FunctionDef
# ClassDef

module.body holds only the top-level statements. The method get sits in the ClassDef node's own body, so it is not in the list.

What to write

Fill in list_functions(source) and list_classes(source):

  • list_functions returns the names of the functions defined at the top level, in source order. Both def (ast.FunctionDef) and async def (ast.AsyncFunctionDef) count.
  • list_classes returns the names of the top-level classes (ast.ClassDef), in source order.
  • Look only at the statements directly in module.body. Methods, nested functions and a def inside a top-level if block all sit inside another node, so skip them.
  • Skip names that start with _, which are private by convention.
  • A decorator does not change the node type. A decorated function is still an ast.FunctionDef.

ast.stmt has no name attribute, so mypy rejects node.name until an isinstance check narrows node to a node type that has one.

What the driver reports

Keep solve as it is. It returns functions and classes, the two lists your functions return.

The hard problem after this one reads the same module.body to work out which names a module exports.