PYTHONPATH 和 sys.path

另一位开发人员和我对于是否应该使用 PYTHONPATHsys.path来允许 Python 在用户(例如开发人员)目录中查找 Python 包存在分歧。

我们有一个具有典型目录结构的 Python 项目:

Project
setup.py
package
__init__.py
lib.py
script.py

在 script.py 中,我们需要执行 import package.lib

但是,当从用户目录工作时,需要执行其他操作。我的解决方案是设置我的 PYTHONPATH包括 "~/Project"。另一个开发人员想把这行代码放在 script.py 的开头:

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

这样 Python 就可以找到 package.lib的本地副本。

我认为这是一个坏主意,因为这一行只对开发人员或从本地副本运行的人有用,但我不能给出一个好的理由为什么这是一个坏主意。

我们应该使用 PYTOHNPATHsys.path,或者是罚款?

124956 次浏览

If the only reason to modify the path is for developers working from their working tree, then you should use an installation tool to set up your environment for you. virtualenv is very popular, and if you are using setuptools, you can simply run setup.py develop to semi-install the working tree in your current Python installation.

I think, that in this case using PYTHONPATH is a better thing, mostly because it doesn't introduce (questionable) unneccessary code.

After all, if you think of it, your user doesn't need that sys.path thing, because your package will get installed into site-packages, because you will be using a packaging system.

If the user chooses to run from a "local copy", as you call it, then I've observed, that the usual practice is to state, that the package needs to be added to PYTHONPATH manually, if used outside the site-packages.

I hate PYTHONPATH. I find it brittle and annoying to set on a per-user basis (especially for daemon users) and keep track of as project folders move around. I would much rather set sys.path in the invoke scripts for standalone projects.

However sys.path.append isn't the way to do it. You can easily get duplicates, and it doesn't sort out .pth files. Better (and more readable): site.addsitedir.

And script.py wouldn't normally be the more appropriate place to do it, as it's inside the package you want to make available on the path. Library modules should certainly not be touching sys.path themselves. Instead, you'd normally have a hashbanged-script outside the package that you use to instantiate and run the app, and it's in this trivial wrapper script you'd put deployment details like sys.path-frobbing.

Along with the many other reasons mentioned already, you could also point outh that hard-coding

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

is brittle because it presumes the location of script.py -- it will only work if script.py is located in Project/package. It will break if a user decides to move/copy/symlink script.py (almost) anywhere else.

In general I would consider setting up of an environment variable (like PYTHONPATH) to be a bad practice. While this might be fine for a one off debugging but using this as
a regular practice might not be a good idea.

Usage of environment variable leads to situations like "it works for me" when some one
else reports problems in the code base. Also one might carry the same practice with the test environment as well, leading to situations like the tests running fine for a particular developer but probably failing when some one launches the tests.

Neither hacking PYTHONPATH nor sys.path is a good idea due to the before mentioned reasons. And for linking the current project into the site-packages folder there is actually a better way than python setup.py develop, as explained here:

pip install --editable path/to/project

If you don't already have a setup.py in your project's root folder, this one is good enough to start with:

from setuptools import setup
setup('project')