我正试图打包我的项目以供分发,但在运行模块时遇到了RuntimeWarning
。
我在Python mailing list上发现了一个错误报告,它表明RuntimeWarning
是Python 3.5.2中引入的新行为。
在阅读错误报告时,似乎出现了一个双重导入,并且这个RuntimeWarning
在警告用户时是正确的。但是,我不知道为了避免这个问题,我需要对自己的项目结构做什么改变。
这是我试图“正确”构建的第一个项目。我想有一个整洁的布局,当我推的代码,和一个项目结构,可以克隆和运行的其他人容易。
我的结构主要基于http://docs.python-guide.org/en/latest/writing/structure/。
我在下面添加了一个最小工作示例的详细信息。
为了复制这个问题,我使用python -m
运行主文件:
(py36) X:\test_proj>python -m proj.proj
C:\Users\Matthew\Anaconda\envs\py36\lib\runpy.py:125: RuntimeWarning:
'proj.proj' found in sys.modules after import of package 'proj', but prior
to execution of 'proj.proj'; this may result in unpredictable behaviour
warn(RuntimeWarning(msg))
This is a test project.`
运行我的测试很好:
(py36) X:\test_proj>python -m unittest tests.test_proj
This is a test project.
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
复制该问题的项目结构如下:
myproject/
proj/
__init__.py
proj.py
tests/
__init__.py
context.py
test_proj.py
在文件proj/proj.py
中:
def main():
print('This is a test project.')
raise ValueError
if __name__ == '__main__':
main()
在proj/__init__.py
中:
from .proj import main
在tests/context.py
中:
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import proj
最后,在tests/test_proj.py
中:
import unittest
from .context import proj
class SampleTestCase(unittest.TestCase):
"""Test case for this sample project"""
def test_raise_error(self):
"""Test that we correctly raise an error."""
with self.assertRaises(ValueError):
proj.main()
if __name__ == '__main__':
unittest.main()
有谁能帮我纠正我的项目结构,以避免这种双重导入的情况?如果有任何帮助,我们将不胜感激。
对于这种特殊情况,双重导入警告是由于
proj/__init__.py
中的这一行引起的:这一行的意思是,当
-m
开关实现完成import proj
步骤时,proj.proj
已经导入了作为导入父包的副作用。避免警告
为了避免出现警告,您需要找到一种方法来确保导入父包不会隐式导入使用
-m
开关执行的包。解决的两个主要选项是:
from .proj import main
行(正如@John Moutafis建议的那样),假设这可以在不破坏API兼容性保证的情况下完成;或者从
proj
子模块中删除if __name__ == "__main__":
块,并将其替换为一个单独的proj/__main__.py
文件,该文件只执行以下操作:如果使用选项2,那么命令行调用也将更改为仅
python -m proj
,而不是引用子模块。选项2的一个更向后兼容的变体是在不从当前子模块中删除CLI块的情况下添加
__main__.py
,当与DeprecationWarning
结合使用时,这是一个特别好的方法:如果
proj/__main__.py
已经用于其他目的,那么您还可以将python -m proj.proj
替换为python -m proj.proj_cli
,其中proj/proj_cli.py
看起来像:为什么存在警告?
当
-m
开关实现即将开始并在__main__
模块中再次运行已导入模块的代码时,会发出此警告,这意味着您将拥有它定义的所有内容的两个不同副本-类、函数、容器等根据应用程序的具体情况,这可能工作正常(这就是为什么它是一个警告而不是一个错误),或者它可能导致奇怪的行为,如模块级的状态修改未按预期共享,或者甚至由于异常处理程序试图从一个实例捕获异常类型而未捕获异常而引发的异常使用了另一个实例的类型。
因此出现了模糊的
this may cause unpredictable behaviour
警告-如果由于运行模块的顶级代码两次而导致出现问题,那么症状可能是任何事情。如何调试更复杂的案例?
虽然在这个特定的示例中,副作用导入直接在
proj/__init__.py
中,但是有一个更微妙、更难调试的变量,父包会执行以下操作:然后是
some_other_module
(或它导入的模块)执行以下操作:假设错误行为是可复制的,调试此类问题的主要方法是以详细模式运行python并检查导入顺序:
这个特定的例子只显示了启动时Fedora上Python2.7所做的基本导入集。在调试与本问题类似的双导入
RuntimeWarning
时,您将在详细输出中搜索“import proj”和“import proj.proj”行,然后仔细查看“import proj.proj”行之前的导入。python-m有点棘手。@ncoghlan已经提供了详细的信息。 当我们尝试使用python-m运行时,默认情况下sys.path/pythonpath中的所有包都被导入。如果您的包对路径中目录中的任何内容都有import语句,则会出现上述警告。
我的PYTHONPATH已经有了项目目录。所以当我这样做的时候
系统抛出警告。因此,如果路径在python path中,则不需要显式导入
如果你看一下double import trap,你会看到:
在
tests/context.py
中删除:
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
这可能会导致问题,并且您的代码仍然按预期工作。
根据评论编辑:
您可以尝试更改代码中的某些部分:
proj/__init__.py
可以完全为空在
test_proj.py
上,应按如下方式更改导入:PS:我无法在Linux上用您的初始代码或我的建议重现警告。
相关问题 更多 >
编程相关推荐