-
Notifications
You must be signed in to change notification settings - Fork 1.3k
change progress bar backend to tqdm #2333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
68 commits
Select commit
Hold shift + click to select a range
1511fd6
add preliminary tqdm work
casperdcl 0ebce9c
create dvc.progress.Tqdm, migrate dvc.remote.base
casperdcl 70ee313
disable old progress
casperdcl c4b9d7d
minor bugfix
casperdcl cc28155
tqdm repo.checkout
casperdcl 62cbeed
tqdm remote.http
casperdcl 6aee6b6
add progress.Tqdm(bytes)
casperdcl 3f0b85c
add progress.Tqdm(truncate)
casperdcl d460d7e
tqdm remote.s3, flake8
casperdcl f61e7d8
tqdm remote.azure
casperdcl 85d4f7e
add progress.Tqdm(desc_truncate)
casperdcl bac3d93
tqdm remote.ssh.connection
casperdcl efd8a07
tqdm remote.local
casperdcl 82bb550
tqdm dvc.utils
casperdcl d6f816b
remove old progress
casperdcl c02807d
tqdm remote.oss
casperdcl ca68eb4
add progress.Tqdm.update_desc(truncate=True)
casperdcl aed3451
pylint
casperdcl 9143f67
black
casperdcl f88bf92
tqdm auto-disabling with dynamic log level
casperdcl 56db2ee
misc minor restructuring
casperdcl 2ac99d8
update dvc.progress test stub
casperdcl 1925d63
fix silly error
casperdcl 2f42db0
fix base progress_callback
casperdcl d0315a3
minor black, test fixes
casperdcl ee4248d
fix and update more tests
casperdcl 120dfff
naive test update
casperdcl ea8f4ba
slight test update
casperdcl 7665782
fix more callback usage
casperdcl 7328ff4
update tqdm requirement
casperdcl 0de87af
progress-aware logging
casperdcl 56d1cca
log to correct stream
casperdcl 032d507
persistent progress for large http files
casperdcl 1d61745
ensure nested bars are cleared away
casperdcl f834e72
fix flake8
casperdcl fbaadcb
fix silly error
casperdcl f373655
minor potential kwargs bugfix
casperdcl 838151d
tidy & avoid excessive requests
casperdcl 3811141
minor tidy
casperdcl 3f8caec
Merge remote-tracking branch 'upstream/master' into tqdm
casperdcl a1f5c03
tidy some ThreadPoolExecutor
casperdcl d9b4ecf
better auto-closing TqdmThreadPoolExecutor
casperdcl c6f73fd
flake8
casperdcl f88c2fe
remove old test
casperdcl 186f0cc
a little tidy
casperdcl 39da372
fix silly request header error
casperdcl 8955a60
add .mailmap
casperdcl b018933
fix py2 MagicMock(sys.stderr).encoding in tests
casperdcl adf05e6
minor rename
casperdcl 60566b3
minor test update
casperdcl 534d6fd
remove defunct test, extraneous comments
casperdcl ac4d03e
remove auto-persistent bars
casperdcl 093ad20
flake8 unused imports
casperdcl 3098460
misc minor style fixes & optimisations
casperdcl 16113c8
update to bleeding-edge StreamHandler.emit
casperdcl 49d4930
add Tqdm units
casperdcl bd1fe67
probably correct oss2 Tqdm units (sparse Mandarin documentation)
casperdcl 34dc163
Tqdm auto leave=False for nested bars
casperdcl 9a2dea8
Tqdm longer descriptions
casperdcl f90fd17
update tqdm version
casperdcl 13e76de
suppress MD5 description prefix
casperdcl 6102caa
hopefully temp fix for uncalled checkout progress_callback
casperdcl 1c8bf11
Merge remote-tracking branch 'upstream/master' into tqdm
casperdcl bee9064
fix uncalled `progress_callback`s
casperdcl 8f7a7a5
fix logic
casperdcl 9b73ff7
avoid duplication
casperdcl 8faa02e
quick review fixes
casperdcl 4bd2ce2
get_files_number test fix
casperdcl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ innosetup/config.ini | |
| *.exe | ||
|
|
||
| .coverage | ||
| .coverage.* | ||
|
|
||
| *.swp | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| Paweł Redzyński <pawelredzynski@gmail.com> | ||
| Dmitry Petrov <dmitry.petrov@nevesomo.com> | ||
| Earl Hathaway <github@earlh.com> | ||
| Nabanita Dash <dashnabanita@gmail.com> | ||
| Kurian Benoy <kurian.bkk@gmail.com> | ||
| Sritanu Chakraborty <sritanu25@gmail.com> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,154 +1,94 @@ | ||
| """Manages progress bars for dvc repo.""" | ||
|
|
||
| from __future__ import print_function | ||
| from __future__ import unicode_literals | ||
|
|
||
| from dvc.utils.compat import str | ||
|
|
||
| import sys | ||
| import threading | ||
| import logging | ||
| from tqdm import tqdm | ||
| from copy import deepcopy | ||
| from concurrent.futures import ThreadPoolExecutor | ||
|
|
||
| CLEARLINE_PATTERN = "\r\x1b[K" | ||
|
|
||
|
|
||
| class Progress(object): | ||
| class TqdmThreadPoolExecutor(ThreadPoolExecutor): | ||
| """ | ||
| Simple multi-target progress bar. | ||
| Ensure worker progressbars are cleared away properly. | ||
| """ | ||
|
|
||
| def __init__(self): | ||
| self._n_total = 0 | ||
| self._n_finished = 0 | ||
| self._lock = threading.Lock() | ||
| self._line = None | ||
|
|
||
| def set_n_total(self, total): | ||
| """Sets total number of targets.""" | ||
| self._n_total = total | ||
| self._n_finished = 0 | ||
|
|
||
| @property | ||
| def is_finished(self): | ||
| """Returns if all targets have finished.""" | ||
| return self._n_total == self._n_finished | ||
|
|
||
| def clearln(self): | ||
| self._print(CLEARLINE_PATTERN, end="") | ||
|
|
||
| def _writeln(self, line): | ||
| self.clearln() | ||
| self._print(line, end="") | ||
| sys.stdout.flush() | ||
|
|
||
| def reset(self): | ||
| with self._lock: | ||
| self._n_total = 0 | ||
| self._n_finished = 0 | ||
| self._line = None | ||
|
|
||
| def refresh(self, line=None): | ||
| """Refreshes progress bar.""" | ||
| # Just go away if it is locked. Will update next time | ||
| if not self._lock.acquire(False): | ||
| return | ||
|
|
||
| if line is None: | ||
| line = self._line | ||
|
|
||
| if sys.stdout.isatty() and line is not None: | ||
| self._writeln(line) | ||
| self._line = line | ||
|
|
||
| self._lock.release() | ||
|
|
||
| def update_target(self, name, current, total): | ||
| """Updates progress bar for a specified target.""" | ||
| self.refresh(self._bar(name, current, total)) | ||
|
|
||
| def finish_target(self, name): | ||
| """Finishes progress bar for a specified target.""" | ||
| # We have to write a msg about finished target | ||
| with self._lock: | ||
| pbar = self._bar(name, 100, 100) | ||
|
|
||
| if sys.stdout.isatty(): | ||
| self.clearln() | ||
|
|
||
| self._print(pbar) | ||
|
|
||
| self._n_finished += 1 | ||
| self._line = None | ||
|
|
||
| def _bar(self, target_name, current, total): | ||
| def __enter__(self): | ||
| """ | ||
| Make a progress bar out of info, which looks like: | ||
| (1/2): [########################################] 100% master.zip | ||
| Creates a blank initial dummy progress bar if needed so that workers | ||
| are forced to create "nested" bars. | ||
| """ | ||
| bar_len = 30 | ||
|
|
||
| if total is None: | ||
| state = 0 | ||
| percent = "?% " | ||
| else: | ||
| total = int(total) | ||
| state = int((100 * current) / total) if current < total else 100 | ||
| percent = str(state) + "% " | ||
|
|
||
| if self._n_total > 1: | ||
| num = "({}/{}): ".format(self._n_finished + 1, self._n_total) | ||
| else: | ||
| num = "" | ||
| blank_bar = Tqdm(bar_format="Multi-Threaded:", leave=False) | ||
| if blank_bar.pos > 0: | ||
| # already nested - don't need a placeholder bar | ||
| blank_bar.close() | ||
| self.bar = blank_bar | ||
| return super(TqdmThreadPoolExecutor, self).__enter__() | ||
|
|
||
| n_sh = int((state * bar_len) / 100) | ||
| n_sp = bar_len - n_sh | ||
| pbar = "[" + "#" * n_sh + " " * n_sp + "] " | ||
| def __exit__(self, *a, **k): | ||
| super(TqdmThreadPoolExecutor, self).__exit__(*a, **k) | ||
| self.bar.close() | ||
|
|
||
| return num + pbar + percent + target_name | ||
|
|
||
| @staticmethod | ||
| def _print(*args, **kwargs): | ||
| import logging | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| if logger.getEffectiveLevel() == logging.CRITICAL: | ||
| return | ||
|
|
||
| print(*args, **kwargs) | ||
|
|
||
| def __enter__(self): | ||
| self._lock.acquire(True) | ||
| if self._line is not None: | ||
| self.clearln() | ||
|
|
||
| def __exit__(self, typ, value, tbck): | ||
| if self._line is not None: | ||
| self.refresh() | ||
| self._lock.release() | ||
|
|
||
| def __call__(self, seq, name="", total=None): | ||
| if total is None: | ||
| total = len(seq) | ||
|
|
||
| self.update_target(name, 0, total) | ||
| for done, item in enumerate(seq, start=1): | ||
| yield item | ||
| self.update_target(name, done, total) | ||
| self.finish_target(name) | ||
|
|
||
|
|
||
| class ProgressCallback(object): | ||
| def __init__(self, total): | ||
| self.total = total | ||
| self.current = 0 | ||
| progress.reset() | ||
|
|
||
| def update(self, name, progress_to_add=1): | ||
| self.current += progress_to_add | ||
| progress.update_target(name, self.current, self.total) | ||
|
|
||
| def finish(self, name): | ||
| progress.finish_target(name) | ||
|
|
||
| class Tqdm(tqdm): | ||
| """ | ||
| maximum-compatibility tqdm-based progressbars | ||
| """ | ||
|
|
||
| progress = Progress() # pylint: disable=invalid-name | ||
| def __init__( | ||
| self, | ||
| iterable=None, | ||
| disable=None, | ||
| bytes=False, # pylint: disable=W0622 | ||
| desc_truncate=None, | ||
| leave=None, | ||
| **kwargs | ||
| ): | ||
| """ | ||
| bytes : shortcut for | ||
| `unit='B', unit_scale=True, unit_divisor=1024, miniters=1` | ||
| desc_truncate : like `desc` but will truncate to 10 chars | ||
| kwargs : anything accepted by `tqdm.tqdm()` | ||
| """ | ||
| kwargs = deepcopy(kwargs) | ||
| if bytes: | ||
| for k, v in dict( | ||
| unit="B", unit_scale=True, unit_divisor=1024, miniters=1 | ||
| ).items(): | ||
| kwargs.setdefault(k, v) | ||
| if desc_truncate is not None: | ||
| kwargs.setdefault("desc", self.truncate(desc_truncate)) | ||
| if disable is None: | ||
| disable = ( | ||
| logging.getLogger(__name__).getEffectiveLevel() | ||
| >= logging.CRITICAL | ||
| ) | ||
| super(Tqdm, self).__init__( | ||
| iterable=iterable, disable=disable, leave=leave, **kwargs | ||
| ) | ||
|
|
||
| def update_desc(self, desc, n=1, truncate=True): | ||
| """ | ||
| Calls `set_description(truncate(desc))` and `update(n)` | ||
| """ | ||
| self.set_description( | ||
| self.truncate(desc) if truncate else desc, refresh=False | ||
| ) | ||
| self.update(n) | ||
|
|
||
| def update_to(self, current, total=None): | ||
| if total: | ||
| self.total = total # pylint: disable=W0613,W0201 | ||
| self.update(current - self.n) | ||
|
|
||
| @classmethod | ||
| def truncate(cls, s, max_len=25, end=True, fill="..."): | ||
| """ | ||
| Guarantee len(output) < max_lenself. | ||
| >>> truncate("hello", 4) | ||
| '...o' | ||
| """ | ||
| if len(s) <= max_len: | ||
| return s | ||
| if len(fill) > max_len: | ||
| return fill[-max_len:] if end else fill[:max_len] | ||
| i = max_len - len(fill) | ||
| return (fill + s[-i:]) if end else (s[:i] + fill) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.