How do I specify line numbers in AST?
I've tried ast.increment_lineno
, but compile()
pays no attention to the changed line numbers:
>>> import ast
>>> example_tree=ast.parse("print('Hello, World!')")
>>> ast.increment_lineno(example_tree, 10)
>>> compile(example_tree,"somefile.py","exec")
<code object <module> at 0x7fb84501a4a0, file "somefile.py", line 1>
I am pretty sure that there should be line 11
. What's wrong?
Code parsed with mode='exec'
(the default) becomes an ast.Module
object, which does not have a lineno
attribute. [Note 1]
As the documentation for module ast
indicates, only stmt
and expr
subclasses have token position attributes (lineno
, col_offset
, end_lineno
and end_col_offset
). ast.increment_lineno
modifies those nodes, but the Module
node has no location attributes so it is always considered to start at line 1.
If you disassemble the code compiled from the modified AST, you'll find that the compiled opcodes do indicate the incremented line numbers, except for the RESUME
opcode at the very beginning, which continues to have line number 0. So backtraces and debugging on the compiled code should show the line numbers you expect.
While researching the answer, I noticed that ast.increment_lineno
does not change the line numbers in the Module's list of TypeIgnore
objects. I think that's a bug, so I reported it. But that's not relevant to your question.
Tools I used to construct this answer:
import ast, dis
# Sample code
code = """
def hello():
print('Hello, world')
"""
tree = ast.parse(code)
ast.increment_lineno(tree, 10)
# View the AST, neatly formatted and including attributes
print(ast.dump(tree, include_attributes=True, indent=2))
# Compile the code
code = compile(tree, 'example.py', mode='exec')
# Look at the disassembly
print(dis.dis(code))
ast.Module
are body
, which is a list of objects derived from ast.stmt
, and type_ignores
, which is a list of ast.TypeIgnore
objects, although the type_ignores
list is empty unless you use type_comments=True
when parsing.