python setup脚本编写

本文转载自:http://lingxiankong.github.io/blog/2013/12/23/Python-setup/

前言

其实对于setup.py和setup.cfg的关注是从OpenStack的源码包中开始的,OpenStack每个组件的发布时都是一个tar.gz包,同样,我们直接从github上clone代码后也会发现两个文件的存在。当阅读Nova或Ceilometer(其他组件可能也会涉及)的代码时,发现setup.cfg中内容对于代码的理解有很大的影响。那么,到底setup.py和setup.cfg是干什么的?

setup.py

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


  1. from distutils.core import setup
  2. setup(name='foo',
  3. version='1.0',
  4. py_modules=['foo'],
  5. )

然后,运行python setup.py sdist为模块创建一个源码包


  1. root@network:/kong/setup# python setup.py sdist
  2. running sdist
  3. running check
  4. warning: check: missing required meta-data: url
  5. warning: check: missing meta-data: either (author and author_email) or (maintainer and maintainer_email) must be supplied
  6. warning: sdist: manifest template 'MANIFEST.in' does not exist (using default file list)
  7. warning: sdist: standard file not found: should have one of README, README.txt
  8. writing manifest file 'MANIFEST'
  9. creating foo-1.0
  10. making hard links in foo-1.0...
  11. hard linking foo.py -> foo-1.0
  12. hard linking setup.py -> foo-1.0
  13. creating dist
  14. Creating tar archive
  15. removing 'foo-1.0' (and everything under it)

在当前目录下,会创建dist目录,里面有个文件名为foo-1.0.tar.gz,这个就是可以分发的包。使用者拿到这个包后,解压,到foo-1.0目录下执行:python setup.py install,那么,foo.py就会被拷贝到python类路径下,可以被导入使用。


  1. root@network:/kong/setup/dist/foo-1.0# python setup.py install
  2. running install
  3. running build
  4. running build_py
  5. creating build
  6. creating build/lib.linux-x86_64-2.7
  7. copying foo.py -> build/lib.linux-x86_64-2.7
  8. running install_lib
  9. copying build/lib.linux-x86_64-2.7/foo.py -> /usr/local/lib/python2.7/dist-packages
  10. byte-compiling /usr/local/lib/python2.7/dist-packages/foo.py to foo.pyc
  11. running install_egg_info
  12. Removing /usr/local/lib/python2.7/dist-packages/foo-1.0.egg-info
  13. Writing /usr/local/lib/python2.7/dist-packages/foo-1.0.egg-info
  14. root@network:/kong/setup/dist/foo-1.0# ll /usr/local/lib/python2.7/dist-packages/foo
  15. foo-1.0.egg-info foo.py foo.pyc

对于Windows,可以执行python setup.py bdist_wininst生成一个exe文件;若要生成RPM包,执行python setup.py bdist_rpm,但系统必须有rpm命令的支持。可以运行下面的命令查看所有格式的支持:


  1. root@network:/kong/setup# python setup.py bdist --help-formats
  2. List of available distribution formats:
  3. --formats=rpm RPM distribution
  4. --formats=gztar gzip'ed tar file
  5. --formats=bztar bzip2'ed tar file
  6. --formats=ztar compressed tar file
  7. --formats=tar tar file
  8. --formats=wininst Windows executable installer
  9. --formats=zip ZIP file
  10. --formats=msi Microsoft Installer

setup函数还有一些参数:

1、packages
告诉Distutils需要处理那些包(包含__init__.py的文件夹)
2、package_dir
告诉Distutils哪些目录下的文件被映射到哪个源码包。一个例子:package_dir = {'': 'lib'},表示“root package”中的模块都在lib目录中。
3、ext_modules
是一个包含Extension实例的列表,Extension的定义也有一些参数。
4、ext_package
定义extension的相对路径
5、requires
定义依赖哪些模块
6、provides
定义可以为哪些模块提供依赖
7、scripts
指定python源码文件,可以从命令行执行。在安装时指定--install-script
8、package_data
通常包含与包实现相关的一些数据文件或类似于readme的文件。如果没有提供模板,会被添加到MANIFEST文件中。
9、data_files
指定其他的一些文件(如配置文件)


  1. setup(...,
  2. data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
  3. ('config', ['cfg/data.cfg']),
  4. ('/etc/init.d', ['init-script'])]
  5. )

规定了哪些文件被安装到哪些目录中。如果目录名是相对路径,则是相对于sys.prefixsys.exec_prefix的路径。如果没有提供模板,会被添加到MANIFEST文件中。

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

  • 所有由py_modulespackages指定的源码文件
  • 所有由ext_moduleslibraries指定的C源码文件
  • scripts指定的脚本文件
  • 类似于test/test*.py的文件
  • README.txt或README,setup.py,setup.cfg
  • 所有package_datadata_files指定的文件

还有一种方式是写一个manifest template,名为MANIFEST.in,定义如何生成MANIFEST文件,内容就是需要包含在分发包中的文件。一个MANIFEST.in文件如下:


  1. include *.txt
  2. recursive-include examples *.txt *.py
  3. prune examples/sample?/build

setup.cfg

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

setup.cfg文件的形式类似于


  1. [command]
  2. option=value
  3. ...

其中,command是Distutils的命令参数,option是参数选项,可以通过python setup.py --help build_ext方式获取。

需要注意的是,比如一个选项是--foo-bar,在setup.cfg中必须改成foo_bar的格式

符合Distutils2的setup.cfg有些不同。包含一些sections:
1、global
定义Distutils2的全局选项,可能包含commands,compilers,setup_hook(定义脚本,在setup.cfg被读取后执行,可以修改setup.cfg的配置)
2、metadata
3、files

  • packages_root:根目录
  • packages
  • modules
  • scripts
  • extra_files

4、command sections

Setuptools

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

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


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

如何让一个egg可被执行?


  1. setup(
  2. # other arguments here...
  3. entry_points = {
  4. 'setuptools.installation': [
  5. 'eggsecutable = my_package.some_module:main_func',
  6. ]
  7. }
  8. )

如何定义一个可选特性?


  1. setup(
  2. name="Project-A",
  3. ...
  4. extras_require = {
  5. 'PDF': ["ReportLab>=1.2", "RXP"],
  6. 'reST': ["docutils>=0.3"],
  7. }
  8. )

特性如何使用呢?需要与entry points结合使用:


  1. setup(
  2. name="Project-A",
  3. ...
  4. entry_points = {
  5. 'console_scripts': [
  6. 'rst2pdf = project_a.tools.pdfgen [PDF]',
  7. 'rst2html = project_a.tools.htmlgen',
  8. # more script entry points ...
  9. ],
  10. }
  11. )

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

插件式开发

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


  1. setup(
  2. # ...
  3. entry_points = {'blogtool.parsers': ['.rst = some_module:a_func']}
  4. )
  5. # 或者
  6. setup(
  7. # ...
  8. entry_points = """
  9. [blogtool.parsers]
  10. .rst = some.nested.module:SomeClass.some_classmethod [reST]
  11. """,
  12. extras_require = dict(reST = "Docutils>=0.3.5")
  13. )

Setuptools中的dependency_links

Setuptools有一个功能叫做 dependency_links

from setuptools import setup


  1. setup(
  2. # ...
  3. dependency_links = [
  4. "http://packages.example.com/snapshots/",
  5. "http://example2.com/p/bar-1.0.tar.gz",
  6. ],
  7. )

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

管理依赖

我们写依赖声明的时候需要在 setup.py 中写好抽象依赖(install_requires),在 requirements.txt 中写好具体的依赖,但是我们并不想维护两份依赖文件,这样会让我们很难做好同步。 requirements.txt 可以更好地处理这种情况,我们可以在有 setup.py 的目录里写下一个这样的 requirements.txt


  1. --index https://pypi.python.org/simple/
  2. -e .

这样 pip install -r requirements.txt 可以照常工作,它会先安装该文件路径下的包,然后继续开始解析抽象依赖,结合 --index 选项后转换为具体依赖然后再安装他们。

这个办法可以让我们解决一种类似这样的情形:比如你有两个或两个以上的包在一起开发但是是分开发行的,或者说你有一个尚未发布的包并把它分成了几个部分。如果你的顶层的包 依然仅仅按照“名字”来依赖的话,我们依然可以使用 requirements.txt 来安装开发版本的依赖包:


  1. --index https://pypi.python.org/simple/
  2. -e https://github.com/foo/bar.git#egg=bar
  3. -e .

这会首先从 https://github.com/foo/bar.Git 来安装包 bar , 然后进行到第二行 -e . ,开始安装 setup 中的抽象依赖,但是包 bar 已经安装过了, 所以 pip 会跳过安装。

Differences between distribute, distutils, setuptools and distutils2

Distutils is the standard tool used for packaging. It works rather well for simple needs, but is limited and not trivial to extend.

Setuptools is a project born from the desire to fill missing distutils functionality and explore new directions. In some subcommunities, it’s a de facto standard. It uses monkey-patching and magic that is frowned upon by Python core developers.

Distribute is a fork of Setuptools that was started by developers feeling that its development pace was too slow and that it was not possible to evolve it. Its development was considerably slowed when distutils2 was started by the same group. 2013-August update: distribute is merged back into setuptools and discontinued.

Distutils2 is a new distutils library, started as a fork of the distutils codebase, with good ideas taken from setup tools (of which some were thoroughly discussed in PEPs), and a basic installer inspired by pip. The actual name you use to import Distutils2 is packaging in the Python 3.3+ standard library, or distutils2 in 2.4+ and 3.1–3.2. (A backport will be available soon.) Distutils2 did not make the Python 3.3 release, and it was put on hold.

PBR

pbr是setuptools的辅助工具,最初是为OpenStack开发(https://launchpad.NET/pbr),基于d2to1

A library for managing setuptools packaging needs in a consistent manner.

pbr会读取和过滤setup.cfg中的数据,然后将解析后的数据提供给setup.py作为参数。包含如下功能:
1、从git中获取Version、AUTHORS and ChangeLog信息
2、Sphinx Autodoc。pbr会扫描project,找到所有模块,生成stub files
3、Requirements。pbr会读取requirements.txt,生成setup函数需要的install_requires/tests_require/dependency_links

这里需要注意,在requirements.txt文件的头部可以使用:--index https://pypi.python.org/simple/,这一行把一个抽象的依赖声明如 requests==1.2.0 转变为一个具体的依赖声明 requests 1.2.0 from pypi.python.org/simple/

4、long_description。从README.rst, README.txt or README file中生成long_description参数

使用pbr很简单:


  1. from setuptools import setup
  2. setup(
  3. setup_requires=['pbr'],
  4. pbr=True,
  5. )

使用pbr时,setup.cfg中有一些配置。在[files]中,有三个key:
packages:指定需要包含的包,行为类似于setuptools.find_packages
namespace_packages:指定namespace packages
data_files: 指定目的目录和源文件路径,一个示例:


  1. [files]
  2. data_files =
  3. etc/pbr = etc/pbr/*
  4. etc/neutron =
  5. etc/api-paste.ini
  6. etc/dhcp-agent.ini
  7. etc/init.d = neutron.init

[entry_points]段跟setuptools的方式相同。

Babel

A collection of tools for internationalizing Python applications

Babel是 Python 的一个国际化工具包,提供了对distutils或setuptools的支持,包含一些命令。

1、compile_catalog
类似于msgfmt工具,takes a message catalog from a PO file and compiles it to a binary MO file.


  1. $ ./setup.py compile_catalog --directory foobar/locale --locale pt_BR
  2. running compile_catalog
  3. compiling catalog to foobar/locale/pt_BR/LC_MESSAGES/messages.mo

2、extract_messages
类似于xgettext,it can extract localizable messages from a variety of difference source files, and generate a PO (portable object) template file from the collected messages.


  1. $ ./setup.py extract_messages --output-file foobar/locale/messages.pot
  2. running extract_messages
  3. extracting messages from foobar/__init__.py
  4. extracting messages from foobar/core.py
  5. ...
  6. writing PO template file to foobar/locale/messages.pot

3、update_catalog
类似于msgmerge,it updates an existing translations catalog based on a PO template file (POT).

setup.py和pip

表面上,python setup.py installpip install都是用来安装python包的,实际上,pip提供了更多的特性,更易于使用。体现在以下几个方面:

  • pip会自动下载依赖,而如果使用setup.py,则需要手动搜索和下载;
  • pip会自动管理包的信息,使卸载/更新更加方便和容易,使用pip uninstall即可。而使用setup.py,必须手动删除,有时容易出错。
  • pip提供了对virtualenv更好的整合。

结语

OK,讲了这么多琐碎的东西,现在去看看Nova或Ceilometer的setup脚本,是不是一下清晰了很多?!但说实话,setup.py的使用,我还不能讲的特别清楚,需要在后续的实战中学习。

时间: 2024-11-29 15:53:21

python setup脚本编写的相关文章

编写Python CGI脚本的教程

  这篇文章主要介绍了编写Python CGI脚本的教程,CGI是Python和服务器软件连接的接口,需要的朋友可以参考下 你是否想使用Python语言创建一个网页,或者处理用户从web表单输入的数据?这些任务可以通过Python CGI(公用网关接口)脚本以及一个Apache web服务器实现.当用户请求一个指定URL或者和网页交互(比如点击""提交"按钮)的时候,CGI脚本就会被web服务器启用.CGI脚本调用执行完毕后,它的输出结果就会被web服务器用来创建显示给用户的

需求驱动学习之Python(如何编写Python脚本替换文件中的多行字符?)

当一个人太执着于某一个东西的时候, 会错过很多美好的东西! Python值得学习的一个工具,不要局限在当前使用的语言中. 在大概3个月之前,Python对我来说一直是个迷.然而,就在3个月前我经理给我一个任务--删除(替换)所有项目源码文件中包含特定几行内容的 所有注释.整个项目源码的大小有1G,在Linux服务器(中高档)上编译需要半个多小时,可见代码量之大,不可能手动去一个一个改.肯定得用脚本去处 理,于是我想到了Python.在这之前没有接触过Python,花了2个星期一顿恶补之后,总算顺

以前编写的inno setup脚本,涵盖了自定义安装界面,调用dll等等应用 (转)

; Script generated by the Inno Setup 脚本向导.   ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!      ; 为1的时候表示定义成试用版本   #define VERSION_TYPE ReadIni('Setup.ini', 'SetupType', 'type', '0')      #if VERSION_TYPE == "2"       

Mustang与Rhino:Java 6中的脚本编写

最新的Java主版本(Java SE 6,又称Mustang)现在正处于beta版本阶段.虽然该版本并不像Java 5的更新那么多,但是它确实有一些有趣的新特性.毋庸置疑,其中的一个就是对脚本编写语言的支持. 诸如PHP.Ruby.JavaScript.Python(或Jython)之类的脚本编写语言被广泛应用于许多领域,并由于其灵活性和简单性而大受欢迎.由于脚本是被解释而不是被编译的,所以可以轻松地从命令行运行和测试它们.这就压缩了编码/测试周期,并提高了开发人员的生产率.脚本通常是动态键入的

如何创建和使用Python CGI脚本

如何创建和使用Python CGI脚本 你是否想使用Python语言创建一个网页,或者处理用户从web表单输入的数据?这些任务可以通过Python CGI(公用网关接口)脚本以及一个Apache web服务器实现.当用户请求一个指定URL或者和网页交互(比如点击""提交"按钮)的时候,CGI脚本就会被web服务器启用.CGI脚本调用执行完毕后,它的输出结果就会被web服务器用来创建显示给用户的网页. 配置Apache web服务器,让其能运行CGI脚本 在这个教程里,我们假设

Shell脚本编写的八条可靠建议(值得收藏)_linux shell

这八个建议,来源于键者几年来编写 shell 脚本的一些经验和教训.事实上开始写的时候还不止这几条,后来思索再三,去掉几条无关痛痒的,最后剩下八条.毫不夸张地说,每条都是精挑细选的,虽然有几点算是老生常谈了. 1. 指定bash shell 脚本的第一行,#!之后应该是什么?如果拿这个问题去问别人,不同的人的回答可能各不相同. 我见过/usr/bin/env bash,也见过/bin/bash,还有/usr/bin/bash,还有/bin/sh,还有/usr/bin/env sh.这算是编程界的

用lotus server.load进行负载测试的的脚本编写问题

问题描述 用lotus server.load进行负载测试的的脚本编写问题 现在公司要lotus开发完了要用lotus server.load进行负载测试测试脚本各种不会写有没有大神做过求教.怎么在脚本里面模拟用户操作比如打开视图表单添加数据之类的

LoadRunner脚本编写(二)

今天有朋友问我,关于loadrunner脚本编第二篇什么时候写,我告诉他都没什么东西了.要学习一门语言,基本的语法和思想很重要.现在每个人都识字,那是不是每个识字的人都可以当作家.不可能,因为大多数人没有作家的思想.编程是一门艺术,我们可以把代码写得很优美,而中国的程序员为什么叫代码工人呢?国为国外的程序员在写一篇优美的"散文",中国的程序员在写"说明文".中国的程序员只是根据需求把一个产品通过语言描述清楚. 扯远了,最近变啰嗦了,呵呵!我想表达的意思就是行编程基本

LoadRunner脚本编写(一)

性能测试工程师要懂代码么?答案是必须的.好多测试员认为在loadrunner中编写脚本很难很牛X ,主要是大多测试人员并未做过开发工作,大学的那点程序基础也忘记的差不多了.还有非计算机专业出身的测试员,所以对代码望都比较望而生畏. 好多人认为loadrunner只支持C语言(包括我之前也一直这么认为),因为loadrunner默认的脚本是C语言的,其实它支持目前所有主流的语言,如:Java User.VB User.VB script User .Javascript User.Mmicroso