文章目录(Table of Contents)
简介
作为 Python 标准的打包及分发工具,setuptools 可以说相当地简单易用。它会随着 Python 一起安装在你的机器上。你只需写一个简短的 setup.py 安装文件,就可以将你的 Python 应用打包。
参考资料
- Python打包分发工具setuptools简介,这是一个比较简洁的教程;
- python的构建工具setup.py,这里对 setup.py 中的设置进行了详细的介绍;
- setuptools详解,这个会对
find_package
和entry_points
说明比较详细;
setup.py 文件编写
这一部分我们直接来看一个 setup.py
文件的例子,然后对这个文件做出详细的解释:
- from __future__ import absolute_import
- from __future__ import division
- from __future__ import print_function
- from setuptools import setup, find_packages
- import os
- here = os.path.abspath(os.path.dirname(__file__)) # 将 setup.py 放在项目的根目录
- setup(
- name = "test",
- version = "1.0",
- keywords = ("test", "xxx"),
- description = "eds sdk",
- long_description = "eds sdk for python",
- license = "Apache License, Version 2.0",
- url = "http://test.com",
- author = "test",
- author_email = "test@gmail.com",
- packages = find_packages(),
- include_package_data = True,
- install_requires=[i.strip() for i in open(os.path.join(os.path.dirname(__file__), 'requirements.txt')).readlines() if i.strip()],
- entry_points = {
- 'console_scripts': [
- 'test = test.help:main'
- ]
- }
- )
下面是 setup.py
中各个参数的解释:
- 基础描述信息:
- name 包名称(起一个响亮的名字)
- version (-V) 包版本
- author 程序的作者
- author_email 程序的作者的邮箱地址
- maintainer 维护者
- maintainer_email 维护者的邮箱地址
- url 程序的官网地址
- license 程序的授权信息
- description 程序的简单描述
- long_description 程序的详细描述
- platforms 程序适用的软件平台列表
- keywords 程序的关键字列表
- classifiers 程序的所属分类列表
- 包的进阶信息:
- packages 需要处理的包目录(包含__init__.py的文件夹),这里通常使用 find_packages(),它默认在和setup.py同一目录下搜索各个含有 __init__.py的包。
- install_requires = ["requests"] 需要安装的依赖包。我们可以首先生成 requirements.txt 文件,接着使用生成的文件生成需要的参数。关于生成 requirements.txt 文件,可以参考Python自动生成requirements.txt文件。
- include_package_data,引入非 Python 文件。默认情况下只会对 Python 源码进行打包,但是如果我们想要将其他静态文件,例如 css 文件,或是 qt 的一些 ui 文件打包进去,就需要使用这个参数。后面会有详细的介绍。
- 制作命令行工具:
- entry_points 动态发现服务和插件,可以用来制作命令行工具。也就是我们可以通过一些简单的命令,来运行 Python 项目中的指定文件或是函数。下面会详细进行介绍。
packages 的说明(自动搜索 Python 包)
我们指定 packages
后会自动将指定的 package
下的源代码进行打包。但是有的时候会有很多 subpackage
,这个时候 setuptools
提供了 find_packages
来找到所有的 subpackages
。下面图展示了大概的效果:
include_package_data(引入非 Python 文件)
默认情况下我们的打包只会对源码进行打包。如果我们还想将其他非 Python 文件也打包,比如静态文件(JS,CSS,图片),应该怎么做呢?这时我们要在项目目录下添加一个 MANIFEST.in
文件夹。假设我们把所有静态文件都放在 static 子目录下,现在的目录结构如下所示:
- setup-demo/
- ├ setup.py # 安装文件
- ├ MANIFEST.in # 清单文件
- └ myapp/ # 源代码
- ├ static/ # 静态文件目录
- ├ __init__.py
- ...
我们首先在清单文件 MANIFEST.in
中,列出想要在包内引入的目录路径:
- include myapp/static *.css # 只包含 static 文件夹中的 css 文件
- recursive-include myapp/xxx * # 递归包含
这里有一点需要注意,最后的文件夹不要加斜杠,也就是不能写成 myapp/static/
这种形式,一定是要写成 myapp/static
这种形式。
上面recursive-include
表明包含子目录。别急,还有一件事要做,就是在 setup.py 中将 include_package_data
参数设为 True:
- #coding:utf8
- from setuptools import setup
- setup(
- name='MyApp', # 应用名
- version='1.0', # 版本号
- packages=['myapp'], # 包括在安装包内的Python包
- include_package_data=True # 启用清单文件MANIFEST.in
- )
之后再次打包或者安装,myapp/static
目录下的所有文件都会被包含在内。如果你想排除一部分文件,可以在 setup.py
中使用exclude_package_date
参数,比如:
- setup(
- ...
- include_package_data=True, # 启用清单文件MANIFEST.in
- exclude_package_date={'':['.gitignore']}
- )
上面的代码会将所有 .gitignore
文件排除在包外。如果上述 exclude_package_date
对象属性不为空,比如{'myapp':['.gitignore']}
,就表明只排除 myapp
包下的所有 .gitignore
文件。
MANIFEST 文件语法
上面我们讲了利用 MANIFEST
来引入非 Python
文件。但除了引入之外,我们还可以排除一些文件。下面是完整的 MANIFEST
的语法:
include
pat1 pat2 ...,Add all files matching any of the listed patterns (Files must be given as paths relative to the root of the project)exclude
pat1 pat2 ...,Remove all files matching any of the listed patterns (Files must be given as paths relative to the root of the project)recursive-include
dir-pattern pat1 pat2 ...,Add all files under directories matching dir-pattern that match any of the listed patternsrecursive-exclude
dir-pattern pat1 pat2 ...,Remove all files under directories matching dir-pattern that match any of the listed patternsglobal-include
pat1 pat2 ...,Add all files anywhere in the source tree matching any of the listed patternsglobal-exclude
pat1 pat2 ...,Remove all files anywhere in the source tree matching any of the listed patternsgraft dir-pattern
,Add all files under directories matching dir-patternprune dir-pattern
,Remove all files under directories matching dir-pattern
install_requires 的说明(自动安装依赖)
这个参数是在 setup.py 文件中指定依赖,然后在使用 setuptools 安装应用时,依赖包的相应版本就会被自动安装。但是通常情况下,手动写依赖会比较麻烦,我们可以使用 pipreqs 首先自动生成 requirement.txt 文件,接着读取这部分文件即可。
- install_requires=[i.strip() for i in open(os.path.join(os.path.dirname(__file__), 'requirements.txt')).readlines() if i.strip()],
entry_points 的说明
entry_points 可以用来创建控制台脚本。我们看上面的例子就会非常好理解:
- entry_points = {
- 'console_scripts': [
- 'test = test.help:main'
- ]
- }
这样在安装完毕之后,我们可以直接在控制台输入 test
,这样是可以运行 test.help
模块下的 main
函数。
执行安装文件
在有了上面的 setup.py
文件之后,我们就可以进行打包,也可以将应用安装在本地的 Python 环境中了。一共有以下的四种安装方式:
创建 egg 包
下面的命令会在当前目录下的 dist
目录内创建一个 egg
文件。文件名格式就是 应用名-版本号-Python版本.egg
。同时你会注意到,当前目录多了 build
和 应用名.egg-info
子目录来存放打包的中间结果。(主要是看 dist 目录下的内容)
- python setup.py bdist_egg
创建 tar.gz 包
这个命令和上例类似,只不过创建的文件类型是 tar.gz,文件名为 应用名-版本号.tar.gz
。也是保存在 dist 文件夹下。
- python setup.py sdist --formats=gztar
安装应用
下面的安装命令会将当前的 Python 应用安装到当前 Python 环境的 site-packages
目录下,这样其他程序就可以像导入标准库一样导入该应用的代码了。
- python setup.py install
这里我们如果想要删除安装的包,可以使用 pip uninstall xxx
来完成删除。
以开发方式安装
如果应用在开发过程中会频繁变更,每次安装还需要先将原来的版本卸掉,这样就会很麻烦。如果使用 develop
开发方式安装的话,应用代码不会真的被拷贝到本地 Python
环境的 site-packages
目录下,而是在 site-packages
目录里创建一个指向当前应用位置的链接。这样如果当前位置的源码被改动,就会马上反映到 site-packages
里。
- python setup.py develop
我们还可以使用 pip install -e
这种方式来「已开发方式安装」。这里 -e
的含义是,Install a project in editable mode (i.e. setuptools "develop mode") from a local project path or a VCS url.
例如我们进入要安装的库所在的文件夹,使用下面的命令进行安装:
- pip install -e .
- 微信公众号
- 关注微信公众号
- QQ群
- 我们的QQ群号
评论