• 如何构建自己的Python包

    前言

    在日常开发中,我们经常会把一些通用的函数、类或者模块抽取出来,以便在不同的项目中复用。随着代码的积累,如何将这些工具封装为一个标准的 Python 包(Python Package),并且方便团队甚至社区使用,就成为一个值得思考的问题。本文将从零开始,介绍如何构建、打包和发布自己的 Python 包。

    为什么要构建 Python 包?

    1. 代码复用:将常用的工具函数和逻辑沉淀为独立模块,避免复制粘贴。

    2. 团队协作:通过 PyPI 或内部源发布,团队成员可以直接 pip install 使用。

    3. 版本管理:独立的包可以有清晰的版本迭代,不影响主项目。

    4. 生态兼容:遵循 Python 社区标准,可以更好地融入现有生态。

    单仓库多模块

    在实际运维开发中,往往不仅仅是一个独立的 Python 包,而是需要在同一个仓库(monorepo) 中维护多个模块(package)。这种模式能够更好地统一管理依赖、版本以及 CI/CD 流程,适合团队协作和多服务共存的场景。

    目录结构示例

    ├── MANIFEST.in
    ├── README.md
    ├── docs
    ├── exec.py
    ├── publish.sh
    ├── pyproject.toml
    ├── src
    │   ├── pytbox
    │   │   ├── __pycache__
    │   │   ├── alert
    │   │   ├── alicloud
    │   │   ├── base.py
    │   │   ├── categraf
    │   │   ├── cli
    │   │   ├── cli.py
    │   │   ├── common
    │   │   ├── database
    │   │   ├── dida365.py
    │   │   ├── feishu
    │   │   ├── log
    │   │   ├── onepassword_connect.py
    │   │   ├── onepassword_sa.py
    │   │   └── utils
    │   └── pytbox.egg-info
    │       ├── PKG-INFO
    │       ├── SOURCES.txt
    │       ├── dependency_links.txt
    │       ├── entry_points.txt
    │       ├── requires.txt
    │       └── top_level.txt
    └── tests
        ├── alert
        │   ├── __pycache__
        │   ├── config_dev.toml
        │   ├── test_ping.py
        │   └── test_traffic.py
        ├── categraf
        │   ├── conf
        │   └── instances.toml
        ├── conftest.py
        ├── test_base.py
        ├── test_feishu.py
        ├── test_logger.py
        ├── test_onepassword_connect.py
        ├── test_onepassword_sa.py
        ├── test_victoriametrics.py
        └── utils
            ├── __pycache__
            └── test_timeutils.py
    
    29 directories, 38 files

    开发环境搭建

    我使用的是 devcontainer 作为开发环境,可以用 VSCode 远程到 Linux 并配置 devcontainer,可以解决环境本身,可以随时更换 Python 版本,还能解决多人协作开发时,环境配置不一致的问题。 通过 VSCode 的 Remote SSH 登录到一台 Linux Server,需要先安装 Docker 环境 在项目根目录下创建 .devcontainer 文件夹,并创建 devcontainer.json 文件,由于内容较长,可以在 github 中搜索 pytbox 项目,并参考我的配置。 Dockfile.dev 参考如下

    # 使用官方 Python 3.11 镜像作为基础镜像
    FROM python:3.11-slim
    
    # 设置工作目录
    WORKDIR /app
    
    ENV PYTHONPATH=/app/automation \
        TZ=Asia/Shanghai \
        PIP_DISABLE_PIP_VERSION_CHECK=1 
    
    # 修改为国内源
    COPY debian.sources /etc/apt/sources.list.d/debian.sources
    
    # 安装系统依赖
    RUN apt-get update && apt-get install -y \
        build-essential \
        curl \
        git \
        && rm -rf /var/lib/apt/lists/*

    创建完成后可以 Shift + Command/Alt + P ,选择 Dev Containers: Retbuild Container Without Cache 构建自己的开发环境,需要注意的是,下载镜像时间会比较久,需要耐心等待

    准备 pyproject.toml 文件

    pyproject.toml 已经取代了过去的 setup.py,成为 构建与依赖管理的统一入口。对于单仓库、多模块项目,它不仅定义了整个仓库的依赖,还能灵活指定每个子模块的配置。

    [build-system]
    requires = ["setuptools>=61.0", "wheel"]
    build-backend = "setuptools.build_meta"
    
    [project]
    name = "myproject"
    version = "0.1.0"
    description = "A monorepo Python project"
    authors = [{ name = "YourName", email = "you@example.com" }]
    license = { text = "MIT" }
    readme = "README.md"
    requires-python = ">=3.8"
    
    dependencies = [
        "requests>=2.0"
    ]
    
    [project.optional-dependencies]
    dev = ["pytest", "black", "ruff"]

    开发环境下安装依赖可以执行 pip install .[dev] ,另外,如果模块依赖其他的包,例如 requests ,推荐手动写入到 dependencies 下

    命令行工具

    如果有命令行工具的需求,也就是说用户使用 pip install pytbox 后,希望执行 pytbox –help 使用命令行工具,可以按如下步骤操作 在 pyproject.toml 下加入

    [project.scripts]
    pytbox = "pytbox.cli:main" # 添加命令行入口点

    在项目根目录创建 exec.py 文件,作为入口

    #!/usr/bin/env python3
    """
    开发环境可执行文件 - 支持新的模块化 CLI
    """
    
    import sys
    from pathlib import Path
    
    # 添加 src 目录到 Python 路径
    src_path = Path(__file__).parent / 'src'
    if src_path.exists():
        sys.path.insert(0, str(src_path))
    
    from pytbox.cli import main
    
    if __name__ == "__main__":
        main()

    在 src/pytbox 下创建 cli.py

    #!/usr/bin/env python3
    """
    Pytbox 命令行工具 - 入口文件(保持向后兼容)
    """
    
    from pytbox.cli import main
    
    if __name__ == "__main__":
        main()

    然后在 src/pytbox/cli 目录下创建 main.py

    #!/usr/bin/env python3
    """
    Pytbox 主命令行入口
    """
    
    import click
    from .categraf import categraf_group
    
    
    @click.group()
    @click.version_option()
    def main():
        """Pytbox 命令行工具集合"""
        pass
    # 注册子命令组
    main.add_command(categraf_group, name='categraf')
    
    if __name__ == "__main__":
        main()

    在 src/pytbox/cli 目录下就可以创建多个目录,以实现各种这样的功能,例如我创建的 src/pytbox/cli/categraf 用于生成 categraf 的配置 示例文件 src/pytbox/cli/categraf/commands.py

    """
    Categraf 相关命令 - 支持 rich 美化输出
    """
    import shutil
    from pathlib import Path
    import click
    from ...utils.richutils import RichUtils
    from ...categraf.build_config import BuildConfig
    
    rich_utils = RichUtils()
    
    @click.group()
    def categraf_group():
        """Categraf 配置管理工具"""
        pass
    
    @categraf_group.command('get-instances')
    @click.option('--output-dir', '-o', type=click.Path(exists=True), default='.')
    def get_instances(output_dir):
        """获取 Categraf 实例配置"""
        instances_template_path = Path(__file__).parent.parent.parent / 'categraf' / 'instances.toml'
        dest_path = Path(output_dir) / 'instances.toml'
        shutil.copy(instances_template_path, dest_path)
        rich_utils.print(msg=f'已将 {instances_template_path} 复制到 {dest_path}', style='info')

    手动发布到 Pypi

    准备 PYPI 的 API Token,这一步需要在 pypi 官网上配置

    export TWINE_USERNAME="__token__"
    export TWINE_PASSWORD="pypi-xxxxxxxxxxxxxxxx"

    安装依赖工具并发布

    pip install build twine
    
    # 构建
    python -m build
    
    # 发布到 PYPI
    twine upload dist/*

    Github action 自动发布到 Pypi

    强烈推荐使用 Action 自动发布,只要推送代码到 Github 就可以自动完成后续的步骤

    1. 在.github/workflows 目录下创建 publish.yml 文件

    2. 示例如下

      name: Publish to PyPI
      
      on:
        push:
          tags: [ 'v*' ]
      
      jobs:
        build:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v4
      
            - name: Set up Python
              uses: actions/setup-python@v5
              with:
                python-version: "3.11"
      
            - name: Install build tools
              run: pip install build twine
      
            - name: Build package
              run: python -m build
      
            - name: Publish to PyPI
              env:
                TWINE_USERNAME: __token__
                TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
              run: twine upload dist/*
    3. 在仓库中的 Settings -> Secrets and variables -> Actions -> Repository secrets 下创建 name 为 PYPI_API_TOKEN 的 secret

    4. 推荐使用 shell 脚本 push 到仓库中,代码有些长,可以在 pytbox 库的根目录下参考 publish.sh

    总结

    在运维开发中,如果有多项目的需求,基本都是需要公共库的,否则需要被多项目调用的模块要重复编写,复制到其他项目非常不方便,并且难以保证代码质量,所以非常推荐编写自己或团队的公共代码库。

    «
    »
以专业成就每一位客户,让企业IT只为效果和安全买单

以专业成就每一位客户,让企业IT只为效果和安全买单

在线咨询
连接中...