Python - 使用Setuptools进行程序打包

时间:2021-12-25 12:39:50

1- Setuptools简介

通过Setuptools可以更方便的创建和发布Python包,特别是那些对其它包具有依赖性的状况;

Python打包用户指南(Python Packaging User Guide

1.1 安装Setuptools

  • 目前官网新版本Python的安装包已自带pip(封装了setuptools),并不需要手工安装;
  • 也可通过pip安装新版本;
$ pip3 show setuptools
Name: setuptools
Version: 40.0.
Summary: Easily download, build, install, upgrade, and uninstall Python packages
Home-page: https://github.com/pypa/setuptools
Author: Python Packaging Authority
Author-email: distutils-sig@python.org
License: UNKNOWN
Location: c:\python36\lib\site-packages
Requires:
Required-by: PyInstaller, pipenv, kiwisolver, ipython, html5lib

1.2 简单的安装脚本

setup.py
# coding=utf-8
from setuptools import setup setup(name='Hello',
version='1.0',
description='A simple example',
author='Anliven',
author_email='anliven@yeah.net',
url='www.cnblogs.com/anliven',
py_modules=['hello'])

setup函数参数详解
创建setup.py文件后,可通过执行“python setup.py --help”命令获得帮助信息
setup函数各参数详解:
>>python setup.py --help
--name 包名称
--version (-V) 包版本
--author 程序的作者
--author_email 程序的作者的邮箱地址
--maintainer 维护者
--maintainer_email 维护者的邮箱地址
--url 程序的官网地址
--license 程序的授权信息
--description 程序的简单描述
--long_description 程序的详细描述
--platforms 程序适用的软件平台列表
--classifiers 程序的所属分类列表
--keywords 程序的关键字列表
--packages 需要打包的目录列表
--py_modules 需要打包的python文件列表
--download_url 程序的下载地址
--cmdclass
--data_files 打包时需要打包的数据文件,如图片,配置文件等
--scripts 安装时需要执行的脚步列表

1.3 获得帮助信息

usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
or: setup.py --help [cmd1 cmd2 ...]
or: setup.py --help-commands
or: setup.py cmd --help

“--help”

$ py - setup.py --help

“--help-commands”

$ py - setup.py --help-commands

命令sdist(Source distribution)

$ py - setup.py sdist --help

2- 构建模块(执行build命令)

  • 确保所在目录有模块文件hello.py;
  • 执行后,将创建build目录及子目录lib,同时将模块文件hello.py复制到子目录中;
  • 目录build:setuptools在此目录组装包以及编译扩展库等;
$ ls -l
total
-rw-r--r-- guowli Oct : hello.py
-rw-r--r-- guowli Oct : setup.py $ cat -n hello.py
# coding=utf-
print("This is a test!") $ cat -n setup.py
# coding=utf-
from setuptools import setup setup(name='Hello',
version='1.0',
description='A simple example',
author='Anliven',
author_email='anliven@yeah.net',
url='www.cnblogs.com/anliven',
py_modules=['hello']) $ py - setup.py build
running build
running build_py
creating build
creating build\lib
copying hello.py -> build\lib $ ls -lR
.:
total
drwxr-xr-x guowli Oct : build/
-rw-r--r-- guowli Oct : hello.py
-rw-r--r-- guowli Oct : setup.py ./build:
total
drwxr-xr-x guowli Oct : lib/ ./build/lib:
total
-rw-r--r-- guowli Oct : hello.py $ cat -n build/lib/hello.py
# coding=utf-
print("This is a test!") $

3- 安装模块(执行install命令)

  • 执行insall命令时会根据需要自动运行build命令,不需要手工执行build命令;
  • 安装过程中,Setuptools将创建一个.egg文件(独立的的Python包);
  • 可用pip命令来确认安装结果;
特别注意:建议使用“--record log.txt”参数来记录安装Python包的安装路径,以便以后卸载;
$ pip3 show hello

$ py - setup.py install --record log.txt
running install
running bdist_egg
running egg_info
creating Hello.egg-info
writing Hello.egg-info\PKG-INFO
writing dependency_links to Hello.egg-info\dependency_links.txt
writing top-level names to Hello.egg-info\top_level.txt
writing manifest file 'Hello.egg-info\SOURCES.txt'
reading manifest file 'Hello.egg-info\SOURCES.txt'
writing manifest file 'Hello.egg-info\SOURCES.txt'
installing library code to build\bdist.win-amd64\egg
running install_lib
running build_py
creating build\bdist.win-amd64
creating build\bdist.win-amd64\egg
copying build\lib\hello.py -> build\bdist.win-amd64\egg
byte-compiling build\bdist.win-amd64\egg\hello.py to hello.cpython-.pyc
creating build\bdist.win-amd64\egg\EGG-INFO
copying Hello.egg-info\PKG-INFO -> build\bdist.win-amd64\egg\EGG-INFO
copying Hello.egg-info\SOURCES.txt -> build\bdist.win-amd64\egg\EGG-INFO
copying Hello.egg-info\dependency_links.txt -> build\bdist.win-amd64\egg\EGG-INFO
copying Hello.egg-info\top_level.txt -> build\bdist.win-amd64\egg\EGG-INFO
zip_safe flag not set; analyzing archive contents...
creating dist
creating 'dist\Hello-1.0-py3.6.egg' and adding 'build\bdist.win-amd64\egg' to it
removing 'build\bdist.win-amd64\egg' (and everything under it)
Processing Hello-1.0-py3..egg
Copying Hello-1.0-py3..egg to c:\python36\lib\site-packages
Adding Hello 1.0 to easy-install.pth file Installed c:\python36\lib\site-packages\hello-1.0-py3..egg
Processing dependencies for Hello==1.0
Finished processing dependencies for Hello==1.0
writing list of installed files to 'log.txt' $ ls -lR
.:
total
drwxr-xr-x guowli Oct : build/
drwxr-xr-x guowli Oct : dist/
drwxr-xr-x guowli Oct : Hello.egg-info/
-rw-r--r-- guowli Oct : hello.py
-rw-r--r-- guowli Oct : log.txt
-rw-r--r-- guowli Oct : setup.py ./build:
total
drwxr-xr-x guowli Oct : bdist.win-amd64/
drwxr-xr-x guowli Oct : lib/ ./build/bdist.win-amd64:
total ./build/lib:
total
-rw-r--r-- guowli Oct : hello.py ./dist:
total
-rw-r--r-- guowli Oct : Hello-1.0-py3..egg ./Hello.egg-info:
total
-rw-r--r-- guowli Oct : dependency_links.txt
-rw-r--r-- guowli Oct : PKG-INFO
-rw-r--r-- guowli Oct : SOURCES.txt
-rw-r--r-- guowli Oct : top_level.txt $ cat log.txt
c:\python36\lib\site-packages\Hello-1.0-py3..egg $ pip3 show hello
Name: hello
Version: 1.0
Summary: A simple example
Home-page: www.cnblogs.com/anliven
Author: Anliven
Author-email: anliven@yeah.net
License: UNKNOWN
Location: c:\python36\lib\site-packages\hello-1.0-py3..egg
Requires:
Required-by: $

4- 安装演示

执行install命令时,使用参数“-n”,将只进行演示,并不真正执行安装;
--dry-run (-n) don't actually do anything
py - setup.py install -n

5- 卸载模块

Setuptools没有提供标准的uninstall命令,需要手工卸载安装的模块;
  • 执行“python setup.py install --record log.txt”命令,安装包的同时记录Python包的安装文件路径;
  • 卸载时,只需要删除log.txt文件记录的安装文件路径,即可卸载;
  • 如在Linux环境下,可使用“cat log.txt | xargs rm –rf”命令删除,即可卸载;
$ cat log.txt
c:\python36\lib\site-packages\Hello-1.0-py3..egg

手工删除

$ ls -l /c/Python36/Lib/site-packages/Hello-1.0-py3..egg
-rw-r--r-- guowli Oct : /c/Python36/Lib/site-packages/Hello-1.0-py3..egg $ pip3 show hello
Name: hello
Version: 1.0
Summary: A simple example
Home-page: UNKNOWN
Author: Anliven
Author-email: anliven@yeah.net
License: UNKNOWN
Location: c:\python36\lib\site-packages\hello-1.0-py3..egg
Requires:
Required-by: $ rm -rf /c/Python36/Lib/site-packages/Hello-1.0-py3..egg $ pip3 show hello

6- 程序打包(创建可分发的Python安装包)

编写安装脚本setup.py后,就可以用来进行程序打包(创建归档文件);
支持多种格式,主要分为sdist(Source distribution,源码发布)和bdist(Built Distribution,可执行文件发布)两类;

6.1 Source Distribution(执行sdist命令)

  • 使用命令sdist(Source distribution)可以打包成源码发布;
  • 使用命令开关“--formats”可指定一种或多种压缩格式(用逗号分隔);
如果执行“py -3 setup.py sdist --formats=tar,zip”,那么将生成.tar和.zip两个格式文件。
确认可使用的压缩格式:
$ py - setup.py sdist --help-formats
List of available source distribution formats:
--formats=bztar bzip2'ed tar-file
--formats=gztar gzip'ed tar-file
--formats=tar uncompressed tar file
--formats=xztar xz'ed tar-file
--formats=zip ZIP file
--formats=ztar compressed tar file

示例:默认压缩格式.tar.gz

$ ls -l
total
-rw-r--r-- guowli Oct : hello.py
-rw-r--r-- guowli Oct : setup.py $ cat -n hello.py
# coding=utf-
print("This is a test!") $ cat -n setup.py
# coding=utf-
from setuptools import setup setup(name='Hello',
version='1.0',
description='A simple example',
author='Anliven',
author_email='anliven@yeah.net',
py_modules=['hello']) $ py - setup.py sdist
running sdist
running egg_info
creating Hello.egg-info
writing Hello.egg-info\PKG-INFO
writing dependency_links to Hello.egg-info\dependency_links.txt
writing top-level names to Hello.egg-info\top_level.txt
writing manifest file 'Hello.egg-info\SOURCES.txt'
reading manifest file 'Hello.egg-info\SOURCES.txt'
writing manifest file 'Hello.egg-info\SOURCES.txt'
warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md running check
warning: check: missing required meta-data: url creating Hello-1.0
creating Hello-1.0\Hello.egg-info
copying files to Hello-1.0...
copying hello.py -> Hello-1.0
copying setup.py -> Hello-1.0
copying Hello.egg-info\PKG-INFO -> Hello-1.0\Hello.egg-info
copying Hello.egg-info\SOURCES.txt -> Hello-1.0\Hello.egg-info
copying Hello.egg-info\dependency_links.txt -> Hello-1.0\Hello.egg-info
copying Hello.egg-info\top_level.txt -> Hello-1.0\Hello.egg-info
Writing Hello-1.0\setup.cfg
creating dist
Creating tar archive
removing 'Hello-1.0' (and everything under it) $ ls -l
total
drwxr-xr-x guowli Oct : dist/
drwxr-xr-x guowli Oct : Hello.egg-info/
-rw-r--r-- guowli Oct : hello.py
-rw-r--r-- guowli Oct : setup.py $ ls -lR
.:
total
drwxr-xr-x guowli Oct : dist/
drwxr-xr-x guowli Oct : Hello.egg-info/
-rw-r--r-- guowli Oct : hello.py
-rw-r--r-- guowli Oct : setup.py ./dist:
total
-rw-r--r-- guowli Oct : ./Hello.egg-info:
total
-rw-r--r-- guowli Oct : dependency_links.txt
-rw-r--r-- guowli Oct : PKG-INFO
-rw-r--r-- guowli Oct : SOURCES.txt
-rw-r--r-- guowli Oct : top_level.txt $

告警提示

可能会出现一些告警提示缺少某些信息,不影响创建归档文件,可以忽略。
如果想去除告警,可以在setup.py所在目录添加相应文件或信息。
warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md
warning: check: missing required meta-data: url

分发与安装

生成的dist目录里的Hello-1.0.tar.gz文件就是归档文件,可将此文件分发给他人,然后对方可将其解压缩,再使用脚本setup.py进行安装。

6-2 Built Distribution(执行bdist命令)

和源码包相比,由于是预先构建好的可执行文件,所以安装更快。
确认可使用的压缩格式:
$ py - setup.py bdist --help-formats
List of available distribution formats:
--formats=rpm RPM distribution
--formats=gztar gzip'ed tar file
--formats=bztar bzip2'ed tar file
--formats=xztar xz'ed tar file
--formats=ztar compressed tar file
--formats=tar tar file
--formats=wininst Windows executable installer
--formats=zip ZIP file
--formats=msi Microsoft Installer
同时为了简化操作,Setuptools 提供了如下命令(执行“py -3 setup.py --help-commands”获得详细信息);
例如:执行“py -3 setup.py bdist_wininst”
  bdist_dumb        create a "dumb" built distribution
bdist_rpm create an RPM distribution
bdist_wininst create an executable installer for MS Windows

示例:wininst格式(Windows executable installer)

$ ls -l
total
-rw-r--r-- guowli Oct : hello.py
-rw-r--r-- guowli Oct : setup.py $ py - setup.py bdist --formats=wininst
running bdist
running bdist_wininst
running build
running build_py
creating build
creating build\lib
copying hello.py -> build\lib
installing to build\bdist.win-amd64\wininst
running install_lib
creating build\bdist.win-amd64
creating build\bdist.win-amd64\wininst
creating build\bdist.win-amd64\wininst\PURELIB
copying build\lib\hello.py -> build\bdist.win-amd64\wininst\PURELIB
running install_egg_info
running egg_info
creating Hello.egg-info
writing Hello.egg-info\PKG-INFO
writing dependency_links to Hello.egg-info\dependency_links.txt
writing top-level names to Hello.egg-info\top_level.txt
writing manifest file 'Hello.egg-info\SOURCES.txt'
reading manifest file 'Hello.egg-info\SOURCES.txt'
writing manifest file 'Hello.egg-info\SOURCES.txt'
Copying Hello.egg-info to build\bdist.win-amd64\wininst\PURELIB\Hello-1.0-py3..egg-info
running install_scripts
creating 'C:\Users\guowli\AppData\Local\Temp\tmppdxufr2q.zip' and adding '.' to it
adding 'PURELIB\hello.py'
adding 'PURELIB\Hello-1.0-py3.6.egg-info\dependency_links.txt'
adding 'PURELIB\Hello-1.0-py3.6.egg-info\PKG-INFO'
adding 'PURELIB\Hello-1.0-py3.6.egg-info\SOURCES.txt'
adding 'PURELIB\Hello-1.0-py3.6.egg-info\top_level.txt'
creating dist
removing 'build\bdist.win-amd64\wininst' (and everything under it) $ ls -lR
.:
total
drwxr-xr-x guowli Oct : build/
drwxr-xr-x guowli Oct : dist/
drwxr-xr-x guowli Oct : Hello.egg-info/
-rw-r--r-- guowli Oct : hello.py
-rw-r--r-- guowli Oct : setup.py ./build:
total
drwxr-xr-x guowli Oct : bdist.win-amd64/
drwxr-xr-x guowli Oct : lib/ ./build/bdist.win-amd64:
total ./build/lib:
total
-rw-r--r-- guowli Oct : hello.py ./dist:
total
-rwxr-xr-x guowli Oct : Hello-1.0.win-amd64.exe* ./Hello.egg-info:
total
-rw-r--r-- guowli Oct : dependency_links.txt
-rw-r--r-- guowli Oct : PKG-INFO
-rw-r--r-- guowli Oct : SOURCES.txt
-rw-r--r-- guowli Oct : top_level.txt $

7- wheel与egg格式

Wheel格式包其实也是一种 built 包,是官方推荐的打包方式,用来替换egg格式包;
  • 创建egg包:“python setup.py bdist_egg”
  • 创建wheel包:“python setup.py bdist_wheel”
执行“py -3 setup.py --help-commands”获得详细信息;
bdist_wheel    create a wheel distribution
bdist_egg create an "egg" distribution

7-1 示例:生成Wheel格式包

使用 wheel 打包,首先要安装 wheel:“pip install wheel”
$ ls -l
total
-rw-r--r-- guowli Oct : hello.py
-rw-r--r-- guowli Oct : setup.py $ py - setup.py bdist_wheel
running bdist_wheel
running build
running build_py
creating build
creating build\lib
copying hello.py -> build\lib
installing to build\bdist.win-amd64\wheel
running install
running install_lib
creating build\bdist.win-amd64
creating build\bdist.win-amd64\wheel
copying build\lib\hello.py -> build\bdist.win-amd64\wheel\.
running install_egg_info
running egg_info
creating Hello.egg-info
writing Hello.egg-info\PKG-INFO
writing dependency_links to Hello.egg-info\dependency_links.txt
writing top-level names to Hello.egg-info\top_level.txt
writing manifest file 'Hello.egg-info\SOURCES.txt'
reading manifest file 'Hello.egg-info\SOURCES.txt'
writing manifest file 'Hello.egg-info\SOURCES.txt'
Copying Hello.egg-info to build\bdist.win-amd64\wheel\.\Hello-1.0-py3..egg-info
running install_scripts
creating build\bdist.win-amd64\wheel\Hello-1.0.dist-info\WHEEL
creating 'D:\Anliven-Running\Zen\PycharmProjects\TestPackage\dist\Hello-1.0-py3-none-any.whl' and adding '.' to it
adding 'hello.py'
adding 'Hello-1.0.dist-info\top_level.txt'
adding 'Hello-1.0.dist-info\WHEEL'
adding 'Hello-1.0.dist-info\METADATA'
adding 'Hello-1.0.dist-info\RECORD'
removing build\bdist.win-amd64\wheel $ ls -lR
.:
total
drwxr-xr-x guowli Oct : build/
drwxr-xr-x guowli Oct : dist/
drwxr-xr-x guowli Oct : Hello.egg-info/
-rw-r--r-- guowli Oct : hello.py
-rw-r--r-- guowli Oct : setup.py ./build:
total
drwxr-xr-x guowli Oct : bdist.win-amd64/
drwxr-xr-x guowli Oct : lib/ ./build/bdist.win-amd64:
total ./build/lib:
total
-rw-r--r-- guowli Oct : hello.py ./dist:
total
-rw-r--r-- guowli Oct : Hello-1.0-py3-none-any.whl ./Hello.egg-info:
total
-rw-r--r-- guowli Oct : dependency_links.txt
-rw-r--r-- guowli Oct : PKG-INFO
-rw-r--r-- guowli Oct : SOURCES.txt
-rw-r--r-- guowli Oct : top_level.txt $

7-2 示例:安装Wheel格式包

可以使用pip直接安装和卸载wheel格式包
$ pip3 show hello

$ ls -l dist/
total
-rw-r--r-- guowli Oct : Hello-1.0-py3-none-any.whl $ pip3 install dist/Hello-1.0-py3-none-any.whl
Processing d:\anliven-running\zen\pycharmprojects\testpackage\dist\hello-1.0-py3-none-any.whl
Installing collected packages: Hello
Successfully installed Hello-1.0 $ pip3 show hello
Name: Hello
Version: 1.0
Summary: A simple example
Home-page: www.cnblogs.com/anliven
Author: Anliven
Author-email: anliven@yeah.net
License: UNKNOWN
Location: c:\python36\lib\site-packages
Requires:
Required-by: $ pip3 uninstall hello
Uninstalling Hello-1.0:
Would remove:
c:\python36\lib\site-packages\hello-1.0.dist-info\*
c:\python36\lib\site-packages\hello.py
Proceed (y/n)? y
Successfully uninstalled Hello-1.0 $ pip3 show hello $

8- 向PyPI注册包

PyPI(Python Package Index):https://pypi.org/
  1. 执行命令“python setup.py register”向标准库注册,将显示菜单;
  2. 根据提示,注册包;
  3. 执行命令“python setup.py sdist upload”上传源码分发包到PyPI;
$ py - setup.py register
running register
running check
warning: check: missing required meta-data: name, version, url warning: check: missing meta-data: either (author and author_email) or (maintainer and maintainer_email) must be supplied We need to know who you are, so please choose either:
. use your existing login,
. register as a new user,
. have the server generate a new password for you (and email it to you), or
. quit
Your selection [default ]:

9- 拾遗

9.1 PYTHONPATH

PYTHONPATH是Python搜索路径,默认import的模块都会从PYTHONPATH里面寻找;
目前安装新Python版本时,有“Add Python to Path”选项,选中则会自动完成环境变量配置;
以在Windows系统中设置PYTHONPATH为例:

To set environment variable for Python, open environment variables setting, new a system variable named PYTHONPATH, set its value to the Python installation path and append PYTHONPATH to path.

C:\Python36\;C:\Python36\Lib\site-packages;C:\Python36\Scripts

打印PYTHONPATH:

>>> import sys
>>> print(sys.path)
['', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36', 'C:\\Python36\\lib\\site-packages', 'C:\\Python36\\lib\\site-packages\\win32', 'C:\\Python36\\lib\\site-packages\\win32\\lib', 'C:\\Python36\\lib\\site-packages\\Pythonwin']
>>>

9-2 对比egg与wheel

一句话总结:wheel文件格式将最终取代egg文件格式,建议使用wheel文件来发布Python包。
Wheel和Egg都是打包的格式,都可用于Python模块的分发与安装;
Egg格式是由Setuptools在2004年引入,通过Setuptools可以识别Egg格式并解析安装;
Wheel格式是由PEP427在2012年定义,本质是zip包格式,最终将替代Egg格式;
Wheel现在被认为是构建和二进制包的标准格式,推荐使用wheel格式发布Python包,目前pip也可直接安装wheel格式包; Wheel和Egg的主要的不同点:
- Wheel有一个官方的PEP427来定义,而Egg没有PEP定义;
- Wheel是一种分发格式,即打包格式。而Egg既是一种分发格式,也是一种运行时安装的格式,并且是可以被import的;
- Wheel文件不会包含.pyc文件;
- Wheel使用和PEP376兼容的.dist-info目录,而Egg使用.egg-info目录;
- Wheel有着更丰富的命名规则;
- Wheel是有版本的。每个Wheel文件都包含wheel规格的版本和打包它的实现;
- Wheel在内部被sysconfig path type管理,因此转向其他格式也更容易;

9-3 Python的包管理工具