Metadata-Version: 2.4
Name: bytehaul
Version: 0.1.2
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Rust
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: System :: Networking
Summary: Python bindings for the bytehaul Rust download library
License: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Documentation, https://github.com/triwinds/bytehaul/tree/main/bindings/python
Project-URL: Homepage, https://github.com/triwinds/bytehaul
Project-URL: Repository, https://github.com/triwinds/bytehaul

# bytehaul

Python bindings for the [bytehaul](https://github.com/triwinds/bytehaul) Rust download library.

[中文使用文档](../../docs/python.zh-CN.md)

## Requirements

- Python 3.9+
- Rust toolchain (for building from source)
- `uv`

Commands below assume you are running them from the repository root.

## Installation

### From source (development)

```bash
uv sync --project bindings/python
cd bindings/python
uv run --project . maturin develop -m Cargo.toml
```

### Build wheel

```bash
cd bindings/python
uv run --project . maturin build --release -m Cargo.toml
```

## Usage

### Simple download

```python
import bytehaul

bytehaul.download("https://example.com/file.bin", "output.bin")
```

### With options

```python
bytehaul.download(
    "https://example.com/file.bin",
    "output.bin",
    max_connections=8,
    max_download_speed=1_000_000,  # 1 MB/s
    headers={"Authorization": "Bearer token"},
)
```

### Network settings

```python
bytehaul.download(
    "https://example.com/file.bin",
    "output.bin",
    proxy="http://127.0.0.1:7890",
    dns_servers=["1.1.1.1", "8.8.8.8:53"],
    enable_ipv6=False,
)
```

### Logging

```python
# Enable debug logging on the convenience function
bytehaul.download(
    "https://example.com/file.bin",
    "output.bin",
    log_level="debug",
)

# Or on the Downloader object
from bytehaul import Downloader

downloader = Downloader(log_level="info")
```

Valid levels: `"off"` (default), `"error"`, `"warn"`, `"info"`, `"debug"`, `"trace"`.

### Object API with progress and cancellation

```python
from bytehaul import Downloader

downloader = Downloader(
    connect_timeout=15.0,
    dns_servers=["1.1.1.1"],
    enable_ipv6=False,
)
task = downloader.download("https://example.com/large.bin", "large.bin")

# Poll progress
snap = task.progress()
print(f"State: {snap.state}, Downloaded: {snap.downloaded}, Speed: {snap.speed:.0f} B/s")

# Cancel if needed
# task.cancel()

# Wait for completion
task.wait()
```

### Error handling

```python
from bytehaul import download, DownloadFailedError, CancelledError, ConfigError

try:
    download("https://example.com/file.bin", "output.bin")
except ConfigError as e:
    print(f"Invalid parameter: {e}")
except CancelledError:
    print("Download was cancelled")
except DownloadFailedError as e:
    print(f"Download failed: {e}")
```

## API Reference

### `download(url, output_path, **options)`

Blocking convenience function. Downloads a file and returns when complete.

### `Downloader(connect_timeout=None, proxy=None, http_proxy=None, https_proxy=None, dns_servers=None, enable_ipv6=None)`

Reusable downloader instance.

- `downloader.download(url, output_path, **options) -> DownloadTask`

### `DownloadTask`

Handle to a running download.

- `task.progress() -> ProgressSnapshot` — current download progress
- `task.cancel()` — cancel the download
- `task.wait()` — block until download completes (releases GIL)

### `ProgressSnapshot`

Frozen snapshot of download progress.

| Attribute      | Type           | Description                     |
|----------------|----------------|---------------------------------|
| `total_size`   | `int \| None`  | Total file size (if known)      |
| `downloaded`   | `int`          | Bytes downloaded so far         |
| `state`        | `str`          | `"pending"`, `"downloading"`, `"completed"`, `"failed"`, `"cancelled"`, `"paused"` |
| `speed`        | `float`        | Current speed in bytes/second   |
| `elapsed_secs` | `float \| None`| Elapsed time in seconds         |

### Download options

| Parameter           | Type             | Default       |
|---------------------|------------------|---------------|
| `headers`           | `dict[str, str]` | `{}`          |
| `max_connections`   | `int`            | `4`           |
| `connect_timeout`   | `float` (secs)   | `30.0`        |
| `read_timeout`      | `float` (secs)   | `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` (secs)   | `1.0`         |
| `retry_max_delay`   | `float` (secs)   | `30.0`        |
| `max_download_speed`| `int`            | `0` (unlimited)|
| `checksum_sha256`   | `str \| None`    | `None`        |
| `log_level`         | `str \| None`    | `None` (`"off"`) |

Valid `log_level` values: `"off"`, `"error"`, `"warn"`, `"info"`, `"debug"`, `"trace"` (case-insensitive).

### Network options

Use these on `Downloader(...)` for the object API, or pass them directly to the blocking `download(...)` helper.

| Parameter      | Type                | Default |
|----------------|---------------------|---------|
| `proxy`        | `str \| None`      | `None`  |
| `http_proxy`   | `str \| None`      | `None`  |
| `https_proxy`  | `str \| None`      | `None`  |
| `dns_servers`  | `list[str] \| None`| `None`  |
| `enable_ipv6`  | `bool \| None`     | `True`  |

## Running tests

```bash
uv sync --project bindings/python
cd bindings/python
uv run --project . maturin develop -m Cargo.toml
uv run --project . pytest tests/ -v
```

## Building wheels for release

Single platform:

```bash
cd bindings/python
uv run --project . maturin build --release -m Cargo.toml
```

Cross-platform (via CI):

```bash
# Linux x86_64 + aarch64, macOS x86_64 + arm64, Windows x86_64
# Use maturin's GitHub Actions: https://github.com/PyO3/maturin-action
```

The project uses `abi3-py39`, so a single wheel per platform covers all Python 3.9+ versions.

## License

MIT. See the repository LICENSE file.


