关于python:PEP8 导入不在sys.path文件的顶部

PEP8 – import not at top of file with sys.path

问题

PEP8有一个将导入放在文件顶部的规则:

Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.

但是,在某些情况下,我可能想做如下的事情:

1
2
3
4
import sys
sys.path.insert("..", 0)

import my_module

在这种情况下,pep8命令行实用程序会标记我的代码:

E402 module level import not at top of file

实现符合sys.path修改的PEP8的最佳方法是什么?

为什么?

我有这段代码是因为我遵循了《Python漫游指南》中给出的项目结构。

该指南建议我有一个my_module文件夹,与tests文件夹分开,这两个文件夹都在同一个目录中。如果我想从tests访问my_module,我想我需要将..添加到sys.path中。


如果只有少数进口产品,您可以忽略这些import行上的pep8:

1
2
3
import sys
sys.path.insert("..", 0)
import my_module  # noqa: E402


通常我在项目的一个子目录foo/tests中有多个包含测试的文件,而我测试的模块在foo/src中。为了在没有导入错误的情况下从foo/tests运行测试,我创建了一个类似这样的文件foo/tests/pathmagic.py

1
2
3
4
5
6
7
8
"""Path hack to make tests work."""

import os
import sys

bp = os.path.dirname(os.path.realpath('.')).split(os.sep)
modpath = os.sep.join(bp + ['src'])
sys.path.insert(0, modpath)

在每个测试文件中,然后我使用

1
import pathmagic  # noqa

作为第一个导入。"noqa"注释防止pycodestylepep8抱怨未使用的进口。


还有另一个解决办法。

1
2
3
4
5
6
7
8
import sys
... all your other imports...

sys.path.insert("..", 0)
try:
    import my_module
except:
    raise


我刚刚遇到了一个类似的问题,我想我找到了一个比公认答案更好的解决方案。

创建一个执行实际sys.path操作的pathmagic模块,但在上下文管理器中进行更改:

1
2
3
4
5
6
7
8
9
10
11
12
13
"""Path hack to make tests work."""

import os
import sys

class context:
    def __enter__(self):
        bp = os.path.dirname(os.path.realpath('.')).split(os.sep)
        modpath = os.sep.join(bp + ['src'])
        sys.path.insert(0, modpath)

    def __exit__(self, *args):
        pass

然后,在您的测试文件中(或者您需要的任何地方),您可以执行以下操作:

1
2
3
4
5
import pathmagic

with pathmagic.context():
    import my_module
    # ...

这样,您就不会收到flake8/pycodestyle的任何投诉,也不需要特别的评论,而且结构似乎很有意义。

为了更加整洁,考虑恢复__exit__块中的路径,尽管这可能会导致延迟导入出现问题(如果将模块代码放在上下文之外),因此可能不值得麻烦。

编辑:在另一个问题的答案中看到了一个更简单的技巧:在你的进口商品下面加上assert pathmagic,以避免noqa的评论。


为了符合PEP8,您应该将项目路径包含到python路径,以便执行相对/绝对导入。

为此,您可以查看以下答案:将目录永久添加到pythonpath