Ted
Ted

Reputation: 395

Parsing python file for all methods and classes

I am trying to build a program which allows the user to browse to a folder which contains python modules. Once the folder has been selected it will list all python files within that folder as well as all the classes and methods for each module. My question is, are there any way I can do this without opening each file and parsing for "def" or "class"? I noticed that there's a function called mro which returns the attribute of a class but that requires me to have access to that class through an import. So is there any way I can get the same result? Thank you in advance!

Upvotes: 3

Views: 6688

Answers (5)

Tim Hallbeck
Tim Hallbeck

Reputation: 651

Thanks, useful example for this first time ast user. Code above with the import, printed output, and without the 1 spelling error ;-)

import ast

classList = []
className = None
methodName = None
fileName = "C:\\fullPathToAPythonFile.py"
fileObject = open(fileName ,"r")
text = fileObject.read()
p = ast.parse(text)
node = ast.NodeVisitor()
for node in ast.walk(p):
    if isinstance(node, ast.FunctionDef) or isinstance(node, ast.ClassDef):
        if isinstance(node, ast.ClassDef):
            className = node.name
        else:
            methodName = node.name
        if className != None and methodName != None:
            subList = (methodName , className)
            classList.append(subList)
            print("class: " + className + ", method: " + methodName)

Upvotes: 0

melMass
melMass

Reputation: 5239

I had to replace a lot of code in one of my modules, here is my way of getting classes and methods:

def listClass(file):

    with open(file,"r") as f:
        p = ast.parse(f.read())

    # get all classes from the given python file.
    classes = [c for c in ast.walk(p) if isinstance(c,ast.ClassDef)]

    out = dict()
    for x in classes:
        out[x.name] = [fun.name for fun in ast.walk(x) if isinstance(fun,ast.FunctionDef)]

    return out

Sample pprint output:

{'Alert': ['__init__',
           'fg',
           'fg',
           'bg',
           'bg',
           'paintEvent',
           'drawBG',
           'drawAlert'],
 'AlertMouse': ['__init__', 'paintEvent', 'mouseMoveEvent'],
 'AlertPopup': ['__init__', 'mousePressEvent', 'keyPressEvent', 'systemInfo']}

Upvotes: 0

Ted
Ted

Reputation: 395

This is what I came up with using the AST module, it has exactly what I was looking for.

def fillClassList(file):
    classList = []
    className = None
    mehotdName = None
    fileName = "C:\Transcriber\Framework\ctetest\RegressionTest\GeneralTest\\" + file
    fileObject = open(fileName,"r")
    text = fileObject.read()
    p = ast.parse(text)
    node = ast.NodeVisitor()
    for node in ast.walk(p):
        if isinstance(node, ast.FunctionDef) or isinstance(node, ast.ClassDef):
            if isinstance(node, ast.ClassDef):
                className = node.name
            else:
                methodName = node.name
            if className != None and methodName != None:
                subList = (methodName , className)
                classList.append(subList)
    return classList

Upvotes: 3

bruno desthuilliers
bruno desthuilliers

Reputation: 77952

Most of Python's implementation (parser included) is available in the stdlib, so by carefully reading the modules index you should find what you need. The first modules / packages that come to mind are importlib, inspect and ast but there surely other modules of interest.

Upvotes: 0

Bill Gribble
Bill Gribble

Reputation: 1797

If you want to know the contents of the file, there's no way around looking into the file :)

Your choice comes down to whether you want to parse out the content-of-interest yourself, or if you want to let Python load the file and then ask it about what it found.

For a very simple Python file like testme.py below you can do something like this (warning: not for those with weak stomachs):

testme.py:

class Foo (object):
    pass

def bar():
    pass 

analyze.py:

import os.path

files = ['testme.py']
for f in files: 
    print f
    modname = os.path.splitext(f)[0]
    exec('import ' + modname)
    mod = eval(modname)
    for symbol in dir(mod):
        if symbol.startswith('__'):
            continue
        print '   ', symbol, type(eval(modname + '.' + symbol))

Output:

testme.py
   Foo <type 'type'>
   bar <type 'function'>

However, that's going to start to get pretty grotty when you expand it to deal with nested packages and modules and broken code and blah blah blah. Might be easier just to grep for class and/or def and go from there.

Have fun with it! I :heart: metaprogramming

Upvotes: 0

Related Questions