Python库项目结构最佳实践:导入和测试

3
我想重构一个我在日常工作中经常使用的 Python 库,以开源的方式发布到 Github 上。 在这样做之前,我希望符合一些 Python 项目结构的最佳实践。 我将在下面描述我想要做的事情,并欢迎您提出建议。
以下是我的库(mylib)的结构:
mylib/
   /examples/
       simple_example.py
   /mylib/
       __init__.py
       foo.py
       bar.py
   /tests/
       test_foo.py
       test_bar.py

这里是文件:
#foo.py
def Foo():
    print("foo.Foo")


#bar.py
import foo

def Bar():
    print("bar.Bar")
    foo.Foo()


#test_bar.py
from ..mylib import bar #doesnt work!

class TestBar(unittest.TestCase):
    def test_1(self):
        bar.Bar()
        self.assertEqual(True, True)

if __name__ == '__main__':
    unittest.main()


#simple_example.py
from .. import foo #doesnt work!
from .. import bar #doesnt work!

if __name__ == '__main__':  
    foo.Foo()
    bar.Bar()

我想要做的是:
1. 最好从/mylib/examples/中执行simple_example.py。
$cd myapp
$cd examples
$python simple_example.py
Traceback (most recent call last):
  File "simple_example.py", line 2, in <module>
    from .. import foo
SystemError: Parent module '' not loaded, cannot perform relative import

2- 从理想情况下的/mylib/tests/中执行单个测试文件:

$cd myapp
$cd tests
$python test_bar.py
Traceback (most recent call last):
  File "test_bar.py", line 3, in <module>
    from ..mylib import bar
SystemError: Parent module '' not loaded, cannot perform relative import    

3- 从mylib根目录执行所有测试
$cd myapp
$python -m unittest discover tests #same problem as above!

所以,simple_example.py和test_bar.py中的问题在于导入语句。 最好的修复方法是什么?
请注意,我想使用Python标准库unittest进行单元测试。
谢谢,
Charlie
1个回答

1
当运行测试代码时,您需要进行绝对导入。这是因为在运行单元测试等操作时,应该假设您的“库”以本地开发模式安装进行测试--不要使用相对导入,因为您不在同一个包中。
例如,在您的test_foo.py文件中,以下是如何导入的:
# test_foo.py
from mylib.foo import Foo

# ... your test code here

通常情况下,你应该只在库代码内部使用相对导入,而不是在测试中使用 =)
希望这可以帮到你。
编辑:在此之前,您还需要以开发模式安装您的库。您可以通过以下两种方式之一来完成:
$ python setup.py develop

或者

$ pip install -e .

任何一个上述命令都将检查您的项目的setup.py文件,该文件告诉Python如何构建/创建您的包,并将其本地安装,以便您可以运行测试/操作它。

也许我漏掉了什么(例如调整sys.path),但是使用您的解决方案,我仍然会收到错误消息“ImportError: No module named mylib.foo”。 - Charlie
你必须先以开发模式安装你的库。在运行任何测试之前,你需要运行 python setup.py develop 命令。我会更新我的答案以反映这一点。 - rdegges
请注意,如果您想在bar.py中使用foo模块,您需要写成from . import foo。需要注意的是,我正在使用Python 3.5版本。您可以在David Beazley在PyCon2015的演讲中的17分40秒处找到更多细节,请参考该链接:https://www.youtube.com/watch?v=0oTh1CXRaQ0 - Charlie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接