用户应该通过pip安装我们的python软件包,或者可以从github存储库中克隆它并从源代码中安装。出于多种原因,例如,用户不应从源树目录中运行import Foo
。缺少C扩展名(numpy
具有相同的问题:read here)。因此,我们想检查用户是否在源代码树中运行import Foo
,但是如何在支持Python 3和2的情况下干净,高效且可靠地执行此操作?
编辑:请注意,此处的源代码树也定义为代码的下载位置(例如,通过git或从源归档文件下载),并且它与代码的安装目录也相反。
我们考虑了以下内容:
setup.py
或其他文件,例如PKG-INFO
,这些文件应仅出现在源文件中。并不是那么优雅,而且检查文件是否存在并不便宜,因为每次import Foo
有人都会进行此检查。同样,也没有什么可以阻止某人将setup.py
放在其lib/python3.X/site-packages/
目录或类似目录中的源树之外。setup.py
的内容,但它也增加了开销,而且解析起来也不是很干净。Foo/__init__.py
的想法,以表明我们现在不在源代码树之内。答案 0 :(得分:6)
由于您在评论中提到了numpy
,并且想像他们一样做,但是还没有完全理解,所以我想我可以分解一下,看看您是否可以实施类似的流程。
您要寻找的错误开始here,这是您在评论和答案中链接的内容,因此您已经知道这一点。它只是尝试导入__config__.py
,如果不存在或无法导入,则会失败。
try:
from numpy.__config__ import show as show_config
except ImportError:
msg = """Error importing numpy: you should not try to import numpy from
its source directory; please exit the numpy source tree, and relaunch
your python interpreter from there."""
raise ImportError(msg)
那么 __ config __。py 文件从哪里来,这有什么帮助?让我们跟随下面...
安装该软件包后,将调用setup
来运行,然后依次执行一些configuration actions。从本质上讲,这就是要确保该软件包已正确安装,而不是从下载目录中运行(我认为这是您要确保的内容)。
这里的关键是这一行:
config.make_config_py() # installs __config__.py
它是从distutils/misc_util.py
导入的,我们可以一直跟踪到here。
def make_config_py(self,name='__config__'):
"""Generate package __config__.py file containing system_info
information used during building the package.
This file is installed to the
package installation directory.
"""
self.py_modules.append((self.name, name, generate_config_py))
然后运行哪个here,并在其中写入一些系统信息和您的__config__.py
函数的show()
文件。
摘要
尝试导入__config__.py
并失败,如果未运行setup.py
,则会生成您想引发的错误,这将触发该文件的正确创建。这样不仅可以确保进行文件检查,还可以确保文件仅存在于安装目录中。在每次导入时都导入一个额外的文件仍然会产生一些开销,但是无论您做什么,都要首先添加进行此检查的开销。
建议
我认为您可以在完成相同任务的同时,实现numpy
正在执行的任务的轻量化版本。
删除distutils
子功能,并在setup.py
文件中创建选中的文件,作为标准安装的一部分。它只会在安装后存在于安装的目录中,除非用户伪造了该目录,否则它不会存在于其他目录中(在这种情况下,他们可能会解决您可能尝试的任何问题)。
作为替代方案(不知道您的应用程序和设置文件在做什么),您可能通常具有一个通常已导入的函数,该函数对应用程序的运行不是关键,但可以使用(在{{在1}}的情况下,这些功能是有关安装的信息,例如numpy
,而不是将这些功能保留在现在放置的位置,而是将它们作为已创建文件的一部分,然后至少要加载一些否则您将从其他任何地方加载。
使用此方法,无论您导入什么,都会带来一些开销,或者引发错误。我认为就引发错误的方法而言,因为它们不在已安装的目录中工作,这是一种非常干净直接的方法。不管使用哪种方法,使用该方法都会带来一些开销,因此我将集中精力保持开销低,简单而不会导致错误。
我不会做复杂的事情,例如解析安装文件或在某处修改诸如version()
之类的必要文件。我认为这些方法更容易出错是正确的。
检查__init__.py
是否存在是可以的,但是我认为它比尝试setup.py
干净得多,后者已经作为标准Python函数进行了优化。他们完成了类似的工作,但我认为实施import
样式会更加直接。