pyrospeed
pyrospeed is an async-first transfer accelerator for Telegram MTProto clients that follow the Pyrogram API shape. It focuses on single-file upload/download throughput, bounded memory usage, cancellation safety, and a small integration surface.
The package does not replace Pyrogram/Kurigram. You keep your existing Client; pyrospeed accelerates the file-transfer path.
Why this design
Telegram's MTProto file documentation explicitly allows/recommends:
- upload chunks up to 512 KiB;
- multiple in-flight upload calls;
- multiple parallel call queues over separate TCP connections;
- download requests with offsets/limits (Pyrogram exposes these through
Client.get_file()).
pyrospeed applies those primitives while keeping queues bounded.
Highlights
- Fully asynchronous public API.
- Parallel large-file upload using multiple media sessions.
- Parallel ranged download of one file using independent
get_file()ranges. - Bounded upload task window; RAM use is predictable.
- Disk I/O can be moved off the event loop with
asyncio.to_thread(default). - Instance-scoped upload patch. It does not modify
pyrogram.Clientglobally. - Reference-counted patch context for overlapping sends.
- Native fallback for small uploads, unknown-size downloads, and in-memory downloads.
- Sync or async progress callbacks.
- SOLID-oriented separation: compatibility binding, session factory, uploader, downloader, patch, facade.
- Zero mandatory runtime dependencies beyond the Pyrogram-compatible client you already use.
Supported implementations
pyrospeed uses capability detection rather than a hard-coded fork name.
| Implementation | Import namespace | Expected support |
|---|---|---|
| Pyrogram 2.x | pyrogram |
Yes |
| Kurigram 2.x | pyrogram |
Yes |
| Pyrofork 2.x | pyrogram |
Yes |
| Hydrogram | hydrogram |
Capability-detected |
| Other Pyrogram forks | usually pyrogram |
Works when the required raw/session/get_file API is compatible |
Historical releases and private forks can change internals. pyrospeed intentionally falls back to native behavior where possible instead of claiming unsafe universal compatibility.
Installation
From this source tree:
python -m pip install .
Choose one Telegram implementation, not several packages that provide the same import namespace:
# Kurigram
python -m pip install "kurigram>=2" tgcrypto
# or archived upstream Pyrogram
python -m pip install "pyrogram>=2" tgcrypto
# or Pyrofork
python -m pip install "pyrofork>=2" tgcrypto
You can also install the matching local extra, e.g. python -m pip install '.[kurigram]'.
30-second usage
import asyncio
from pyrogram import Client
from pyrospeed import PyroSpeed
app = Client(
"my_account",
api_id=12345,
api_hash="...",
# Important for concurrent get_file calls used by parallel download.
max_concurrent_transmissions=8,
)
async def main():
async with app:
speed = PyroSpeed(app)
message = await speed.send_document(
"me",
"/data/linux.iso",
progress=lambda current, total: print(current, total),
)
saved_path = await speed.download(
message,
file_name="downloads/",
)
print(saved_path)
asyncio.run(main())
Kurigram and Pyrofork retain the from pyrogram import Client import style, so the same code is normally used.
Accelerate all high-level uploads on one Client
If your application already calls app.send_document(), app.send_video(), etc., install the instance patch:
from pyrospeed import PyroSpeed
speed = PyroSpeed(app)
speed.install()
# These calls now use pyrospeed's large-file save_file path.
await app.send_document("me", "archive.tar.zst")
await app.send_video("me", "movie.mkv")
speed.uninstall()
Or scope it:
async with PyroSpeed(app):
await app.send_document("me", "large.bin")
Only that app instance is changed. No class-level monkey patch is performed.
Direct upload
upload_file() returns Telegram's raw InputFile / InputFileBig, matching the object expected by Pyrogram internals:
uploaded = await speed.upload_file("large.bin")
This is useful for advanced raw-API code.
Generic send wrapper
Any async client method that internally calls save_file() can be accelerated:
await speed.send("send_photo", "me", "photo.jpg")
await speed.send("send_voice", "me", "voice.ogg")
Small files default to the framework's native implementation because setup overhead usually dominates. Advanced users can set small_file_strategy="parallel"; pyrospeed then uses SaveFilePart and computes the required whole-file MD5 while streaming the parts.
Parallel download
download() accepts:
- a Pyrogram
Messagecontaining media; - a media object (
message.document,message.video, ...); - a file-id string if
file_size=is also supplied.
path = await speed.download(message.document, "downloads/file.zip")
# file-id-only usage
path = await speed.download(
message.document.file_id,
"downloads/file.zip",
file_size=message.document.file_size,
)
The downloader splits the file into ranges. Pyrogram's get_file() treats offset and limit in 1-MiB chunk units, so each worker can fetch a distinct range and write it at the correct offset.
Unknown-size or in-memory downloads fall back to Client.download_media() by default.
Tuning
from pyrospeed import (
PyroSpeed,
TransferConfig,
UploadConfig,
DownloadConfig,
)
config = TransferConfig(
upload=UploadConfig(
connections=8,
inflight_per_connection=2,
queue_factor=2,
progress_interval=0.2,
disk_io="thread",
small_file_strategy="native",
max_parallel_files=1,
),
download=DownloadConfig(
workers=8,
segment_chunks=4, # 4 MiB per ranged get_file call
queue_factor=2,
progress_interval=0.2,
override_client_semaphore=True,
max_parallel_files=1,
),
)
speed = PyroSpeed(app, config)
Built-in presets:
TransferConfig.conservative() # 2 upload connections / 2 download workers
TransferConfig.balanced() # 4 / 4
TransferConfig.aggressive() # 8 / 8
Upload memory model
Approximate buffered payload memory:
connections × inflight_per_connection × queue_factor × part_size
Balanced defaults:
4 × 2 × 2 × 512 KiB ≈ 8 MiB payload buffer
Protocol objects, Python tasks, encryption buffers, sockets, and the client itself add overhead.
Choosing the fastest profile
There is no globally fastest connection count. Throughput depends on:
- account/server-side limits;
- latency to the Telegram DC;
- bandwidth and packet loss;
- proxy/VPN overhead;
- CPU (MTProto encryption);
- storage read/write speed;
- Telegram Premium/non-Premium throttling policies;
- DC and current server load.
Start with balanced and benchmark 2x1, 4x1, 4x2, 6x2, 8x2. Stop increasing parallelism when throughput plateaus or latency/error rate rises.
Progress callbacks
Both sync and async callbacks are supported:
async def progress(current: int, total: int):
pct = current * 100 / total if total else 0
print(f"{pct:6.2f}%")
await speed.send_document("me", "file.bin", progress=progress)
Callbacks are throttled by progress_interval, with a final completion callback.
Cancellation and failure behavior
- A failed upload request cancels outstanding upload tasks and closes owned temporary media sessions.
- Downloads first write to
*.pyrospeed.partand atomically replace the destination after success. - Failed/cancelled downloads remove the temporary file when possible.
- The native Pyrogram
FILE_PART_X_MISSINGresend contract is preserved: if the framework calls patchedsave_file(..., file_id=..., file_part=N), pyrospeed resends that exact part.
Event-loop behavior
Regular filesystem I/O is not truly non-blocking on all operating systems. The default disk_io="thread" moves file reads/writes to asyncio.to_thread, so the caller remains async and the main event loop is not held during disk operations. Set disk_io="inline" only when you have measured that the extra scheduling overhead is worse for your workload.
Security
Never commit:
- Telegram session strings;
- API hashes;
- bot tokens;
.sessiondatabases.
Examples read secrets from environment variables. pyrospeed does not log credentials.
Diagnostics
pyrospeed-doctor
It reports installed Telegram client packages and runtime import versions.
Tests
The core tests do not require Telegram credentials or network access:
PYTHONPATH=src python -m unittest discover -s tests -v
Benchmark
See examples/benchmark.py. It can compare several upload profiles against the native implementation using your own Telegram account. Benchmark with a disposable file/message and delete benchmark messages afterward.
Architecture
PyroSpeed facade
├── FrameworkBinding # fork/module capability detection
├── SessionFactory # independent media sessions
├── ParallelUploader # chunk scheduling + bounded in-flight tasks
├── ParallelDownloader # ranged get_file + random-access writer
├── ProgressDispatcher # concurrency-safe progress aggregation
└── InstanceUploadPatch # reversible per-client integration
This separation keeps Telegram/fork compatibility logic out of transfer scheduling and makes individual components testable.
Known limits
- Telegram itself sets file-size, rate, flood-wait, Premium, and DC policy limits; pyrospeed cannot bypass them.
- Download acceleration depends on a fork exposing Pyrogram-compatible
get_file(file_id, file_size, limit, offset, ...)semantics. - CDN/file-reference errors are delegated to the client's own
get_file()implementation. - Installing multiple Pyrogram forks simultaneously is unsupported because several distributions provide the same
pyrogrammodule. - Increasing connections indefinitely is counterproductive; tune with real measurements.
Documentation
docs/API.md— public API and configuration fieldsdocs/DESIGN.md— architecture, SOLID boundaries, scheduling, cleanupdocs/PERFORMANCE.md— benchmarking and tuning methodologydocs/COMPATIBILITY.md— fork/version capability strategy and fallback rulesdocs/QUICKSTART_FA.md— راهاندازی سریع فارسیCHANGELOG.md— release history
License
MIT for pyrospeed's own code. Pyrogram/Kurigram/Pyrofork and Telegram are separate projects with their own licenses and terms.
Release files for pyrospeed 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyrospeed-0.1.0.tar.gz | 19.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyrospeed-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.0 kB
Release files / pyrospeed-0.1.0.tar.gz
| Download URL | pyrospeed-0.1.0.tar.gz |
|---|---|
| Size | 19.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9877dec3a68ac838f33e10ef1d7c17a8ffe168a255fe2401facf5ac52fce2221
|
|
BLAKE2b-256 checksum How to use checksums |
ddbb80c1ce68f6c1a802e5fb44bc3b55522d1680878250411fc6e629bada9ec4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-requests/2.34.2
|
Release files / pyrospeed-0.1.0-py3-none-any.whl
| Download URL | pyrospeed-0.1.0-py3-none-any.whl |
|---|---|
| Size | 20.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b77be935533d858746ae3dd71435a9dd03e04b367afd0b17d29984545896427b
|
|
BLAKE2b-256 checksum How to use checksums |
eac0aa4161a4a2d0a99315e51ec90e3083f04b2d79dc2eeed70886dd1d289256
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-requests/2.34.2
|