我如何在运行时检查python模块是否有效而不导入它?
我有一个包,其中只包含一个子包,我需要在运行时导入其中一个子包,但我需要测试它们是否有效。以下是我的文件夹结构:我如何在运行时检查python模块是否有效而不导入它?,python,python-2.7,eval,python-import,python-importlib,Python,Python 2.7,Eval,Python Import,Python Importlib,我有一个包,其中只包含一个子包,我需要在运行时导入其中一个子包,但我需要测试它们是否有效。以下是我的文件夹结构: game/ __init__.py game1/ __init__.py constants.py ... game2/ __init__.py constants.py ... 目前,启动时运行的代码执行以下操作: import pkgutil import game as _game # Detect the known games f
game/
__init__.py
game1/
__init__.py
constants.py
...
game2/
__init__.py
constants.py
...
目前,启动时运行的代码执行以下操作:
import pkgutil
import game as _game
# Detect the known games
for importer,modname,ispkg in pkgutil.iter_modules(_game.__path__):
if not ispkg: continue # game support modules are packages
# Equivalent of "from game import <modname>"
try:
module = __import__('game',globals(),locals(),[modname],-1)
except ImportError:
deprint(u'Error in game support module:', modname, traceback=True)
continue
submod = getattr(module,modname)
if not hasattr(submod,'fsName') or not hasattr(submod,'exe'): continue
_allGames[submod.fsName.lower()] = submod
因此,如果存在与导入语句与vis错误检查完全等效的语句-而不导入模块,我需要一个明确的答案。这是我的问题,许多回答者和评论者回答了不同的问题。如果您想编译文件而不导入它(在当前解释器中),您可以使用: 上述代码将错误写入
std.error
。如果要引发异常,则必须将doraise
设置为True
(默认值False
)。因此,您的代码将是:
from py_compile import compile, PyCompileError
try:
compile('/path/to/valid/python/file.py', doraise=True)
valid_file = True
except PyCompileError:
valid_file = False
根据:
将源文件编译为字节码并写出字节码缓存文件。源代码从名为file的文件加载。字节码写入cfile
,默认为file+'c'
('o',如果在当前解释器中启用了优化)。如果指定了dfile,则在错误消息中它将用作源文件的名称,而不是文件名。如果doraise
为true,则编译文件时遇到错误时会引发PyCompileError
。如果doraise
为false(默认值),则会将错误字符串写入sys.stderr
,但不会引发异常
检查以确保未导入编译的模块(在当前解释器中):
也许您正在寻找
py\u compile
或compileall
模块。这里是文档:
您可以将所需的作为模块加载,并从程序中调用它。
例如:
import py_compile
try:
py_compile.compile(your_py_file, doraise=True)
module_ok = True
except py_compile.PyCompileError:
module_ok = False
你不能有效地做你想做的事。为了查看包是否“有效”,您需要运行它——而不仅仅是检查它是否存在——因为它可能有错误或未满足的依赖项 使用
pycompile
和compileall
仅测试是否可以编译python文件,而不是导入模块。这两者之间有很大的区别
import foo
可以表示/foo.py
或/foo/\uuuuu init\uuuuu.py
/site-packages/
中有多个版本,或者python正在寻找许多可能的模块位置之一,那么事情就会变得棘手 from makebelieve import nothing
raise ValueError("ABORT")
上面的代码将被编译,但如果您导入它们。。。如果未安装makebelie
,它将引发ImportError,如果安装,它将引发ValueError
我的建议是:
sys.modules.keys()中的内容代码>。如果您担心加载的外部模块,您可以覆盖import
,以记录软件包加载的内容。我写的一个糟糕的分析包就是一个例子:[我忘了我从哪里想到要覆盖导入。可能是芹菜
?]正如一些人所指出的,卸载模块完全依赖于解释器——但几乎所有选项都有许多缺点
popen
启动新的解释器。iepopen('python','-m','module_name')
。如果您对每个需要的模块都这样做,那么这将有很大的开销(每个解释器和导入的开销),但是您可以编写一个“.py”文件来导入您需要的所有内容,并尝试运行它。在这两种情况下,您都必须分析输出——因为导入“有效”包可能会在执行过程中导致可接受的错误。我记不起子流程是否继承了您的环境变量,但我相信它确实继承了。子进程是一个全新的操作系统进程/解释器,因此模块将加载到该短暂进程的内存中我相信,
imp.find_模块
至少满足您的一些要求:
快速测试表明它不会触发导入:
>>> import imp
>>> import sys
>>> len(sys.modules)
47
>>> imp.find_module('email')
(None, 'C:\\Python27\\lib\\email', ('', '', 5))
>>> len(sys.modules)
47
>>> import email
>>> len(sys.modules)
70
下面是我的一些代码(尝试对模块进行分类)中的一个示例用法:我们已经有了一个(免责声明:我没有编写该代码,我只是当前的维护者),其加载模块
:
def load_module(self,fullname):
if fullname in sys.modules:
return sys.modules[fullname]
else: # set to avoid reimporting recursively
sys.modules[fullname] = imp.new_module(fullname)
if isinstance(fullname,unicode):
filename = fullname.replace(u'.',u'\\')
ext = u'.py'
initfile = u'__init__'
else:
filename = fullname.replace('.','\\')
ext = '.py'
initfile = '__init__'
try:
if os.path.exists(filename+ext):
with open(filename+ext,'U') as fp:
mod = imp.load_source(fullname,filename+ext,fp)
sys.modules[fullname] = mod
mod.__loader__ = self
else:
mod = sys.modules[fullname]
mod.__loader__ = self
mod.__file__ = os.path.join(os.getcwd(),filename)
mod.__path__ = [filename]
#init file
initfile = os.path.join(filename,initfile+ext)
if os.path.exists(initfile):
with open(initfile,'U') as fp:
code = fp.read()
exec compile(code, initfile, 'exec') in mod.__dict__
return mod
except Exception as e: # wrap in ImportError a la python2 - will keep
# the original traceback even if import errors nest
print 'fail', filename+ext
raise ImportError, u'caused by ' + repr(e), sys.exc_info()[2]
因此,我想我可以用可重写的方法替换访问sys.modules
缓存的部分,这些方法将在我的重写中不使用缓存:
因此:
并定义:
class FakeUnicodeImporter(UnicodeImporter):
_modules_to_discard = {}
def _check_imported(self, fullname):
return fullname in sys.modules or fullname in self._modules_to_discard
def _get_imported(self, fullname):
try:
return sys.modules[fullname]
except KeyError:
return self._modules_to_discard[fullname]
def _add_to_imported(self, fullname, mod):
self._modules_to_discard[fullname] = mod
@classmethod
def cleanup(cls):
cls._modules_to_discard.clear()
然后,我在sys.meta_路径中添加了导入器,很好:
importer = sys.meta_path[0]
try:
if not hasattr(sys,'frozen'):
sys.meta_path = [fake_importer()]
perform_the_imports() # see question
finally:
fake_importer.cleanup()
sys.meta_path = [importer]
对吧??错了
Traceback (most recent call last):
File "bash\bush.py", line 74, in __supportedGames
module = __import__('game',globals(),locals(),[modname],-1)
File "Wrye Bash Launcher.pyw", line 83, in load_module
exec compile(code, initfile, 'exec') in mod.__dict__
File "bash\game\game1\__init__.py", line 29, in <module>
from .constants import *
ImportError: caused by SystemError("Parent module 'bash.game.game1' not loaded, cannot perform relative import",)
然而,这以一种新的方式爆发了——或者说是两种方式:
- 对游戏/软件包的引用保存在sys.modules中的
顶级软件包实例中:bash
因为bash\ __init__.py the_code_in_question_is_here.py game\ ...
作为game
导入。该引用包含对所有bash.game
子包的引用,因此这些子包从未被垃圾收集game1、game2、
- 同一
模块实例将对另一个模块(brec)的引用保留为bash
。此引用作为bash.brec
从导入。。在game\game1中导入brec而不触发导入,以更新
。然而,在另一个模块中,从…brec import SomeClass导入SomeClass
格式的导入确实触发了导入,并且brec模块的另一个实例在sys.modules中结束。该实例有一个未更新的
,并使用属性进行了破坏SomeClass
class FakeUnicodeImporter(UnicodeImporter): _modules_to_discard = {} def _check_imported(self, fullname): return fullname in sys.modules or fullname in self._modules_to_discard def _get_imported(self, fullname): try: return sys.modules[fullname] except KeyError: return self._modules_to_discard[fullname] def _add_to_imported(self, fullname, mod): self._modules_to_discard[fullname] = mod @classmethod def cleanup(cls): cls._modules_to_discard.clear()
importer = sys.meta_path[0] try: if not hasattr(sys,'frozen'): sys.meta_path = [fake_importer()] perform_the_imports() # see question finally: fake_importer.cleanup() sys.meta_path = [importer]
Traceback (most recent call last): File "bash\bush.py", line 74, in __supportedGames module = __import__('game',globals(),locals(),[modname],-1) File "Wrye Bash Launcher.pyw", line 83, in load_module exec compile(code, initfile, 'exec') in mod.__dict__ File "bash\game\game1\__init__.py", line 29, in <module> from .constants import * ImportError: caused by SystemError("Parent module 'bash.game.game1' not loaded, cannot perform relative import",)
class FakeUnicodeImporter(UnicodeImporter): _modules_to_discard = set() def _check_imported(self, fullname): return fullname in sys.modules or fullname in self._modules_to_discard def _add_to_imported(self, fullname, mod): super(FakeUnicodeImporter, self)._add_to_imported(fullname, mod) self._modules_to_discard.add(fullname) @classmethod def cleanup(cls): for m in cls._modules_to_discard: del sys.modules[m]
bash\ __init__.py the_code_in_question_is_here.py game\ ...