diff --git a/changelog/877.feature.rst b/changelog/877.feature.rst new file mode 100644 index 00000000..c2fb4462 --- /dev/null +++ b/changelog/877.feature.rst @@ -0,0 +1 @@ +Use Rich instead of tqdm for upload progress bar. diff --git a/mypy.ini b/mypy.ini index 5f2d5780..87242265 100644 --- a/mypy.ini +++ b/mypy.ini @@ -5,7 +5,7 @@ show_traceback = True warn_redundant_casts = True warn_unused_configs = True warn_unused_ignores = True -; Enabling this will fail on subclasses of untyped imports, e.g. tqdm +; Enabling this will fail on subclasses of untyped imports, e.g. pkginfo ; disallow_subclassing_any = True disallow_any_generics = True disallow_untyped_calls = True @@ -33,10 +33,6 @@ ignore_missing_imports = True [mypy-rfc3986] ignore_missing_imports = True -[mypy-tqdm] -; https://github.com/tqdm/tqdm/issues/260 -ignore_missing_imports = True - [mypy-urllib3] ; https://github.com/urllib3/urllib3/issues/867 ignore_missing_imports = True diff --git a/setup.cfg b/setup.cfg index 30b18682..578a846d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -41,11 +41,10 @@ install_requires= requests >= 2.20 requests-toolbelt >= 0.8.0, != 0.9.0 urllib3 >= 1.26.0 - tqdm >= 4.14 importlib-metadata >= 3.6 keyring >= 15.1 rfc3986 >= 1.4.0 - rich + rich >= 12.0.0 include_package_data = True [options.entry_points] diff --git a/tests/test_repository.py b/tests/test_repository.py index 68b688e9..2c6bd454 100644 --- a/tests/test_repository.py +++ b/tests/test_repository.py @@ -185,18 +185,21 @@ def test_package_is_registered(default_repo): @pytest.mark.parametrize("disable_progress_bar", [True, False]) -def test_disable_progress_bar_is_forwarded_to_tqdm( +def test_disable_progress_bar_is_forwarded_to_rich( monkeypatch, tmpdir, disable_progress_bar, default_repo ): """Toggle display of upload progress bar.""" @contextmanager - def progressbarstub(*args, **kwargs): + def ProgressStub(*args, **kwargs): assert "disable" in kwargs assert kwargs["disable"] == disable_progress_bar - yield + yield pretend.stub( + add_task=lambda description, total: None, + update=lambda task_id, completed: None, + ) - monkeypatch.setattr(repository, "ProgressBar", progressbarstub) + monkeypatch.setattr(repository.rich.progress, "Progress", ProgressStub) default_repo.disable_progress_bar = disable_progress_bar default_repo.session = pretend.stub( diff --git a/twine/repository.py b/twine/repository.py index bf1379b0..decfcfd4 100644 --- a/twine/repository.py +++ b/twine/repository.py @@ -12,12 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. import logging -import sys from typing import Any, Dict, List, Optional, Set, Tuple, cast import requests import requests_toolbelt -import tqdm +import rich.progress import urllib3 from requests import adapters from requests_toolbelt.utils import user_agent @@ -38,16 +37,6 @@ logger = logging.getLogger(__name__) -class ProgressBar(tqdm.tqdm): - def update_to(self, n: int) -> None: - """Update the bar in the way compatible with requests-toolbelt. - - This is identical to tqdm.update, except ``n`` will be the current - value - not the delta as tqdm expects. - """ - self.update(n - self.n) # will also do self.n = n - - class Repository: def __init__( self, @@ -156,17 +145,28 @@ def _upload(self, package: package_file.PackageFile) -> requests.Response: ("content", (package.basefilename, fp, "application/octet-stream")) ) encoder = requests_toolbelt.MultipartEncoder(data_to_send) - with ProgressBar( - total=encoder.len, - unit="B", - unit_scale=True, - unit_divisor=1024, - miniters=1, - file=sys.stdout, + + with rich.progress.Progress( + "[progress.percentage]{task.percentage:>3.0f}%", + rich.progress.BarColumn(), + rich.progress.DownloadColumn(), + "•", + rich.progress.TimeRemainingColumn( + compact=True, + elapsed_when_finished=True, + ), + "•", + rich.progress.TransferSpeedColumn(), disable=self.disable_progress_bar, - ) as bar: + ) as progress: + task_id = progress.add_task("", total=encoder.len) + monitor = requests_toolbelt.MultipartEncoderMonitor( - encoder, lambda monitor: bar.update_to(monitor.bytes_read) + encoder, + lambda monitor: progress.update( + task_id, + completed=monitor.bytes_read, + ), ) resp = self.session.post(