我正在尝试编写一个 Python 脚本/函数来帮助确定从哪里导入一些东西。我想要这个功能专门用于 PyQt4,但它可以扩展并用于 Python 标准库。
为此,我需要为给定的搜索词动态搜索模块、共享对象、类以及可能更多“类型”的事物。
我想传递一个模块/类/等。作为字符串,动态导入,然后搜索。
该函数的接口将是这样的:
search(object_to_search, search_term)
例子:
search('datetime', 'today') -> 'datetime.datetime.today'
search('PyQt4', 'NonModal') -> 'PyQt4.QtCore.Qt.NonModal'
search('PyQt4', 'QIcon') -> 'PyQt4.QtGui.QIcon'
我想这也可以支持像“*”这样的通配符来搜索所有内容,
sys.path
但这超出了我目前所关心的范围。
这种类型的脚本对 PyQt4 尤其有用。上面有很多“类似枚举”的东西NonModal
,找到导入或引用它们的位置可能很麻烦。
我倾向于大量使用 C++ Qt 文档来获取 PyQt4 信息,因为将示例转换为 Python 通常很容易。C++ Qt 文档更全面。找到适合 Qt 的示例代码也容易得多。但是,如果不手动搜索文档或猜测,在 PyQt4 中很难找到其中一些“类枚举”属性的导入位置。目标是自动化这个过程,让脚本告诉我我正在寻找的东西的位置。
我尝试了几种不同的实现方式,包括使用 递归搜索对象dir()
、使用inspect
模块以及用于共享对象的 hacky 版本的导入/发现。下面是我的各种实现,其中没有一个真正工作得很好。
这种事情甚至可能吗?如果是这样,最好的方法是什么?另外,我试图通过省略内置的东西、函数等来减少我搜索的东西的数量,但我不相信这是必要的,甚至是正确的。
尝试的解决方案:
def search_obj_inspect(obj, search_term):
"""Search given object for 'search_term' with 'inspect' module"""
import inspect
def searchable_type(obj):
is_class = inspect.isclass(obj)
root_type_or_obj = is_class and obj not in [object, type]
abstract_class = is_class and inspect.isabstract(obj)
method_or_func = inspect.ismethod(obj) or inspect.isfunction(obj)
try:
hidden_attribute = obj.__name__.startswith('__')
except AttributeError:
hidden_attribute = False
searchable = True
# Avoid infinite recursion when looking through 'object' and 'type' b/c
# they are members of each other
if True in [abstract_class, root_type_or_obj, hidden_attribute,
method_or_func, inspect.isbuiltin(obj)]:
searchable = False
return searchable
# FIXME: Search obj itself for search term? obj.__name__ == search_term?
for n, v in inspect.getmembers(obj,
predicate=lambda x: searchable_type(x)):
if search_term in n:
try:
return v.__name__
except AttributeError:
return str(v)
return None
def search_obj_dir(obj, search_term):
"""Search given object and all it's python submodules for attr"""
import re
import types
SEARCHABLE_TYPES = (types.ModuleType, types.ClassType, types.InstanceType,
types.DictProxyType)
for item in dir(obj):
if item.startswith('__'):
continue
if re.search(search_term, item):
return obj.__dict__[item].__module__
else:
try:
submod = obj.__dict__[item]
except (KeyError, AttributeError):
# Nothing left to recursively search
continue
if not isinstance(submod, SEARCHABLE_TYPES):
continue
#mod = search_obj_dir(submod, search_term)
#if mod:
#return '.'.join([mod, submod.__name__])
return None
def search_so(module, attr):
"""Search given modules shared objects for attr"""
import os
import re
try:
path = module.__path__[0]
except (AttributeError, IndexError):
return False
for root, dirs, files in os.walk(path):
for filename in files:
if filename.endswith('.so'):
py_module_name = filename.split('.')[0]
so_module = __import__(module.__name__, globals(), locals(),
[py_module_name])
if re.search(attr, py_module_name):
return ''.join([module.__name__, '.', py_module_name])
try:
found = search_obj_dir(
so_module.__dict__[py_module_name], attr)
except KeyError:
# This isn't a top-level so, might be a subpackage
# FIXME: Could recursively search SO as well, but imports
# might get weird
continue
if found:
return found