别再只会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.0'
3. 深度配置setup.py
setup.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(
name='mylogger-pro',
version='0.1.0',
description='专业级的Python日志处理工具',
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
author='你的名字',
author_email='your.email@example.com',
url='https://github.com/yourname/mylogger',
packages=find_packages(exclude=['tests*']),
include_package_data=True,
install_requires=read_requirements(),
python_requires='>=3.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-cli=mylogger.cli:main',
],
},
)
关键参数解析:
| 参数 | 说明 | 示例值 |
|---|---|---|
name | 包名,pip安装时使用 | 'mylogger-pro' |
version | 遵循语义化版本规范 | '0.1.0' |
install_requires | 运行时依赖 | ['python-dateutil>=2.8.1'] |
python_requires | Python版本要求 | '>=3.6' |
entry_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.0
4.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_password
5. 开发模式与生产模式的最佳实践
根据使用场景,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/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
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 --cov=mylogger --cov-report=xml
配套的测试依赖可以在setup.py中通过extras_require定义:
# setup.py
extras_require={
'test': [
'pytest>=6.0',
'pytest-cov>=2.0',
'flake8>=3.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, stacklevel=2 ) -
下个主版本:移除弃用功能
通过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 = [
"setuptools>=42",
"wheel"
]
build-backend = "setuptools.build_meta"
这个配置文件会确保构建你的包时使用指定版本的setuptools,避免因环境差异导致构建失败。
更多推荐


所有评论(0)