gpt4 book ai didi

python - 在不运行Python包的情况下检查它的内容?

转载 作者:行者123 更新时间:2023-12-01 04:47:52 25 4
gpt4 key购买 nike

我想要一个函数,给定 name这导致了 NameError ,可以识别Python包,可能是 import编辑解决它。

这部分相当简单,我已经完成了,但现在我有一个额外的问题:我想在不引起副作用的情况下完成它。这是我现在使用的代码:

def necessaryImportFor(name):
from pkgutil import walk_packages
for package in walk_packages():
if package[1] == name:
return name
try:
if hasattr(__import__(package[1]), name):
return package[1]
except Exception as e:
print("Can't check " + package[1] + " on account of a " + e.__class__.__name__ + ": " + str(e))
print("No possible import satisfies " + name)

问题是这段代码实际上__import__每个模块。这意味着导入每个模块的所有副作用都会发生。在测试我的代码时,我发现导入所有模块可能导致的副作用包括:

  • 启动 tkinter 应用程序
  • 通过 getpass 请求密码
  • 请求其他 inputraw_input
  • 打印消息 ( import this )
  • 打开网站 ( import antigravity )

我考虑的一个可能的解决方案是找到每个模块的路径(如何?在我看来,唯一的方法是通过 import ing 模块,然后使用 inspect 中的一些方法) ,然后解析它以查找每个 class , def ,和=它本身并不在 class 内或def ,但这似乎是一个巨大的 PITA,我认为它不适用于用 C/C++ 而不是纯 Python 实现的模块。

另一种可能性是启动一个子 Python 实例,其输出重定向到 devnull并在那里执行检查,如果花费太长时间就杀死它。这将解决前四个项目符号,第五个项目符号是一种特殊情况,我可以跳过 antigravity 。但是必须在这个单一函数中启动数千个 Python 实例似乎有点……繁重且效率低下。

有没有人有我没有考虑过的更好的解决方案?例如,是否有一种简单的方法可以告诉 Python 生成 AST 或其他内容,而无需实际导入模块?

最佳答案

所以我最终编写了一些方法,可以列出源文件中的所有内容,而无需导入源文件。

ast 模块似乎没有特别详细的文档记录,因此这有点像 PITA 试图弄清楚如何提取所有感兴趣的内容。尽管如此,经过今天约 6 个小时的反复试验,我还是能够将其组合在一起,并在我的计算机上的 3000 多个 Python 源文件上运行它,而没有引发任何异常。

def listImportablesFromAST(ast_):
from ast import (Assign, ClassDef, FunctionDef, Import, ImportFrom, Name,
For, Tuple, TryExcept, TryFinally, With)

if isinstance(ast_, (ClassDef, FunctionDef)):
return [ast_.name]
elif isinstance(ast_, (Import, ImportFrom)):
return [name.asname if name.asname else name.name for name in ast_.names]

ret = []

if isinstance(ast_, Assign):
for target in ast_.targets:
if isinstance(target, Tuple):
ret.extend([elt.id for elt in target.elts])
elif isinstance(target, Name):
ret.append(target.id)
return ret

# These two attributes cover everything of interest from If, Module,
# and While. They also cover parts of For, TryExcept, TryFinally, and With.
if hasattr(ast_, 'body') and isinstance(ast_.body, list):
for innerAST in ast_.body:
ret.extend(listImportablesFromAST(innerAST))
if hasattr(ast_, 'orelse'):
for innerAST in ast_.orelse:
ret.extend(listImportablesFromAST(innerAST))

if isinstance(ast_, For):
target = ast_.target
if isinstance(target, Tuple):
ret.extend([elt.id for elt in target.elts])
else:
ret.append(target.id)
elif isinstance(ast_, TryExcept):
for innerAST in ast_.handlers:
ret.extend(listImportablesFromAST(innerAST))
elif isinstance(ast_, TryFinally):
for innerAST in ast_.finalbody:
ret.extend(listImportablesFromAST(innerAST))
elif isinstance(ast_, With):
if ast_.optional_vars:
ret.append(ast_.optional_vars.id)
return ret

def listImportablesFromSource(source, filename = '<Unknown>'):
from ast import parse
return listImportablesFromAST(parse(source, filename))

def listImportablesFromSourceFile(filename):
with open(filename) as f:
source = f.read()
return listImportablesFromSource(source, filename)

上面的代码涵盖了标题问题:如何在不运行 Python 包的情况下检查它的内容?

但这给你留下了另一个问题:如何仅从 Python 包的名称获取其路径?

这是我为解决这个问题而写的:

class PathToSourceFileException(Exception):
pass

class PackageMissingChildException(PathToSourceFileException):
pass

class PackageMissingInitException(PathToSourceFileException):
pass

class NotASourceFileException(PathToSourceFileException):
pass

def pathToSourceFile(name):
'''
Given a name, returns the path to the source file, if possible.
Otherwise raises an ImportError or subclass of PathToSourceFileException.
'''

from os.path import dirname, isdir, isfile, join

if '.' in name:
parentSource = pathToSourceFile('.'.join(name.split('.')[:-1]))
path = join(dirname(parentSource), name.split('.')[-1])
if isdir(path):
path = join(path, '__init__.py')
if isfile(path):
return path
raise PackageMissingInitException()
path += '.py'
if isfile(path):
return path
raise PackageMissingChildException()

from imp import find_module, PKG_DIRECTORY, PY_SOURCE

f, path, (suffix, mode, type_) = find_module(name)
if f:
f.close()
if type_ == PY_SOURCE:
return path
elif type_ == PKG_DIRECTORY:
path = join(path, '__init__.py')
if isfile(path):
return path
raise PackageMissingInitException()
raise NotASourceFileException('Name ' + name + ' refers to the file at path ' + path + ' which is not that of a source file.')

一起尝试这两段代码,我有这个功能:

def listImportablesFromName(name, allowImport = False):
try:
return listImportablesFromSourceFile(pathToSourceFile(name))
except PathToSourceFileException:
if not allowImport:
raise
return dir(__import__(name))

最后,这是我在问题中提到的我想要的函数的实现:

def necessaryImportFor(name):
packageNames = []

def nameHandler(name):
packageNames.append(name)

from pkgutil import walk_packages
for package in walk_packages(onerror=nameHandler):
nameHandler(package[1])
# Suggestion: Sort package names by count of '.', so shallower packages are searched first.
for package in packageNames:
# Suggestion: just skip any package that starts with 'test.'
try:
if name in listImportablesForName(package):
return package
except ImportError:
pass
except PathToSourceFileException:
pass
return None

这就是我度过周日的方式。

关于python - 在不运行Python包的情况下检查它的内容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29049400/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com