• Python深入:Distutils发布Python模块


             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脚本可以这样写:

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

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

     

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

    python setup.py sdist

            对于Windows而言,命令是: 

    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目录,然后运行:

    python setup.py install

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

    # 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
    

             该命令生成的文件是:

    /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文件:

    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):

    python setup.py bdist_rpm

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

    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脚本:

    setup(name='Distutils',
          version='1.0',
          description='Python Distribution Utilities',
          author='Greg Ward',
          author_email='gward@python.net',
          url='https://www.python.org/sigs/distutils-sig/',
          packages=['distutils', 'distutils.command'],
         )
    

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

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

          

    2.1列出整个包

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

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

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

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

    error: package directory 'foo' does not exist

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

    package init file 'foo/__init__.py' not found (or not a regular file)

          

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

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

    setup(name='foo',
          version='1.0',
          package_dir = {'':'lib'},
          packages = ['foo']
         )

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

    setup.py
    lib/
        foo/
            __init__.py
            foo.py

            最后生成的文件是:

    usrlocallibpython2.7dist-packages foo-1.0.egg-info

    usrlocallibpython2.7dist-packagesfoo\__init__.py

    usrlocallibpython2.7dist-packagesfoo\__init__.pyc

    usrlocallibpython2.7dist-packagesfoofoo.py

    usrlocallibpython2.7dist-packagesfoofoo.pyc

     

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

          package_dir = {'foo':'lib'},
          packages = ['foo',’foo.bar’]
    

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

    setup.py
    lib/
        __init__.py
        foo.py
        bar/
            __init__.py
            bar.py

           最后生成的文件是:

    usrlocallibpython2.7dist-packages foo-1.0.egg-info

    usrlocallibpython2.7dist-packagesfoo\__init__.py

    usrlocallibpython2.7dist-packagesfoo\__init__.pyc

    usrlocallibpython2.7dist-packagesfoofoo.py

    usrlocallibpython2.7dist-packagesfoofoo.pyc

    usrlocallibpython2.7dist-packagesfooar\__init__.py

    usrlocallibpython2.7dist-packagesfooar\__init__.pyc

    usrlocallibpython2.7dist-packagesfooarar.py

    usrlocallibpython2.7dist-packagesfooarar.pyc

     

    2.2列出单独的模块

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

    setup(name='foo',
          version='1.0',
          py_modules = ['mod1', 'pkg.mod2']
         )
    

             它描述了两个模块,一个在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选项改变这种对应关系。所以,源码树如下:

    setup.py
    mod1.py
    pkg/
        __init__.py
        mod2.py

             最终生成的文件是:

    usrlocallibpython2.7dist-packagesfoo-1.0.egg-info

    usrlocallibpython2.7dist-packagesmod1.py

    usrlocallibpython2.7dist-packagesmod1.pyc

    usrlocallibpython2.7dist-packagespkg\__init__.py

    usrlocallibpython2.7dist-packagespkg\__init__.pyc

    usrlocallibpython2.7dist-packagespkgmod2.py

    usrlocallibpython2.7dist-packagespkgmod2.pyc

     

    2.3扩展模块

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

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

    Extension('foo', ['foo.c'])

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

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

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

     

    2.3.1 扩展名和包

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

    Extension('foo', ['src/foo1.c', 'src/foo2.c'])

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

    usrlocallibpython2.7dist-packagesfoo.so

    usrlocallibpython2.7dist-packagesfoo-1.0.egg-info

     

            又比如下面的语句:

    Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c'])

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

    usrlocallibpython2.7dist-packagespkgfoo.so

    usrlocallibpython2.7dist-packagesfoo-1.0.egg-info

     

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

    setup(...,
          ext_package='pkg',
          ext_modules=[Extension('foo', ['src/foo.c']),
                       Extension('subpkg.bar', ['src/bar.c'])]
         )
    

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

    setup.py
    src/
        foo.c
        bar.c

             最终生成的文件是:

    usrlocallibpython2.7dist-packagesfoo-1.0.egg-info

    usrlocallibpython2.7dist-packagespkgfoo.so

    usrlocallibpython2.7dist-packagespkgsubpkgar.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选项:

    setup(...,
          ext_modules=[Extension('_foo', ['foo.i'],
                                 swig_opts=['-modern', '-I../include'])],
          py_modules=['foo'],
         )

             或者是使用如下命令:

    > 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),所以,这些文件必须存在于包的源码目录中。在安装时,也会创建相应的目录。

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

    setup.py
    src/
        mypkg/
            __init__.py
            module.py
            data/
                tables.dat
                spoons.dat
                forks.dat

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

    setup(...,
          packages=['mypkg'],
          package_dir={'mypkg': 'src/mypkg'},
          package_data={'mypkg': ['data/*.dat']},
          )
    

    2.7安装其他文件

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

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

             (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。

     

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

    [command]
    option=value
    ...

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

     

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

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

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

            

            比如,运行以下命令:

    python setup.py build_ext --inplace

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

    [build_ext]
    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选项指定用户和群组。比如:

    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文件的内容:

    # file GENERATED by distutils, do NOT edit
    setup.py
    lib/__init__.py
    lib/foo.py
    lib/bar/__init__.py
    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文件,则可以使用如下命令:

    python setup.py sdist --manifest-only

    4.3 MANIFEST.in模板

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

    include *.txt
    recursive-include examples *.txt *.py
    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命令来生成构建发布。比如,在包的源码树中运行下面的命令:

    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命令,该选项可用于指定生成的构建发布的格式,比如命令:

    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选项。比如源码树如下:

    setup.py
    foo.py
    bar.py

             setup脚本如下:

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

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

    usrlibpython2.7site-packagesfoobar-1.0-py2.7.egg-info

    usrlibpython2.7site-packages foo.py

    usrlibpython2.7site-packages foo.pyc

    usrlibpython2.7site-packages bar.py

    usrlibpython2.7site-packages bar.pyc

     

    7.1纯Python发布(包)

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

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

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

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

     

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

    setup.py
    src/      
            foo.py
            bar.py

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

    from distutils.core import setup
    setup(name='foobar',
          version='1.0',
          package_dir={'': 'src'},
          packages=[''],
          )

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

     

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

    setup.py
    foobar/
            __init__.py
            foo.py
            bar.py

             setup脚本如下:

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

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

    usrlibpython2.7site-packagesfoobar-1.0-py2.7.egg-info

    usrlibpython2.7site-packagesfoobar __init__.py

    usrlibpython2.7site-packagesfoobar __init__.pyc

    usrlibpython2.7site-packagesfoobarfoo.py

    usrlibpython2.7site-packagesfoobarfoo.pyc

    usrlibpython2.7site-packagesfoobarar.py

    usrlibpython2.7site-packagesfoobarar.pyc

     

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

    setup.py
    src/
            __init__.py
            foo.py
            bar.py

             则相应的setup脚本如下:

    from distutils.core import setup
    setup(name='foobar',
          version='1.0',
          package_dir={'foobar': 'src'},
          packages=['foobar'],
          )

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

     

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

    setup.py
    __init__.py
    foo.py
    bar.py

             setup脚本如下:

    from distutils.core import setup
    setup(name='foobar',
          version='1.0',
          package_dir={'foobar': ''},
          packages=['foobar'],
          )

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

     

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

    setup.py
    src/
            __init__.py
            foo.py
            bar.py
            subfoo/
                    __init__.py
                    blah.py

             setup脚本如下:

    from distutils.core import setup
    setup(name='foobar',
          version='1.0',
          package_dir = {'foobar':'src'},
          packages=['foobar', 'foobar.subfoo'],
          )

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

    usrlibpython2.7site-packagesfoobar-1.0-py2.7.egg-info

    usrlibpython2.7site-packagesfoobar __init__.py

    usrlibpython2.7site-packagesfoobar __init__.pyc

    usrlibpython2.7site-packagesfoobarfoo.py

    usrlibpython2.7site-packagesfoobarfoo.pyc

    usrlibpython2.7site-packagesfoobarar.py

    usrlibpython2.7site-packagesfoobarar.pyc

    usrlibpython2.7site-packagesfoobarsubfoo\__init__.py

    usrlibpython2.7site-packagesfoobarsubfoo\__init__.pyc

    usrlibpython2.7site-packagesfoobarsubfoolah.py

    usrlibpython2.7site-packagesfoobarsubfoolah.pyc

     

    7.3单独的扩展模块

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

    setup.py
    foo.c

             如果setup脚本如下:

    from distutils.core import setup
    from distutils.extension import Extension
    setup(name='foobar',
          version='1.0',
          ext_modules=[Extension('foo', ['foo.c'])],
          )

             则生成的文件是:

    usrlibpython2.7site-packagesfoobar-1.0-py2.7.egg-info

    usrlibpython2.7site-packages foo.so

     

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

    from distutils.core import setup
    from distutils.extension import Extension
    setup(name='foobar',
          version='1.0',
          ext_modules=[Extension('foopkg.foo', ['foo.c'])],
          )

             则生成的文件是:

    usrlibpython2.7site-packagesfoobar-1.0-py2.7.egg-info

    usrlibpython2.7site-packagesfoopkg 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

  • 相关阅读:
    selenium环境搭建
    noip2020游记
    [HNOI2007]分裂游戏——博弈论好题
    [SCOI2007]压缩——区间dp
    赛道修建——二分答案
    玩诈欺的小杉——异或优化的状压dp
    【佛山市选2013】排列——发现性质与转化问题
    电话线铺设——难实现的最小生成树
    备用钥匙——分情况讨论的好dp
    喝喝喝——稍加推导的好转化
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247106.html
Copyright © 2020-2023  润新知