Instantly share code, notes, and snippets.

Embed
What would you like to do?
Hack attempt at building something that searches module for a given term to determine where to import it from.
#!/usr/bin/env python
import argparse
import inspect
import logging
import pydoc
import pkgutil
import sys
log = logging.getLogger("import_searcher")
# Don't search these modules
# Including pyuic because it has argument parsing on import and could print
# misleading error messages due to missing arguments. Authors didn't properly
# wrap their parsing in a method.. :)
IGNORE_MODULES = ['PyQt4.uic.pyuic']
def _parse_args():
parser = argparse.ArgumentParser(
description='Search module for specific module/class/attribute',
prog='import_searcher')
parser.add_argument('-m', '--module', action='store', dest='search_module',
required=False, default='PyQt4',
help='Module to search within')
parser.add_argument('-s', '--search_term', action='store',
dest='search_term', required=True,
help='Term to search for')
levels = ['critical', 'debug', 'info', 'warning', 'error']
parser.add_argument('-l', '--log-level', action="store", dest="level",
default='warning', choices=levels)
args = vars(parser.parse_args())
# FIXME: This option doesn't work yet. Need to figure out how to get pydoc
# to find and print help...
#parser.add_argument('-p', '--show-pydoc-help', action='store_true',
#dest='show_pydoc_help', required=False, default=False,
#help='Show pydoc help on found objects')
args['show_pydoc_help'] = False
# Get a standard log level from logging module base on given string
level = getattr(logging, args['level'].upper(), logging.WARNING)
args['level'] = level
return args
def _try_safeimport(name):
"""Try a pydoc.safeimport on name and log errors"""
try:
obj = pydoc.safeimport(name)
except pydoc.ErrorDuringImport as err:
log.debug('Error importing: %s' % (err.filename))
log.debug(' %s %s' % (err.exc.__name__, err.value))
raise err
return obj
def _object_to_string(obj):
"""Turn object into a friendly string with preference for __name__"""
try:
obj_name = obj.__name__
except AttributeError:
obj_name = str(obj)
return obj_name
def _search_module_and_class(obj, search_term, show_pydoc_help):
"""Search given object recursively for search_term"""
for k, v in inspect.getmembers(obj, inspect.isclass or inspect.ismodule):
# User searching for class or module directly
if search_term in str(v):
obj_name = _object_to_string(obj)
value_name = _object_to_string(v)
print 'Class/module: %s' % ('.'.join([obj_name, value_name]))
# Search attributes
for ck, cv in inspect.getmembers(v):
if search_term in ck:
obj_name = _object_to_string(obj)
class_or_module_name = _object_to_string(v)
full_location = '.'.join([obj_name, class_or_module_name, ck])
print 'Attribute: %s = %s' % (full_location, cv)
if show_pydoc_help:
pydoc.help(full_location)
def search(name, search_term, show_pydoc_help=False):
"""Search recursively for search_term in object imported from 'name'"""
if name in IGNORE_MODULES:
return
try:
obj = _try_safeimport(name)
except pydoc.ErrorDuringImport:
return
# See if we have a package or something else
try:
path = obj.__path__
except AttributeError:
# Search module/class recursively
return _search_module_and_class(obj, search_term, show_pydoc_help)
# Search this package recursively
for i, m, ispkg in pkgutil.iter_modules(path):
search('.'.join([name, m]), search_term, show_pydoc_help)
def main():
args = _parse_args()
handler = logging.StreamHandler(stream=sys.stdout)
log.addHandler(handler)
log.setLevel(args['level'])
search(args['search_module'], args['search_term'], args['show_pydoc_help'])
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment