3

The goal is to detect if a builtin function such as eval() is used in some code.

def foo(a):
    eval('a = 2')

I have tried the following approach:

ex_ast = ast.parse(inspect.getsource(foo))

for node in ast.walk(ex_ast):
if isinstance(node, ast.FunctionDef):
    print(node.name)

The function name foo is printed as the output.

I know that Builtin functions don't have constructors. They are in the type Module. So 1 approach would be using types.FunctionType in an isinstance call.

But since I'm using AST nodes. They cannot be transformed back into code. I have to check for each node if they are types.FunctionType:

for node in ast.walk(ex_ast):
    if isinstance(node, ast.FunctionType):
        print(node.name)

I got these errors:

AttributeError: module 'ast' has no attribute 'FunctionType'

How should I correctly identify if a specific Buildin Function is used in code? Thanks!

HayleyL
  • 39
  • 1
  • 6
  • "I know that Builtin functions don't have constructors. They are in the `type` Module." - you have seriously misunderstood things here. – user2357112 May 13 '16 at 19:30
  • @user2357112 I learned that from here [link](http://stackoverflow.com/questions/624926/how-to-detect-whether-a-python-variable-is-a-function), can you explain that further if you are sure? thanks! – HayleyL May 13 '16 at 19:32
  • That answer is saying that there is no built-in function you call to create functions, and that the type object representing the type of functions is available in the `types` module. You've taken the words involved and built similar-looking but wrong sentences out of them. – user2357112 May 13 '16 at 19:42

1 Answers1

1

When you write eval(whatever) in your code, eval is looked up by an ordinary global variable lookup. You should look for an ast.Name node representing a use of the variable name eval:

for node in ast.walk(ex_ast):
    if isinstance(node, ast.Name) and node.id == 'eval':
        # Found it.

Since you have an actual function object, not just the source code, you can also check for variables that shadow the built-in in a slightly more reliable manner than if you just had the function's source:

if ('eval' in foo.__code__.co_varnames     # local variable
    or 'eval' in foo.__code__.co_cellvars  # local variable used by nested function
    or 'eval' in foo.__code__.co_freevars  # local variable from enclosing function
    or 'eval' in foo.__globals__):         # global variable
    # Some variable is shadowing the built-in.

This won't catch globals added after the check, and it won't do anything about accesses to the built-in by a different name (for example, x = eval; x('whatever')). Whether it's worthwhile is up to you.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • Thanks that solved my question! @Kupiakos that is `and node.id`, thanks for pointing it out! – HayleyL May 13 '16 at 19:44
  • This assumes that nothing is shadowing the builtin. Considering how many people write `list = []` in their code, this solution is incomplete. – Alex Hall May 13 '16 at 19:51
  • @AlexHall: Sure, but such shadowing can only be determined at runtime. In any case, people are much less likely to shadow `eval`, and if they do, it's not such a bad thing to flag that too. – user2357112 May 13 '16 at 19:54
  • PyCharm warns me about shadowing all the time. I don't know what using the `ast` module is like but can't you parse the scope before the appearance of the function and check if the name appears in an assignment or import statement? It won't handle 100% of cases but it'll catch the common ones. And `eval` is just OP's example AFAICT. – Alex Hall May 13 '16 at 19:58