检查包是否从源树中导入

时间:2019-04-29 09:57:21

标签: python package python-import

用户应该通过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的想法,以表明我们现在不在源代码树之内。

1 个答案:

答案 0 :(得分:6)

由于您在评论中提到了numpy,并且想像他们一样做,但是还没有完全理解,所以我想我可以分解一下,看看您是否可以实施类似的流程。


__ init __。py

您要寻找的错误开始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.py

安装该软件包后,将调用setup来运行,然后依次执行一些configuration actions。从本质上讲,这就是要确保该软件包已正确安装,而不是从下载目录中运行(我认为这是您要确保的内容)。

这里的关键是这一行:

config.make_config_py() # installs __config__.py

misc_util.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样式会更加直接。