Skip to content

Instantly share code, notes, and snippets.

@snim2
Created May 16, 2010 13:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save snim2/402869 to your computer and use it in GitHub Desktop.
Save snim2/402869 to your computer and use it in GitHub Desktop.
Find exception names in a Python module
#!/usr/bin/env python
import compiler.ast
import compiler.visitor as visitor
__author__ = 'Sarah Mount <s.mount@wlv.ac.uk>'
__date__ = 'May 2010'
class ExceptionFinder(visitor.ASTVisitor):
"""List all exceptions raised by a module.
"""
def __init__(self, filename):
visitor.ASTVisitor.__init__(self)
self.filename = filename
self.exns = set()
return
def __visitName(self, node):
"""Should not be called by generic visit, otherwise every name
will be reported as an exception type.
"""
self.exns.add(node.name)
return
def __visitCallFunc(self, node):
"""Should not be called by generic visit, otherwise every name
will be reported as an exception type.
"""
self.__visitName(node.node)
return
def visitRaise(self, node):
"""Visit a raise statement.
Cheat the default dispatcher.
"""
if isinstance(node.expr1, compiler.ast.Name):
self.__visitName(node.expr1)
elif isinstance(node.expr1, compiler.ast.CallFunc):
self.__visitCallFunc(node.expr1)
return
if __name__ == '__main__':
import sys
if len(sys.argv) <= 1:
print 'Give me a file name!'
print 'Looking for exception types in: %s' % sys.argv[1]
lint = ExceptionFinder(sys.argv[1])
compiler.walk(compiler.parseFile(sys.argv[1]),
lint,
walker=lint,
verbose=5)
for exn in lint.exns:
print '%s:%s is an exception type' % (sys.argv[1], exn)
@shadycuz
Copy link

@snim2 Is there a way to only call and parse a single function from a module?

like:

from http.client import HTTPConnection

compiler.walk(HTTPConnection.get_response)

@snim2
Copy link
Author

snim2 commented Jul 30, 2022

This was written for Python2.7 BTW, so you would need to adapt it a bit for Python3, but I think you can do something like this:

$ python3
Python 3.8.10 (default, Jun 22 2022, 20:18:18) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ast
>>> from http.client import HTTPConnection
>>> ast.walk(HTTPConnection.getresponse)
<generator object walk at 0x7ff58eff0c80>
>>> 

@shadycuz
Copy link

@snim2 Thanks for the reply. This seems so much harder then I expected. It seems by default the AST only parses what it sees. It doesn't drill down into other function calls.

I'm trying to walk all code that might be called. Like both sides of an if statement. To record what exceptions might be raised from a given starting point.

Take this example

def raise_a():
    raise Exception('testing')

def raise_b():
    raise TypeError()

def test_raise():
    raise_a()
    raise_b()
    raise ValueError()

If I walk test_raise then I see the calls to raise_a() and raise_b() but ast.walk does not walk the AST for the raise_a and raise_b.

I have actually found a hacky workaround that does work. But I'm having trouble with my call to HTTPConnection.getresponse.

I cant start a new gist if you are interested in what I am up to.

@snim2
Copy link
Author

snim2 commented Jul 30, 2022 via email

@shadycuz
Copy link

shadycuz commented Oct 4, 2022

@snim2 The initial release of Deep-AST is out. Thanks for the inspiration, I will make sure I add this gist to the README.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment