Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get all variable and method names used in script

It can be weird but I am looking for a way to get automatically all variables and method within a python script.

For example,

a = 1
b = 2
c = 3
myList = range(10)

def someMethod(x): 
    something = do_something()
    return something

f = someMethod(b)

print f

I want to get

a, b, c, someMethod, something, f

This is a demonstrative example, I want to do this for a much bigger script.

like image 833
farhawa Avatar asked Nov 05 '15 20:11

farhawa


3 Answers

The ast module can do this fairly easily. If we assume the source is stored in a variable named source (could be read from a file):

import ast

root = ast.parse(source)
names = sorted({node.id for node in ast.walk(root) if isinstance(node, ast.Name)})

That loses ordering to gain uniquification and friendly display order, but you could just use a list comprehension or generator expression instead of a set comprehension if you don't need uniqueness but want ordering. The resulting list is:

['a', 'b', 'c', 'do_something', 'f', 'myList', 'range', 'someMethod', 'something', 'x']

Unlike the other solutions posted so far, this will recurse into classes and functions to get the names used inside them, and doesn't require you to import the module or class to check, nor does it require you to implement recursive processing yourself; any syntactically valid Python code will work.

Oddly, on Python 3 (substituting a valid print function call), you get:

 ['a', 'b', 'c', 'do_something', 'f', 'myList', 'print', 'range', 'someMethod', 'something']

which adds print (as expected; it's a name now, not a keyword statement), but omits x. You didn't ask for x (the argument received by someMethod), and this doesn't produce it on Python 3. Names in function prototypes appear to not create a ast.Name node there, go figure. You can pull that info out of the ast.FunctionDef node from the arg attribute of each entry in the list node.args.args, but it's probably still not comprehensive; I suspect other definition related names might be missed, e.g. in class declarations with inheritance. You'd need to poke around with some examples to make sure you're checking everything (assuming you want stuff like x and want to work on Python 3).

That said, x would show up just fine if you referenced it; if you passed it to do_something or used it in any way besides receiving and discarding it, it would show up.

You can also make an effort to only handle names assigned to, not used (to exclude do_something, range) by extending the test to:

names = sorted({node.id for node in ast.walk(root) if isinstance(node, ast.Name) and not isinstance(node.ctx, ast.Load)})

But that will also drop someMethod (in both Py2 and Py3) because the definition itself doesn't produce an ast.Name, only the use of it does. So again, you'd have to delve a little deeper into the ast.Node internals for ast.FunctionDef, ast.ClassDef, etc. to get the names that aren't walk-ed directly.

like image 105
ShadowRanger Avatar answered Nov 11 '22 04:11

ShadowRanger


There are a lot of variables imported by python so you're going to get a long list, but vars() will work.

a = 1
b = 2
c = 3
myList = range(10)

def someMethod(x): 
    something = 4
    return something

f = someMethod(b)

print vars()

in terminal:

 $ python temp.py
{'a': 1, 'c': 3, 'b': 2, 'myList': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'f': 4, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'temp.py', '__package__': None, 'someMethod': <function someMethod at 0x10759b488>, '__name__': '__main__', '__doc__': None}

EDIT
You can clean it up a bit by checking for type of variables

import types
all_vars = dict(vars())
for var_name, var in all_vars.iteritems():
    if type(var) not in [types.ModuleType, types.FunctionType] and not var_name.startswith("_"):
        print var_name

in terminal:

$ python temp.py
a
c
b
myList
f
like image 31
Ben Avatar answered Nov 11 '22 03:11

Ben


You can use globals() function to get all the global names in your module, and since python has some global names by default like __builtins__ you can escape them :

Since globals() return a dictionary contains the names and the values and builtin names have a format like __builtins__ you can filter them:

>>> print([var for var in globals().keys() if '__' not in var])
['someMethod', 'c', 'b', 'a', 'myList']

But note that it won't give you the local names inside the function like something. For that aim you can see inspect module https://docs.python.org/3/library/inspect.html#inspect.getmembers

like image 2
Mazdak Avatar answered Nov 11 '22 03:11

Mazdak