Distutils可以用来在Python环境中构建和安装额外的模块。新的模块可以是纯Python的,也可以是用C/C++写的扩展模块,或者可以是Python包,包中包含了由C和Python编写的模块。

一:Distutils简介

1.1概念和术语

对于模块开发者以及需要安装模块的使用者来说,Distutils的使用都很简单,作为一个开发者,除了编写源码之外,还需要:

编写setup脚本(一般是setup.py);

编写一个setup配置文件(可选);

创建一个源码发布;

创建一个或多个构建(二进制)发布(可选);

有些模块开发者在开发时不会考虑多个平台发布,所以就有了packagers的角色,它们从模块开发者那取得源码发布,然后在多个平台上面进行构建,并发布多个平台的构建版本。

1.2简单例子

由python编写的setup脚本一般都非常简单。作为autoconf类型的配置脚本,setup脚本可以在构建和安装模块发布时运行多次。

比如,如果需要发布一个叫做foo的模块,它包含在一个文件foo.py,那setup脚本可以这样写:

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

setup函数的参数表示提供给Distutils的信息,这些参数分为两类:包的元数据(包名、版本号)以及包的信息(本例中是一个Python模块的列表);模块由模块名表示,而不是文件名(对于包和扩展而言也是这样);建议可以提供更多的元数据,比如你的名字,email地址和项目的URL地址。

编写好setup.py之后,就可以创建该模块的源码发布了:

[plain] view plaincopy
  1. python setup.py sdist

对于Windows而言,命令是:

[plain] view plaincopy
  1. setup.py sdist

sdist命令会创建一个archive 文件(比如Unix上的tar文件,Windows上的zip文件),它包含setup.py, foo.py。该archive文件命名为foo-1.0.tar.gz(zip),解压之后的目录名是foo-1.0。

如果一个用户希望安装foo模块,他只需要下载foo-1.0.tar.gz,解压,进入foo-1.0目录,然后运行:

[plain] view plaincopy
  1. python setup.py install

该命令最终会将foo.py复制到Python环境存放第三方模块的目录中。在linux环境下,运行该命令的输出是:

[plain] view plaincopy
  1. # python setup.py install
  2. running install
  3. running build
  4. running build_py
  5. creating build
  6. creating build/lib
  7. copying foo.py -> build/lib
  8. running install_lib
  9. copying build/lib/foo.py -> /usr/lib/python2.7/site-packages
  10. byte-compiling /usr/lib/python2.7/site-packages/foo.py to foo.pyc
  11. running install_egg_info
  12. Writing /usr/lib/python2.7/site-packages/foo-1.0-py2.7.egg-info

该命令生成的文件是:

/usr/lib/python2.7/site-packages/foo-1.0-py2.7.egg-info

/usr/lib/python2.7/site-packages/foo.py

/usr/lib/python2.7/site-packages/foo.pyc

这个简单的例子展示了Distutils的基本概念。第一,开发者和安装者有同样的用户接口,也就是setup脚本,但他们使用的Distutils命令不同,sdist命令几乎只有开发者使用,而install对于安装者更常用。

如果希望使用者的使用尽可能的简单,则可以创建多个构建发布。比如,如果在Windows中,可以使用bdist_wininst命令创建一个exe安装文件,下面的命令会在当前目录中创建foo-1.0.win32.exe文件:

[plain] view plaincopy
  1. python setup.py bdist_wininst

其他的构建发布有RPM(由bdist_rpm命令实现),Solaris pkgtool(bdist_pkgtool),以及HP-UX swinstall (bdist_sdux)。

比如,下面的命令将会创建RPM文件foo-1.0.noarch.rpm(bdist_rpm命令必须运行于基于RPM的系统,比如Red Hat Linux, SuSE Linux, Mandrake Linux):

[plain] view plaincopy
  1. python setup.py bdist_rpm

可以通过下面的命令得到当前支持的发布格式:

[plain] view plaincopy
  1. python setup.py bdist --help-formats

1.3基本术语:

模块(module):       Python中可复用的基本代码单元,可由其他代码import的一块代码,这里我们只关注三种类型的模块:纯python模块,扩展模块和包。

纯python模块(pure Python module):      由python编写的模块,包含在单独的py文件中(或者是pyc/pyo文件)。

扩展模块(extension module):由实现Python的底层语言编写的模块(C/C++ for Python, Java for Jython)。通常包含在单独的动态加载文件中,比如Unix中的so文件,windows中的DLL文件,或者是Jython扩展的java类文件。(注意,目前为止Distutils只能处理Python的C/C++扩展)

包(package):包是含其他模块的模块,经常由包含__init__.py文件的目录发布。

Root包(root package):       包层次关系中的根(它不是真正的包,因为它不包含__init__.py文件)。

1.4 Distutils术语

模块发布(module distribution):一些Python模块的集合,它们将会被一起安装。一些常见的模块发布有Numeric Python,PyXML,PIL,mxBase。

纯模块发布:一个只包含纯python模块和包的模块发布。

非纯模块发布:至少包含一个扩展模块的模块发布。

发布根:源码树的根目录;setup.py所在的目录。

二:编写setup脚本

setup脚本是使用Distutils构建、发布和安装模块的核心。setup脚本的作用是向Distutils描述发布模块的信息。从上面那个简单的例子中可知,setup脚本主要是调用setup函数,而且模块开发者向Distutils提供的模块信息多数是由setup函数的关键字参数提供的。

下面是一个更高级一些的例子:Distutils模块本身的setup脚本:

[python] view plaincopy
  1. setup(name='Distutils',
  2. version='1.0',
  3. description='Python Distribution Utilities',
  4. author='Greg Ward',
  5. author_email='gward@python.net',
  6. url='https://www.python.org/sigs/distutils-sig/',
  7. packages=['distutils', 'distutils.command'],
  8. )

上面这个脚本有更多的元数据,列出的是两个包(packages),而不是列出每个模块。因为Distutils包含多个模块,这些模块分成了两个包;如果列出所有模块的话则是冗长且难以维护的。

注意,在setup脚本中的路径必须以Unix形式来书写,也就是由”/”分割的。Distutils会在使用这些路径之前,将这种表示方法转换为适合当前平台的格式。

2.1列出整个包

Setup函数的packages参数是一个列表,其中包含了Distutils需要处理(构建、发布、安装等)的所有包。要实现此目的,那么包名和目录名必须能够相互对应,比如包名是distutils,则意味着在发布的根目录(setup脚本所在目录)下存在distutils子目录;再比如在setup脚本中packages = ['foo'],意味着要在setup脚本所在目录下存在相应的foo目录和foo/__init__.py文件。

比如如果setup脚本内容如下:

[python] view plaincopy
  1. setup(name='foo',
  2. version='1.0',
  3. packages = ['foo']
  4. )

而setup脚本所在目录并没有foo目录(只有一个setup.py脚本),则在执行python setup.py bdist命令时,会打印如下错误:

[plain] view plaincopy
  1. error: package directory 'foo' does not exist

如果创建了foo目录,但是没有foo/__init__.py文件,则Distutils会产生下面的警告,但是仍会处理该包:

[plain] view plaincopy
  1. package init file 'foo/__init__.py' not found (or not a regular file)

可以使用package_dir选项来改变这种默认的对应规则。package_dir是个字典,其中的key是要安装的包名,如果为空,则表明是root package,value就是该包(key)对应的源码树的目录。

比如如果setup.py内容如下:

[python] view plaincopy
  1. setup(name='foo',
  2. version='1.0',
  3. package_dir = {'':'lib'},
  4. packages = ['foo']
  5. )

则必须在目录中存在lib子目录,lib/foo子目录,以及文件lib/foo/__init__.py。所以源码树如下:

[plain] view plaincopy
  1. setup.py
  2. lib/
  3. foo/
  4. __init__.py
  5. foo.py

最后生成的文件是:

\usr\local\lib\python2.7\dist-packages\ foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\foo\__init__.py

\usr\local\lib\python2.7\dist-packages\foo\__init__.pyc

\usr\local\lib\python2.7\dist-packages\foo\foo.py

\usr\local\lib\python2.7\dist-packages\foo\foo.pyc

另外一个例子,foo包对应lib目录,所以,foo.bar包就对应着lib/bar子目录。所以如果在setup.py中这么写:

[python] view plaincopy
  1. package_dir = {'foo':'lib'},
  2. packages = ['foo',’foo.bar’]

则必须存在lib/__init__.py,  lib/bar/__init__.py文件。源码树如下:

[plain] view plaincopy
  1. setup.py
  2. lib/
  3. __init__.py
  4. foo.py
  5. bar/
  6. __init__.py
  7. bar.py

最后生成的文件是:

\usr\local\lib\python2.7\dist-packages\ foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\foo\__init__.py

\usr\local\lib\python2.7\dist-packages\foo\__init__.pyc

\usr\local\lib\python2.7\dist-packages\foo\foo.py

\usr\local\lib\python2.7\dist-packages\foo\foo.pyc

\usr\local\lib\python2.7\dist-packages\foo\bar\__init__.py

\usr\local\lib\python2.7\dist-packages\foo\bar\__init__.pyc

\usr\local\lib\python2.7\dist-packages\foo\bar\bar.py

\usr\local\lib\python2.7\dist-packages\foo\bar\bar.pyc

2.2列出单独的模块

如果发布中仅包含较少的模块,你可能更喜欢列出所有模块,而不是列出包,特别是在root package中存在单一模块的情况(或者根本就没有包)。可以使用py_modules参数,比如下面的例子:

[python] view plaincopy
  1. setup(name='foo',
  2. version='1.0',
  3. py_modules = ['mod1', 'pkg.mod2']
  4. )

它描述了两个模块,一个在root package中,另一个在pkg包中。根据默认的包/目录对应规则,这两个模块存在于文件mod1.py和pkg/mod2.py中,并且要存在pkg/__init__.py文件(不存在的话,会产生报警:package init file 'pkg/__init__.py' not found (or not a regular file))。当然,也可以使用package_dir选项改变这种对应关系。所以,源码树如下:

[plain] view plaincopy
  1. setup.py
  2. mod1.py
  3. pkg/
  4. __init__.py
  5. mod2.py

最终生成的文件是:

\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\mod1.py

\usr\local\lib\python2.7\dist-packages\mod1.pyc

\usr\local\lib\python2.7\dist-packages\pkg\__init__.py

\usr\local\lib\python2.7\dist-packages\pkg\__init__.pyc

\usr\local\lib\python2.7\dist-packages\pkg\mod2.py

\usr\local\lib\python2.7\dist-packages\pkg\mod2.pyc

2.3扩展模块

在Distutils中描述扩展模块较描述纯python模块要复杂一些。对于纯python模块,仅需要列出模块或包,然后Distutils就会去寻找合适的文件,这对于扩展模块来说是不够的,你还需要指定扩展名、源码文件以及其他编译/链接需要的参数(需要包含的目录,需要连接的库等等)

描述扩展模块可以由setup函数的关键字参数ext_modules实现。ext_modules是Extension实例的列表,每一个Extension实例描述了一个独立的扩展模块。比如发布中包含一个独立的扩展模块称为foo,由foo.c实现,且无需其他编译链接指令,那么下面的语句就可以描述该扩展模块:

[python] view plaincopy
  1. Extension('foo', ['foo.c'])

Extension可以从distutils.core中随setup一起引入。因此,对于仅包含一个扩展模块的发布来说,setup脚本如下:

[python] view plaincopy
  1. from distutils.core import setup, Extension
  2. setup(name='foo',
  3. version='1.0',
  4. ext_modules=[Extension('foo', ['foo.c'])],
  5. )

底层的扩展构建机制是由build_ext命令实现的。Extension类在描述Python扩展时具有很大的灵活性。

2.3.1 扩展名和包

通常,Extension类的构造函数的第一个参数都是扩展的名字,比如下面的语句:

[python] view plaincopy
  1. Extension('foo', ['src/foo1.c', 'src/foo2.c'])

如果执行python  setup.py bdist,就会调用相应的编译器和连接器命令,最终根据生成foo.so文件,存放在发布包的根目录中,最终生成的文件是:

\usr\local\lib\python2.7\dist-packages\foo.so

\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

又比如下面的语句:

[python] view plaincopy
  1. Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c'])

使用的源文件是一样的,最终生成的结果文件也是一样的foo.so,唯一的不同是最终的结果文件存放的目录,是在发布包的根目录下的pkg目录下。因此最终生成的文件是:

\usr\local\lib\python2.7\dist-packages\pkg\foo.so

\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

如果一个包下有多个扩展,而且要把这些扩展都放在统一的目录下,则可以使用ext_package关键字,比如下面的语句:

[python] view plaincopy
  1. setup(...,
  2. ext_package='pkg',
  3. ext_modules=[Extension('foo', ['src/foo.c']),
  4. Extension('subpkg.bar', ['src/bar.c'])]
  5. )

上面的描述将会编译src/foo.c为pkg.foo,将src/bar.c编译为pkg.subpkg.bar。因此源码树如下:

[plain] view plaincopy
  1. setup.py
  2. src/
  3. foo.c
  4. bar.c

最终生成的文件是:

\usr\local\lib\python2.7\dist-packages\foo-1.0.egg-info

\usr\local\lib\python2.7\dist-packages\pkg\foo.so

\usr\local\lib\python2.7\dist-packages\pkg\subpkg\bar.so

2.3.2 扩展的源码文件

Extension构建函数的第二个参数是源文件的列表。目前Distutils仅支持C、C++和Objective-C扩展,所以这些源码文件就是C、C++和Objective-C的源码文件。(C++源码文件的扩展名可以是.cc和.cpp,Unix和Windows编译器都支持)

不过还可以在列表中包含SWIG接口文件(.i文件),build_ext命令知道如何处理SWIG接口文件。尽管会发生报警,但是可以像下面这样传递SWIG选项:

[python] view plaincopy
  1. setup(...,
  2. ext_modules=[Extension('_foo', ['foo.i'],
  3. swig_opts=['-modern', '-I../include'])],
  4. py_modules=['foo'],
  5. )

或者是使用如下命令:

[plain] view plaincopy
  1. > python setup.py build_ext --swig-opts="-modern -I../include"

在一些系统上,该列表中还可以包含能由编译器处理的非源码文件。当前只支持Windows message 文本文件(.mc)和Visual C++的资源定义文件(.rc)。它们将会编译为二进制文件.res并且链接进可执行文件中。

2.3.3其他选项

Extension还可以指定其他选项,比如可以指定头文件目录,define或undefine宏、需要链接的库,链接时和运行时搜索库的路径等等。具体可参阅:

https://docs.python.org/2/distutils/setupscript.html#preprocessor-options

https://docs.python.org/2/distutils/setupscript.html#library-options

https://docs.python.org/2/distutils/setupscript.html#other-options

2.4发布和包的关系

发布和包有三种关系:它依赖其他包,它服务于其他包,它淘汰其他包。这些关系可以分别用setup函数的参数requires ,provides 和obsoletes 来指定,具体参阅:https://docs.python.org/2/distutils/setupscript.html#relationships-between-distributions-and-packages

2.5安装脚本

模块通常不自己运行,而是由脚本引入。除了可以安装模块之外,还可以安装能直接运行的脚本,具体参阅https://docs.python.org/2/distutils/setupscript.html#installing-scripts

2.6安装package data

有时包中还需要安装其他文件,这些文件与包的实现密切相关,或者是包含文档信息的文本文件等,这些文件就叫做package data。

使用setup函数中的package_data参数可以向packages中添加package data。该参数的值必须是个字典,字典的key就是package name,value是个list,其中包含了需要复制到package中的一系列路径。这些路径都是相对于包目录而言的(比如package_dir),所以,这些文件必须存在于包的源码目录中。在安装时,也会创建相应的目录。

比如,如果包中有一个包含数据文件的子目录,源码树如下:

[plain] view plaincopy
  1. setup.py
  2. src/
  3. mypkg/
  4. __init__.py
  5. module.py
  6. data/
  7. tables.dat
  8. spoons.dat
  9. forks.dat

相应的setup函数可以这样写:

[python] view plaincopy
  1. setup(...,
  2. packages=['mypkg'],
  3. package_dir={'mypkg': 'src/mypkg'},
  4. package_data={'mypkg': ['data/*.dat']},
  5. )

2.7安装其他文件

可以通过data_files选项来安装除了上面提到过的文件之外的其他文件,比如配置文件,数据文件等。data_files是个列表,列表中的元素是(directory, files),比如:

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

(directory, files)中,directory表示文件最终要被安装到的地方,如果它是相对路径的话,则是相对于installation prefix而言(对于纯python包而言,就是sys.prefix;对于扩展包,则是sys.exec_prefix)。files是要安装的文件,其中的目录信息(安装前)是相对于setup.py所在目录而言的,安装时,setup.py根据files的信息找到该文件,然后将其安装到directory中。

2.8元数据

Setup脚本可以包含很多发布的元数据,比如名称、版本、作者等信息,具体列表和注意信息,参阅https://docs.python.org/2/distutils/setupscript.html#additional-meta-data

2.9调试setup脚本

如果在运行setup脚本是发生了错误,则Distutils会打印出简单的错误信息,对于开发者而言这些错误信息可能不足以找到错误的原因。所以可以通过设置环境变量DISTUTILS_DEBUG,将其置为任意值(不能是空字符串),Distutils就会打印其执行过程的详细信息,并且在发生异常时打印全部的traceback,并且在像C编译器这样的外部程序发生错误时,打印整个命令行。

三:配置文件

一般情况下,在构建发布时无法将所有的选项都确定下来,有些选项的值可能来自于用户,或者用户的系统。这也就是配置文件setup.cfg存在的目的,用户可以通过修改该配置文件进行选项的配置。

在构建时,选项的处理顺序是setup脚本、配置文件,命令行。所以,安装者可以通过修改setup.cfg文件来覆盖setup.py中的选项;也可以通过运行setup.py时的命令行选项,来覆盖setup.cfg。

配置文件的基本语法如下:

[plain] view plaincopy
  1. [command]
  2. option=value
  3. ...

command就是Distutils的命令(比如build_py,install等),option就是命令支持的选项。配置文件中的空行、注释(以’#’开头,直到行尾)会被忽略。

可以通过--help选项得到某个命令支持的选项,比如:

[plain] view plaincopy
  1. > python setup.py --help build_ext
  2. [...]
  3. Options for 'build_ext' command:
  4. --build-lib (-b)     directory for compiled extension modules
  5. --build-temp (-t)    directory for temporary files (build by-products)
  6. --inplace (-i)       ignore build-lib and put compiled extensions into the
  7. source directory alongside your pure Python modules
  8. --include-dirs (-I)  list of directories to search for header files
  9. --define (-D)        C preprocessor macros to define
  10. --undef (-U)         C preprocessor macros to undefine
  11. --swig-opts          list of SWIG command line options
  12. [...]

注意,命令行中的选项”--foo-bar”,在配置文件中要写成”foo_bar”。

比如,运行以下命令:

[plain] view plaincopy
  1. python setup.py build_ext --inplace

如果不希望每次执行命令时都输入”--inplace”选项,则可以在配置文件中写明:

[plain] view plaincopy
  1. [build_ext]
  2. inplace=1

其他例子和注意事项,可以参阅https://docs.python.org/2/distutils/configfile.html

四:源码发布

之前已经提到过,使用sdist命令可以创建包的源码发布,该命令最终生成一个archive文件。Unix上默认的文件格式是.tar.gz,在Windows上的是ZIP文件。可以使用”--formats”选项指定生成的格式,比如:python setup.py sdist --formats=gztar,zip,执行该命令后,就会生成两个文件foo-1.0.tar.gz 和foo-1.0.zip。

支持的格式有:

Format

Description

zip

zip file (.zip)

gztar

gzip’ed tar file (.tar.gz)

bztar

bzip2’ed tar file (.tar.bz2)

ztar

compressed tar file (.tar.Z)

tar

tar file (.tar)

当在Unix上使用tar格式时(gztar,bztar,ztar或tar),可以通过owner和group选项指定用户和群组。比如:

[plain] view plaincopy
  1. python setup.py sdist --owner=root --group=root

4.1指定发布的文件

如果没有明确的列出需要发布的文件,则sdist命令默认在源码发布中包含下列文件:

由py_modules和packages选项指定的所有python源码文件;

由ext_modules或libraries选项指定的所有C源码文件;

由scripts指定的脚本;

测试脚本:test/test*.py;

README.txt (或者README), setup.py 和setup.cfg;

package_data指定的所有文件;

data_files指定的所有文件。

如果还需要发布其他额外的文件,典型的做法是编写一个叫做MANIFEST.in的manifest模板。manifest模板包含如何创建MANIFEST文件的一系列指令,sdist命令会解析该模板,根据模板中的指令,以及找到的文件生成MANIFEST。

文件MANIFEST中明确的列出了包含在源码发布中的所有文件。比如下面就是一个MANIFEST文件的内容:

[plain] view plaincopy
  1. # file GENERATED by distutils, do NOT edit
  2. setup.py
  3. lib/__init__.py
  4. lib/foo.py
  5. lib/bar/__init__.py
  6. lib/bar/bar.py

4.2 Manifest相关选项

sdist命令的执行步骤如下:

if the manifest file (MANIFEST by default) exists and the first line does not have a comment indicating it is generated from MANIFEST.in, then it is used as is, unaltered;

if the manifest file doesn’t exist or has been previously automatically generated, read MANIFEST.in and create the manifest;

if neither MANIFEST nor MANIFEST.in exist, create a manifest with just the default file set;

use the list of files now in MANIFEST (either just generated or read in) to create the source distribution archive(s).

如果仅仅需要(重新)创建MANIFEST文件,则可以使用如下命令:

[plain] view plaincopy
  1. python setup.py sdist --manifest-only

4.3 MANIFEST.in模板

如果存在MANIFEST.in文件,则sdist命令就会根据该文件生成MANIFEST。在MANIFEST.in文件中,一行一个命令,每一个命令指定了源码发布中需要包含或者需要排除的文件,比如下面的例子:

[plain] view plaincopy
  1. include *.txt
  2. recursive-include examples *.txt *.py
  3. prune examples/sample?/build

很容易看出,上面的命令的意思是:包含所有的.txt文件;包含examples目录下的所有.txt或者.py文件;排除所有匹配examples/sample?/build的目录。所有这些过程,都是在标准规则执行之后执行的,所以可以在模板文件中排除标准集合中的文件。关于MANIFEST文件的其他内容,参阅https://docs.python.org/2/distutils/sourcedist.html

五:构建发布(Built Distributions)

所谓的构建发布(built distribution),即是指二进制包,或是指安装文件。当然它未必真的是二进制,而有可能包含Python源码和字节码。

构建发布是为了方便安装者而创建的,比如对于基于RPM的Linux用户来说,它可以是二进制RPM包,而对于Windows用户来说,它可以是一个可执行的安装文件等。

创建包的构建发布,是前面介绍的packager的主要职责。它们拿到包的源码发布之后,使用setup脚本以及bdist命令来生成构建发布。比如,在包的源码树中运行下面的命令:

[plain] view plaincopy
  1. python setup.py bdist

Distutils就会创建发布,执行“伪”安装(在build目录中),并且创建当前平台下的默认格式的构建发布。构建发布在Unix中的默认格式是一个”dumb”的tar文件(之所以称之为”dumb”,是因为该tar文件只有解压到特定的目录下才能工作),而在Windows上是一个简单可执行安装文件。

所以,在Unix上运行上面的命令之后,就会在dist目录中生成foo-1.0.linux-i686.tar.gz文件,在合适的位置解压该文件,就安装了foo模块,等同于下载了该模块的源码发布之后运行python setup.py install命令。所谓合适的位置,要么是文件系统的根目录,要么是Python的prefix目录,这取决于bdist_dump的命令选项。

bdist命令有一个--formats选项,类似于sdist命令,该选项可用于指定生成的构建发布的格式,比如命令:

[plain] view plaincopy
  1. python setup.py bdist --format=zip

在Unix上运行该命令,就会创建foo-1.0.linux-i686.zip文件,在根目录下解压该文件就安装了foo模块。构建发布支持的格式如下:

gztar

gzipped tar file (.tar.gz)

ztar

compressed tar file (.tar.Z)

tar

tar file (.tar)

zip

zip file (.zip)

rpm

RPM

pkgtool

Solaris pkgtool

sdux

HP-UX swinstall

wininst

self-extracting ZIP file for Windows

msi

Microsoft Installer.

当然,也可以不使用--formats选项,而是用bdist的子命令,直接创建相应的格式。比如使用bdist_dump命令可以生成所有的dumb archive格式(tar,ztar,gztar和zip),bdist_rpm会生成源码和二进制的RPM包,bdist的子命令如下表:

Command

Formats

bdist_dumb

tar, ztar, gztar, zip

bdist_rpm

rpm, srpm

bdist_wininst

wininst

bdist_msi

msi

具体的子命令信息,可以参阅https://docs.python.org/2/distutils/builtdist.html

六:Distutils与PYPI

PYPI,也就是Python Package Index,它是Python第三方模块的集中营,Python开发者可以向PYPI上传自己的Python模块。PYPI中存放了发布文件以及发布的元数据。

Distutils提供了register和upload命令,来直接向PYPI推送元数据和发布文件,详细内容可以参阅https://docs.python.org/2/distutils/packageindex.html

七:简单示例

7.1纯Python发布(模块)

如果只是发布几个模块,这些模块没有放在包中,可是使用py_modules选项。比如源码树如下:

[plain] view plaincopy
  1. setup.py
  2. foo.py
  3. bar.py

setup脚本如下:

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

安装之后,会生成以下文件:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\ foo.py

\usr\lib\python2.7\site-packages\ foo.pyc

\usr\lib\python2.7\site-packages\ bar.py

\usr\lib\python2.7\site-packages\ bar.pyc

7.1纯Python发布(包)

如果有很多模块需要发布,则可以将这些模块放到统一的包中,然后在setup脚本中指明要发布的包,而不是列出所有的模块。

即使模块没有放到包中,也可以通过向setup脚本声明root包的方法来发布,与实际的包不同,根目录下可以没有__init__.py文件。比如上面的例子,源码树保持不变,setup脚本也可以这样写:

[python] view plaincopy
  1. from distutils.core import setup
  2. setup(name='foobar',
  3. version='1.0',
  4. packages=[''],
  5. )

空字符串就意味着root包。安装之后,生成的文件跟上面是一样的。

如果将源文件放到发布根目录下的子目录中,比如源码树:

[plain] view plaincopy
  1. setup.py
  2. src/
  3. foo.py
  4. bar.py

这种情况依然可以用声明root包的方式来发布,只不过需要使用package_dir选项来指明包和目录的关系:

[python] view plaincopy
  1. from distutils.core import setup
  2. setup(name='foobar',
  3. version='1.0',
  4. package_dir={'': 'src'},
  5. packages=[''],
  6. )

安装之后生成的文件跟之前是一样的。

更常见的做法是将多个模块组织在同一个包中,比如在包foobar中包含foo和bar模块,源码树如下:

[plain] view plaincopy
  1. setup.py
  2. foobar/
  3. __init__.py
  4. foo.py
  5. bar.py

setup脚本如下:

[python] view plaincopy
  1. from distutils.core import setup
  2. setup(name='foobar',
  3. version='1.0',
  4. packages=['foobar'],
  5. )

安装之后,会生成以下文件:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\foobar\ __init__.py

\usr\lib\python2.7\site-packages\foobar\ __init__.pyc

\usr\lib\python2.7\site-packages\foobar\foo.py

\usr\lib\python2.7\site-packages\foobar\foo.pyc

\usr\lib\python2.7\site-packages\foobar\bar.py

\usr\lib\python2.7\site-packages\foobar\bar.pyc

如果不想以模块所在的子目录名来定义包名,则可以使用package_dir选项,比如源码树如下:

[plain] view plaincopy
  1. setup.py
  2. src/
  3. __init__.py
  4. foo.py
  5. bar.py

则相应的setup脚本如下:

[python] view plaincopy
  1. from distutils.core import setup
  2. setup(name='foobar',
  3. version='1.0',
  4. package_dir={'foobar': 'src'},
  5. packages=['foobar'],
  6. )

安装之后生成的文件与上面的例子是一样的。

或者,直接将所有模块放到发布的根目录下:

[plain] view plaincopy
  1. setup.py
  2. __init__.py
  3. foo.py
  4. bar.py

setup脚本如下:

[python] view plaincopy
  1. from distutils.core import setup
  2. setup(name='foobar',
  3. version='1.0',
  4. package_dir={'foobar': ''},
  5. packages=['foobar'],
  6. )

安装之后生成的文件与上面的例子是一样的。

如果涉及到子包的话,则必须在packages选项中明确的指出。不过,package_dir中的值却会自动扩展到其子目录。比如源码树如下:

[plain] view plaincopy
  1. setup.py
  2. src/
  3. __init__.py
  4. foo.py
  5. bar.py
  6. subfoo/
  7. __init__.py
  8. blah.py

setup脚本如下:

[python] view plaincopy
  1. from distutils.core import setup
  2. setup(name='foobar',
  3. version='1.0',
  4. package_dir = {'foobar':'src'},
  5. packages=['foobar', 'foobar.subfoo'],
  6. )

安装之后,生成文件如下:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\foobar\ __init__.py

\usr\lib\python2.7\site-packages\foobar\ __init__.pyc

\usr\lib\python2.7\site-packages\foobar\foo.py

\usr\lib\python2.7\site-packages\foobar\foo.pyc

\usr\lib\python2.7\site-packages\foobar\bar.py

\usr\lib\python2.7\site-packages\foobar\bar.pyc

\usr\lib\python2.7\site-packages\foobar\subfoo\__init__.py

\usr\lib\python2.7\site-packages\foobar\subfoo\__init__.pyc

\usr\lib\python2.7\site-packages\foobar\subfoo\blah.py

\usr\lib\python2.7\site-packages\foobar\subfoo\blah.pyc

7.3单独的扩展模块

扩展模块由选项ext_modules指定。package_dir选项对扩展模块的源码文件没有作用,它只影响纯Python模块。比如源码树如下:

[plain] view plaincopy
  1. setup.py
  2. foo.c

如果setup脚本如下:

[python] view plaincopy
  1. from distutils.core import setup
  2. from distutils.extension import Extension
  3. setup(name='foobar',
  4. version='1.0',
  5. ext_modules=[Extension('foo', ['foo.c'])],
  6. )

则生成的文件是:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\ foo.so

如果源码树不变,setup脚本如下:

[python] view plaincopy
  1. from distutils.core import setup
  2. from distutils.extension import Extension
  3. setup(name='foobar',
  4. version='1.0',
  5. ext_modules=[Extension('foopkg.foo', ['foo.c'])],
  6. )

则生成的文件是:

\usr\lib\python2.7\site-packages\foobar-1.0-py2.7.egg-info

\usr\lib\python2.7\site-packages\foopkg\ foo.so

八:其他

运行install命令,会首先运行build命令,然后运行子命令install_lib,install_data和install_scripts。

Distutils可以进行扩展,比如增加新的命令、修改现有的命令。可参阅https://docs.python.org/2/distutils/extending.html

Distutils的API参阅https://docs.python.org/2/distutils/apiref.html

Python模块开发【Distutils】相关推荐

  1. 计算机科学与技术python方向是什么意思-第一模块·开发基础-第1章 Python基础语法...

    Python开发工具课前预习 01 Python全栈开发课程介绍1 02 Python全栈开发课程介绍2 03 Python全栈开发课程介绍3 04 编程语言介绍(一) 05 编程语言介绍(二)机器语 ...

  2. Python程序开发——第七章 模块与包

    目录 一.模块的定义 二.导入模块.调用模块 (一)import语句 (二)from-import语句 三.标准模块库 (一)sys模块 (二)os模块 (三)random模块 (四)time模块 四 ...

  3. aardio 安装 Python 模块,快速开发界面,生成独立 EXE 一把梭

    aardio 开发图形界面利索,与 Python 交互也方便. aardio + Python 开发的程序可以一键生成独立 EXE 文件,自带绿色 Python 运行时,生成的 EXE 也不大. 但是 ...

  4. Python OpenCV开发MR智能人脸识别打卡系统(四、服务模块设计)

    需要源码请点赞关注收藏后评论区留言私信~~~ 整体系统讲解如下 Python OpenCV开发MR智能人脸识别打卡系统(一.需求分析与系统设计) Python OpenCV开发MR智能人脸识别打卡系统 ...

  5. 想做Python开发,这8种常用Python模块,你必须得知道!

    8种常用Python模块 前言 time模块 1.时间戳(timestamp) 2.格式化的时间字符串(Format String) 3.结构化的时间(struct time) datetime模块 ...

  6. Python OpenCV开发MR智能人脸识别打卡系统(三、工具模块设计)

    需要源码请点赞关注收藏后评论区留言私信~~~ 整体系统讲解如下 Python OpenCV开发MR智能人脸识别打卡系统(一.需求分析与系统设计) Python OpenCV开发MR智能人脸识别打卡系统 ...

  7. 用Tkinter打造自己的Python IDE开发工具(4)利用HP_tk模块设计自己的代码编辑器

    用Tkinter打造自己的Python IDE开发工具(4)利用HP_tk模块设计自己的代码编辑器 HP_tk.py模块是小白量化第二代量化系统中的开发模块.其中HP_tk.py模块是小白量化系统GU ...

  8. 用Tkinter打造自己的Python IDE开发工具(5)利用HP_tka模块设计自己的中文代码编辑器

    用Tkinter打造自己的Python IDE开发工具(5)利用HP_tka模块设计自己的中文代码编辑器 前面我们介绍了在Tkinter中使用exec()函数运行用户程序的方法.exec()采用多线程 ...

  9. 使用ruby和python快速开发metasploit自定义模块

    使用ruby和python快速开发metasploit自定义模块 前言 本文的内容主要分为两个部分: 提供一个基本ruby模块代码框架,并快速开发自定义ruby模块 提供一个基本python模块代码框 ...

最新文章

  1. Mybatis获取插入记录的自增长ID
  2. 搜索引擎工作的基础流程与原理
  3. LeetCode Contains Duplicate III(滑动窗口)
  4. window 查找 java 进程中占用cpu比较高的线程
  5. onlyoffice启用HTTPS
  6. js几种常见排序的实现
  7. Java 读写txt文件 中文乱码问题
  8. APACHE服务器出现No input file specified.解决方案
  9. aria-hidden读屏
  10. DataFrame基础操作
  11. mysql 5.6.15_mysql5.6.15问题如何解决
  12. Python机器学习算法基础概述
  13. 轮廓检测论文解读 | 整体嵌套边缘检测HED | CVPR | 2015
  14. CST软件基本操作—1
  15. 运营方法论——增长黑客
  16. Word不计算封面、目录页数将正文页码修改为第几页共几页的格式
  17. Arrays.sort排二维数组
  18. 传感器如何将消息发送给云服务器,通过 NodeMCU (ESP8266) 将传感器数据上传至 MQTT 云服务...
  19. Numpy用法详细总结:学习numpy如何使用,看这一篇文章就足够了
  20. 数字化经营快速发展,微火专注做智慧数字经营系统解决方案提供商

热门文章

  1. 计算机网络之网络概述:2、标准化工作及其相关组织
  2. 栈溢出笔记1.12 栈Cookie
  3. C/C++面试题—旋转数组的最小数字
  4. C#单元测试如何查看输出的调试信息?
  5. 码农的自我修炼之路-----BST
  6. 初学WPF,做一款小游戏练习一下
  7. mongodb sharding 试用(四)
  8. 递增输出链表结点,删除重复结点
  9. 【会议】2009-11-13
  10. 谷歌在华遭遇首例关键词官司