使用 setup.py 制作 Python 安装包

发布时间: 更新时间: 总字数:1299 阅读时间:3m 作者: IP上海 分享 网址

如何制作python的安装包?python模块的打包工具又有哪些?在OpenStack源码包中到底setup.py和setup.cfg是干什么的?

Demo

我们从例子开始。假设你要分发一个叫 foo 的模块,文件名 foo.pysetup.py 内容如下:

[root@xiexianbin_cn py]# ll
total 8
-rw-r--r-- 1 root root  13 Nov 15 22:07 foo.py
-rw-r--r-- 1 root root 106 Nov 15 22:07 setup.py
[root@xiexianbin_cn py]# cat foo.py
print 'echo'
[root@xiexianbin_cn py]# cat setup.py
from distutils.core import setup

setup(name='foo',
	version='1.0',
	py_modules=['foo'],
)

然后,创建一个源码包:

$ python setup.py sdist

在当前目录下,会创建 dist 目录,里面有个文件名为 foo-1.0.tar.gz,这时就是可以分发的包。操作如下:

[root@xiexianbin_cn py]# ll dist/
total 4
-rw-r--r-- 1 root root 399 Nov 15 22:08 foo-1.0.tar.gz
[root@xiexianbin_cn py]# cd dist/
[root@xiexianbin_cn dist]# tar -zxvf foo-1.0.tar.gz
foo-1.0/
foo-1.0/foo.py
foo-1.0/PKG-INFO
foo-1.0/setup.py
[root@xiexianbin_cn dist]# cd foo-1.0/
[root@xiexianbin_cn foo-1.0]# python setup.py install
running install
running build
running build_py
creating build
creating build/lib
copying foo.py -> build/lib
running install_lib
copying build/lib/foo.py -> /usr/lib/python2.7/site-packages
byte-compiling /usr/lib/python2.7/site-packages/foo.py to foo.pyc
running install_egg_info
Writing /usr/lib/python2.7/site-packages/foo-1.0-py2.7.egg-info
[root@xiexianbin_cn foo-1.0]# ll /usr/lib/python2.7/site-packages/foo
foo-1.0-py2.7.egg-info  foo.py                  foo.pyc

这时 foo.py 就会被拷贝到python类路径下,可以被导入使用。

执行sdist命令时,默认会打包哪些东西呢?

  • 所有由py_modules或packages指定的源码文件
  • 所有由ext_modules或libraries指定的C源码文件
  • 由scripts指定的脚本文件
  • 类似于test/test*.py的文件
  • README.txt或README,setup.py,setup.cfg
  • 所有package_data或data_files指定的文件

setup.cfg 介绍

setup.cfg 提供一种方式,可以让包的开发者提供命令的默认选项,同时为用户提供修改的机会。对setup.cfg 的解析,是在 setup.py 之后,在命令行执行前。

setup.cfg 文件的形式类似如下:

[command]
option=value
...

其中,command 是 Distutils 的命令参数,option 是参数选项,可以通过 python setup.py --help build_ext 方式获取。需要注意的是,比如一个选项是--foo-bar,在setup.cfg中必须改成 foo_bar 的格式。

Setuptools

上面的 setup.py 和 setup.cfg 都是遵循python标准库中的Distutils,而setuptools工具针对Python官方的distutils做了很多针对性的功能增强,比如依赖检查,动态扩展等。很多高级功能我就不详述了,自己也没有用过,等用的时候再作补充。

一个典型的遵循setuptools的脚本:

from setuptools import setup, find_packages
setup(
	name = "HelloWorld",
	version = "0.1",
	packages = find_packages(),
	scripts = ['say_hello.py'],
	# Project uses reStructuredText, so ensure that the docutils get
	# installed or upgraded on the target machine
	install_requires = ['docutils>=0.3'],
	package_data = {
		# If any package contains *.txt or *.rst files, include them:
		'': ['*.txt', '*.rst'],
		# And include any *.msg files found in the 'hello' package, too:
		'hello': ['*.msg'],
	},
	# metadata for upload to PyPI
	author = "Me",
	author_email = "me@example.com",
	description = "This is an Example Package",
	license = "PSF",
	keywords = "hello world example examples",
	url = "http://example.com/HelloWorld/",   # project home page, if any
	# could also include long_description, download_url, classifiers, etc.
)

如何让一个egg可被执行?

setup(
	# other arguments here...
	entry_points = {
		'setuptools.installation': [
			'eggsecutable = my_package.some_module:main_func',
		]
	}
)

如何定义一个可选依赖?

setup(
	name="Project-A",
	...
	extras_require = {
		'PDF':  ["ReportLab>=1.2", "RXP"],
		'reST': ["docutils>=0.3"],
	}
)s

entry points使用:

setup(
	name="Project-A",
	...
	entry_points = {
		'console_scripts': [
			'rst2pdf = project_a.tools.pdfgen [PDF]',
			'rst2html = project_a.tools.htmlgen',
			# more script entry points ...
		],
	}
)

或者被其他project依赖:install_requires = [“Project-A[PDF]”]

插件式开发

我想大家最熟悉的就是这个特性了吧。比如一个博客系统想用不同的插件支持不同的语言输出格式,那么就可以定义一个“entry point group”,不同的插件就可以注册“entry point”,插件注册的示例:

setup(
	# ...
	entry_points = {'blogtool.parsers': ['.rst = some_module:a_func']}
)

或者

setup(
	# ...
	entry_points = """
		[blogtool.parsers]
		.rst = some.nested.module:SomeClass.some_classmethod [reST]
	""",
	extras_require = dict(reST = "Docutils>=0.3.5")
)

Setuptools有一个功能叫做 dependency_links

from setuptools import setup

setup(
	# ...
	dependency_links = [
		"http://packages.example.com/snapshots/",
		"http://example2.com/p/bar-1.0.tar.gz",
	],
)

这一功能除去了依赖的抽象特性,直接把依赖的获取url标在了setup.py里。就像在Go语言中修改依赖包一样,我们只需要修改依赖链中每个包的 dependency_links 。

参考

  1. https://bootstrap.pypa.io/get-pip.py
  2. http://docs.python.org/2/distutils/introduction.html
  3. http://pythonhosted.org/setuptools/
  4. https://docs.python.org/3/distutils/setupscript.html
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数