Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python AST, ast.NodeTransformer ,TypeError: required field 'lineno' missing from stmt

I'd be v. gratefull to learn something, usefull, as for now, I've been moving blindly. so the problem lays in python's ast.NodeTransformer. I was wondering if one is able to add an function to existing class using this way, and not getting mad.

this is how I proceded so far.

import ast, inspect, cla # cla is a name of class to which we want to add a new function

klass = inspect.getsource(cla)
tree = ast.parse(klass)
st = '''def function(): return 1'''
Foo = ast.parse(st)

class AddFunc(ast.NodeTransformer):
      def visit_ClassDef(self, node):
          return node, node.body + Foo.body
          self.generic_visit(node)

inst = AddFunc()
stuff = i.visit(tree)

# now the trouble begins, a compiling..
co = compile(stuff, filename='<ast>', mode='exec')

# i get TypeError: required "lineno" missing from stmt

I have tried (unsuccesfully as you could probably guess) to handle this by using ast library helper functions ast.fix_missing_locations(), and ast.copy_locaction(), but in most cases I've ended up guessing or facing AttributeError by tuple which is inside AddFunc class. Have anybody some idea, how to manage this?

like image 290
user3663978 Avatar asked Jan 10 '23 13:01

user3663978


2 Answers

kolko's answer is correct, in this instance, you need to return an AST node. Foo in this case is a module, and its body should be the statements constructing the function, which can simply be spliced into the class definition.

It helps to understand that ast.NodeTransformer is very particular:

  • it calls the visit method by the specific class name, ignoring hierarchy
  • it evaluates the return value with isinstance(AST) and isinstance(list), anything else is ignored!

You may also get this error or the closely related "lineno missing from expr" even when you're using ast.fix_missing_locations to add location data.

If this happens, it's because your AST has an element that's not allowed in that place. Use astunparse.dump to carefully examine the structure. Within a module body, there can only be statements. Within a function def, there can only be statements. Then there are specific places that you can have an expression.

To solve this, write out the python you're expecting to generate, parse it, dump it and check it against what you're generating.

like image 166
Ben Avatar answered Jan 13 '23 02:01

Ben


I found answer in twitter: https://twitter.com/mitsuhiko/status/91169383254200320 mitsuhiko: "TypeError: required field "lineno" missing from stmt" — no, what you actually mean is "tuple is not a statement'. #helpfulpython

You return here tuple, but u must return ast

class AddFunc(ast.NodeTransformer):
  def visit_ClassDef(self, node):
      node.body += Foo.body
      return node
like image 36
kolko Avatar answered Jan 13 '23 01:01

kolko