# bytehaul Python 使用文档
本文介绍如何在当前仓库中构建、安装并使用 `bytehaul` 的 Python 绑定。
[English Python Guide](../bindings/python/README.md)
## 环境要求
- Python 3.9+
- Rust toolchain
- `uv`
下面的命令默认在仓库根目录执行。
## 初始化开发环境
先同步 Python 侧的开发依赖:
```bash
uv sync --project bindings/python
```
## 安装开发版本
将 Rust 扩展编译并安装到当前 `uv` 管理的环境中:
```bash
cd bindings/python
uv run --project . maturin develop -m Cargo.toml
```
安装完成后,就可以直接在 Python 中导入 `bytehaul`。
## 构建 Wheel
如果你需要分发或本地验证 wheel,可以执行:
```bash
cd bindings/python
uv run --project . maturin build --release -m Cargo.toml
```
`maturin` 会在命令输出中打印 wheel 的生成路径。
## 快速开始
### 阻塞式下载
```python
import bytehaul
bytehaul.download("https://example.com/file.bin", output_path="output.bin")
# 自动根据响应头或 URL 推断文件名,并落到目标目录
bytehaul.download("https://example.com/file.bin", output_dir="downloads")
```
### 传入下载参数
```python
from pathlib import Path
import bytehaul
bytehaul.download(
"https://example.com/file.bin",
output_path=Path("output.bin"),
max_connections=8,
max_download_speed=1_000_000,
headers={"Authorization": "Bearer token"},
checksum_sha256="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
)
```
`output_path` 同时支持 `str` 和 `pathlib.Path`。
### 配置代理、DNS 和 IPv6
```python
import bytehaul
bytehaul.download(
"https://example.com/file.bin",
output_path="output.bin",
proxy="http://127.0.0.1:7890",
dns_servers=["1.1.1.1", "8.8.8.8:53"],
doh_servers=["https://dns.google/dns-query"],
enable_ipv6=False,
)
```
`doh_servers` 需要传 HTTPS URL。如果使用像 `dns.google` 这样的域名,bytehaul 会在构建客户端时先用一次系统解析器来 bootstrap 这个 DoH 端点的 IP 地址。
对象 API 下,`dns_servers`、`doh_servers`、`enable_ipv6` 仍放在 `Downloader(...)` 构造器上;`proxy`、`http_proxy`、`https_proxy` 既可以放在 `Downloader(...)` 上作为默认值,也可以在 `downloader.download(...)` 时按任务覆盖:
```python
from bytehaul import Downloader
downloader = Downloader(
connect_timeout=15.0,
dns_servers=["1.1.1.1"],
doh_servers=["https://dns.google/dns-query"],
enable_ipv6=False,
)
task = downloader.download(
"https://example.com/file.bin",
output_path="file.bin",
proxy="http://127.0.0.1:7890",
)
```
### 日志
```python
# 在便捷函数中启用调试日志
bytehaul.download(
"https://example.com/file.bin",
output_path="output.bin",
log_level="debug",
)
# 或在 Downloader 对象上设置
from bytehaul import Downloader
downloader = Downloader(log_level="info")
```
可选级别:`"off"`(默认)、`"error"`、`"warn"`、`"info"`、`"debug"`、`"trace"`(不区分大小写)。
### 使用对象 API 获取进度和取消下载
```python
from bytehaul import Downloader
downloader = Downloader(connect_timeout=15.0)
task = downloader.download(
"https://example.com/large.bin",
output_path="large.bin",
proxy="http://127.0.0.1:7890",
max_connections=8,
resume=True,
)
snap = task.progress()
print(
f"state={snap.state} downloaded={snap.downloaded} "
f"speed={snap.speed:.0f} B/s eta={snap.eta_secs}"
)
# 如有需要可暂停或取消
# task.pause()
# task.cancel()
task.wait()
```
注意:
- `task.wait()` 会消费当前任务句柄,不能重复调用。
- `task.wait()` 返回后,不能再调用 `task.progress()` 获取进度。
### 错误处理
```python
from bytehaul import ConfigError, CancelledError, PausedError, DownloadFailedError, download
try:
download("https://example.com/file.bin", output_path="output.bin")
except ConfigError as exc:
print(f"参数无效: {exc}")
except PausedError:
print("下载已暂停")
except CancelledError:
print("下载已取消")
except DownloadFailedError as exc:
print(f"下载失败: {exc}")
```
## API 概览
### `download(url, output_path=None, output_dir=None, **options)`
阻塞式便捷函数。调用后会一直等待,直到下载完成或失败。
- `output_path`:显式文件名或相对输出路径
- `output_dir`:输出目录
- 省略 `output_path` 时,会按 `Content-Disposition` → URL 路径 → `download` 自动选择文件名
- 若未设置 `output_dir`,仍可直接传绝对 `output_path`
### `Downloader(connect_timeout=None, proxy=None, http_proxy=None, https_proxy=None, dns_servers=None, doh_servers=None, enable_ipv6=None)`
可复用的下载器实例。
- `downloader.download(url, output_path=None, output_dir=None, **options) -> DownloadTask`
传给 `Downloader(...)` 的代理参数会作为默认值保留。若需要按任务切换代理,可在 `downloader.download(...)` 上继续传 `proxy`、`http_proxy` 或 `https_proxy` 覆盖本次下载。
### `DownloadTask`
运行中下载任务的句柄。
- `task.progress()`:获取当前进度快照
- `task.pause()`:暂停下载并尽量落盘控制文件
- `task.cancel()`:取消下载
- `task.wait()`:阻塞等待下载结束
### `ProgressSnapshot`
进度快照对象,包含以下常用属性:
| `total_size` | `int \| None` | 文件总大小,未知时为 `None` |
| `downloaded` | `int` | 已下载字节数 |
| `state` | `str` | 当前状态,如 `pending`、`downloading`、`completed`、`cancelled`、`paused` |
| `speed` | `float` | 最近窗口内的下载速度,单位为字节/秒 |
| `eta_secs` | `float \| None` | 预计剩余秒数 |
| `elapsed_secs` | `float \| None` | 已耗时秒数 |
`speed` 和 `eta_secs` 使用同一条最近吞吐窗口。`speed` 不是全程平均速度;在最近样本不足或总大小未知时,`eta_secs` 会保持为 `None`。
## 常用参数
| `output_path` | `str \| Path \| None` | `None` | 显式文件名或相对输出路径 |
| `output_dir` | `str \| Path \| None` | `None` | 输出目录 |
| `headers` | `dict[str, str]` | `{}` | 自定义请求头 |
| `max_connections` | `int` | `4` | 最大并发连接数 |
| `connect_timeout` | `float` | `30.0` | 连接超时,单位秒 |
| `read_timeout` | `float` | `60.0` | 读取超时,单位秒 |
| `memory_budget` | `int` | `67108864` | 内存预算,单位字节 |
| `file_allocation` | `"none" \| "prealloc"` | `"prealloc"` | 文件预分配策略 |
| `resume` | `bool` | `True` | 是否启用断点续传 |
| `piece_size` | `int` | `1048576` | 分片大小,单位字节 |
| `min_split_size` | `int` | `10485760` | 文件大于该值时才拆分 |
| `max_retries` | `int` | `5` | 最大重试次数 |
| `retry_base_delay` | `float` | `1.0` | 重试基础退避时间,单位秒 |
| `retry_max_delay` | `float` | `30.0` | 重试最大退避时间,单位秒 |
| `max_download_speed` | `int` | `0` | 最大下载速度,`0` 表示不限速 |
| `checksum_sha256` | `str \| None` | `None` | 下载完成后的 SHA-256 校验值 |
| `log_level` | `str \| None` | `None`(`"off"`) | 日志级别 |
## 网络层参数
对象 API 中,`proxy`、`http_proxy`、`https_proxy` 既可用于 `Downloader(...)` 默认值,也可用于 `downloader.download(...)` 的单次覆盖;`dns_servers`、`doh_servers`、`enable_ipv6` 仍通过 `Downloader(...)` 配置。
| `proxy` | `str \| None` | `None` | 为所有 HTTP/HTTPS 请求设置统一代理 |
| `http_proxy` | `str \| None` | `None` | 仅为 HTTP 请求设置代理 |
| `https_proxy` | `str \| None` | `None` | 仅为 HTTPS 请求设置代理 |
| `dns_servers` | `list[str] \| None` | `None` | 自定义 DNS 服务器,支持 `IP` 或 `IP:PORT` |
| `doh_servers` | `list[str] \| None` | `None` | 自定义 DNS-over-HTTPS 端点,传入 `https://...` URL |
| `enable_ipv6` | `bool \| None` | `True` | 是否允许解析和连接 IPv6 地址 |
## 运行测试
```bash
cd bindings/python
uv run --project . pytest
```
如果刚拉起环境,建议先执行一次:
```bash
uv sync --project bindings/python
cd bindings/python
uv run --project . maturin develop -m Cargo.toml
uv run --project . pytest
```