如何轻松分发有Python模块依赖的Python软件?在Unix上安装Python包的挫折
我的目标是发布一个Python包,这个包依赖于几个其他广泛使用的Python包。我的包需要一些写得很好的、在Pypi上有的包,比如pandas、scipy和numpy,并在setup.py文件中指定了需要的版本,比如“numpy >= 1.5”。
我发现,对于那些对Unix很熟悉但并不是Python打包专家的用户来说,安装像我这样的包是非常令人沮丧的,几乎是不可能的,即使他们知道怎么写Python代码。即使使用那些应该很简单的包管理工具,他们也会遇到困难。我在想,是否有更简单的方法来解决这个痛苦的过程,或者我的经历只是反映了当前Python打包和分发的困难现状。
假设用户把你的包下载到他们的系统上。大多数人会尝试“简单粗暴”地安装,比如使用:
$ python setup.py install
因为如果你在网上搜索安装Python包的说明,通常会出现这样的内容。但对于大多数用户来说,这样做会失败,因为他们通常没有在Unix/Linux服务器上进行全局安装的权限。经过更多的搜索,他们会发现“--prefix”选项,并尝试:
$ python setup.py install --prefix=/some/local/dir
由于用户对Python打包的复杂性并不熟悉,他们会随便选择一个目录作为--prefix
的参数,比如"~/software/mypackage/"
。这个目录并不是一个干净整洁的目录,所有其他Python包都在这里,因为大多数用户并不知道这些细节。如果他们安装另一个包“myotherpackage”,可能会传入"~/software/myotherpackage"
,你可以想象这将导致后续在PYTHONPATH
上进行繁琐的调整和其他复杂问题。
继续安装过程,调用"setup.py install"
并带上"--prefix"
,一旦用户尝试使用这个包,就会失败,尽管看起来安装是正确的,因为某个依赖包可能缺失(比如pandas、scipy或numpy),而且没有使用包管理工具。他们会尝试单独安装这些包。即使成功了,由于给"--prefix"
的目录不标准,这些包也不可避免地不会在PYTHONPATH
中,耐心的用户会尝试修改他们的PYTHONPATH
,以便让依赖包可见。
在这个阶段,用户可能会被一个懂Python的朋友告知,他们应该使用像"easy_install"
这样的包管理工具,来安装软件并处理依赖关系。在安装"easy_install"
后(这可能很困难),他们会尝试:
$ easy_install setup.py
这同样会失败,因为用户通常没有权限在生产Unix服务器上全局安装软件。经过更多阅读,他们会了解到"--user"
选项,并尝试:
$ easy_install setup.py --user
他们会遇到错误:
usage: easy_install [options] requirement_or_url ...
or: easy_install --help
error: option --user not recognized
他们会非常困惑,为什么他们的easy_install
没有--user
选项,而网上明显有描述这个选项的页面。他们可能会尝试升级他们的easy_install
到最新版本,但发现仍然失败。
如果他们继续咨询Python打包专家,他们会发现有两个版本的easy_install
,都叫做"easy_install"
,这让人更加困惑,一个是“distribute”的一部分,另一个是“setuptools”的一部分。恰好只有"distribute"
的easy_install
支持"--user"
,而绝大多数服务器/系统管理员安装的是"setuptools"
的easy_install
,因此本地安装将不可能。请记住,这些“distribute”和“setuptools”之间的区别对不懂Python包管理的人来说是毫无意义且难以理解的。
在这个时候,我会失去90%甚至是最有决心、最聪明和最耐心的用户,他们尝试安装我的软件包——这也是理所当然的!他们想要安装的是一款用Python写的软件,而不是成为Python包分发的专家,这实在是太复杂和混乱了。他们会放弃,并对浪费的时间感到沮丧。
pip/virtualenv而不是easy_install
。安装pip
和virtualenv
,并弄清楚这些工具是如何工作的,以及它们与传统的"python setup.py"
或"easy_install"
调用有什么不同,本身就是耗时且困难的,再次对于那些只是想安装一款简单Python软件并使用它的用户来说,这要求太高了。即使是那些走这条路的用户,也会对他们用easy_install
或setup.py install --prefix
安装的依赖是否仍然可以与pip/virtualenv
一起使用感到困惑,或者一切是否需要从头开始重新安装。
如果其中一个或多个包依赖于安装与默认版本不同的Python,这个问题会更加严重。确保你的Python包管理器使用你想要的Python版本,并且所需的依赖安装在相关的Python 2.x目录而不是Python 2.y中,这将让用户感到无比沮丧,他们肯定会在这个阶段放弃。
有没有更简单的方法来安装Python软件,而不需要用户深入了解Python包、路径和位置的所有技术细节?例如,我不是一个大Java用户,但偶尔会使用一些Java工具,我不记得在安装Java软件时需要担心X和Y的依赖关系,我也不知道Java的包管理是怎么回事(我很高兴我不知道——我只是想用一个恰好是用Java写的工具)。我记得下载一个Jar包后,你只需获取它,它通常就能工作。
Python有没有类似的东西?一种分发软件的方式,不依赖于用户追踪所有这些依赖和版本?一种可能将所有相关包编译成一个自包含的东西,用户只需下载并作为二进制文件使用的方法?
我想强调的是,这种沮丧即使在向熟悉Unix的用户分发包的狭窄目标下也会发生,这使得问题更简单,因为不需要担心跨平台的问题等。我假设用户熟悉Unix,甚至可能知道Python,但他们并不想了解(也不想被告知)Python打包的细节和各种包管理器之间的复杂关系。这个问题的一个令人不安的特点是,即使所有的Python包依赖都是知名、写得好的、在Pypi上维护良好的包,比如Pandas、Scipy和Numpy,这个问题仍然存在。并不是说我依赖于一些不知名的、没有正确形成的包:相反,我使用的是许多人可能依赖的最主流的包。
任何帮助或建议都将不胜感激。我认为Python是一种很棒的语言,拥有很好的库,但我发现几乎不可能以一种简单的方式分发我用它编写的软件(一旦它有依赖关系),让人们能够轻松地在本地安装并运行。我想澄清的是,我编写的软件不是供程序使用的Python库,而是用户作为独立程序运行的可执行脚本。谢谢。
2 个回答
我们目前正在努力让用户更容易地在不同平台上安装Python软件,特别是可以参考这两个链接:https://python-packaging-user-guide.readthedocs.org/en/latest/future.html 和 http://www.python.org/dev/peps/pep-0453/。
现在,关于两个竞争版本的easy_install的问题已经解决了,之前的竞争版本“distribute”已经合并回setuptools的主开发线。
目前关于跨平台分发和安装Python软件的最佳建议可以在这里找到:https://packaging.python.org/。
我们还开发一些软件项目,这些项目依赖于numpy、scipy和其他PyPI包。目前,管理远程安装的最佳工具是zc.buildout。这个工具非常好用。你只需从他们的网站下载一个启动脚本,然后把它和你的包一起分发。接着,你需要写一个“本地部署”文件,通常叫做buildout.cfg
,这个文件会说明如何在本地安装这个包。你把bootstrap.py
文件和buildout.cfg
文件一起打包发出去——我们在自己的Python包中使用MANIFEST.in
文件,确保这两个文件会被包含在PyPI分发的zip或tar包里。当用户解压这个包时,它会执行两个命令:
$ python bootstrap.py # this will download zc.buildout and setuptools
$ ./bin/buildout # this will build and **locally** install your package + deps
这个包会被编译,所有的依赖项都会本地安装,这意味着安装你包的用户甚至不需要管理员权限,这个功能很不错。脚本通常会放在./bin
目录下,用户解压后可以直接执行它们。zc.buildout
使用setuptools
与PyPI进行交互,所以你期待的功能都能直接使用。
如果你觉得zc.buildout
的功能不够强大,可以很容易地扩展它——你可以创建所谓的“配方”,帮助用户生成额外的配置文件,从网上下载其他东西,或者实例化自定义程序。zc.buildout网站上有一个视频教程,详细解释了如何使用buildout以及如何扩展它。我们的项目Bob广泛使用buildout来分发科学用途的包。如果你有兴趣,可以访问这个页面,里面有详细的说明,教我们的开发者如何设置他们的Python包,以便其他人可以使用zc.buildout
在本地构建和安装它们。