Python包分发终极指南:深入掌握setuptools打包技术

一、现代打包工具链架构

1. 打包生态系统演进

现代标准
旧版兼容
源代码
setuptools
构建工具
分发格式
wheel
egg
pip安装

2. 核心组件关系

# pyproject.toml 声明构建系统(PEP 518)
[build-system]
requires = ["setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"

# setup.cfg 声明包元数据
[metadata]
name = my_package
version = 1.0.0

# setup.py 复杂配置入口(可选)
import setuptools
setuptools.setup()

二、基础包结构规范

最小可行项目结构

my_package/
├── src/
│   └── my_package/        # 包源代码
│       ├── __init__.py
│       └── module.py
├── tests/                  # 测试代码
├── docs/                   # 文档
├── setup.cfg               # 主要配置
├── setup.py                # 复杂逻辑入口
├── pyproject.toml          # 构建系统配置
├── MANIFEST.in             # 额外文件包含规则
└── README.md

关键文件解析

1. setup.cfg 核心配置
[metadata]
name = my-awesome-package
version = attr: my_package.__version__
description = 革命性的Python工具
long_description = file: README.md
long_description_content_type = text/markdown
author = 张伟
author_email = [email protected]
license = MIT
url = https://github.com/zhangwei/my_package
classifiers =
    Development Status :: 5 - Production/Stable
    Intended Audience :: Developers
    License :: OSI Approved :: MIT License
    Programming Language :: Python :: 3

[options]
package_dir = 
    = src
packages = find:
install_requires =
    requests>=2.25
    numpy
python_requires = >=3.8
include_package_data = True

[options.packages.find]
where = src
2. setup.py 高级配置
#!/usr/bin/env python
import setuptools
import re

# 动态读取版本号
def get_version():
    with open("src/my_package/__init__.py") as f:
        return re.search(r'__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read()).group(1)

# C扩展编译
my_extension = setuptools.Extension(
    'my_package.accelerate',
    sources=['src/my_package/accelerate.c'],
    include_dirs=['/usr/local/include'],
)

# 自定义安装后操作
class CustomInstall(setuptools.command.install.install):
    def run(self):
        super().run()
        print("执行自定义安装后操作...")

setuptools.setup(
    version=get_version(),
    ext_modules=[my_extension],
    cmdclass={'install': CustomInstall}
)

三、依赖管理高级策略

1. 分层依赖声明

[options]
install_requires =
    base_lib>=1.2

[options.extras_require]
test =
    pytest>=6.0
    coverage
dev =
    %(test)s
    black
    flake8
gui =
    pyqt5

2. 环境标记支持

[options.extras_require]
# 特定平台依赖
:sys_platform == "win32" =
    pywin32
:sys_platform == "linux" =
    dbus-python

# Python版本限定
:python_version < "3.8" =
    typing_extensions

四、打包内容控制技术

1. MANIFEST.in 模式

# 包含所有.md文件
include *.md

# 递归包含data目录
recursive-include src/my_package/data *

# 排除测试数据
exclude tests/test_data/*

2. 程序入口点注册

[options.entry_points]
console_scripts =
    my-tool = my_package.cli:main

gui_scripts =
    my-gui = my_package.gui:launch

# 框架集成点
flask.commands =
    seed = my_package.management:seed_db

3. 数据文件包含

[options.package_data]
my_package =
    data/*.json
    locales/*.mo
    templates/*.html

# 非包数据文件
[options.data_files]
share/my_package =
    config/default.yaml
/etc/my_package =
    config/production.yaml

五、平台特定打包方案

1. C扩展编译最佳实践

from setuptools import setup, Extension
import sys

define_macros = []
if sys.platform == "win32":
    libraries = ['ole32']
else:
    libraries = []

extensions = [
    Extension(
        'my_package.accelerate',
        ['src/accelerate.c'],
        define_macros=define_macros,
        libraries=libraries,
        extra_compile_args=["-O3"]
    )
]

setup(ext_modules=extensions)

2. 多平台资源文件处理

from setuptools import setup
import platform

# 平台特定数据文件
data_files = []
if platform.system() == "Windows":
    data_files.append(('Lib/site-packages/my_package', ['data/win_dlls/*']))
elif platform.system() == "Darwin":
    data_files.append(('/usr/local/lib', ['data/mac_libs/*']))

setup(data_files=data_files)

六、构建与发布工作流

1. 专业构建流程

# 安装构建工具
python -m pip install --upgrade build

# 创建wheel包
python -m build --wheel

# 检查包质量
python -m pip install twine
twine check dist/*

# 上传到PyPI
twine upload dist/*

2. 自动化发布脚本

#!/bin/bash
# release.sh
set -e

# 清理环境
rm -rf build dist

# 更新版本
bumpversion patch  # 使用bumpversion管理版本

# 构建包
python -m build

# 测试安装
python -m venv test-env
source test-env/bin/activate
pip install dist/*.whl
pytest
deactivate

# 发布
twine upload dist/*

七、企业级打包模板

模板1:纯Python库

pure_python_pkg/
├── src/
│   └── package/
│       ├── __init__.py
│       └── core.py
├── tests/
├── setup.cfg
├── pyproject.toml
└── MANIFEST.in

模板2:带C扩展的包

with_c_extension/
├── src/
│   ├── package/
│   │   ├── __init__.py
│   │   └── core.py
│   └── c_src/                # C源代码目录
│       ├── module.c
│       └── header.h
├── setup.py                  # 处理C扩展编译
├── CMakeLists.txt            # 可选CMake支持
└── .github/workflows/        # 多平台CI
    ├── linux.yml
    ├── windows.yml
    └── macos.yml

模板3:CLI工具包

cli_tool/
├── src/
│   └── package/
│       ├── __init__.py
│       ├── cli/             # CLI模块
│       │   ├── __main__.py
│       │   └── commands.py
├── setup.cfg
[options.entry_points]
console_scripts =
    my-tool = package.cli.__main__:main

八、安全与合规实践

1. 签名验证

# 生成GPG密钥
gpg --gen-key

# 签名包
gpg --detach-sign -a dist/package-1.0.0.tar.gz

# 验证签名
twine upload --sign --identity [email protected] dist/*

2. SBOM生成

# 安装生成工具
pip install cyclonedx-bom

# 生成软件物料清单
cyclonedx-py -o bom.xml

3. 合规性元数据

[metadata]
license_files =
    LICENSE
    NOTICE

# 供应商信息
project_urls =
    Source = https://github.com/your/project
    Security = https://github.com/your/project/security

九、常见问题解决方案

问题1:跨平台路径处理

# 错误方式:硬编码路径
data_files = [('/usr/share/data', ['data/file.csv'])]

# 正确方案:使用平台逻辑
from sys import platform
from setuptools import setup

if platform == "win32":
    target = "$env:ProgramData/MyApp"
elif platform == "darwin":
    target = "/Library/Application Support/MyApp"
else:
    target = "/usr/share/myapp"

setup(data_files=[(target, ['data/file.csv'])])

问题2:动态版本管理

# setup.py
import re
import os

def get_version():
    # 优先使用环境变量(CI环境)
    if "RELEASE_VERSION" in os.environ:
        return os.environ["RELEASE_VERSION"]
    
    # 从Git标签获取
    try:
        from setuptools_scm import get_version
        return get_version()
    except ImportError:
        pass
    
    # 从代码读取
    version_file = "src/package/__init__.py"
    with open(version_file) as f:
        return re.search(r'__version__ = "(.+?)"', f.read()).group(1)

setup(version=get_version())

问题3:资源文件丢失

# setup.cfg
[options]
include_package_data = True

# MANIFEST.in
include src/package/templates/*.html
recursive-include src/package/static *

十、专业打包工作流

全流程自动化

代码变更
CI流水线
版本更新?
自动版本号
运行测试
构建wheel
多平台测试
发布到TestPyPI
安全扫描
生产发布

工具链推荐

任务 推荐工具
版本管理 bumpversion / setuptools-scm
依赖解析 pip-tools
包构建 build
包发布 twine
多平台构建 cibuildwheel
文档生成 Sphinx + readthedocs
合规扫描 cyclonedx-bom

总结

通过本教程,您将掌握:

  1. 标准化打包流程:符合PyPA最佳实践的项目结构
  2. 专业级分发能力:支持源码、wheel和系统包
  3. 复杂场景处理:C扩展、数据文件、平台差异
  4. 自动化发布流水线:集成CI/CD的企业级方案
  5. 安全合规保障:签名验证、SBOM生成等高级特性

专家建议:使用check-manifest工具验证文件包含完整性,定期运行pip-audit检查依赖漏洞。

遵循这些专业打包范式,让您的Python项目具备工业级分发质量,轻松应对从个人工具到企业级组件的各种分发场景!

你可能感兴趣的:(python,python,打包,setuptools,PyPI发布,Python分发,wheel打包,Python库开发)