别再只会pip install了!手把手教你用setup.py打包自己的Python工具库(附完整代码)
从脚本到工具库Python开发者必备的打包分发实战指南当你第三次在不同项目里复制粘贴同一段日志处理代码时是否想过该给这些流浪代码一个正式的家Python开发者常陷入这样的困境——我们熟练使用pip安装各种库却对自己编写的实用工具缺乏系统化管理。本文将带你突破这个瓶颈用一个真实案例演示如何将零散脚本转化为可安装、可分享的专业工具库。1. 为什么你的代码值得被打包每次复制粘贴代码不仅是效率问题更隐藏着版本混乱的风险。想象这样一个场景你在项目A中修复了日志工具的时区bug却忘记同步到项目B这种不一致性会随着时间推移演变成维护噩梦。打包你的代码库能带来三个核心价值版本控制通过setup.py明确定义版本号所有依赖项目都能明确知道自己使用的版本依赖管理自动处理第三方库依赖避免在我的机器上能运行的经典问题协作共享团队成员可以通过简单的pip install使用你的工具无需关心实现细节以一个日志处理工具为例原始代码可能只是简单的logger.py# logger.py import logging from datetime import datetime def init_logger(name): logger logging.getLogger(name) logger.setLevel(logging.INFO) formatter logging.Formatter( %(asctime)s - %(name)s - %(levelname)s - %(message)s, datefmt%Y-%m-%d %H:%M:%S %Z ) handler logging.StreamHandler() handler.setFormatter(formatter) logger.addHandler(handler) return logger这段看似简单的代码其实已经具备了被打包的价值。接下来我们将逐步把它转化为专业工具库。2. 构建标准化项目结构专业的分发包始于合理的项目结构。以下是我们推荐的日志工具库基础布局mylogger/ ├── mylogger/ # 主包目录 │ ├── __init__.py # 包初始化文件 │ ├── core.py # 核心功能实现 │ └── utils.py # 辅助工具函数 ├── tests/ # 测试目录 │ └── test_core.py # 单元测试 ├── setup.py # 打包配置文件 ├── README.md # 项目说明 └── requirements.txt # 开发依赖关键文件说明__init__.py使Python将目录识别为包可以在此暴露主要接口core.py迁移原始logger.py的功能并进行扩展setup.py打包的配方文件定义如何构建和安装你的包将原有代码重构到新结构中时注意保持接口的向后兼容性。例如在__init__.py中# mylogger/__init__.py from .core import init_logger __all__ [init_logger] __version__ 0.1.03. 深度配置setup.pysetup.py是打包过程的核心合理的配置能极大提升用户体验。以下是一个功能丰富的配置示例# setup.py import os from setuptools import setup, find_packages def read_requirements(): 解析requirements.txt文件 with open(requirements.txt) as f: return [line.strip() for line in f if line.strip()] setup( namemylogger-pro, version0.1.0, description专业级的Python日志处理工具, long_descriptionopen(README.md).read(), long_description_content_typetext/markdown, author你的名字, author_emailyour.emailexample.com, urlhttps://github.com/yourname/mylogger, packagesfind_packages(exclude[tests*]), include_package_dataTrue, install_requiresread_requirements(), python_requires3.6, classifiers[ Development Status :: 4 - Beta, Intended Audience :: Developers, Programming Language :: Python :: 3, Programming Language :: Python :: 3.6, Programming Language :: Python :: 3.7, Programming Language :: Python :: 3.8, Programming Language :: Python :: 3.9, ], entry_points{ console_scripts: [ mylogger-climylogger.cli:main, ], }, )关键参数解析参数说明示例值name包名pip安装时使用mylogger-proversion遵循语义化版本规范0.1.0install_requires运行时依赖[python-dateutil2.8.1]python_requiresPython版本要求3.6entry_points创建命令行工具见示例提示find_packages()会自动发现项目中的包使用exclude参数可以过滤测试等非发布目录4. 高级打包技巧与发布流程基础配置完成后让我们探索一些提升打包质量的高级技巧4.1 多格式打包与发布现代Python打包支持多种格式推荐同时生成源码包和wheel# 生成源码包(.tar.gz) python setup.py sdist # 生成wheel包(.whl) python setup.py bdist_wheel # 同时生成两种格式 python setup.py sdist bdist_wheel生成的文件会存放在dist/目录下结构如下dist/ ├── mylogger-pro-0.1.0.tar.gz # 源码包 └── mylogger_pro-0.1.0-py3-none-any.whl # 通用wheel包4.2 自动化版本管理手动更新setup.py中的版本号容易出错可以使用bumpversion工具自动化这个过程安装工具pip install bumpversion创建.bumpversion.cfg配置文件[bumpversion] current_version 0.1.0 commit True tag True [bumpversion:file:setup.py]更新版本号bumpversion patch # 0.1.0 → 0.1.1 bumpversion minor # 0.1.1 → 0.2.0 bumpversion major # 0.2.0 → 1.0.04.3 私有仓库分发除了PyPI你也可以将包发布到私有仓库# 上传到私有仓库 twine upload --repository-url http://your.pypi.server dist/*配置~/.pypirc文件简化认证[distutils] index-servers pypi internal [pypi] username: your_pypi_username password: your_pypi_password [internal] repository: http://your.pypi.server username: your_internal_username password: your_internal_password5. 开发模式与生产模式的最佳实践根据使用场景Python包有两种安装方式生产模式标准安装pip install mylogger-pro-0.1.0.tar.gz将包安装到Python的site-packages适合最终用户使用开发模式可编辑安装pip install -e .创建指向源码目录的符号链接修改代码立即生效无需重新安装适合包开发者使用对比两种模式特性生产模式开发模式安装位置site-packages原目录代码修改需重新安装立即生效适用场景生产环境开发环境依赖处理安装时解析安装时解析注意开发模式下虽然包可以即时更新但新增文件可能需要重新运行安装命令6. 质量保障与持续集成专业的工具库需要配套的自动化测试和构建流程。以下是推荐的CI配置示例以GitHub Actions为例# .github/workflows/test.yml name: Test on: [push, pull_request] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: [3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkoutv2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-pythonv2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e .[test] - name: Run tests run: | pytest --covmylogger --cov-reportxml配套的测试依赖可以在setup.py中通过extras_require定义# setup.py extras_require{ test: [ pytest6.0, pytest-cov2.0, flake83.9, ], },这样开发者可以通过以下命令安装测试依赖pip install -e .[test]7. 版本兼容性与长期维护策略随着项目发展版本管理变得至关重要。遵循语义化版本规范(SemVer)能有效管理用户预期MAJOR不兼容的API修改MINOR向下兼容的功能新增PATCH向下兼容的问题修正维护多版本分支的推荐工作流主分支(main)保持最新稳定版为每个大版本创建维护分支(如1.x)使用标签标记每个发布版本当需要弃用某些功能时采用分阶段策略当前版本发出弃用警告import warnings warnings.warn( old_function will be removed in v2.0, use new_function instead, DeprecationWarning, stacklevel2 )下个主版本移除弃用功能通过setup.py的classifiers字段明示版本支持状态classifiers[ ... Programming Language :: Python :: 3 :: Only, Programming Language :: Python :: 3.6, Programming Language :: Python :: 3.7, Programming Language :: Python :: 3.8, Programming Language :: Python :: 3.9, Programming Language :: Python :: 3.10, ],在项目根目录添加pyproject.toml确保构建环境隔离[build-system] requires [ setuptools42, wheel ] build-backend setuptools.build_meta这个配置文件会确保构建你的包时使用指定版本的setuptools避免因环境差异导致构建失败。