pip包详解

pip包详解

参考文章:https://pythonav.com/wiki/detail/6/95/

参考文章:https://blog.csdn.net/weixin_36338224/article/details/109539961

在 python 中最常用的第三包安装命令pip install package;这个包通常都是别人直接发布在平台上的功能包;

平常我们最常用的就是使用pip来安装第三方包,但是我们也可以将我们自己的源代码封装成pip包;形成开箱即用的代码工具,可以极大的提高我们的代码效率;

1.pip 包的简单制作

pip 包的制作主要分为,目录创建,程序开发,项目打包,文件上传等步骤;

1.1 PyPi介绍

image-20230106225502424

可以把PyPi是一个包管理平台,我们常用的所有包都会托管在这个平台,我们通过pip install xx他会在PyPi找到这个包并下载到我们的电脑的上。

对于模块开发者本质上需要做三件事:

  • 编写模块
  • 将模块进行打包
  • 上传到PyPi(要是制作本地的私有包,本步骤可以忽略)
    • 注册账号
    • 安装上传工具
    • 基于工具上传

调用者只要做两件事:

  • 通过pip install 模块去安装;
  • 调用模块

1.2 简单制作

假设,我们现在要做一个名加fucker的模块;

首先创建如下的目录

fucker
├── LICENSE           # 声明,给模块使用者看,说白了就是使用者是否可以免费用于商业用途等。
├── README.md         # 模块介绍
├── demos             # 使用案例
├── fucker            # 模块代码目录
│   └── __init__.py
└── setup.py          # 给setuptools提供信息的脚本(名称、版本等)
1.2.1 License

License文件就是咱们模块的许可证,给模块的使用者看,说白了就是使用者是否可以免费用于商业用途等。一般开源的软件会选择相对宽泛许可证 MIT,即:作者保留版权,无其他任何限制。

Copyright (c) 2018 The Python Packaging Authority
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

更多其他许可证参见:https://choosealicense.com/

1.2.2 readme

readme.md就是对于当前模块的描述信息工具,一般用markdown的格式进行编写:

fucker 是一个非常优秀的工具

1.2.3 demos目录

该文件夹一般会写一些模块的使用示例,用于使用者可以快速的将模块应用到开发中;

1.2.4 setup.py

setup.py文件其实就是一个配置文件,用于给setuptools提供一些模块相关的信息,如:模块名称,模块版本,适用 python 版本,作者,github地址等;

import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()
    
setuptools.setup(
    name="fucker",  # 模块名称
    version="1.0",  # 当前版本
    author="wupeiqi",  # 作者
    author_email="wupeiqi@live.com",  # 作者邮箱
    description="一个非常NB的包",  # 模块简介
    long_description=long_description,  # 模块详细介绍,这里可以读取readme文件
    long_description_content_type="text/markdown",  # 模块详细介绍格式
    # url="https://github.com/wupeiqi/fucker",  # 模块github地址
    packages=setuptools.find_packages(),  # 自动找到项目中导入的模块
    # 模块相关的元数据
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    # 依赖模块
    install_requires=[
        'pillow',
    ],
    python_requires='>=3',
)

说明:setuptools 是一个包管理工具,用于打包和安装模块;

1.2.5 fucker 目录

插件内容,可以将相关代码在此处编写wp.py

def func():
    print("一个神奇的包")

最后文件的如下所示:

fucker
├── LICENSE
├── README.md
├── demos
├── fucker
│   ├── __init__.py
│   └── wp.py
└── setup.py

1.3 打包

上述步骤中将文件夹和代码编写好之后,就需要对代码进行打包处理。

1.3.1 安装打包工具

打包代码需先安装setuptools和wheel两个工具,可以单独安装,也可以安装pip,从而自动安装这两个工具。

因为安装 Python3 的时候自带安装pip的功能,因此这两个打包工具如果装好 Python3 的话则是免安装的;

注意:已安装用户,如要更新setuptools和wheel两个工具,可通过如下命令:

python -m pip install --upgrade setuptools wheel
1.3.2 打包代码
# 在终端中进入setup所在的目录;执行如下语句
python setup.py sdist bdist_wheel

打包之后的路径如下

fucker
├── LICENSE
├── README.md
├── fucker
│   ├── __init__.py
│   └── wp.py
├── fucker.egg-info
│   ├── PKG-INFO
│   ├── SOURCES.txt
│   ├── dependency_links.txt
│   └── top_level.txt
├── build
│   ├── bdist.macosx-10.6-intel
│   └── lib
│       └── fucker
│           ├── __init__.py
│           └── wp.py
├── demos
├── dist
│   ├── fucker-0.0.1-py3-none-any.whl
│   └── fucker-0.0.1.tar.gz
└── setup.py

image-20230107230027524

打包产生的文件如下,后面会进行详细描述;

1.4上传发布

文件打包完毕后,需要将打包之后的文件上传到PyPI,如果想要上传是需要先去 https://pypi.org/ 注册一个账号。

  • 安装用于发布模块的工具:twine 【已安装无需重复安装】

    python -m pip install --upgrade twine
    或
    pip install --upgrade twine
    # 提示:python -m 的作用是 run library module as a script (terminates option list)
    
  • 发布上传

    python -m twine upload --repository-url https://upload.pypi.org/legacy/  dist/*
    或
    twine upload --repository-url https://upload.pypi.org/legacy/  dist/*
    

    上传时,提示需要输入PyPI的用户名和密码.

1.5 安装使用

  • 如果发布在远程上的话可以直接使用pip安装使用

    pip install fucker
    
    from fuker import wp
    
    wp.func()
    

    image-20230107225855901

  • 如果我们没有上传,只是想将我们的代码打包成开箱可用的状态,则可以在解压缩的包里面,或者是setup.py的目录中,直接执行python setup.py install ,则该包也会被安装到 python 的环境中;

    # 打包时
    python setup.py sdist bdist_wheel
    # 安装时
    python setup.py install
    

2.setup.py深入学习

上述的打包命令:

# 在终端中进入setup所在的目录;执行如下语句
python setup.py sdist bdist_wheel

python 发展了很多年了,项目打包的工具也很成熟了;你可能听过disutils,distutils, distutils2,setuptools等等,好像很熟悉,却又很模式,他们之间都有什么关系呢?他们之间的关系可以看参考文档中的详细解释,可以简单的了理解为前面的几个是不太完善的setuptools

2.1 setuptools

setuptools 是一个优秀的,可靠的 Python 包安装与分发工具。

安装 setuptools 的方式主要是两种:

  • 源码安装

    源码安装:在 https://pypi.org/project/setuptools/#files 中下载 zip 包 解压执行 python setup.py install 安装

  • 新版本的 python3

    新版本的 python3 中一般都会自带 setuptools;在 python 中创建出的虚拟环境中自动也有已经安装好的包;

2.1.1 easy_install

注:本命令不是特别常用,简单了解即可;

# 通过包名,从PyPI寻找最新版本,自动下载、编译、安装
$ easy_install pkg_name

# 通过包名从指定下载页寻找链接来安装或升级包
$ easy_install -f http://pythonpaste.org/package_index.html 

# 指定线上的包地址安装
$ easy_install http://example.com/path/to/MyPackage-1.2.3.tgz

# 从本地的 .egg 文件安装
$ easy_install xxx.egg

# 在安装时你可以添加额外的参数
指定安装目录:--install-dir=DIR, -d DIR
指定用户安装:--user

以上仅介绍了 easy_install 的一些常用的方法,想要了解更多,你可以点击官方文档:https://setuptools.readthedocs.io/en/latest/easy_install.html

总结一句:setuptools 是官方提供的一个专业用于包分发的工具,若只从安装的角度来看,它的功能确实简单。它更大的意义是对包的分发很有用,定制化程序非常高,我们现在也还在用它进行版本包的发布。

2.2 源码包和二进制包

python 的包分为两种:源码包和二进制包;

2.2.1 以源码的方式发布

源码包安装的过程,是先解压,在编译,最后才安装,所以他是跨平台的,由于每次安装都要进行编译,相对二进制安装包安装的方式来说,安装速度比较慢;

源码包的本质是一个压缩包,其常见的格式有如下几种:

格式后缀
zip.zip
gztar.tar.gz
ztar.tar,Z
tar.tar

最常用的是.tar.gz,通常在 linux 中使用;

2.2.2 以二进制包形式发布

二进制的包省去了编译的过程,直接解压安装,所以安装速度较源码来说更快。由于不同平台编译出来的包无法通通,需要事先编译好多个平台的包。

二进制包常见的格式有:

格式后缀
egg.egg
wheel.whl

Egg 格式是由 setuptools 在 2004 年引入,而 Wheel 格式是由 PEP427 在 2012 年定义。Wheel 的出现是为了替代 Egg,它的本质是一个zip包,其现在被认为是 Python 的二进制包的标准格式。

这两种格式有什么区别呢?

  • Wheel 有一个官方的 PEP427 来定义,而 Egg 没有 PEP 定义。
  • Wheel 是一种分发格式,即打包格式。而 Egg 既是一种分发格式,也是一种运行时安装的格式,并且是可以被直接 import。
  • Wheel 文件不会包含 .pyc 文件
  • Wheel 使用和 PEP376 兼容的 .dist-info 目录,而 Egg 使用 .egg-info 目录
  • Wheel 有着更丰富的命名规则。
  • Wheel 是有版本的。每个 Wheel 文件都包含 wheel 规范的版本和打包的实现。
  • Wheel 在内部被 sysconfig path type 管理,因此转向其他格式也更容易

wheel 包可以通过 pip 来安装,只不过需要先安装 wheel 模块,然后再使用 pip 的命令。

$ pip install wheel
$ pip wheel --wheel-dir=/local/wheels pkg

**注:**可以直接安装pip install xxx.whl

2.3 详解 setup.py

在上述的打包案例中,我们可以发现最重要的就是setup.py文件。

import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()
    
setuptools.setup(
    name="fucker",  # 模块名称
    version="1.0",  # 当前版本
    author="wupeiqi",  # 作者
    author_email="wupeiqi@live.com",  # 作者邮箱
    description="一个非常NB的包",  # 模块简介
    url="https://github.com/wupeiqi/fucker",  # 模块github地址
    packages=setuptools.find_packages(),  # 自动找到项目中导入的模块
)

这是上述打包文件的缩减版,下面我们将慢慢来扩充setup.py函数,增加更多的参数;

2.3.1 程序分类信息

classifiers参数说明包的分类信息。所有支持的分类列表见:https://pypi.org/pypi?%3Aaction=list_classifiers

# 模块相关的元数据
classifiers=[
    "Programming Language :: Python :: 3",    	 # 目标 Python 的版本
    "License :: OSI Approved :: MIT License",	 # 许可证信息
    "Operating System :: OS Independent",		 # 操作系统
    
    # 发展时期,常见的如下
    #   3 - Alpha
    #   4 - Beta
    #   5 - Production/Stable
    'Development Status :: 3 - Alpha',    # 开发状态
    # 开发的目标用户
    'Intended Audience :: Developers',
    
    # 属于什么类型
    'Topic :: Software Development :: Build Tools'
]

最常见的系统编写信息,最开的案例中,我们只写了 3 个,如果是个人使用的工具包,则不需要将信息填写的复杂;

2.3.2 文件分发
setuptools.setup(
    name="fucker",  # 模块名称
    version="1.0",  # 当前版本
    author="wupeiqi",  # 作者
    author_email="wupeiqi@live.com",  # 作者邮箱
    description="一个非常NB的包",  # 模块简介
    url="https://github.com/wupeiqi/fucker",  # 模块github地址
    packages=setuptools.find_packages(),  # 自动找到项目中导入的模块
    
    # 安装过程中,需要安装的静态文件,如配置文件、service文件、图片等
    data_files=[
        ('', ['conf/*.conf']),
        ('/usr/lib/systemd/system/', ['bin/*.service']),
    ],

    # 希望被打包的文件
    package_data={
        '':['*.txt'],
        'bandwidth_reporter':['*.txt']
    },
    
    # 不打包某些文件
    exclude_package_data={
        'bandwidth_reporter':['*.txt']
    }
)

除了以上的参数配置以外,还可以使用一个叫做MANIFEST.in的文件,来控制文件的分发。下面是一个MANIFET.in文件的示例:

include *.txt
recursive-include examples *.txt *.py
prune examples/sample?/build

这些配置,规定了如下几点

  • 所有根目录下的以 txt 为后缀名的文件,都会分发
  • 根目录下的 examples 目录 和 txt、py文件都会分发
  • 路径匹配上 examples/sample?/build 不会分发

MANIFEST.in 需要放在和 setup.py 同级的顶级目录下,setuptools 会自动读取该文件。

通常打包的时候不会需要大量的文件,因此我们也会直接使用在setup.py文件中使用;

2.3.4 依赖包的下载
from setuptools import setup, find_packages


setup(
    ...

    # 表明当前模块依赖哪些包,若环境中没有,则会从pypi中下载安装
    install_requires=['docutils>=0.3'],

    # setup.py 本身要依赖的包,这通常是为一些setuptools的插件准备的配置
    # 这里列出的包,不会自动安装。
    setup_requires=['pbr'],

    # 仅在测试时需要使用的依赖,在正常发布的代码中是没有用的。
    # 在执行python setup.py test时,可以自动安装这三个库,确保测试的正常运行。
    tests_require=[
        'pytest>=3.3.1',
        'pytest-cov>=2.5.1',
    ],

    # 用于安装setup_requires或tests_require里的软件包
    # 这些信息会写入egg的 metadata 信息中
    dependency_links=[
        "http://example2.com/p/foobar-1.0.tar.gz",
    ],

    # install_requires 在安装模块时会自动安装依赖包
    # 而 extras_require 不会,这里仅表示该模块会依赖这些包
    # 但是这些包通常不会使用到,只有当你深度使用模块时,才会用到,这里需要你手动安装
    extras_require={
        'PDF':  ["ReportLab>=1.2", "RXP"],
        'reST': ["docutils>=0.3"],
    }
)

以上是各种情况参数的说明,但是我们最常用的就是第一个install_requires,关于这个参数的最常用的用法如下:

  • 'argparse',只包含包名,这种形式只检查包的存在性,不检查版本。方便,但不利于控制风险
  • 'setuptools==38.2.4',指定版本。 这种形式把风险降到了最低,确保了开发、测试与部署的版本一致,不会出现意外。 缺点是不利于更新,每次更新都需要改动代码。
  • 'docutils >= 0.3',这是比较常用的形式。 当对某个库比较信任时,这种形式可以自动保持版本为最新。
  • 'Django >= 1.11, != 1.11.1, <= 2',这是比较复杂的形式。 如这个例子,保证了Django的大版本在1.11和2之间,也即1.11.x;并且,排除了已知有问题的版本1.11.1(仅举例)。 对于一些大型、复杂的库,这种形式是最合适的。
  • 'requests[security, socks] >= 2.18.4',这是包含了额外的可选依赖的形式。 正常安装requests会自动安装它的install_requires中指定的依赖,而不会安装securitysocks这两组依赖。 这两组依赖是定义在它的extras_require中。 这种形式,用在深度使用某些库时。
2.3.5 安装环境的限制

有些库并不是在所以的 Python 版本中都适用的,若一个库安装在一个未兼容的 Python 环境中,理论上不应该在使用时才报错,而应该在安装过程就使其失败,提示禁止安装。

这样的功能,可以使用 python_requires 来实现。

setup(
    ...
    python_requires='>=2.7, <=3',
)
2.3.6 生成可执行文件的分发
from setuptools import setup, find_packages


setup(
    name="mytest",
    version="1.0",
    author="wangbm",
    author_email="wongbingming@163.com",
    description="Learn to Pack Python Module",
    url="http://iswbm.com/", 
    packages=find_packages(),

    # 用来支持自动生成脚本,安装后会自动生成 /usr/bin/foo 的可执行文件
    # 该文件入口指向 foo/main.py 的main 函数
    entry_points={
        'console_scripts': [
            'foo = foo.main:main'
        ]
    },

    # 将 bin/foo.sh 和 bar.py 脚本,生成到系统 PATH中
    # 执行 python setup.py install 后
    # 会生成 如 /usr/bin/foo.sh 和 如 /usr/bin/bar.py
    scripts=['bin/foo.sh', 'bar.py']
)

上面的 scripts 里面有shpy后缀,那么安装后,setuptools 会原封不动的移动到/usr/bin中,并添加可执行权限.

若你想对这些文件再作一些更改,比如去掉多余的后缀,可以这样做

from setuptools.command.install_scripts import install_scripts

class InstallScripts(install_scripts):

    def run(self):
        setuptools.command.install_scripts.install_scripts.run(self)

        # Rename some script files
        for script in self.get_outputs():
            if basename.endswith(".py") or basename.endswith(".sh"):
                dest = script[:-3]
            else:
                continue
            print("moving %s to %s" % (script, dest))
            shutil.move(script, dest)

setup(
    ...
    scripts=['bin/foo.sh', 'bar.py'],

    cmdclass={
        "install_scripts": InstallScripts
    }
)

这个方法类似于django使用django-admin startproject name我们猜测就是这种的打包方式,后期我们将阅读 django 的部分源码进行解析,该命令的真正原理;

2.3.6 ext_modules

ext_modules参数用于构建 C 或者 C++ 扩展的包。其实 Extension 实例的列表,每一个 Extension 实例描述了一个独立的扩展模块,扩展模块可以设置扩展包名,头文件、源文件、链接库及其路径、宏定义和编辑参数等。如:

setup(
    # other arguments here...
    ext_modules=[
        Extension('foo',
                  glob(path.join(here, 'src', '*.c')),
                  libraries = [ 'rt' ],
                  include_dirs=[numpy.get_include()])
    ]
)
2.3.7 指定 release

setup.py里面只能指定 version,而不能指定 release,如果你需要变更版本号,可以使用 --release 参数进行指定

python setup.py bdist_rpm --release=20200617

setup.py 的参数非常多,能够不借助文档写好一个setup.py好像没那么简单。为了备忘,我整理了 setup 函数常用的一些参数:setup.py 的参数非常多,参考文献中整理的如下:

img

2.3.8 构建包
  • 构建源码包

    用于发布一个 python 模块或项目,将源码打包成.tar.gz或者zip

    python setup.py sdist
    

    那么这种包如何安装呢?

    # 方法一
    easy_install xxx.tar.gz
    
    # 方法二
    # 将压缩包解压后,进入文件的目录
    python setup.py install
    

    使用 sdist 将根据当前平台创建默认格式的存档。在类 Unix 平台上,将创建后缀后为 .tar.gz 的 gzip 压缩的tar文件分发包,而在Windows上为 ZIP 文件。

    当然,你也可以通过指定你要的发布包格式来打破这个默认行为

    $ python setup.py sdist --formats=gztar,zip
    你可以指定的格式有哪些呢?

    创建一个压缩的tarball和一个zip文件。可用格式为:

    img

    对以上的格式,有几点需要注意一下:

    在版本3.5中才添加了对 xztar 格式的支持
    zip 格式需要你事先已安装相应的模块:zip程序或zipfile模块(已成为Python的标准库)
    ztar 格式正在弃用,请尽量不要使用
    另外,如果您希望归档文件的所有文件归root拥有,可以这样指定

    python setup.py sdist --owner=root --group=root
    
  • 构建二进制包

    在windows中我们习惯了双击 exe 进行软件的安装,Python 模块的安装也同样支持 打包成 exe 这样的二进制软件包。

    $ python setup.py bdist_wininst
    

    而在 Linux 中,大家也习惯了使用 rpm 来安装包,对此你可以使用这条命令实现 rpm 包的构建;

    $ python setup.py bdist_egg
    

    若你喜欢使用 easy_install 或者 pip 来安装离线包。你可以将其打包成 egg 包

    $ python setup.py bdist_egg
    

    若你的项目,需要安装多个平台下,既有 Windows 也有 Linux,按照上面的方法,多种格式我们要执行多次命令,为了方便,你可以一步到位,执行如下这条命令,即可生成多个格式的进制包

    $ python setup.py bdist
    
2.3.9 安装包

正常情况下,我们都是通过以上构建的源码包或者二进制包进行模块安装。

但在编写 setup.py 的过程中,可能不能一步到位,需要多次调试,这时候如何测试自己写的 setup.py 文件是可用的呢?

这时候你可以使用这条命令,它会将你的模块安装至系统全局环境中

# 虚拟环境中,可以直接安装到虚拟环境中;
$ python setup.py install

如若你的项目还处于开发阶段,频繁的安装模块,也是一个麻烦事。

这时候你可以使用这条命令安装,该方法不会真正的安装包,而是在系统环境中创建一个软链接指向包实际所在目录。这边在修改包之后不用再安装就能生效,便于调试。

# 生产环境中安装;
$ python setup.py develop
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值