-
-
Save snim2/402869 to your computer and use it in GitHub Desktop.
#!/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) |
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>
>>>
@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 Is there a way to only call and parse a single function from a module?
like: