An elegant way to test AST for Python for equality (not object reference or identity)

Not sure about the terminology here, but will it be the difference between eq? and equal? in a schema or the difference between == and strncmp with C strings; where in each case, the first returns false for two different lines that actually have the same content, and the second - true.

I am looking for the latest operation for Python AST.

Now I am doing this:

 import ast def AST_eq(a, b): return ast.dump(a) == ast.dump(b) 

which, apparently, works, but feels like a disaster awaiting its appearance. Does anyone know a better way?

Edit : unfortunately, when I go to compare two attributes AST < __dict__ , this comparison is used by default using the individual element methods __eq__ . __eq__ are implemented as trees of other __eq__ , and their __eq__ seems to verify referential identity. Therefore, neither the direct == nor the solution in Thomas's work works. (In addition, I also do not want to subclass each type of AST node to insert this custom __eq__ .)

+4
source share
3 answers

I ran into the same problem. I tried to go like this: first, unscathed AST to some simpler view (dicton tree):

 def simplify(node): if isinstance(node, ast.AST): res = vars(node).copy() for k in 'lineno', 'col_offset', 'ctx': res.pop(k, None) for k, v in res.iteritems(): res[k] = simplify(v) res['__type__'] = type(node).__name__ return res elif isinstance(node, list): return map(simplify, node) else: return node 

and then you can just compare these views:

 data = open("/usr/lib/python2.7/ast.py").read() a1 = ast.parse(data) a2 = ast.parse(data) print simplify(a1) == simplify(a2) 

will give you True

EDIT

Just realized that there is no need to create a dict, so you can only do:

 def compare_ast(node1, node2): if type(node1) is not type(node2): return False if isinstance(node1, ast.AST): for k, v in vars(node1).iteritems(): if k in ('lineno', 'col_offset', 'ctx'): continue if not compare_ast(v, getattr(node2, k)): return False return True elif isinstance(node1, list): return all(itertools.starmap(compare_ast, itertools.izip(node1, node2))) else: return node1 == node2 
+3
source

The following works with Python 2 or 3 and is faster than using itertools:

EDIT: WARNING :

Apparently, this code may hang in some (weird) situations. As a result, I cannot recommend it.

 def compare_ast(node1, node2): if type(node1) != type(node2): return False elif isinstance(node1, ast.AST): for kind, var in vars(node1).items(): if kind not in ('lineno', 'col_offset', 'ctx'): var2 = vars(node2).get(kind) if not compare_ast(var, var2): return False return True elif isinstance(node1, list): if len(node1) != len(node2): return False for i in range(len(node1)): if not compare_ast(node1[i], node2[i]): return False return True else: return node1 == node2 
+2
source

In Python, the object identifier is compared using the is operator (which, unlike == , cannot be overloaded). If it is not fulfilled by an idiot, == will not compare identity, but rather equality (if possible and realized, of course). And in the case of the built-in string class, this is certainly not the case.

However, there may be another problem with your implementation - since the dump creates very accurate information (suitable for debugging), two asters, for example. a variable named differently can be considered != . This may or may not be what you want.

+1
source

Source: https://habr.com/ru/post/1316594/


All Articles