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 install
  build_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 extensions
  build_scripts     "build" scripts (copy and fixup #! line)
  clean             clean up temporary files from 'build' command
  install           install everything from build directory
  install_lib       install all Python modules (extensions and pure Python)
  install_headers   install C/C++ header files
  install_scripts   install scripts (Python or otherwise)
  install_data      install data files
  sdist             create a source distribution (tarball, zip file, etc.)
  register          register the distribution with the Python package index
  bdist             create a built (binary) distribution
  bdist_dumb        create a "dumb" built distribution
  bdist_rpm         create an RPM distribution
  check             perform some checks on the package
  upload            upload binary package to PyPI

Extra commands:
  alias             define a shortcut to invoke one or more commands
  bdist_egg         create an "egg" distribution
  develop           install package in 'development mode'
  dist_info         DO NOT CALL DIRECTLY, INTERNAL ONLY: create .dist-info directory
  easy_install      Find/get/install Python packages
  editable_wheel    DO NOT CALL DIRECTLY, INTERNAL ONLY: create PEP 660 editable wheel
  egg_info          create a distribution's .egg-info directory
  install_egg_info  Install an .egg-info directory for the package
  rotate            delete older distributions, keeping N newest files
  saveopts          save supplied options to setup.cfg or other config file
  setopt            set an option in setup.cfg or another config file
  test              run unit tests after in-place build (deprecated)
  upload_docs       Upload documentation to sites other than PyPi such as devpi
  bdist_wheel       create a wheel distribution

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

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中安装包的方式有很多种:

    1. 源码包:python setup.py install
    2. 在线安装: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_packages
    
    setup(
        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.md
    
    running 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.gz
      Preparing metadata (setup.py) ... done
    Building wheels for collected packages: myhello
      Building wheel for myhello (setup.py) ... done
      Created wheel for myhello: filename=myhello-1.0-py3-none-any.whl size=1338 sha256=9a7be51148e7290b12310f8a5fbf77e8a3a8ad5d5402ece97ae3c8fd18d2d34c
      Stored 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 other
            standards-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 other
            standards-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 other
            standards-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 other
            standards-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 file
    
    Installed /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.in
        exclude_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.in
       exclude_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.in
       exclude_package_date={'':['.gitignore']},
       install_requires=[         # 依赖列表
           'Flask>=0.10',
           'Flask-SQLAlchemy>=1.5,<=2.1'
       ]
    )
    

    find_packages()方法可以限定你要搜索的路径,比如使用find_packages(‘src’)就表明只在”src”子目录下搜索所有的Python包。

    setuptools简介

    作者:阿汤哥的程序之路

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python setuptools入门简介

    发表回复