# qpip

`qpip` 是一个面向 Python 开发者的命令行工具：默认用更快的镜像执行 `pip`，同时补上几个常见的项目级工作流，包括脚本运行、项目初始化和虚拟环境管理。

它的核心设计很简单：

- 始终调用当前解释器的 `python -m pip`
- 默认注入国内镜像，但不强行覆盖你已经手动指定的源
- 尽量保持 `pip` 的原始参数习惯，只增加少量高频别名和项目命令
- 优先让你直接运行命令，而不是先手动处理 shell 激活状态

## 功能概览

- 支持 `install` / `i`
- 支持 `update` / `up`，等价于 `pip install --upgrade ...`
- 支持 `uninstall` / `un`
- 对 `install`、`download`、`wheel`、`index`、`search` 这类网络相关命令自动注入镜像
- 支持位置镜像和 `--mirror` 两种选源方式
- 支持 `--dry-run` 预览最终命令
- 支持 `qpip run`，按 `pyproject.toml` 运行项目脚本
- 支持 `qpip init`，快速生成基础项目文件
- 支持 `qpip env ...` 项目环境命令组
- 支持 `qpip exec`、`qpip info`、`qpip doctor`
- 输出、错误提示、帮助文本均为中文

## 安装

```bash
pip install qpip
```

要求：

- Python 3.8+

## 快速开始

```bash
qpip install requests
qpip add rich
qpip i httpx
qpip up fastapi
qpip remove requests
qpip tsinghua install numpy
qpip env create
qpip exec python -V
qpip --dry-run install pandas
```

## 镜像机制

内置镜像：

- `ali`: `https://mirrors.aliyun.com/pypi/simple/`
- `tsinghua`: `https://pypi.tuna.tsinghua.edu.cn/simple/`
- `douban`: `https://pypi.doubanio.com/simple/`
- `pypi`: `https://pypi.org/simple/`

镜像别名：

- `aliyun` -> `ali`
- `tuna` -> `tsinghua`
- `pip` -> `pypi`
- `official` -> `pypi`

查看镜像列表：

```bash
qpip --list-mirrors
qpip mirror list
qpip mirror current
qpip mirror use tsinghua
```

修改默认镜像：

```bash
set QPIP_DEFAULT_MIRROR=tsinghua
qpip install httpx
```

如果 `QPIP_DEFAULT_MIRROR` 的值无效，`qpip` 会给出警告并回退到默认镜像 `ali`。

### 什么时候会自动注入镜像

会自动加上 `-i <mirror-url>` 的命令：

- `install`
- `i`
- `update`
- `up`
- `download`
- `wheel`
- `index`
- `search`

不会覆盖你已经明确指定源的场景。如果参数里已经有以下任意选项，`qpip` 不再注入镜像：

- `-i`
- `--index-url`
- `--extra-index-url`
- `-f`
- `--find-links`
- `--no-index`

## 命令用法

### 安装、升级、卸载

安装：

```bash
qpip install requests
qpip i -r requirements.txt
qpip douban install pandas
```

升级：

```bash
qpip update requests
qpip up requests fastapi
qpip up -r requirements.txt
```

`qpip update` / `qpip up` 会转换为：

```bash
python -m pip install --upgrade ...
```

如果 `update` 后没有提供包名，也没有 `-r/--requirement`，命令会报错。

卸载：

```bash
qpip uninstall requests
qpip un -y requests
qpip uninstall -r requirements.txt
```

`qpip uninstall` / `qpip un` 会转换为：

```bash
python -m pip uninstall ...
```

### 直接透传 pip 子命令

除了上面的别名，`qpip` 也可以直接透传其他 `pip` 子命令：

```bash
qpip download httpx
qpip wheel requests
qpip index versions pip
```

最终仍然是通过当前解释器执行：

```bash
python -m pip ...
```

### 全局选项

帮助：

```bash
qpip --help
```

预览最终命令但不执行：

```bash
qpip --dry-run install fastapi
qpip --dry-run init -y
qpip --dry-run run test
qpip --dry-run env create
```

按名称指定镜像：

```bash
qpip --mirror tsinghua install numpy
```

也可以把镜像名放在最前面：

```bash
qpip tsinghua install numpy
qpip pip install numpy
```

如果要把以 `-` 开头的参数原样传给 `pip`，请用 `--` 截断 `qpip` 自己的参数解析：

```bash
qpip -- --version
```

## 项目脚本

`qpip run` 的目标是提供接近 `npm run` 的体验。

它会在当前目录查找 `pyproject.toml`，并按下面的优先级读取脚本：

1. `[tool.qpip.scripts]`
2. `[project.scripts]`
3. `[project.gui-scripts]`
4. `[tool.poetry.scripts]`

如果存在 `[tool.qpip.scripts]`，就只使用它，不再回退后面的表。

### `tool.qpip.scripts` 示例

```toml
[tool.qpip.scripts]
test = "python -m unittest discover -s tests -v"
build = "python -m build"
"lint:check" = "python -m ruff check ."
"test:argv" = ["python", "-m", "pytest", "-q"]
```

说明：

- 字符串形式适合已有 shell 风格命令
- 字符串数组形式表示显式参数列表，`qpip run` 会尽量直接执行，不依赖 shell 解析

列出脚本：

```bash
qpip run
```

执行脚本：

```bash
qpip run test
qpip run build
qpip run lint:check
```

向脚本传参：

```bash
qpip run test -- -k api
qpip run build -- --wheel
```

说明：

- `qpip run <script>` 在当前项目目录执行脚本
- 传递额外参数时必须使用 `--`
- 如果脚本名包含 `:` 等特殊字符，TOML key 需要加引号
- 没有 `pyproject.toml` 时会报错
- 没有任何受支持的 scripts 表时也会报错

### 兼容已有项目脚本

如果项目没有 `[tool.qpip.scripts]`，`qpip run` 会兼容这些表：

```toml
[project.scripts]
serve = "demo.cli:main"

[project.gui-scripts]
desktop = "demo.gui:start"

[tool.poetry.scripts]
worker = "demo.worker:main"
```

这些脚本会按 entry point 方式导入并执行目标函数。

## 项目初始化

```bash
qpip init
qpip init -y
qpip init --yes
```

`qpip init` 会在当前目录生成基础项目文件。

生成的 `pyproject.toml` 至少包含如下核心结构；实际内容会根据交互输入补充 `authors`、`license` 等字段：

```toml
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "your-project"
version = "0.1.0"
description = ""
readme = "README.md"
requires-python = ">=3.8"
dependencies = []

[tool.qpip.scripts]
test = "python -m unittest discover -s tests -v"
```

如果当前目录没有 `README.md`，还会额外创建一个最小 README。

### 交互模式

不带 `-y` 时，会依次询问这些字段：

- 项目名称
- 版本
- 描述
- Python 版本要求
- 作者
- 邮箱
- 许可证

默认值来源：

- 项目名称：当前目录名规范化后的结果
- 版本：`0.1.0`
- Python 版本要求：`>=3.8`
- 作者：`QPIP_INIT_AUTHOR`、`GIT_AUTHOR_NAME`、`USERNAME`、`USER` 中的第一个可用值
- 邮箱：`QPIP_INIT_EMAIL` 或 `GIT_AUTHOR_EMAIL`
- 许可证：`MIT`

交互模式会先预览生成内容，再确认是否写入：

```text
确认写入 pyproject.toml 吗？([Y]/n):
```

### 行为说明

- `--dry-run` 只预览，不写文件
- 如果当前目录已经存在 `pyproject.toml`，会直接取消，避免覆盖
- 当前 `init` 只接受 `-y` 和 `--yes`

## 项目环境

创建项目虚拟环境：

```bash
qpip env create
```

它会执行：

```bash
python -m venv .venv
```

同时会确保当前项目根目录的 `.gitignore` 包含：

```gitignore
.venv/
```

如果 `.gitignore` 不存在会自动创建；如果已经存在对应规则则不会重复追加。

### 激活命令

查看当前 shell 对应的激活命令：

```bash
qpip env use
```

默认输出示例：

- Linux / macOS: `source .venv/bin/activate`
- Windows PowerShell: `& .\.venv\Scripts\Activate.ps1`
- Windows Bash: `source .venv/Scripts/activate`
- Windows CMD: `call .\.venv\Scripts\activate.bat`

`qpip env use` 只输出命令，不会直接修改父 shell 会话。要在当前终端立即生效：

POSIX shell：

```bash
eval "$(qpip env use)"
```

PowerShell：

```powershell
qpip env use | Invoke-Expression
```

CMD：

```bat
for /f "delims=" %i in ('qpip env use') do %i
```

### 检查是否已激活

```bash
qpip env activated
```

输出规则：

- 当前项目的 `.venv` 已激活时返回 `0`
- 未激活任何虚拟环境，或当前激活的是其他虚拟环境时返回 `1`

这让它既可以直接看结果，也可以用于脚本判断：

```bash
qpip env activated && echo ok
```

查看当前项目环境状态：

```bash
qpip env current
```

输出当前项目环境路径：

```bash
qpip env path
qpip env python
qpip env pip
```

### 退出虚拟环境

```bash
qpip env deactivate
```

这个命令同样只输出当前 shell 可执行的退出命令。

### 在项目环境中直接执行命令

```bash
qpip exec python -V
qpip exec pytest -q
```

`qpip exec` 不要求你先手动激活 `.venv`。

### 查看项目状态

```bash
qpip info
qpip doctor
```

- `qpip info` 用于查看项目、环境、镜像和脚本摘要
- `qpip doctor` 用于诊断当前目录是否已经具备可工作的项目环境；未激活环境只会提示，不会直接视为失败

### 手动指定 shell 类型

如果自动识别 shell 结果不符合你的终端环境，可以设置 `QPIP_SHELL` 手动覆盖。

可用值包括：

- `linux`
- `mac`
- `macos`
- `darwin`
- `powershell`
- `pwsh`
- `windows-powershell`
- `bash`
- `git-bash`
- `windows-bash`
- `cmd`
- `windows-cmd`

示例：

```bash
set QPIP_SHELL=powershell
qpip env use
```

## 环境变量

- `QPIP_DEFAULT_MIRROR`: 设置默认镜像
- `QPIP_INIT_AUTHOR`: `qpip init` 的默认作者名
- `QPIP_INIT_EMAIL`: `qpip init` 的默认邮箱
- `QPIP_SHELL`: 覆盖 `env use` / `env deactivate` 的 shell 检测结果

## 项目配置

`qpip` 会读取 `pyproject.toml` 中的 `[tool.qpip]`：

```toml
[tool.qpip]
mirror = "tsinghua"
venv-dir = ".custom-venv"
```

也可以直接通过命令查看和更新：

```bash
qpip config list
qpip config get mirror
qpip mirror use tsinghua
```

## 开发

仓库自身提供了两个项目脚本：

```bash
qpip run test
qpip run build
```

等价配置位于：

```toml
[tool.qpip.scripts]
test = "python -m unittest discover -s tests -v"
build = "python -m build"
```

## License

MIT
