diff --git a/httpie/output/ui/rich_progress.py b/httpie/output/ui/rich_progress.py index d2cfd38c70..ddc96100e7 100644 --- a/httpie/output/ui/rich_progress.py +++ b/httpie/output/ui/rich_progress.py @@ -28,7 +28,11 @@ def console(self) -> 'Console': return self.env.rich_error_console def _print_summary( - self, is_finished: bool, observed_steps: int, time_spent: float + self, + is_finished: bool, + observed_steps: float, + time_spent: float, + total_steps: Optional[float] = None, ): from rich import filesize @@ -37,7 +41,10 @@ def _print_summary( else: verb = 'Interrupted' - total_size = filesize.decimal(observed_steps) + if total_steps is None: + total_steps = observed_steps + + total_size = filesize.decimal(total_steps) avg_speed = filesize.decimal(observed_steps / time_spent) minutes, seconds = divmod(time_spent, 60) @@ -63,6 +70,7 @@ class StatusDisplay(BaseDisplay): def start( self, *, total: Optional[float], at: float, description: str ) -> None: + self.initial_observed = at self.observed = at self.description = ( f'[progress.description]{description}[/progress.description]' @@ -89,8 +97,9 @@ def stop(self, time_spent: float) -> None: if time_spent: self._print_summary( is_finished=True, - observed_steps=self.observed, + observed_steps=self.observed - self.initial_observed, time_spent=time_spent, + total_steps=self.observed, ) @@ -107,6 +116,7 @@ def start( ) assert total is not None + self.initial_completed = at self.console.print(f'[progress.description]{description}') self.progress_bar = Progress( '[', @@ -136,6 +146,7 @@ def stop(self, time_spent: Optional[float]) -> None: [task] = self.progress_bar.tasks self._print_summary( is_finished=task.finished, - observed_steps=task.completed, + observed_steps=task.completed - self.initial_completed, time_spent=time_spent, + total_steps=task.completed, ) diff --git a/tests/test_downloads.py b/tests/test_downloads.py index b646a0e6a5..ec62aaf5ff 100644 --- a/tests/test_downloads.py +++ b/tests/test_downloads.py @@ -1,17 +1,20 @@ import os import tempfile import time -import requests +from io import StringIO from unittest import mock from urllib.request import urlopen import pytest +import requests +from rich.console import Console from requests.structures import CaseInsensitiveDict from httpie.downloads import ( parse_content_range, filename_from_content_disposition, filename_from_url, get_unique_filename, ContentRangeError, Downloader, PARTIAL_CONTENT ) +from httpie.output.ui.rich_progress import ProgressDisplay from .utils import http, MockEnvironment @@ -247,6 +250,28 @@ def test_download_resumed(self, mock_env, httpbin_both): downloader.chunk_downloaded(b'45') downloader.finish() + def test_resumed_download_summary_uses_current_transfer_for_speed(self): + stderr = StringIO() + env = MockEnvironment( + stderr=stderr, + stderr_isatty=False, + show_displays=True, + ) + env.rich_error_console = Console( + file=stderr, + force_terminal=False, + no_color=True, + width=120, + ) + display = ProgressDisplay(env) + + display.start(total=5, at=3, description='Downloading to file.bin') + display.update(2) + display.stop(time_spent=2) + + summary = stderr.getvalue() + assert 'Done. 5 bytes in 00:2.00000 (1 byte/s)' in summary + def test_download_with_redirect_original_url_used_for_filename(self, httpbin): # Redirect from `/redirect/1` to `/get`. expected_filename = '1.json'