setuptools打包Python CLI pip安装后报No module named 'mycli'如何解决
报错ModuleNotFoundError: No module named 'mycli'本质是安装后的Python环境中找不到名为mycli的可导入模块,问题出在目录结构和打包配置不匹配,和Click框架本身无关。
你当前配置里的find_packages()只会扫描setup.py所在目录下带__init__.py的子文件夹作为待安装的包,现有结构存在几个明显问题:
- 项目根目录下的
__init__.py不属于任何子包,不会被打包逻辑识别 - 根目录下的
mycli.py是独立单文件,不会被find_packages()自动收集 module1、module2文件夹没有__init__.py,本身不构成合法Python包,不会被打包- 入口点配置
mycli = mycli:mycli要求导入mycli模块下的mycli函数,但实际安装时根本没有把对应代码装进环境,自然触发找不到模块的错误。
1. 调整为标准Python包目录结构
删掉项目根目录下无效的__init__.py,新建真正的mycli包目录存放所有业务代码,调整后结构如下:
mycli-project/ # 项目根文件夹,名称可自定义,不要和Python包名混淆 ├── requirements.txt ├── setup.py ├── venv/ └── mycli/ # 这才是会被安装到Python环境的正式包 ├── __init__.py # 空文件即可,标识当前文件夹是Python包 ├── mycli.py # 原有的CLI入口代码 ├── module1/ │ ├── __init__.py │ └── module1相关业务代码 └── module2/ ├── __init__.py └── module2相关业务代码
注:Python 3.3+虽支持隐式命名空间包,但打包时给所有需要导入的文件夹加
__init__.py,可以避免绝大多数奇奇怪怪的导入问题。如果是无额外子模块的单文件CLI,可以不用建包目录,在setup.py里加py_modules=['mycli']指定打包单文件即可,但你有module1、module2两个自定义模块,必须整理为标准包结构才能正常导入。
2. 修正代码导入路径
原mycli.py里的导入语句要对应新的包结构调整,否则安装后会找不到子模块:
# 原写法 from module1 import Service1 改为相对导入 from .module1 import Service1 # 也可以写全路径 from mycli.module1 import Service1
3. 修正setup.py入口点配置
由于mycli入口函数现在存放在mycli包下的mycli.py文件中,原来的入口点路径没有写全模块层级,要对应修改:
from setuptools import setup, find_packages setup( name='mycli', version='0.1', packages=find_packages(), install_requires=[ 'click==8.1.3', ], entry_points={ "console_scripts": [ # 格式遵循:命令名 = 包名.模块名:入口函数名 "mycli = mycli.mycli:mycli" ] }, )
如果想保留原来的短入口点写法,可以在mycli/__init__.py里加一行from .mycli import mycli,把入口函数导入到包顶层,就可以继续用mycli = mycli:mycli的配置。
4. 重新安装验证
先卸载之前安装的错误版本,再重新安装即可:
pip uninstall mycli -y pip install -r requirements.txt
安装完成后直接执行mycli命令即可正常运行。
后续再遇到打包后命令运行报错,可以先进入Python交互环境执行import mycli,如果这步就报错,说明包根本没被正确安装,优先排查目录结构和setup.py的包收集配置,不用先纠结Click逻辑或者入口点写法。
内容的提问来源于stack exchange,提问作者Clyde Barrow

