6

I'm using the ast module to parse docstrings in a Python module to turn our docs into read the docs format. I'm using the following to get the function names and docstrings into a list of dicts which the rest of my code works well with. I'm looking for something to get the parameters of the function as well:

good_file = (file for file in os.listdir() if file[-3:] == '.py' and file != '__init__.py')

functions = []

for file in good_file:
     with open(file, 'r') as f:
         module = ast.parse(f.read())

     for node in module.body:
         if isinstance(node, ast.FunctionDef):
         entry = {"docs": ast.get_docstring(node), "fn_name": node.name, "params": ???}

         functions.append(entry)

I'm looking for what I can use to fill in the function's parameters into the dict. Thanks!

2
  • 1
    Did you try looking at node.args? Commented Jul 10, 2018 at 18:08
  • 1
    Do this interactively. Try dir(node) and see what attributes are available and just poke around. Commented Jul 10, 2018 at 18:12

1 Answer 1

15

The Abstract Grammar section of the ast documentation tells you where to find the parameter definitions in a FunctionDef node:

stmt = FunctionDef(identifier name, arguments args,
                   stmt* body, expr* decorator_list, expr? returns)

In the parameters is a sequence of type name entries; the names become attributes on the node. The types are further covered in the documentation (with several 'builtin' types listed at the top, which are reflected as Python strings and integers). Types with * after them are sequences (lists), a question mark means they can be set to None.

So each FunctionDef node has name, args, body, decorator_list and returns attributes. The args attribute is a new node, of type arguments, also documented:

arguments = (arg* args, arg? vararg, arg* kwonlyargs, expr* kw_defaults,
             arg? kwarg, expr* defaults)

so FunctionDef.args.args is a list of arguments, each an arg object, etc.

arg is documented as

arg = (identifier arg, expr? annotation)
       attributes (int lineno, int col_offset)

where identifier is a built-in type, so just a string here.

You probably want to look at the ast.dump() function, which will give you a quick overview of an AST node:

>>> source = """def foo(bar, baz=None, *args, **kwargs): pass"""
>>> module = ast.parse(source)
>>> ast.dump(module)
"Module(body=[FunctionDef(name='foo', args=arguments(args=[arg(arg='bar', annotation=None), arg(arg='baz', annotation=None)], vararg=arg(arg='args', annotation=None), kwonlyargs=[], kw_defaults=[], kwarg=arg(arg='kwargs', annotation=None), defaults=[NameConstant(value=None)]), body=[Pass()], decorator_list=[], returns=None)])"

From there you can then explore and further 'dump' information to get to the actual data you need:

>>> function = module.body[0]
>>> ast.dump(function.args)
"arguments(args=[arg(arg='bar', annotation=None), arg(arg='baz', annotation=None)], vararg=arg(arg='args', annotation=None), kwonlyargs=[], kw_defaults=[], kwarg=arg(arg='kwargs', annotation=None), defaults=[NameConstant(value=None)])"
>>> function.args.args
[<_ast.arg object at 0x109852fd0>, <_ast.arg object at 0x109852ef0>]
>>> [a.arg for a in function.args.args]
['bar', 'baz']

The defaults are attached to the last names in the args or kw_args sequences (defaults for args, kw_defaults for kwonlyargs); a list of N defaults attached to the last N names in args or kwosnlyargs. Any catch-all names (*args and **kwargs in my example) are listed separately.

Sign up to request clarification or add additional context in comments.

2 Comments

@MartijnPieters but when I try to print the defaults, I am getting ast object like this [<_ast.NameConstant object at 0x000001CF53C28E10>, <_ast.Str object at 0x000001CF53C28E48>, <_ast.Num object at 0x000001CF53C28E80>]. Currently I am getting value using this logic print([b.__dict__.get(b._fields[0]) for b in node.args.defaults]). But I am sure there will be a better way to get the default arguments. Any idea?
@TonyMontana sorry, I can’t really make out what you are asking or what you expected to find. Comments are not really suitable for this kind of assistance either though, perhaps a new question would be a better fit. At any rate, for AST traversal, I recommend you use the ast.walk() and ast.NodeVisitor utilities (I have an example of the latter).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.