1

Here in this question, I was asking for a way to convert function names from CamelCase to snake_case, one of the comments suggested using AST. I found a code snippet to find all function calls in a script

import ast
from collections import deque


class FuncCallVisitor(ast.NodeVisitor):
    def __init__(self):
        self._name = deque()

    @property
    def name(self):
        return '.'.join(self._name)

    @name.deleter
    def name(self):
        self._name.clear()

    def visit_Name(self, node):
        self._name.appendleft(node.id)

    def visit_Attribute(self, node):
        try:
            self._name.appendleft(node.attr)
            self._name.appendleft(node.value.id)
        except AttributeError:
            self.generic_visit(node)


def get_func_calls(tree):
    func_calls = []
    for node in ast.walk(tree):
         if isinstance(node, ast.Call):
            callvisitor = FuncCallVisitor()
            callvisitor.visit(node.func)
            func_calls.append(callvisitor.name)

    return func_calls

if __name__ == '__main__':

    tree = ast.parse(open("some_dir").read())
    print(get_func_calls(tree))

using this code I have all function calls in my script, now I want to write a code that converts this name to snake_case. I found this code snippet to modify a node in AST tree

class RewriteName(ast.NodeTransformer):

    def visit_Name(self, node):
        return ast.copy_location(ast.Subscript(
            value=ast.Name(id='data', ctx=ast.Load()),
            slice=ast.Index(value=ast.Str(s=node.id)),
            ctx=node.ctx
        ), node)

tree = RewriteName().visit(tree)

I didn't understand how to use it to serve my purpose. Any explanation or other pieces of advice?

cs95
  • 379,657
  • 97
  • 704
  • 746
Ali.Wassouf
  • 309
  • 4
  • 21

1 Answers1

0

I am kind of late to this, but maybe it will be found in the future.

Anyway, here is a quick hack at it. Actually, you were almost there with your solution. The name method returns your name, then you can arbitrarily change that. So in your def get_func_calls(tree) call you can manipulate the string and re-assign the new name to the Call object.

ccName = callvisitor.name # work with some local var
new_name = ''             # the new func name
for char_i in range(len(ccName)): # go over the name
    if ccName[char_i].isupper():  # check if the current char is with uppercase
        if ccName[char_i - 1] == '.': # check if the previous character is a dot
            new_name += ccName[char_i].lower() # if it is, make the char to lowercase
        else:
            new_name += '_' + ccName[char_i].lower() # otherwise add the snake_
    else:
        new_name += ccName[char_i] # just add the rest of the lower chars
callvisitor._name = new_name       # just re-asign the new name 
func_calls.append(callvisitor._name)

This is definitely not a pretty solution and it also depends if you want to change only function definitions or every single function call in a file, but this should give you an idea on how to change the ast.

mnestorov
  • 4,116
  • 2
  • 14
  • 24