1-扯淡!Python包管理工具的发展史

before

Python3.6 + Windows10

关于Python包管理工具的发展和使用本次共整理为三篇:

  1. 第一篇(即本篇)主要要概述Python包管理工具的发展史,以及各包管理工具的简单使用。
  2. 第二篇则是主要介绍包(模块)的分发,也就是如何将你的包打包,然后分发给别人使用。
  3. 第三篇则是主要研究在包分发时,如何加密你的源码,防止源码泄露。

有失误或者引用链接不可用时,多多包涵,也欢迎留言斧正。
开始吧!

about

Python最吸引人的地方之一就是有丰富的资源,包括许多的第三方库。那么针对这些库的管理(安装、卸载等),就需要有专门的工具来完成。而Python经过这么多年的发展,经历了大大小小的版本,而跟随Python版本升级,Python的包管理工具也在不断地演化。幸运的是,如果你是位年轻的Python爱好者,那么你非常幸运地,因为你现在使用的包管理工具——pip,简单而又好用。足以应付绝大多数的场景了。但观今宜鉴古,无古不成今,我们今天就扯一扯Python包管理工具的发展。

几种包管理工具

  • distutils
  • setuptools
  • distlib
  • distribute
  • disutils2
  • pip

包管理工具的发展

distutils

distutils包管理工具发布于2000年,它支持在Python中构建和安装其他模块,包括纯模块,也可以是扩展模块、包。

我们可以使用通过distutils实现的setup.py来操作包。

for example

  • 如果你想分发一个名为foo的模块,

首先有一个test目录:

test/
	|- foo.py
	|- setup.py

你可以在setup.py中可以这样写:

from distutils.core import setup
setup(name='foo', version='1.0', py_modules=['foo'])

foo.py可以简单的写上一行代码:

print("this is foo modules...")

然后在终端(cmd)中可以使用Python解释器执行setup.py文件:

python setup.py sdist

sdist将创建一个存档文件(例如,Unix上的tarball,Windows上的ZIP文件),其中包含我们的安装脚本setup.pyfoo.py。存档文件将命名foo-1.0.tar.gz(或.zip),并将解压缩到目录中foo-1.0

如果要下载使用该模块,那么,我们所要做的就是下载foo-1.0.tar.gz(或.zip),解压缩它,并从 foo-1.0目录运行:

python setup.py install

上述命令执行后,foo.py这个模块将会安装在我们的Python解释器的Lib\site-packages目录内,之后,我们就可以像正常调用模块一样使用foo模块:

Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:14:34) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import foo
this is foo modules...

除此之外,setup.py也可以将模块打包成rpm包或者exe安装包:

python setup.py bdist_rpm     
python setup.py bdist_wininst

更多关于setup.py脚本,请参考Writing the Setup Script,注意:bdist_rpm命令使用rpm可执行文件,因此必须在基于rpm的系统上运行,例如Red Hat LinuxSuSE LinuxMandrake Linux

setuptools

如果你已经阅读过distutils的文档,你可能会看到这样一段话并引发你的好奇:

Most Python users will not want to use this module directly, but instead use the cross-version tools maintained by the Python Packaging Authority. In particular, setuptools is an enhanced alternative to distutils that provides:

	- support for declaring project dependencies
	- additional mechanisms for configuring which files to include in source releases (including plugins for integration with version control systems)
	- the ability to declare project “entry points”, which can be used as the basis for application plugin systems
	- the ability to automatically generate Windows command line executables at installation time rather than needing to prebuild them
	- consistent behaviour across all supported Python versions
The recommended pip installer runs all setup.py scripts with setuptools, even if the script itself only imports distutils. Refer to the Python Packaging User Guide for more information.

setuptools增强distutils 功能的集合,功能齐全,主动维护且稳定的库,旨在帮助打包Python项目,其中包装包括:

  • Python包和模块定义
  • 分发包元数据
  • 测试钩子
  • 项目安装
  • 特定于平台的详细信息
  • Python 3支持

setuptools基本使用

  • 安装或者升级setuptools
pip install -U setuptools
  • 一个简单的例子

有这样一个目录结构:

test/
	|- say_hello.py
	|- setup.py

say_hello.py内只需一行简单的代码即可:

print("Hello girl")

那么setup.py可以这样写:

from setuptools import setup, find_packages
setup(
    name='hello',
    version='1.0',
    packages=find_packages(),
    scripts=['say_hello.py'],
)

接下来,在终端中用Python解释器执行setup.py脚本:

python setup.py sdist

此时会生成包括disthello.egg-info两个目录,我们一路解压dist目录内的压缩包,直到碰到say_hello.py文件,然后继续用Python解释器执行:

python setup.py install

然后,say_hello模块将会安装在我们的Python解释器的Lib\site-packages目录内,之后,我们就可以像正常调用模块一样使用该模块了:

Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:14:34) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import say_hello
Hello girl

demo写到这里,是不是,是不是跟distutils的操作一样了?是的,就是一样的。但仅仅如此并不足以说setuptools的强大,上面只是演示了setuptools如何构建和分发包,接下来,研究点高级点的。

setuptools包含了easy_install这个管理工具。easy_install发布于2004年,作为setuptools的一部分,它可以很轻松的管理包。通俗的说,setuptools是项目名称而easy_install这是该项目产生的工具。

  • 下载和安装包

我们从pypi中的SQLObject上下载一个SQLObject包到本地。

easy_install SQLObject	# 官网
easy_install SQLObject-3.7.0.tar.gz  # 下到本地的是tar.gz包
python36 -m easy_install SQLObject-3.7.0.tar.gz	# 指定Python解释器下载
  • 从指定的下载链接中下载并安装
easy_install -f http://pythonpaste.org/package_index.html SQLObject	# 官网
easy_install -f https://pypi.tuna.tsinghua.edu.cn/simple sqlobject==3.7  # 清华源下载
easy_install -i https://pypi.tuna.tsinghua.edu.cn/simple sqlobject==3.7  # 清华源下载
  • 将已安装的软件包升级到PyPI上列出的最新版本
easy_install --upgrade PyProtocols	# 官网
easy_install -U sqlobject

easy_install还又其他的玩法就一一列举了,更多参见[easy_install工具](easy_install --upgrade PyProtocols),这里需要说明的是,当你看到有说ez_install的时候,也代指easy_install,因为你下载到本地的easy_install就叫ez_install.py,说白了,setuptools是基础的组件,而easy_install是具体的工具实现。

distutils2

Distutils2是取代Distutils的包装库。它有三个主要受众:

  • 想要分发代码的Python作者
  • 想要安装模块或应用程序的最终用户
  • 需要支持库构建的包装相关工具的开发人员

它包含更多的功能,并试图希望以packaging作为名称而成为Python3.3标准库的一部分,而且希望能一统包管理的江湖,从2009年开始开发直到2012年——就暂时停滞了,更多参见Pypi关于distutils2的描述。

distlib

distlibdistutils2的部分实现,它为distutils2/packaging提供底层的功能来增加高级的API,并试图使其便于使用。如果希望了解更多的关于distlib,这里你可以认为是distutils2项目没有成为Python3.3标准库一部分的原因,请参见官网对于distlib的相关概述

distribute

可能Python的开发者们在开发setuptools的时候,原本半年的任务,俩月就就干完了!但也不能闲着啊,就着手搞个版本升级啥的好拿工资!以setuptools开发太慢了为由,就做了一个新的分支,没错,这个分支就是distribute!,估计又搞了一个月,就又搞完了!那也不能闲着啊,剩下俩月就把这个'disribute'分支有合并到setuptools中了。所以distributesetuptools是一个东东,更多参见distribute

pip

自从2008年发布后,pip就成为了Python包管理的事实标准。pip认为是easy_install的代替品,但pip仍然是建立在setuptools的基础组件之上。

pip希望不再使用eggs格式而希望采用源码发行版,也就是python setup.py sdist来创建包,因为这样可以充分利用[requirements file format提供的很多便利的功能。

pip的基本使用点这里。除此之外,pip除了上述链接中的简单实用之外,也可以利用requirements来实现依赖安装,在setup.py中,也存在一个install_requirements表来指定依赖的安装,欲了解更多setup.pyrequirements的区别猛戳这里,或者来看这篇Stack Overflow的文章。

另外,pip也支持git/svn/hg等流行的VCS系统,比如直接从gzzip压缩包安装,支持搜索包、指定服务器安装等功能。

这篇文档介绍了pipeasy_install的区别。

而关于requirements,for example,当我们开发Django项目的时候,开始第一个项目使用的是Django1.8版本;而另一个项目则使用Django2.1版本,要知道版本不同,语法可能不同。并且两个项目所需要的依赖也不同,那么怎么来让两个项目隔离开,并且跟Python解释器的大的环境做隔离呢(比如Python环境中有300个模块,两个项目只用到了其中若干个)?你项目上线后如何下载依赖模块(你可能都不知道你的整个项目都是用到了什么模块,因为你开发时都下载到了Python解释器环境中了)?这里就用到了requirements,为了解决上述问题,我们一般在开发时,都会创建一个虚拟环境,将新的项目创建在虚拟环境中,这个环境下的解释器只为你的项目服务,那么你这里就可以通过requirements的相关命令(比如在你的项目目录下)来生成一个requirements.txt文件,命令如下:

pip install requirements	# 首先要下载该模块
pip freeze > requirements.txt	# 生成依赖关系的文本文件

该文本文件中只记录了你这个项目所用到的依赖和版本。这样再将项目上线后,我们可以通过pip再将所有的依赖一次性的下载回来,这大大的节约了时间。

pip install -r requirements.txt

关于requirements使用的更多参考。

Eggs

Eggs在2004年成为setuptools的一种文件格式,它使用.egg作为扩展名,用于Python模块的安装。

一个简单的例子

现在需要这样的一个目录格式:

say_egg/   # 首先有个say_egg目录
	|- hi_egg/	# say_egg目录中有个hi_egg目录
		|- __init__.py	# hi_egg目录中又有__init__.py和hi_girl.py
		|- hi_girl.py
	|- setup.py		# say_egg目录中又有setup.py

现在,开始填充代码。hi_girl.py中:

def hello():
    print('hello 个锤子,你个蛋蛋')

__init__.py中需要要一行代码:

name = 'hi_egg'

setup.py这么写:

from setuptools import setup, find_packages
setup(
    name='hi_egg',   # 该属性与你的包名hi_egg对应
    version='1.0',   # 版本
    packages=find_packages()    # setuptools会自动查找包名
)

然后在与setup.py文件同级的目录中,用终端执行:

python setup.py bdist_egg

之后,你会在say_egg/目录发现几个新生成的目录文件,而在dist/目录内就会发现有个hi_egg-1.0-py3.6.egg文件,你如果将扩展名egg改为zip的话,就会发现其中包含两个目录EGG-INFOhi_egg,而hi_egg目录就是我们可以将要下载使用的包。

现在,让我们将终端定位到这个dist/目录中,执行下载命令:

python -m easy_install hi_egg-1.0-py3.6.egg

然后,这个包就下载到解释器的Lib/site-packages目录中了,我们来测试:

Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:14:34) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import hi_egg
>>> hi_egg.name
'hi_egg'
>>> from hi_egg import hi_girl
>>> hi_girl.hello()
hello 个锤子,你个蛋蛋

我们在pypi中下载的egg包也是同样的方式下载到我们的库中的。更多关于egg的信息,请点击PEAK——PythonEggs,我们后面会再用egg搞些事情。

wheel

Python据说是孪生的,不是是卵生,哦,是蛋生的,所以,蛋孵化出来,就是出来个——wheelwheel同样用于Python模块的安装(废话!),于2012年在PEP 427中被正式引入,并且希望代替eggswheel使用.whl作为扩展名(你也可以简单理解wheel的本质是zip包格式),提供bdist_wheel作为setuptools的扩展命令,来生成wheel包。wheel包内包含了py文件,以及经过编译的pyd文件,这样,就可以根据在不具备编译的环境中,选择合适的Python环境进行安装。

首先要想使用wheel必须下载,这里可以使用pip命令来下载:

pip install wheel
python -m wheel version	# 查看版本

一般用法

egg文件转为wheel包:

python -m wheel convert hi_egg-1.0-py3.6.egg

然后,你就发现一个本地多个了一个wheel文件hi_egg-1.0-py3.6-none-any.whl

既然说wheel包是压缩包,那么就可以解压缩:

python -m wheel unpack hi_egg-1.0-py3.6-none-any.whl

在本地的同级目录,就得到一个目录hi_egg-1.0,该目录内同样包含两个子目录hi_egg-1.0.dist-infohi_egg,而hi_egg中则就是我们想要的包。此时我们可以手动把包拷贝到解释器的site-packages中去,或者可以下载:

pip install hi_egg-1.0-py3.6-none-any.whl

来测试,如果你跟我一样是一路测试下来的话,请把在egg部分下载好的包卸载,卸载命令是:

pip uninstall hi_egg  # 遇到提示选择Y

然后在来测试:

Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:14:34) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import hi_egg
>>> hi_egg.name
'hi_egg'
>>> from hi_egg import hi_girl
>>> hi_girl.hello()
hello 个锤子,你个蛋蛋

欲了解更多wheel,请参考Python WheelWheel vs Eggs

小结

我们该如何选择包管理工具呢?一般情况下请毋庸置疑的选择pip,如果遇到模块分发的话,肯定选择wheel的格式!

另外,模块分发请参见博客

无论是wheelegg或是pip都是基于setuptools,所以请牢记这个玩意儿。

最后,学习几个专业术语:

term description
模块(module) Python中代码可重用性的基本单元:由一些其他代码导入的代码块。这里有三种类型的模块:纯Python模块,扩展模块和包
纯Python模块(pure Python module) 用Python编写并包含在单个.py文件中的模块(以及可能的相关.pyc文件)。有时也被称为“纯模块”
扩展模块(extension module) 用Python实现的低级语言编写的模块:C / C ++ for Python,Java for Jython。通常包含在单个可动态加载的预编译文件中,例如.soUnix上的Python扩展的共享对象()文件,.pydWindows上的Python扩展的DLL(给定扩展名)或Jython扩展的Java类文件。(请注意,目前,Distutils仅处理Python的C / C ++扩展。)
包(package) 包含其他模块的模块; 通常包含在文件系统的目录中,并通过文件的存在区别于其他目录__init__.py
根包(root package) 包层次结构的根。(这不是真正的包,因为它没有__init__.py文件。但是我们必须把它称之为。)绝大多数标准库都在根包中,许多小型的独立第三方模块也是如此。不属于较大的模块集合。与常规软件包不同,根软件包中的模块可以在许多目录中找到:实际上,列出的每个目录都会sys.path向根软件包提供模块

关于distutils的术语

term description
模块分发(module distribution) 要安装分布式在一起作为一个单一的下载资源的意思和Python模块的集合集体。一些众所周知的模块分发的示例是Numeric Python,PyXML,Pillow或mxBase。(这将被称为,除了该术语已在Python上下文中使用:单个模块分发可能包含零个,一个或多个Python包。)
纯模块分发(pure module distribution) 模块分发,仅包含纯Python模块和包。有时也被称为“纯粹的分配”。
非纯模块分发(non-pure module distribution) 包含至少一个扩展模块的模块分发。有时也被称为“非纯粹的分布”。
分配根(distribution root) 源树(或源代码分发)的顶级目录; setup.py存在的目录。通常 setup.py会从此目录运行。

本文是经过参考网上的资源,自己加以整理而成,如有失误,欢迎指正,更多的参考:

distutils | 使用MinGW在MS Windows平台上构建Python模块 | pypi | easy_install | python包管理工具解惑(本文也主要参考该博客的思路)

posted @ 2019-01-15 16:54  听雨危楼  阅读(809)  评论(1编辑  收藏  举报