setup()函数仅负责元数据声明、依赖配置和入口点注册,业务逻辑必须置于包内分层实现:CLI轻量解析、Service封装复用逻辑、配置与代码解耦。

setup() 函数本身不是用来承载业务逻辑的,它只负责项目元数据声明、依赖配置和入口点注册。所谓“在 setup 中拆分业务逻辑”,本质是误解——真正需要拆分的是你的项目代码结构,而 setup.py 或 pyproject.toml 只是“告诉别人怎么装你、怎么调你”。
setup 里能拆的只有配置职责
你不能也不该把校验用户、处理订单、解析日志这些逻辑塞进 setup()。但 setup 的内容可以且应该按关注点分离:
- 元数据独立:名称、版本、作者等信息可抽到单独文件(如 _version.py 或 pyproject.toml 的 [project] 段),避免硬编码
-
依赖分组管理:用 extras_require 区分 dev、test、cli 等场景所需依赖,比如:
"dev": ["pytest", "black"], "cli": ["click"] -
入口点按功能归类:多个命令不堆在一行,而是用清晰命名组织:
'console_scripts': ['mytool-sync=mytool.cli:sync_main', 'mytool-report=mytool.cli:report_main']
真正的业务逻辑拆分发生在项目内部
setup 只是“门牌号”,业务逻辑必须放在包内合理分层。例如:
- CLI 命令函数要轻量:每个 entry_point 指向的函数(如 sync_main)只做三件事——解析参数、调用 service、打印结果
- Service 层封装复用逻辑:把数据库操作、API 调用、校验规则写在 services/ 目录下,函数接受纯 Python 参数,不依赖 Flask/Click 上下文
- 配置与逻辑解耦:连接串、超时值等从 config.py 或环境变量读取,不要写死在 service 函数里
一个典型结构示意
你的项目目录应类似这样:
mytool/
├── pyproject.toml # setup 配置集中地,含 entry_points
├── mytool/
│ ├── __init__.py
│ ├── cli.py # 仅参数解析 + 调用 service
│ └── services/
│ ├── __init__.py
│ ├── sync.py # 真正的同步逻辑,无 click.Context
│ └── report.py # 独立的数据生成逻辑
└── tests/
└── test_sync.py # 可直接 import sync.py 中的函数测试这样,setup 不承担逻辑,却通过 entry_points 精准暴露能力;业务逻辑分散在可测试、可复用的模块中,自然就实现了关注点分离。


















