Python 包构建教程

setuptools 和 setup.py

Setuptoolsdistutils 都是用于编译、分发和安装 python 包的一个工具,特别是在包依赖问题场景下很是有用,它是一个强大的包管理工具。Setuptools 是 distutils 的增强版。编译、分发和安装 python 包的一个关键的事就是编写 setup 脚本。setup 脚本的主要做用在于向包管理工具 Setuptoolsdistutils 说明你的模块分发细节,因此 Setuptools 支持大量的命令以操做你的包。setup 脚本主要调用一个 setup() 方法,许多提供给 Setuptools 的信息都以 keyword arguments 的参数形式提供给 setup() 方法。html

你所须要作的事 & 一些概念

对于包开发者和使用者,所须要作的事:python

  • 编写 setup.py 脚本,用于处理你的包
  • (可选)编写 setup 配置文件
  • 建立源码分发文件,python setup.py sdist
  • (可选)建立二进制分发文件,python setup.py bdist

对于包使用者,只须要 python setup.py install,即可以成功安装 python 包。linux

基础概念

  • module 模块:module 是 python 中代码重用的基本单元,一个 module 能够经过 import 语句导入到另外一个 module;module 分为:pure python module(纯 python 模块)、extension module(扩展模块)和 package(包)
  • pure python module:纯 python 模块是用纯 python 语言编写的模块,单一的 .py 文件做为一个模块使用,也就是一个 .py 能够称为模块了
  • extension module:扩展模块是用底层的 C/C++、Objective-C或 Java 编写的模块,一般包含了一个动态连接库,好比 so、dll 或 Java,目前 distutils 只支持 C/C++ 和 Objective-C,不支持 Java 编写扩展模块;可是 python 提供了一个 JCC 这样一个用于生成访问 Java 类接口的 C++ 代码的胶水模块,应该也是可使用 Java 编写模块的。
  • package:包是一个带有 __init__.py 文件的文件夹,用于包含其余模块
  • root package:root package 是包的最顶层,它不是实质性的包,由于它不包含 __init__.py 文件。大量的标准库位于 root package,由于它们不属于一个任何更大的模块集合了。实际上,每个 sys.path 列举出来的文件夹都是 root package,你能够在这些文件夹中找到大量的模块。
  • distribution:模块分发,一个归档在一块儿的 python 模块集合,它做为一个可下载安装的资源,方便用户使用,做为开发者便须要努力建立一个易于使用的 distribution
  • distribution root:源代码树的最顶层,也就是 setup.py 所在的位置。

关于源码分发文件和二进制分发文件

源码分发文件是将包分享给其余人更为推荐的一种形式,由于源码分发文件比二进制分发文件更适合跨平台,这样使用者能够在本身的机器上经过编译获得本身的机器相关的包代码而且进行安装。ios

示例和分发选择

  • 若是你只是发布几个脚本文件而已,特别是它们逻辑上不属于同一个包,你可使用 py_modules 选项一个一个地指定;
  • 若是你须要发布的模块文件太多,使用 py_modules 一个一个指定比较麻烦,特别是模块位于多个包中,那么你可使用 packages 指定整个包,另外只须要另外指定 package_dir,位于 distribution root 下的模块文件也能够被处理;
  • setuptools 帮助文档声明,package_dirpy_modules 也能够支持分发任何没有包含 __init__.py 文件夹下的模块,经测试安装过程没有报错,可是没有包含 __init__.py 的文件夹下的模块是没有被正确安装的!所以,若是 python 模块分布在不一样的文件夹,最好是在该文件夹下建立一个 __init__.py 文件,以表示它是一个包。

pure python module

举个简单的例子,你须要发布两个模块 foo 以及 bar.bar,以供别人使用(import foo 和 import bar.bar)。
其目录树以下:git

pure_module
├── bar
│   └── bar.py
│   └── __init__.py
├── foo.py
└── setup.py

如上图所示,pure_module 目录下包含了一个 foo 模块以及一个 bar 包,同时在 bar 包下还包含了一个 bar 模块。算法

一个仅使用 py_modules 的 setup 脚本能够这样写:windows

from setuptools import setup

NAME = 'foo'
VERSION = '1.0'
PY_MODULES = ['foo', 'bar.bar']

setup(name = NAME
        , version = VERSION
        , py_modules = PY_MODULES)

py_modules 指定了 foo 模块以及 bar.bar 模块。app

经过 python setup.py install --user --prefix= 进行安装后,即可以直接经过 import foo 和 import bar.bar 直接使用了。curl

  • 注意:经测试,若是 .py 文件位于其余文件夹,该文件夹须要建立一个容许为空的 __init__.py 文件,表示为一个 package,不然安装后不能正常使用其余文件夹的模块。

package

上一节的例子中,bar.bar 属于 bar 包,foo 位于 distribution root,安装后属于 root package,在 Setuptools 中 "" 能够用于表示 root package。因此下面展现两种 setup 脚本的写法:

pure_module
├── bar
│   └── bar.py
│   └── __init__.py
├── foo.py
└── setup.py

仅使用 packages 的 setup.py 文件以下:

from setuptools import setup

NAME = 'foo'
VERSION = '1.0'
PACKAGES = ['', 'bar']

setup(name = NAME
        , version = VERSION
        , packages = PACKAGES
        )

如上,packages 包含了 package 的列表 root package 以及 bar,这样便能轻松覆盖到 distribution root 下的 foo.py 和 bar 文件夹下的 bar.py 了,在存在大量模块的状况下,省去像 py_modules 同样穷举模块的麻烦。在 python 中,默认的状况下,包的名字和目录的名字是一致的,好比 bar 包对应了 bar 目录,且包的路径表示是相对于 distribution root 的(也就是 setup.py 所在目录)。好比 packages = ['foo'] ,Setuptools 会在 setup.py 所在目录下寻找 foo/__init__.py,并将 foo/ 下的全部模块包含进去。

另一个关键字是 package_dir,它的做用是将 package 映射到其余目录,这样的一个好处是方便将 package 移到其余目录而不用修改 packages 的参数值。举个例子,假设咱们如今须要把 bar 移到 foobar 目录下,按照原来的脚本,Setuptools 是没法成功找到 bar 包的。

package_dir
├── foobar
│   ├── bar.py
│   └── __init__.py
├── foo.py
└── setup.py

经过 package_dir = = {'bar':'foobar'},将原来的 bar package 映射到 foobar 下。完整的脚本以下:

from setuptools import setup

NAME = 'foo'
VERSION = '1.0'
PACKAGE_DIR = {'bar':'foobar'}
PACKAGES = ['', 'bar']

setup(name = NAME
        , version = VERSION
        , package_dir = PACKAGE_DIR
        , packages = PACKAGES
        )

package_dir 是一个字典,它的 key 是 package 名("" 表示 root package),value 是相对于 distribution root 的目录名。在上面的例子中, package_dir = = {'bar':'foobar'} 改变了 packages 中 package 对应的目录位置,这样当 Setuptools 在找 bar package 时,会在 foobar 目录下找相应的 __init__.py 文件。

  • 注意,package_dir 会影响 packages 下列出的全部 package,好比,packages =['bar', 'bar.lib'],package_dir 不只会影响全部和 bar 有关的 package,bar.lib 也会相应被映射到 foorbar.lib

extension module

扩展模块须要使用 ext_modules 参数。上面所说的 package_dirpackages 都是针对纯 python 模块,而 ext_modules 针对的是使用 C/C++ 底层语言所写的模块。下面举个最简单的例子,扩展模块仅包含一个 foo.cpp 文件,其中定义了可供 python 调用的 myPrint 函数。

#include <iostream>
#include <string>

using namespace std;

void myPrint(string text)
{
    cout <<  text << endl;
}
.
├── setup.py
└── src
    ├── foo.cpp
    ├── foo.h
    └── PythonWrapAPI.cpp

如今,咱们想要发布扩展模块供别人使用咱们的 myPrint 方法。想要 python 中成功导入你的包,须要利用额外的代码封装将被调用的方法。这里的 PythonWrapAPI.cpp 的做用就是使用 Python 提供的库封装你所写的接口,它是处在 python 和 C++ 间的胶水库,当 python 调用你的 C++ 方法时,因为语言类型的差异,须要作转换。

PythonWrapAPI.cpp 以下:

#include "foo.h"
#include <string>
#include <python2.6/Python.h>

using namespace std;

/*
Notice:Python Interface Wrap
*/

static PyObject *_myPrint(PyObject *self, PyObject *args)
{
    char *text;

    // 解析 Python 传过来的参数
    if (!PyArg_ParseTuple(args, "s", &text))
        return NULL;

    myPrint(text);
    return Py_None;
}

static PyMethodDef ExtestMethods[] =
{
    { "myPrint", _myPrint, METH_VARARGS },
    { NULL, NULL },
};

PyMODINIT_FUNC initmyprint(void) {
    (void)Py_InitModule("myprint", ExtestMethods);
}
  • wrapper 函数 _myPrint。它负责将 Python 的参数转化为C/C++的参数(PyArg_ParseTuple),而后调用实际的 myPrint,并处理 myPrint 的返回值,最终返回给Python环境。须要注意的是,C/C++ 中无返回值时,不能直接返回 NULL,而是须要返回 Py_None。
  • 导出表 ExtestMethods。它负责告诉Python这个模块里有哪些函数能够被 Python 调用。导出表的名字能够随便起,每一项有4个参数:第一个参数是提供给 Python 环境的函数名称,第二个参数是_myPrint,即 wrapper 函数。第三个参数的含义是参数变长,第四个参数是一个说明性的字符串。导出表老是以{NULL, NULL, 0, NULL}结束。说明,第 3 和 4 个参数能够省略。
  • 导出函数 initmyprint。这个的名字不是任取的,是你的 module 名称添加前缀init。导出函数中将模块名称与导出表进行链接。

扩展模块和纯 python 模块有点不太同样,咱们导入的 package 名字与 setup() 的 packages 或者 package_dir 参数是一致的,可是扩展模块的名字是由 Extension 实例的 name 参数决定的,且须要和导出函数对应的initxxx 名字以及 Py_InitModule 方法对应的第一个参数相同。

最后,咱们须要编写 setup 脚本编译咱们的 cpp 文件为 so 动态连接库,并进行相应的封装。运行 python setup.py install,setuptools 会帮咱们自动编译。

from setuptools import setup, Extension, find_packages

# package name, import NAME
NAME = "foo"
VERSION = '1.0.0'
# an Extension instance list
EXT_MODULES = [ 
                Extension(
                    name = 'myprint' 
                    , sources=['src/foo.cpp','src/PythonWrapAPI.cpp']
                    , include_dirs = ['src']
                    )   
                ]   
setup(name = NAME
    , version = VERSION
    , ext_modules = EXT_MODULES
    , )

ext_modules 是一个 Extension 实例列表,Extension 的参数 sources 用于指定全部源文件位置,include_dirs 指定头文件位置,同时还可使用 library_dirs 和 libraries 指定外部连接库,以及 extra_compile_args 指定额外的编译参数。

package 元信息参数

在编写一个 package 的时候,尽可能提供更多的元信息,这样使用者更加可以了解到 package 的相关信息,而且有些信息会被 PyPi 使用。

元信息 类型 描述 说明
name short string package 名字, 这里用于 pypi 的显示, 而不是用于 import, import 的包名与 packages 和 package_dir 参数一致 1
version short string package 的发布版本, 建议:major.minor[.patch[.sub]] 1
author short string package 做者名字 3
author_email short string package 做者邮箱 3
maintainer short string package 维护者名字 3
maintainer_email short string package 维护者邮箱 3
url short string package 项目地址 1
description short string 简介
long_description long string 显示于 pypi 的介绍
download_url short string package 下载地址 2
classifiers strings list 分类符, 这样便于 pypi 索引, 由 pypi 固定提供, https://pypi.python.org/pypi?%3Aaction=list_classifiers 2
platforms strings list 支持的平台列表
license short string 受权协议
  1. 必填
  2. 若是为了兼容 2.2.3 或 2.3 版本,不建议使用此字段
  3. 若是提供了 maintainer,那么 distutils 会将其加入 PKG-INFO

package 内容参数

py_modules 列举每一个模块

py_modules 是一个字符串列表,用于指定全部的模块,即 py 文件模块。 若是你只是发布几个脚本文件而已,特别是它们逻辑上不属于同一个 package。

好比,py_modules = ['mod1', 'pkg.mod2']。这指定了两个模块,一个位于 root package,而另外一个位于 pkg package。若是没有使用 package_dir 从新映射 package 和目录的关系的话,那么这两个模块分别对应了 mod1.py 以及 pkg/mod2.py 文件,而且在 pkg 文件夹下还存在 __init__.py 文件。

package 列举每一个包

若是你须要发布的模块文件太多,使用 py_modules 一个一个指定比较麻烦,特别是模块位于多个包中,那么你可使用 packages 指定整个包。

packages 是一个包名列表,packages 参数告诉 Setuptools 处理列举出的 package 下全部纯 python 模块。在文件系统中,默认地,package 的名字与目录是一一对应的,也就是说,packages = ['foo'],Setuptools 会去查找 foo/__init__.py 文件。

package_dir 从新映射 package 和目录的关系

当你想要重命名你的 package 所在的文件夹,或者想要移动整个 package 到其余目录下,通常状况下,一旦你的源代码布局改变,你须要从新修改 packages。可是 package_dir 能够从新映射 package 和目录的关系。好比你将 root package 下的模块和 package 移到 lib 目录下,那么你只须要在 package_dir 中将 root package 映射到 lib 下。好比 package_dir = {'': 'lib'}

再举个例子,好比一下目录结果,当我使用 package_dir= {'bar':'foobar'} 和 packages= ['bar']时,Setuptools 根据 packages 参数查找 bar package 时,会在 foobar 文件夹下找相应的 __init__.py 文件。

package_dir
├── foobar
│   ├── bar.py
│   └── __init__.py
└── setup.py
  • 注意,package_dir 会影响 packages 下列出的全部 package,好比,packages =['bar', 'bar.lib'],package_dir 不只会影响全部和 bar 有关的 package,bar.lib 也会相应被映射到 foorbar.lib

install_requires 能够声明 package 安装时所需的依赖模块及其版本。安装 package 时,Setuptools 便可以从 PyPi 上自动下载其所依赖的模块,而且将依赖信息包含进 Python Eggs 中。

好比咱们在本身的 package 中用到了一个 python 非标准库 pycurl 和 xmltodict,当咱们的 package 在别的机器上使用时便会报错。为了解决这个问题,咱们可使用 install_requires = ['pycurl', 'xmltodict'] 将 pycurl 和 xmltodict 加入 package 依赖。

install_requires 能够是 string 或 string list,指明所须要依赖的模块。当 install_requires 为 string 类型,而且依赖多于 1 个时,每一个依赖的声明都要另起一行。

最新版本的 Setuptools 的install_requires 有另外两个做用:

  1. 在运行时,任何脚本都会检查其依赖模块的正确性,而且确保正确的依赖版本都加入到 sys.path 中(假若有多个版本的话)。
  2. Python Egg distributions 将会包含依赖相关的元信息。

前面说到,Setuptools 便可以从 PyPi 上自动下载其所依赖的模块,可是在某些环境下没法正常访问 Pypi 下,咱们也能够经过 dependency_links 参数 指定到本身的 python 源,这样即可以解决下载问题。

好比,dependency_links = ['http://xxx/xmltodict', 'http://xxx/pycurl']

dependency_links 是一个字符串列表,包含了依赖的下载 URL。

Setuptools 对连接的支持比较强大!

下载的资源能够知足如下条件:

  • 经过 python setup.py sdist 进行分发的压缩文件,默认状况下在 linux 为 .tar.gz,在 windows 为 zip
  • 单一的 py 文件
  • VCS 仓库(Subversion, Mercurial, Git)

URL 连接能够是:

  • 能够直接下载的 URL
  • 包含资源下载连接的网页 URL
  • 仓库 URL

当包含资源下载连接的网页 URL 中存在多个版本时,Setuptools 会根据版本要求下载合适的版本。

通常,比较好的方式是网页 URL 方式。咱们也可使用 SourceForge 的 showfiles.php 连接来下载咱们所依赖的模块。

若是依赖的模块是一个 py 文件时,你必须在 URL 添加 "#egg=project-version" 后缀,以指出模块的名字和版本,另外须要确保将模块名和版本中出现的 - 替换为 _。EasyInstall 将会识别这个后缀而且自动建立一个 setup.py 脚本,将咱们的 py 文件包装为 egg 文件。若是为 VCS,将会 checkout 对应的版本,建立一个临时文件夹并执行 setup.py bdist_egg,安装所需的依赖。

在使用 VCS 的状况下,你也可使用 #egg=project-version 指定要使用的版本。你能够经过在 #egg=project-version 前加入 @REV 来指定要 checkout 的版本。另外你也能够经过在 URL 前加上如下标识显式声明 URL 使用 的是 VCS:

  • Subversion:svn+URL
  • Git:git+URL
  • Mercurial:hg+URL

所以使用 VCS 更复杂的一个示例为: vcs+proto://host/path@revision#egg=project-version

ext_module Python 调用 C/C++

Python 的可扩展性特别强,不只支持 python 语言的扩展模块,并且支持其余语言的扩展。

Python 调用 C++ 的详细文档能够查看 https://docs.python.org/2/extending/building.html

这里假设已经懂得怎么调用 C++ 方法了,接下来只须要使用 ext_module 参数,使 Setuptools 可以编译和安装扩展模块了。

ext_module 参数是一个 Extension 实例列表,Extension 相似于 gcc/g++ 的所需参数,包含了指定源文件、头文件、依赖的静态库或动态库、额外的编译参数、宏定义等功能。

name 扩展模块名字

name 是一个字符串,用于指定扩展模块的名字。

packagespackage_dir 用于支持 python 语言编写模块,其 import 语句使用的包名与 packagespackage_dir 中所指定的名字是一致的。可是扩展模块的名字是由 Extension 实例的 name 参数决定的,且须要和导出函数对应的initxxx 名字以及 Py_InitModule 方法对应的第一个参数相同。定义好模块的名字 xxx 后,咱们即可以使用 import xxx 使用咱们本身的模块了。

sources 和 include_dirs

sources 为用于指定要编译源文件的字符串列表,好比,sources=['foo/foo.cpp', 'bar/bar.cpp'],Setuptools 支持 C/C++ 以及 Objective-C。
include_dirs 为用于指定编译须要的头文件目录的字符串列表,好比,include_dirs=['foo/include', 'bar/include']。若是头文件位于 distribution root 目录,须要使用 '.' 表示头文件位于当前目录,不能为 '',不然将找不到头文件。

另外还支持 extra_objects 向连接程序传递 object 文件,好比 .o 文件。

define_macros 和 undef_macros

gcc 支持在编译的时候定义新的宏变量和取消某个宏变量的定义,具体的选项 [-Dmacro[=defn]...] [-Umacro]。Extension 也支持这样的选项。

你可使用 define_macrosundef_macros 定义新的宏变量和取消某个宏变量的定义。

define_macros 是一个 (name, value) 元组列表,其中的 name 为宏变量名字符串,value 为对应的值,能够为字符串、数字或为 None 类型(说明:官方文档没有声明 value 能够为数字,可是通过测试,只要是 python 支持的数字类型均可以用于 value,可是最好仍是使用字符串的形式,这样脚本的兼容性会更好).

好比,define_macros=[('DEBUG', None), ('FOO', '1'), ('BAR', 2), ('FOOBAR', '"abc"')],gcc 对应的编译选项结果为 -DDEBUG -DFOO=1 -DBAR=2 -DFOOBAR="abc"

undef_macrosdefine_macros 简单得多,它就是一个宏变量字符串列表,举个例子,咱们想要取消以上定义的宏变量,对应的 undef_macros 值为 undef_macros=['DEBUG', 'FOO', 'BAR', 'FOOBAR']

libraries 和 library_dirs

Setuptools 对 C/C++ 库的引用方法和 gcc 同样,具体的规则能够参考 gcc。

libraries 为要添加的库的名字字符串列表,而 library_dirs 为要添加的库所在的目录,举个例子:

.
├── setup.py
└── curl
    ├── include
        ├── curl.h
        ├── test.h
    ├── lib
        ├── libcurl.a
        ├── libtest.a

其对应的参数为 libraries=['curl', 'test']library_dirs=['curl/lib']include_dirs=[‘curl/include’]

注意:在实际的使用过程当中碰到过一个连接错误的坑,Setuptools 在编译的时候报错:

libcurl.a : relocation against .rodata can not be used when making a shared object:recompile with -fPIC
libcurl.a : could not read symbols:Bad value

前面提到,python 在建立扩展模块时会将源文件编译为动态连接库,动态连接库在加载的时候,内存位置是不固定的,因此咱们连接的外部库代码也须要所有使用相对地址,这样代码即可以加载到内存的任意位置。由于有的库没有使用 -fPIC 选项进行编译,致使库最终在连接到 so 文件时报错。

解决方案是使用 -fPIC 从新编译 libcurl.a 库。

extra_compile_args

在编译扩展模块时,Setuptools 会自动指定编译参数,好比下面一个模块的编译:

gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -DDEBUG -DFOO=1 -DBAR=2 -DFOOBAR="abc" -Isrc -I/usr/include/python2.6 -c src/foo.cpp -o build/temp.linux-x86_64-2.6/src/foo.o
gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -DDEBUG -DFOO=1 -DBAR=2 -DFOOBAR="abc" -Isrc -I/usr/include/python2.6 -c src/PythonWrapAPI.cpp -o build/temp.linux-x86_64-2.6/src/PythonWrapAPI.o
g++ -pthread -shared build/temp.linux-x86_64-2.6/src/foo.o build/temp.linux-x86_64-2.6/src/PythonWrapAPI.o -L/usr/lib64 -lpython2.6 -o build/lib.linux-x86_64-2.6/myprint.so

这么多的编译参数绝大部分是 Setuptools 自动指定的,可是若是咱们还想要在每一个文件的编译再加上额外的编译选项,可使用 extra_compile_argsextra_link_args,其中 extra_link_args 选项用于连接。

extra_compile_args 是一个编译选项字符串列表,每一个编译选项都要单独做为一个字符串,不能并在一块儿,不然会报错。

建立源码分发

建议使用源码分发的形式发布你的包,而不是二进制发布形式,这样包将更方便跨平台。

sdist 命令

建立源码分发的命令为:python setup.py sdist,命令执行后会建立 dist 目录,收集一些必要的文件以及 setup 脚本,生成一个压缩文件,用户安装时,只须要解压,而后执行 python setup.py install 命令,将进行编译和安装,将相应的文件存放到 python 第三方库目录下。

sdist 比较经常使用的一个选项是 --format,选择压缩的格式。好比,使用 zip 进行压缩,python setup.py sdist --format=zip

格式 后缀
zip .zip
gztar .tar.gz
bztar .tar.bz2
ztar .tar.Z
tar .tar

说明:python setup.py sdist --format=zip, tar,Setuptools 会分别使用 zip 和 tar 进行压缩,将同时产生两个压缩文件。

setuptools 和 distutils 对于文件查找的算法是同样的:

  • 全部在 py_modulespackages 指定的对应模块文件
  • 全部在 ext_moduleslibraries 选项指定的源文件和库
  • scripts 选项指定的脚本文件
  • 全部相似测试脚本的文件,好比:test/test*.py (低版本的包管理工具可能不支持)
  • README.txt(或 README),setup.py 以及 setup.cfg(README 文件目前没法支持更多的后缀格式)
  • package_data 选项指定的文件
  • data_files 选项指定的文件

另外在使用过程当中,遇到 Setuptools 的一个巨坑,确实能够包含文件,可是它并不总能包含文件,这是有前提的。

bdist是发布二进制文件,sdist是发布源文件。而在旧版本的 python 中(2.7 之前), package_data只有在使用 bdist 时候才有用,也就是若是使用 sdist,是没法正确包含文件的。而在新版本中,会自动把package_data 里面的内容添加到 MANIFEST 文件中。

MANIFEST.in 模版文件

当咱们使用 sdist 进行分发包时,若是须要包含额外的文件,可使用 MANIFEST.in 文件,在该文件中列举出须要包含的文件。当咱们执行 sdist 时,将会对 MANIFEST.in 文件进行检查,读取解释并生成 MANIFEST 文件,该文件列举了全部须要包含进包的文件。位于 distribution root 下的 MANIFEST.in 文件每行对应一条包含一系列文件的命令。

相关文章
相关标签/搜索