Skip to content

Releases: plutopulp/rheo

v0.7.0: Manager-level retries & Python 3.11+

17 Dec 17:37
495974f

Choose a tag to compare

🚨 Breaking Changes

  • Minimum Python version is now 3.11+: Type checking and runtime features (e.g., StrEnum, asyncio.Timeout, typing.Unpack) require Python 3.11 or later. CI and metadata updated accordingly.

✨ What's New

  • Manager-level retry handler configuration: DownloadManager now accepts retry_handler and threads it through WorkerPool and workers. Retry events flow through the shared emitter. Integration tests cover retry counts, custom policies (e.g., 404 as transient), and default no-retry behaviour.
  • New example: examples/07_retry_handling.py demonstrates basic retries, custom policies, event subscription for retries, and no-retry behaviour.

🔧 Internal Improvements

  • Docs and badges: Reordered badges, added "commits since release" badge, refreshed retry docs to show manager-level configuration and custom policies.
  • Tooling: CI matrix and mypy target aligned to Python 3.11–3.14; packaging metadata updated.
  • Cleanup: Removed legacy src/rheo/main.py demo script and unused tests/fixtures/ data. Dropped dead FileConfig.max_retries field.

📝 Full Details

See the complete changelog: CHANGELOG.md


📦 Installation

pip install --upgrade rheopy

🙏 Thank You

Thanks for using Rheo! If you spot any issues, please open an issue on GitHub.

v0.6.0: Cancellation & HTTP Client Abstraction

17 Dec 11:37
a7c707c

Choose a tag to compare

🚨 Breaking Changes

  • Event subscription API: manager.on() now returns a Subscription handle with unsubscribe(). The previous manager.off() is removed. Event names are typed via DownloadEventType (StrEnum).
  • HTTP client abstraction: DownloadManager, WorkerPool, and DownloadWorker now depend on BaseHttpClient (default AiohttpClient) instead of raw aiohttp.ClientSession. Custom clients must implement BaseHttpClient.

Migration examples:

# Before
sub = manager.on("download.completed", handler)
manager.off("download.completed", handler)

# After
sub = manager.on(DownloadEventType.COMPLETED, handler)
sub.unsubscribe()
# Custom client injection
client = AiohttpClient()  # or your BaseHttpClient impl
async with DownloadManager(http_client=client) as manager:
    ...

✨ What's New

  • Selective cancellation: manager.cancel(download_id) -> CancelResult with cooperative cancellation for queued downloads and task-level cancellation for in-progress downloads. DownloadCancelledEvent now includes cancelled_from (QUEUED vs IN_PROGRESS).
  • Cancellation observability: DownloadStats now reports cancelled count via manager.stats.
  • File exists policy: DestinationResolver + FileExistsPolicy centralise file-exists handling with default_file_exists_strategy; workers emit skips when policy returns None.
  • Typed events: DownloadEventType enum for event names, Subscription handle returned from manager.on().
  • Benchmarks: Added minimal benchmarks/ suite (pytest-benchmark) with HTTP fixture server for throughput profiling.

🔧 Internal Improvements

  • HTTP client layer: Introduced BaseHttpClient ABC, AiohttpClient implementation, SSL factory helpers, and lifecycle management; manager only opens clients it owns.
  • Exception hierarchy: New RheoError base; InfrastructureErrorHttpClientErrorClientNotInitialisedError; DownloadManagerError now derives from RheoError.
  • CI hardening: Poetry 2.x install flags (--all-groups), reordered caching, and version diagnostics.
  • Docs & examples: Updated to demonstrate DownloadEventType, Subscription, and selective cancellation usage.

📝 Full Details

See the complete changelog: CHANGELOG.md


📦 Installation

pip install --upgrade rheopy

🙏 Thank You

Thanks for using Rheo! If you encounter any issues with the migration, please open an issue.

v0.5.0: Event System Overhaul

09 Dec 12:57
84b77c8

Choose a tag to compare

🚨 Breaking Changes

This release overhauls the event system for a cleaner, more intuitive API.

Event Subscription:

  • Subscribe to events via manager.on() instead of tracker.on()
  • Tracker is now observe-only (no longer emits events)

Event Names:

  • All events renamed from worker.* to download.* namespace
  • Example: worker.starteddownload.started

Migration Example:

# Before (v0.4.0)
tracker.on("download.completed", handler)

# After (v0.5.0)
manager.on("download.completed", handler)

# Wildcard subscription for all events
manager.on("*", lambda e: print(e.event_type))

✨ What's New

Manager as Event Facade (#74):

  • manager.on(event, handler) - Subscribe to download events
  • manager.off(event, handler) - Unsubscribe from events
  • manager.on("*", handler) - Receive all events
  • manager.get_download_info(id) - Query download state
  • manager.stats - Get aggregate statistics

New Lifecycle Events (#69, #70, #71):

  • download.queued - When added to queue
  • download.skipped - When file exists and strategy is SKIP
  • download.cancelled - When download is cancelled
  • download.validating - When hash validation starts
  • download.retrying - Before retry attempt

Embedded Validation Results (#70):

  • download.completed and download.failed events include validation field
  • Self-contained ValidationResult with is_valid, expected/calculated hash

New Examples (#75):

  • 04_progress_display.py - Real-time progress bar with speed/ETA
  • 05_event_logging.py - Lifecycle debugging with wildcard subscription
  • 06_batch_summary.py - Batch download with summary report

CLI Progress Restored:

  • Real-time progress bar with speed and ETA during downloads
  • Detailed validation and error output on completion

🔧 Internal Improvements

Pydantic Events (#66):

  • All events migrated to Pydantic models with validation
  • Immutable, UTC-timestamped, type-safe

Centralised Event Wiring (#73):

  • Manager owns all event wiring
  • WorkerPool is tracker-agnostic
  • Cleaner separation of concerns

Shared Emitter Architecture (#74):

  • Single emitter owned by Manager, shared across queue/pool/workers
  • Unified subscription point

📝 Full Details

See the complete changelog: CHANGELOG.md


📦 Installation

pip install --upgrade rheopy

🙏 Thank You

Thanks for using Rheo! If you encounter any issues with the migration, please open an issue.

v0.4.0: File Safety & Cancellation Handling

01 Dec 16:45
1931562

Choose a tag to compare

Release Title

v0.4.0: File Safety & Cancellation Handling


Release Notes

🚨 Breaking Changes

This release introduces safer defaults that may require code changes.

File Exists Handling:

  • Existing files are now SKIPped by default (previously silently overwritten)
  • New FileExistsStrategy enum: SKIP, OVERWRITE, ERROR

Context Manager Safety:

  • PendingDownloadsError raised if exiting async with without handling downloads
  • Must call wait_until_complete() or close() explicitly

Migration Example:

# Before (v0.3.0)
async with DownloadManager(download_dir=Path("./downloads")) as manager:
    await manager.add(files)
    # Silent cancellation, silent overwrite

# After (v0.4.0)
async with DownloadManager(download_dir=Path("./downloads")) as manager:
    await manager.add(files)
    await manager.wait_until_complete()  # Required

# To restore overwrite behavior:
async with DownloadManager(
    download_dir=Path("./downloads"),
    file_exists_strategy=FileExistsStrategy.OVERWRITE,
) as manager:
    ...

✨ What's New

FileExistsStrategy (#63):

  • SKIP (default): Skip download if file exists, log and continue
  • OVERWRITE: Replace existing file with new download
  • ERROR: Raise FileExistsError if file exists
  • Configurable at manager level or per-file via FileConfig.file_exists_strategy

PendingDownloadsError (#62):

  • Prevents silent data loss from cancelled downloads
  • Raised when exiting context manager with unhandled pending work
  • New queue.pending_count property for checking pending downloads

🐛 Bug Fixes

Partial File Cleanup (#60):

  • Cancelled downloads now properly clean up partial files
  • Previously, CancelledError bypassed cleanup logic, leaving corrupted files

🔧 Internal Improvements

Domain Purity (#61):

  • Removed I/O from FileConfig domain model
  • Directory creation moved to DownloadWorker using async aiofiles.os.makedirs
  • Examples updated to use destination_subdir for better organization

📝 Full Details

See the complete changelog: CHANGELOG.md

📦 Installation

pip install --upgrade rheopy

🙏 Thank You

Thanks for using Rheo! If you encounter any issues with the migration, please open an issue.

v0.3.0: Simpified Shutdown & Download IDs

01 Dec 09:29

Choose a tag to compare

Release Notes

🚨 Breaking Changes

This release simplifies the shutdown API and adopts a download ID model for tracking and deduplication.

API Changes:

  • DownloadManager.cancel_all() removed
    • Use close(wait_for_current=True/False) instead
  • WorkerPool.stop() and WorkerPool.request_shutdown() removed from the public interface
    • Worker pools now expose a single shutdown(wait_for_current: bool) method

Migration Example (manager shutdown):

Before (v0.2.0)

async with DownloadManager(...) as manager:
    ...
    await manager.cancel_all(wait_for_current=True)

After (v0.3.0)

async with DownloadManager(...) as manager:
    ...
    await manager.close(wait_for_current=True)

Behavioural Note:

  • The context manager (async with DownloadManager(...)) continues to perform an immediate shutdown on exit (cancels in-flight downloads and cleans up resources).
  • Use close(wait_for_current=True) if you need a graceful shutdown from user code.

✨ What's New

1. Simplified Shutdown API

WorkerPool:

  • Single public method: shutdown(wait_for_current: bool = True)
    • wait_for_current=True → graceful shutdown (finish in-flight downloads)
    • wait_for_current=False → immediate cancellation (cancel worker tasks, then clean up)
  • Internal cancellation logic is inlined for clearer control flow.
  • _request_shutdown() is now private and used internally by shutdown().

DownloadManager:

  • close(wait_for_current: bool = False) is now the one place to control shutdown semantics:
    • wait_for_current=True → graceful shutdown
    • wait_for_current=False → immediate shutdown
  • Context manager methods now delegate to lifecycle methods:
    • __aenter__open()
    • __aexit__close(wait_for_current=False)
  • This makes the public API smaller and easier to reason about.

2. Complete Download ID System

The Download ID system introduced in earlier work is now fully wired through the stack.

Key properties:

  • Each download has a stable 16-character hex ID derived from URL + relative_destination_path.
  • Same URL to different destinations ⇒ different IDs.
  • Same URL to same destination ⇒ same ID (treated as a duplicate).

Where it’s used:

  • FileConfig.id – generated/computed ID for each download.
  • DownloadInfo.id – stored with each tracked download.
  • DownloadTracker – now keys by download_id instead of URL.
  • Worker events and tracker events – all carry download_id.
  • PriorityDownloadQueue – uses IDs for deduplication:
    • Duplicate downloads (same URL+destination) are skipped rather than re-queued.

User-visible effects:

  • Adding the same FileConfig (same URL+destination) multiple times will not enqueue duplicate downloads.
  • Tracker queries and integration points now consistently use download_id (e.g. FileConfig.id) rather than raw URLs.

🐛 Fixes & Example Updates

  • Hash validation example (03_hash_validation.py)

    • Fixed tracker lookup to use file_config.id instead of URL
  • Progress tracking example (04_progress_tracking.py)

    • Fixed on_progress handler
  • Documentation updated to:

    • Reflect shutdown API simplification (close() instead of shutdown() / cancel_all()).
    • Describe the Download ID system and deduplication behaviour.

📝 Full Details

See the complete changelog:
CHANGELOG.md – v0.3.0

📦 Installation

pip install --upgrade rheopy

🙏 Thank You

Thanks for using Rheo! If you run into any issues upgrading to v0.3.0 or have feedback on the new shutdown and ID semantics, please open an issue.

v0.2.0: Download-Centric API & WorkerPool

25 Nov 11:18
b4dcaf0

Choose a tag to compare

Release Notes

Breaking Changes

This release includes a major refactor of the DownloadManager API to provide a cleaner, more intuitive interface focused on downloads rather than internal implementation details.

API Changes:

  • add_to_queue()add()
  • queue.join()wait_until_complete()
  • start_workers()open() or use context manager
  • stop_workers()close() or use context manager
  • shutdown()cancel_all()
  • request_shutdown() removed (use cancel_all())
  • max_workers parameter → max_concurrent
  • worker= parameter → worker_factory=

Migration Example:

# Before (v0.1.0)
async with DownloadManager(max_workers=5) as manager:
    await manager.add_to_queue(files)
    await manager.queue.join()

# After (v0.2.0)
async with DownloadManager(max_concurrent=5) as manager:
    await manager.add(files)
    await manager.wait_until_complete()

What's New

New DownloadManager Methods:

  • add(files) - Add files to download queue
  • wait_until_complete(timeout=None) - Wait for all downloads to finish
  • cancel_all(wait_for_current=False) - Cancel pending downloads
  • open() / close() - Manual lifecycle management
  • is_active property - Check if manager is running

WorkerPool Extraction (#43):

  • New WorkerPool class for managing worker lifecycle
  • Better separation of concerns: manager orchestrates, pool manages workers
  • Comprehensive test suite with 15+ tests

Worker Isolation (#42):

  • Per-task worker instances with isolated event emitters
  • Prevents race conditions
  • WorkerFactory Protocol for dependency injection

Package Reorganization (#41):

  • Restructured into focused subdirectories: worker/, validation/, retry/
  • Improved code organization and extensibility

Examples & Docs:

  • Unique filenames in examples to prevent overwrites
  • All documentation updated with new API
  • Fixed PyPI documentation links

Full Details

See the complete changelog: CHANGELOG.md

Installation

pip install --upgrade rheopy

Thank You

Thanks for using Rheo! If you encounter any issues with the migration, please open an issue.

Initial Release

20 Nov 23:47
20ae660

Choose a tag to compare

Initial Release

First public release of Rheo - concurrent HTTP download orchestration with async I/O.

Features

  • Concurrent downloads with worker pool
  • Priority queue
  • Hash validation (MD5, SHA256, SHA512)
  • Retry logic with exponential backoff
  • Real-time speed & ETA tracking
  • Event-driven architecture
  • CLI tool (rheo download)
  • Full type hints

Installation

pip install rheopy See the README for full documentation.