distutils(包分发的始祖) 简介
distutils 是 Python 的一个标准库,从命名上很容易看出它是一个分发(distribute)工具(utlis),它是 Python 官方开发的一个分发打包工具,所有后续的打包工具,全部都是基于它进行开发的
distutils 的精髓在于编写 setup.py,它是模块分发与安装的指导文件
setuptools 简介
setuptools 是 distutils 增强版,不包括在标准库中。其扩展了很多功能,能够帮助开发者更好的创建和分发 Python 包。大部分 Python 用户都会使用更先进的 setuptools 模块。
setup.py命令
help
查看所有支持的命令
(practice) tangsiqi@tangsiqideMacBook-Pro demo % python setup.py --help-commands
Standard commands:build build everything needed to installbuild_py "build" pure Python modules (copy to build directory)build_ext build C/C++ extensions (compile/link to build directory)build_clib build C/C++ libraries used by Python extensionsbuild_scripts "build" scripts (copy and fixup #! line)clean clean up temporary files from 'build' commandinstall install everything from build directoryinstall_lib install all Python modules (extensions and pure Python)install_headers install C/C++ header filesinstall_scripts install scripts (Python or otherwise)install_data install data filessdist create a source distribution (tarball, zip file, etc.)register register the distribution with the Python package indexbdist create a built (binary) distributionbdist_dumb create a "dumb" built distributionbdist_rpm create an RPM distributioncheck perform some checks on the packageupload upload binary package to PyPIExtra commands:alias define a shortcut to invoke one or more commandsbdist_egg create an "egg" distributiondevelop install package in 'development mode'dist_info DO NOT CALL DIRECTLY, INTERNAL ONLY: create .dist-info directoryeasy_install Find/get/install Python packageseditable_wheel DO NOT CALL DIRECTLY, INTERNAL ONLY: create PEP 660 editable wheelegg_info create a distribution's .egg-info directoryinstall_egg_info Install an .egg-info directory for the packagerotate delete older distributions, keeping N newest filessaveopts save supplied options to setup.cfg or other config filesetopt set an option in setup.cfg or another config filetest run unit tests after in-place build (deprecated)upload_docs Upload documentation to sites other than PyPi such as devpibdist_wheel create a wheel distributionusage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]or: setup.py --help [cmd1 cmd2 ...]or: setup.py --help-commandsor: setup.py cmd --help
build
构建安装时所需的所有内容:
python setup.py build
install
安装包到系统环境中。该命令会将当前项目安装到当前Python环境的”site-packages”目录下,这样其他项目就可以像导入标准库一样导入该项目的代码了
python setup.py install
develop
以开发方式安装包。
如果项目在开发过程中会频繁变更,每次安装还需要先将原来的版本卸掉,会很麻烦。使用”develop”开发方式安装的话,项目代码不会真的被拷贝到本地Python环境的”site-packages”目录下,而是在”site-packages”目录里创建一个指向当前项目位置的链接。这样如果当前位置的源码被改动,就会马上反映到”site-packages”里。
python setup.py develop
register、upload
用于包的上传发布
python setup.py register
# 发布源码包
python setup.py sdist upload
sdist
构建源码分发包。该命令会在当前目录下的”dist”目录内创建一个压缩包,默认在 Windows 下为 zip 格式,Linux 下为 tag.gz 格式 ,也可以通过指定–formats参数指定压缩包格式。
执行 sdist 命令时,默认会被打包的文件:
- 所有 py_modules 或 packages 指定的源码文件
- 所有 ext_modules 指定的文件
- 所有 package_data 或 data_files 指定的文件
- 所有 scripts 指定的脚本文件
README、README.txt、setup.py 和 setup.cfg文件
该命令构建的包主要用于发布,例如上传到 pypi 上
python setup.py sdist
bdist
构建一个二进制的分发包
python setup.py bdist
bdist_egg
构建一个 egg 分发包,经常用来替代基于 bdist 生成的模式。该命令会在当前目录下的”dist”目录内创建一个”egg”文件,名为”MyApp-1.0-py2.7.egg”。文件名格式就是”项目名-版本号-Python版本.egg”。同时你会注意到,当前目录多了”build”和”MyApp.egg-info”子目录来存放打包的中间结果
python setup.py bdist_egg
bdist_wheel
构建一个 wheel 分发包,egg 包是过时的,whl 包是新的标准。同上面类似,只是打成的包的后缀是.whl
python setup.py bdist_wheel
简单例子
python中安装包的方式有很多种:
- 源码包:python setup.py install
- 在线安装:pip install 包名 / easy_install 包名
pip install的东西从哪里来的?
从PyPI (Python Package Index)来的,官网是: https://pypi.python.org/pypi
执行pip install terminaltranslator命令的时候,它就会去从官方网站搜terminaltranslator,搜到了就下载压缩包并解压安装,如果没有搜索到就会报错。
源码包安装
源码包安装就是你在本地编写好了一个模块,自己安装在本地使用,别人(即使是你自己)都不能 pip install xxx 下载你的模块
准备工作
# 1.首先创建我们需要的目录结构和文件(自行创建)
# 当前测试的目录是: /tmp/demo`-- demo|-- helloapp| |-- hello.py| `-- __init__.py`-- setup.py# 2.编辑 setup.py
from setuptools import setup, find_packagessetup(name="myhello",version="1.0",author="tsq",author_email="865466036@qq.com",packages=find_packages(),
)# 3.编辑 hello.py
def hello_func():print("HelloWorld")if __name__ == '__main__':hello_func()
打tar包
# 进入setup.py所在的那层目录
cd demo# 检查setup.py 是否有错误(warning不是错误)
python setup.py check# 安装
(practice) tangsiqi@tangsiqideMacBook-Pro demo % python setup.py sdist
running sdist
running egg_info
creating myhello.egg-info
writing myhello.egg-info/PKG-INFO
writing dependency_links to myhello.egg-info/dependency_links.txt
writing top-level names to myhello.egg-info/top_level.txt
writing manifest file 'myhello.egg-info/SOURCES.txt'
reading manifest file 'myhello.egg-info/SOURCES.txt'
writing manifest file 'myhello.egg-info/SOURCES.txt'
warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.mdrunning check
creating myhello-1.0
creating myhello-1.0/myhello
creating myhello-1.0/myhello.egg-info
copying files to myhello-1.0...
copying setup.py -> myhello-1.0
copying myhello/__init__.py -> myhello-1.0/myhello
copying myhello/hello.py -> myhello-1.0/myhello
copying myhello.egg-info/PKG-INFO -> myhello-1.0/myhello.egg-info
copying myhello.egg-info/SOURCES.txt -> myhello-1.0/myhello.egg-info
copying myhello.egg-info/dependency_links.txt -> myhello-1.0/myhello.egg-info
copying myhello.egg-info/top_level.txt -> myhello-1.0/myhello.egg-info
Writing myhello-1.0/setup.cfg
creating dist
Creating tar archive
removing 'myhello-1.0' (and everything under it)
(practice) tangsiqi@tangsiqideMacBook-Pro demo % ls
dist myhello myhello.egg-info setup.py
(practice) tangsiqi@tangsiqideMacBook-Pro demo % cd dist
(practice) tangsiqi@tangsiqideMacBook-Pro dist % ls
myhello-1.0.tar.gz
(practice) tangsiqi@tangsiqideMacBook-Pro dist % pip install myhello-1.0.tar.gz
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Processing ./myhello-1.0.tar.gzPreparing metadata (setup.py) ... done
Building wheels for collected packages: myhelloBuilding wheel for myhello (setup.py) ... doneCreated wheel for myhello: filename=myhello-1.0-py3-none-any.whl size=1338 sha256=9a7be51148e7290b12310f8a5fbf77e8a3a8ad5d5402ece97ae3c8fd18d2d34cStored in directory: /Users/tangsiqi/Library/Caches/pip/wheels/3a/db/cf/d984476e0b41811a7a7e36a258d5f6419a69c66d8a5986311d
Successfully built myhello
Installing collected packages: myhello
Successfully installed myhello-1.0
(practice) tangsiqi@tangsiqideMacBook-Pro dist % pip list|grep myhello
myhello 1.0
结果
打包之后多出两个文件夹,分别是demo.egg-info和dist。
demo.egg-info是必要的安装信息,
而dist中的压缩包就是安装包,此时默认的tar包
(practice) tangsiqi@tangsiqideMacBook-Pro demo % python
Python 3.8.17 | packaged by conda-forge | (default, Jun 16 2023, 07:11:32)
[Clang 14.0.6 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from myhello import hello
>>> hello.hello_func()
HelloWorld
>>> exit()
打egg包
(practice) tangsiqi@tangsiqideMacBook-Pro demo % ls
myhello setup.py
(practice) tangsiqi@tangsiqideMacBook-Pro demo % python setup.py bdist_egg
running bdist_egg
running egg_info
creating myhello.egg-info
writing myhello.egg-info/PKG-INFO
writing dependency_links to myhello.egg-info/dependency_links.txt
writing top-level names to myhello.egg-info/top_level.txt
writing manifest file 'myhello.egg-info/SOURCES.txt'
reading manifest file 'myhello.egg-info/SOURCES.txt'
writing manifest file 'myhello.egg-info/SOURCES.txt'
installing library code to build/bdist.macosx-11.0-arm64/egg
/Users/tangsiqi/miniconda3/envs/practice/lib/python3.8/site-packages/setuptools/_distutils/cmd.py:66: SetuptoolsDeprecationWarning: setup.py install is deprecated.
!!********************************************************************************Please avoid running ``setup.py`` directly.Instead, use pypa/build, pypa/installer or otherstandards-based tools.See https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html for details.********************************************************************************!!self.initialize_options()
running install_lib
running build_py
creating build
creating build/lib
creating build/lib/myhello
copying myhello/hello.py -> build/lib/myhello
copying myhello/__init__.py -> build/lib/myhello
creating build/bdist.macosx-11.0-arm64
creating build/bdist.macosx-11.0-arm64/egg
creating build/bdist.macosx-11.0-arm64/egg/myhello
copying build/lib/myhello/hello.py -> build/bdist.macosx-11.0-arm64/egg/myhello
copying build/lib/myhello/__init__.py -> build/bdist.macosx-11.0-arm64/egg/myhello
byte-compiling build/bdist.macosx-11.0-arm64/egg/myhello/hello.py to hello.cpython-38.pyc
byte-compiling build/bdist.macosx-11.0-arm64/egg/myhello/__init__.py to __init__.cpython-38.pyc
creating build/bdist.macosx-11.0-arm64/egg/EGG-INFO
copying myhello.egg-info/PKG-INFO -> build/bdist.macosx-11.0-arm64/egg/EGG-INFO
copying myhello.egg-info/SOURCES.txt -> build/bdist.macosx-11.0-arm64/egg/EGG-INFO
copying myhello.egg-info/dependency_links.txt -> build/bdist.macosx-11.0-arm64/egg/EGG-INFO
copying myhello.egg-info/top_level.txt -> build/bdist.macosx-11.0-arm64/egg/EGG-INFO
zip_safe flag not set; analyzing archive contents...
creating dist
creating 'dist/myhello-1.0-py3.8.egg' and adding 'build/bdist.macosx-11.0-arm64/egg' to it
removing 'build/bdist.macosx-11.0-arm64/egg' (and everything under it)
打wheel包
(practice) tangsiqi@tangsiqideMacBook-Pro demo % ls
myhello setup.py
(practice) tangsiqi@tangsiqideMacBook-Pro demo % python setup.py bdist_wheel
running bdist_wheel
running build
running build_py
creating build
creating build/lib
creating build/lib/myhello
copying myhello/hello.py -> build/lib/myhello
copying myhello/__init__.py -> build/lib/myhello
/Users/tangsiqi/miniconda3/envs/practice/lib/python3.8/site-packages/setuptools/_distutils/cmd.py:66: SetuptoolsDeprecationWarning: setup.py install is deprecated.
!!********************************************************************************Please avoid running ``setup.py`` directly.Instead, use pypa/build, pypa/installer or otherstandards-based tools.See https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html for details.********************************************************************************!!self.initialize_options()
installing to build/bdist.macosx-11.0-arm64/wheel
running install
running install_lib
creating build/bdist.macosx-11.0-arm64
creating build/bdist.macosx-11.0-arm64/wheel
creating build/bdist.macosx-11.0-arm64/wheel/myhello
copying build/lib/myhello/hello.py -> build/bdist.macosx-11.0-arm64/wheel/myhello
copying build/lib/myhello/__init__.py -> build/bdist.macosx-11.0-arm64/wheel/myhello
running install_egg_info
running egg_info
creating myhello.egg-info
writing myhello.egg-info/PKG-INFO
writing dependency_links to myhello.egg-info/dependency_links.txt
writing top-level names to myhello.egg-info/top_level.txt
writing manifest file 'myhello.egg-info/SOURCES.txt'
reading manifest file 'myhello.egg-info/SOURCES.txt'
writing manifest file 'myhello.egg-info/SOURCES.txt'
Copying myhello.egg-info to build/bdist.macosx-11.0-arm64/wheel/myhello-1.0-py3.8.egg-info
running install_scripts
creating build/bdist.macosx-11.0-arm64/wheel/myhello-1.0.dist-info/WHEEL
creating 'dist/myhello-1.0-py3-none-any.whl' and adding 'build/bdist.macosx-11.0-arm64/wheel' to it
adding 'myhello/__init__.py'
adding 'myhello/hello.py'
adding 'myhello-1.0.dist-info/METADATA'
adding 'myhello-1.0.dist-info/WHEEL'
adding 'myhello-1.0.dist-info/top_level.txt'
adding 'myhello-1.0.dist-info/RECORD'
removing build/bdist.macosx-11.0-arm64/wheel
(practice) tangsiqi@tangsiqideMacBook-Pro demo % ls
build dist myhello myhello.egg-info setup.py
(practice) tangsiqi@tangsiqideMacBook-Pro demo % cd dist
(practice) tangsiqi@tangsiqideMacBook-Pro dist % pip list|grep my
(practice) tangsiqi@tangsiqideMacBook-Pro dist % pip install myhello-1.0-py3-none-any.whl
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Processing ./myhello-1.0-py3-none-any.whl
Installing collected packages: myhello
Successfully installed myhello-1.0
(practice) tangsiqi@tangsiqideMacBook-Pro dist % pip list|grep my
myhello 1.0
(practice) tangsiqi@tangsiqideMacBook-Pro dist % python
Python 3.8.17 | packaged by conda-forge | (default, Jun 16 2023, 07:11:32)
[Clang 14.0.6 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from myhello import hello
>>> hello.hello_func()
HelloWorld
>>> exit()
快捷操作–打包|安装
python setup.py install
(practice) tangsiqi@tangsiqideMacBook-Pro demo % ls
myhello setup.py
(practice) tangsiqi@tangsiqideMacBook-Pro demo % python setup.py install
running install
/Users/tangsiqi/miniconda3/envs/practice/lib/python3.8/site-packages/setuptools/_distutils/cmd.py:66: SetuptoolsDeprecationWarning: setup.py install is deprecated.
!!********************************************************************************Please avoid running ``setup.py`` directly.Instead, use pypa/build, pypa/installer or otherstandards-based tools.See https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html for details.********************************************************************************!!self.initialize_options()
/Users/tangsiqi/miniconda3/envs/practice/lib/python3.8/site-packages/setuptools/_distutils/cmd.py:66: EasyInstallDeprecationWarning: easy_install command is deprecated.
!!********************************************************************************Please avoid running ``setup.py`` and ``easy_install``.Instead, use pypa/build, pypa/installer or otherstandards-based tools.See https://github.com/pypa/setuptools/issues/917 for details.********************************************************************************!!self.initialize_options()
running bdist_egg
running egg_info
creating myhello.egg-info
writing myhello.egg-info/PKG-INFO
writing dependency_links to myhello.egg-info/dependency_links.txt
writing top-level names to myhello.egg-info/top_level.txt
writing manifest file 'myhello.egg-info/SOURCES.txt'
reading manifest file 'myhello.egg-info/SOURCES.txt'
writing manifest file 'myhello.egg-info/SOURCES.txt'
installing library code to build/bdist.macosx-11.0-arm64/egg
running install_lib
running build_py
creating build
creating build/lib
creating build/lib/myhello
copying myhello/hello.py -> build/lib/myhello
copying myhello/__init__.py -> build/lib/myhello
creating build/bdist.macosx-11.0-arm64
creating build/bdist.macosx-11.0-arm64/egg
creating build/bdist.macosx-11.0-arm64/egg/myhello
copying build/lib/myhello/hello.py -> build/bdist.macosx-11.0-arm64/egg/myhello
copying build/lib/myhello/__init__.py -> build/bdist.macosx-11.0-arm64/egg/myhello
byte-compiling build/bdist.macosx-11.0-arm64/egg/myhello/hello.py to hello.cpython-38.pyc
byte-compiling build/bdist.macosx-11.0-arm64/egg/myhello/__init__.py to __init__.cpython-38.pyc
creating build/bdist.macosx-11.0-arm64/egg/EGG-INFO
copying myhello.egg-info/PKG-INFO -> build/bdist.macosx-11.0-arm64/egg/EGG-INFO
copying myhello.egg-info/SOURCES.txt -> build/bdist.macosx-11.0-arm64/egg/EGG-INFO
copying myhello.egg-info/dependency_links.txt -> build/bdist.macosx-11.0-arm64/egg/EGG-INFO
copying myhello.egg-info/top_level.txt -> build/bdist.macosx-11.0-arm64/egg/EGG-INFO
zip_safe flag not set; analyzing archive contents...
creating dist
creating 'dist/myhello-1.0-py3.8.egg' and adding 'build/bdist.macosx-11.0-arm64/egg' to it
removing 'build/bdist.macosx-11.0-arm64/egg' (and everything under it)
Processing myhello-1.0-py3.8.egg
Copying myhello-1.0-py3.8.egg to /Users/tangsiqi/miniconda3/envs/practice/lib/python3.8/site-packages
Adding myhello 1.0 to easy-install.pth fileInstalled /Users/tangsiqi/miniconda3/envs/practice/lib/python3.8/site-packages/myhello-1.0-py3.8.egg
Processing dependencies for myhello==1.0
Finished processing dependencies for myhello==1.0
python使用setuptools打包
特性文件
setup.py、MANIFEST.in、setup.cfg
引入非Python文件
上例中,我们只会将”myhello”包下的源码打包,如果想引入静态文件,如JS、CSS、图片等,怎么做?
答:在项目根目录下添加一个”MANIFEST.in”文件夹。假设我们把所有静态文件都放在”static”子目录下,现在的项目结构如下:
demo/├ setup.py # 安装文件├ MANIFEST.in # 清单文件└ myhello/ # 源代码├ static/ # 静态文件目录├ __init__.py...
我们在清单文件”MANIFEST.in”中,列出想要在包内引入的目录路径:
recursive-include myhello/static *
recursive-include myhello/xxx *
recursive-include表明包含子目录
在”setup.py”中将include_package_data参数设为True:
#coding:utf8
from setuptools import setup
setup(name='MyHello', # 项目名version='1.0',# 版本号packages=['myhello'], # 包括在安装包内的Python包include_package_data=True # 启用清单文件MANIFEST.in
)
如果你想排除一部分文件,可以在”setup.py”中使用exclude_package_date参数:
setup(...include_package_data=True, # 启用清单文件MANIFEST.inexclude_package_date={'':['.gitignore']}
)
依赖管理
我们的项目会依赖其他Python模块,如何在setup.py中管理这些依赖呢?
答:修改”setup.py”文件,加入install_requires参数
#coding:utf8
from setuptools import setup
setup(name='MyHello', # 项目名version='1.0',# 版本号packages=['myhello'], # 包括在安装包内的Python包include_package_data=True, # 启用清单文件MANIFEST.inexclude_package_date={'':['.gitignore']},install_requires=[ # 依赖列表'Flask>=0.10','Flask-SQLAlchemy>=1.5,<=2.1']
)
上面的代码中,我们声明了应用依赖Flask 0.10及以上版本,和Flask-SQLAlchemy 1.5及以上、2.1及以下版本。setuptools会先检查本地有没有符合要求的依赖包,如果没有的话,就会从PyPI中获得一个符合条件的最新的包安装到本地。
可以通过dependency_links指定依赖包下载路径。install_requires中的包在安装时会先去PyPI下载并安装,如果包在PyPI中找不到,则会从dependency_links标识的URL中获取:
setup(...install_requires=[ # 依赖列表'Flask>=0.10','Flask-SQLAlchemy>=1.5,<=2.1'],dependency_links=[ # 依赖包下载路径'http://example.com/dependency.tar.gz']
)
自动搜索Python包
packages=[‘myhello’] :将Python包”myapp”下的源码打包。
之前我们在”setup.py”中指定了packages=[‘myhello’],说明将Python包”myhello”下的源码打包。但是如果我们的应用很大,逐一列举需要打包的源码包会很麻烦,这时就需要用到setuptools提供的find_packages()方法来自动搜索可以引入的Python包。
packages=find_packages() :自动搜索可以引入的Python包,它默认在与 setup.py 文件同一目录下搜索各个含有 init.py 的目录做为要添加的包
#coding:utf8
from setuptools import setup, find_packages
setup(name='MyHello', # 应用名version='1.0', # 版本号packages=find_packages(), # 包括在安装包内的Python包include_package_data=True, # 启用清单文件MANIFEST.inexclude_package_date={'':['.gitignore']},install_requires=[ # 依赖列表'Flask>=0.10','Flask-SQLAlchemy>=1.5,<=2.1']
)
find_packages()方法可以限定你要搜索的路径,比如使用find_packages(‘src’)就表明只在”src”子目录下搜索所有的Python包。
setuptools简介