Coverage for /Volumes/workspace/python-progressbar/.tox/py38/lib/python3.8/site-packages/progressbar/bar.py: 100%

340 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-10-26 14:16 +0200

1from __future__ import annotations 

2 

3import logging 

4import math 

5import os 

6import sys 

7import time 

8import timeit 

9import warnings 

10from copy import deepcopy 

11from datetime import datetime 

12 

13from python_utils import types 

14 

15try: # pragma: no cover 

16 from collections import abc 

17except ImportError: # pragma: no cover 

18 import collections as abc 

19 

20from python_utils import converters 

21 

22from . import widgets 

23from . import widgets as widgets_module # Avoid name collision 

24from . import base 

25from . import utils 

26 

27logger = logging.getLogger(__name__) 

28 

29 

30T = types.TypeVar('T') 

31 

32 

33class ProgressBarMixinBase(object): 

34 

35 def __init__(self, **kwargs): 

36 self._finished = False 

37 

38 def start(self, **kwargs): 

39 pass 

40 

41 def update(self, value=None): 

42 pass 

43 

44 def finish(self): # pragma: no cover 

45 self._finished = True 

46 

47 def __del__(self): 

48 if not self._finished: # pragma: no cover 

49 try: 

50 self.finish() 

51 except Exception: 

52 pass 

53 

54 

55class ProgressBarBase(abc.Iterable, ProgressBarMixinBase): 

56 pass 

57 

58 

59class DefaultFdMixin(ProgressBarMixinBase): 

60 

61 def __init__(self, fd: types.IO = sys.stderr, 

62 is_terminal: bool | None = None, 

63 line_breaks: bool | None = None, 

64 enable_colors: bool | None = None, **kwargs): 

65 if fd is sys.stdout: 

66 fd = utils.streams.original_stdout 

67 

68 elif fd is sys.stderr: 

69 fd = utils.streams.original_stderr 

70 

71 self.fd = fd 

72 self.is_ansi_terminal = utils.is_ansi_terminal(fd) 

73 

74 # Check if this is an interactive terminal 

75 self.is_terminal = utils.is_terminal( 

76 fd, is_terminal or self.is_ansi_terminal) 

77 

78 # Check if it should overwrite the current line (suitable for 

79 # iteractive terminals) or write line breaks (suitable for log files) 

80 if line_breaks is None: 

81 line_breaks = utils.env_flag('PROGRESSBAR_LINE_BREAKS', 

82 not self.is_terminal) 

83 self.line_breaks = line_breaks 

84 

85 # Check if ANSI escape characters are enabled (suitable for iteractive 

86 # terminals), or should be stripped off (suitable for log files) 

87 if enable_colors is None: 

88 enable_colors = utils.env_flag('PROGRESSBAR_ENABLE_COLORS', 

89 self.is_ansi_terminal) 

90 

91 self.enable_colors = enable_colors 

92 

93 ProgressBarMixinBase.__init__(self, **kwargs) 

94 

95 def update(self, *args, **kwargs): 

96 ProgressBarMixinBase.update(self, *args, **kwargs) 

97 

98 line = converters.to_unicode(self._format_line()) 

99 if not self.enable_colors: 

100 line = utils.no_color(line) 

101 

102 if self.line_breaks: 

103 line = line.rstrip() + '\n' 

104 else: 

105 line = '\r' + line 

106 

107 try: # pragma: no cover 

108 self.fd.write(line) 

109 except UnicodeEncodeError: # pragma: no cover 

110 self.fd.write(line.encode('ascii', 'replace')) 

111 

112 def finish(self, *args, **kwargs): # pragma: no cover 

113 if self._finished: 

114 return 

115 

116 end = kwargs.pop('end', '\n') 

117 ProgressBarMixinBase.finish(self, *args, **kwargs) 

118 

119 if end and not self.line_breaks: 

120 self.fd.write(end) 

121 

122 self.fd.flush() 

123 

124 

125class ResizableMixin(ProgressBarMixinBase): 

126 

127 def __init__(self, term_width: int | None = None, **kwargs): 

128 ProgressBarMixinBase.__init__(self, **kwargs) 

129 

130 self.signal_set = False 

131 if term_width: 

132 self.term_width = term_width 

133 else: # pragma: no cover 

134 try: 

135 self._handle_resize() 

136 import signal 

137 self._prev_handle = signal.getsignal(signal.SIGWINCH) 

138 signal.signal(signal.SIGWINCH, self._handle_resize) 

139 self.signal_set = True 

140 except Exception: 

141 pass 

142 

143 def _handle_resize(self, signum=None, frame=None): 

144 'Tries to catch resize signals sent from the terminal.' 

145 

146 w, h = utils.get_terminal_size() 

147 self.term_width = w 

148 

149 def finish(self): # pragma: no cover 

150 ProgressBarMixinBase.finish(self) 

151 if self.signal_set: 

152 try: 

153 import signal 

154 signal.signal(signal.SIGWINCH, self._prev_handle) 

155 except Exception: # pragma no cover 

156 pass 

157 

158 

159class StdRedirectMixin(DefaultFdMixin): 

160 

161 def __init__(self, redirect_stderr: bool = False, 

162 redirect_stdout: bool = False, **kwargs): 

163 DefaultFdMixin.__init__(self, **kwargs) 

164 self.redirect_stderr = redirect_stderr 

165 self.redirect_stdout = redirect_stdout 

166 self._stdout = self.stdout = sys.stdout 

167 self._stderr = self.stderr = sys.stderr 

168 

169 def start(self, *args, **kwargs): 

170 if self.redirect_stdout: 

171 utils.streams.wrap_stdout() 

172 

173 if self.redirect_stderr: 

174 utils.streams.wrap_stderr() 

175 

176 self._stdout = utils.streams.original_stdout 

177 self._stderr = utils.streams.original_stderr 

178 

179 self.stdout = utils.streams.stdout 

180 self.stderr = utils.streams.stderr 

181 

182 utils.streams.start_capturing(self) 

183 DefaultFdMixin.start(self, *args, **kwargs) 

184 

185 def update(self, value: float = None): 

186 if not self.line_breaks and utils.streams.needs_clear(): 

187 self.fd.write('\r' + ' ' * self.term_width + '\r') 

188 

189 utils.streams.flush() 

190 DefaultFdMixin.update(self, value=value) 

191 

192 def finish(self, end='\n'): 

193 DefaultFdMixin.finish(self, end=end) 

194 utils.streams.stop_capturing(self) 

195 if self.redirect_stdout: 

196 utils.streams.unwrap_stdout() 

197 

198 if self.redirect_stderr: 

199 utils.streams.unwrap_stderr() 

200 

201 

202class ProgressBar(StdRedirectMixin, ResizableMixin, ProgressBarBase): 

203 '''The ProgressBar class which updates and prints the bar. 

204 

205 Args: 

206 min_value (int): The minimum/start value for the progress bar 

207 max_value (int): The maximum/end value for the progress bar. 

208 Defaults to `_DEFAULT_MAXVAL` 

209 widgets (list): The widgets to render, defaults to the result of 

210 `default_widget()` 

211 left_justify (bool): Justify to the left if `True` or the right if 

212 `False` 

213 initial_value (int): The value to start with 

214 poll_interval (float): The update interval in seconds. 

215 Note that if your widgets include timers or animations, the actual 

216 interval may be smaller (faster updates). Also note that updates 

217 never happens faster than `min_poll_interval` which can be used for 

218 reduced output in logs 

219 min_poll_interval (float): The minimum update interval in seconds. 

220 The bar will _not_ be updated faster than this, despite changes in 

221 the progress, unless `force=True`. This is limited to be at least 

222 `_MINIMUM_UPDATE_INTERVAL`. If available, it is also bound by the 

223 environment variable PROGRESSBAR_MINIMUM_UPDATE_INTERVAL 

224 widget_kwargs (dict): The default keyword arguments for widgets 

225 custom_len (function): Method to override how the line width is 

226 calculated. When using non-latin characters the width 

227 calculation might be off by default 

228 max_error (bool): When True the progressbar will raise an error if it 

229 goes beyond it's set max_value. Otherwise the max_value is simply 

230 raised when needed 

231 prefix (str): Prefix the progressbar with the given string 

232 suffix (str): Prefix the progressbar with the given string 

233 variables (dict): User-defined variables variables that can be used 

234 from a label using `format='{variables.my_var}'`. These values can 

235 be updated using `bar.update(my_var='newValue')` This can also be 

236 used to set initial values for variables' widgets 

237 

238 A common way of using it is like: 

239 

240 >>> progress = ProgressBar().start() 

241 >>> for i in range(100): 

242 ... progress.update(i + 1) 

243 ... # do something 

244 ... 

245 >>> progress.finish() 

246 

247 You can also use a ProgressBar as an iterator: 

248 

249 >>> progress = ProgressBar() 

250 >>> some_iterable = range(100) 

251 >>> for i in progress(some_iterable): 

252 ... # do something 

253 ... pass 

254 ... 

255 

256 Since the progress bar is incredibly customizable you can specify 

257 different widgets of any type in any order. You can even write your own 

258 widgets! However, since there are already a good number of widgets you 

259 should probably play around with them before moving on to create your own 

260 widgets. 

261 

262 The term_width parameter represents the current terminal width. If the 

263 parameter is set to an integer then the progress bar will use that, 

264 otherwise it will attempt to determine the terminal width falling back to 

265 80 columns if the width cannot be determined. 

266 

267 When implementing a widget's update method you are passed a reference to 

268 the current progress bar. As a result, you have access to the 

269 ProgressBar's methods and attributes. Although there is nothing preventing 

270 you from changing the ProgressBar you should treat it as read only. 

271 ''' 

272 

273 #: Current progress (min_value <= value <= max_value) 

274 value: T 

275 #: Maximum (and final) value. Beyond this value an error will be raised 

276 #: unless the `max_error` parameter is `False`. 

277 max_value: T 

278 #: The time the progressbar reached `max_value` or when `finish()` was 

279 #: called. 

280 end_time: datetime 

281 #: The time `start()` was called or iteration started. 

282 start_time: datetime 

283 #: Seconds between `start_time` and last call to `update()` 

284 seconds_elapsed: float 

285 

286 _DEFAULT_MAXVAL = base.UnknownLength 

287 # update every 50 milliseconds (up to a 20 times per second) 

288 _MINIMUM_UPDATE_INTERVAL = 0.050 

289 

290 def __init__(self, min_value=0, max_value=None, widgets=None, 

291 left_justify=True, initial_value=0, poll_interval=None, 

292 widget_kwargs=None, custom_len=utils.len_color, 

293 max_error=True, prefix=None, suffix=None, variables=None, 

294 min_poll_interval=None, **kwargs): 

295 ''' 

296 Initializes a progress bar with sane defaults 

297 ''' 

298 StdRedirectMixin.__init__(self, **kwargs) 

299 ResizableMixin.__init__(self, **kwargs) 

300 ProgressBarBase.__init__(self, **kwargs) 

301 if not max_value and kwargs.get('maxval') is not None: 

302 warnings.warn('The usage of `maxval` is deprecated, please use ' 

303 '`max_value` instead', DeprecationWarning) 

304 max_value = kwargs.get('maxval') 

305 

306 if not poll_interval and kwargs.get('poll'): 

307 warnings.warn('The usage of `poll` is deprecated, please use ' 

308 '`poll_interval` instead', DeprecationWarning) 

309 poll_interval = kwargs.get('poll') 

310 

311 if max_value: 

312 if min_value > max_value: 

313 raise ValueError('Max value needs to be bigger than the min ' 

314 'value') 

315 self.min_value = min_value 

316 self.max_value = max_value 

317 self.max_error = max_error 

318 

319 # Only copy the widget if it's safe to copy. Most widgets are so we 

320 # assume this to be true 

321 if widgets is None: 

322 self.widgets = widgets 

323 else: 

324 self.widgets = [] 

325 for widget in widgets: 

326 if getattr(widget, 'copy', True): 

327 widget = deepcopy(widget) 

328 self.widgets.append(widget) 

329 

330 self.widgets = widgets 

331 self.prefix = prefix 

332 self.suffix = suffix 

333 self.widget_kwargs = widget_kwargs or {} 

334 self.left_justify = left_justify 

335 self.value = initial_value 

336 self._iterable = None 

337 self.custom_len = custom_len 

338 self.initial_start_time = kwargs.get('start_time') 

339 self.init() 

340 

341 # Convert a given timedelta to a floating point number as internal 

342 # interval. We're not using timedelta's internally for two reasons: 

343 # 1. Backwards compatibility (most important one) 

344 # 2. Performance. Even though the amount of time it takes to compare a 

345 # timedelta with a float versus a float directly is negligible, this 

346 # comparison is run for _every_ update. With billions of updates 

347 # (downloading a 1GiB file for example) this adds up. 

348 poll_interval = utils.deltas_to_seconds(poll_interval, default=None) 

349 min_poll_interval = utils.deltas_to_seconds(min_poll_interval, 

350 default=None) 

351 self._MINIMUM_UPDATE_INTERVAL = utils.deltas_to_seconds( 

352 self._MINIMUM_UPDATE_INTERVAL) 

353 

354 # Note that the _MINIMUM_UPDATE_INTERVAL sets the minimum in case of 

355 # low values. 

356 self.poll_interval = poll_interval 

357 self.min_poll_interval = max( 

358 min_poll_interval or self._MINIMUM_UPDATE_INTERVAL, 

359 self._MINIMUM_UPDATE_INTERVAL, 

360 float(os.environ.get('PROGRESSBAR_MINIMUM_UPDATE_INTERVAL', 0)), 

361 ) 

362 

363 # A dictionary of names that can be used by Variable and FormatWidget 

364 self.variables = utils.AttributeDict(variables or {}) 

365 for widget in (self.widgets or []): 

366 if isinstance(widget, widgets_module.VariableMixin): 

367 if widget.name not in self.variables: 

368 self.variables[widget.name] = None 

369 

370 @property 

371 def dynamic_messages(self): # pragma: no cover 

372 return self.variables 

373 

374 @dynamic_messages.setter 

375 def dynamic_messages(self, value): # pragma: no cover 

376 self.variables = value 

377 

378 def init(self): 

379 ''' 

380 (re)initialize values to original state so the progressbar can be 

381 used (again) 

382 ''' 

383 self.previous_value = None 

384 self.last_update_time = None 

385 self.start_time = None 

386 self.updates = 0 

387 self.end_time = None 

388 self.extra = dict() 

389 self._last_update_timer = timeit.default_timer() 

390 

391 @property 

392 def percentage(self): 

393 '''Return current percentage, returns None if no max_value is given 

394 

395 >>> progress = ProgressBar() 

396 >>> progress.max_value = 10 

397 >>> progress.min_value = 0 

398 >>> progress.value = 0 

399 >>> progress.percentage 

400 0.0 

401 >>> 

402 >>> progress.value = 1 

403 >>> progress.percentage 

404 10.0 

405 >>> progress.value = 10 

406 >>> progress.percentage 

407 100.0 

408 >>> progress.min_value = -10 

409 >>> progress.percentage 

410 100.0 

411 >>> progress.value = 0 

412 >>> progress.percentage 

413 50.0 

414 >>> progress.value = 5 

415 >>> progress.percentage 

416 75.0 

417 >>> progress.value = -5 

418 >>> progress.percentage 

419 25.0 

420 >>> progress.max_value = None 

421 >>> progress.percentage 

422 ''' 

423 if self.max_value is None or self.max_value is base.UnknownLength: 

424 return None 

425 elif self.max_value: 

426 todo = self.value - self.min_value 

427 total = self.max_value - self.min_value 

428 percentage = 100.0 * todo / total 

429 else: 

430 percentage = 100.0 

431 

432 return percentage 

433 

434 def get_last_update_time(self): 

435 if self._last_update_time: 

436 return datetime.fromtimestamp(self._last_update_time) 

437 

438 def set_last_update_time(self, value): 

439 if value: 

440 self._last_update_time = time.mktime(value.timetuple()) 

441 else: 

442 self._last_update_time = None 

443 

444 last_update_time = property(get_last_update_time, set_last_update_time) 

445 

446 def data(self): 

447 ''' 

448 

449 Returns: 

450 dict: 

451 - `max_value`: The maximum value (can be None with 

452 iterators) 

453 - `start_time`: Start time of the widget 

454 - `last_update_time`: Last update time of the widget 

455 - `end_time`: End time of the widget 

456 - `value`: The current value 

457 - `previous_value`: The previous value 

458 - `updates`: The total update count 

459 - `total_seconds_elapsed`: The seconds since the bar started 

460 - `seconds_elapsed`: The seconds since the bar started modulo 

461 60 

462 - `minutes_elapsed`: The minutes since the bar started modulo 

463 60 

464 - `hours_elapsed`: The hours since the bar started modulo 24 

465 - `days_elapsed`: The hours since the bar started 

466 - `time_elapsed`: The raw elapsed `datetime.timedelta` object 

467 - `percentage`: Percentage as a float or `None` if no max_value 

468 is available 

469 - `dynamic_messages`: Deprecated, use `variables` instead. 

470 - `variables`: Dictionary of user-defined variables for the 

471 :py:class:`~progressbar.widgets.Variable`'s 

472 

473 ''' 

474 self._last_update_time = time.time() 

475 self._last_update_timer = timeit.default_timer() 

476 elapsed = self.last_update_time - self.start_time 

477 # For Python 2.7 and higher we have _`timedelta.total_seconds`, but we 

478 # want to support older versions as well 

479 total_seconds_elapsed = utils.deltas_to_seconds(elapsed) 

480 return dict( 

481 # The maximum value (can be None with iterators) 

482 max_value=self.max_value, 

483 # Start time of the widget 

484 start_time=self.start_time, 

485 # Last update time of the widget 

486 last_update_time=self.last_update_time, 

487 # End time of the widget 

488 end_time=self.end_time, 

489 # The current value 

490 value=self.value, 

491 # The previous value 

492 previous_value=self.previous_value, 

493 # The total update count 

494 updates=self.updates, 

495 # The seconds since the bar started 

496 total_seconds_elapsed=total_seconds_elapsed, 

497 # The seconds since the bar started modulo 60 

498 seconds_elapsed=(elapsed.seconds % 60) 

499 + (elapsed.microseconds / 1000000.), 

500 # The minutes since the bar started modulo 60 

501 minutes_elapsed=(elapsed.seconds / 60) % 60, 

502 # The hours since the bar started modulo 24 

503 hours_elapsed=(elapsed.seconds / (60 * 60)) % 24, 

504 # The hours since the bar started 

505 days_elapsed=(elapsed.seconds / (60 * 60 * 24)), 

506 # The raw elapsed `datetime.timedelta` object 

507 time_elapsed=elapsed, 

508 # Percentage as a float or `None` if no max_value is available 

509 percentage=self.percentage, 

510 # Dictionary of user-defined 

511 # :py:class:`progressbar.widgets.Variable`'s 

512 variables=self.variables, 

513 # Deprecated alias for `variables` 

514 dynamic_messages=self.variables, 

515 ) 

516 

517 def default_widgets(self): 

518 if self.max_value: 

519 return [ 

520 widgets.Percentage(**self.widget_kwargs), 

521 ' ', widgets.SimpleProgress( 

522 format='(%s)' % widgets.SimpleProgress.DEFAULT_FORMAT, 

523 **self.widget_kwargs), 

524 ' ', widgets.Bar(**self.widget_kwargs), 

525 ' ', widgets.Timer(**self.widget_kwargs), 

526 ' ', widgets.AdaptiveETA(**self.widget_kwargs), 

527 ] 

528 else: 

529 return [ 

530 widgets.AnimatedMarker(**self.widget_kwargs), 

531 ' ', widgets.BouncingBar(**self.widget_kwargs), 

532 ' ', widgets.Counter(**self.widget_kwargs), 

533 ' ', widgets.Timer(**self.widget_kwargs), 

534 ] 

535 

536 def __call__(self, iterable, max_value=None): 

537 'Use a ProgressBar to iterate through an iterable' 

538 if max_value is not None: 

539 self.max_value = max_value 

540 elif self.max_value is None: 

541 try: 

542 self.max_value = len(iterable) 

543 except TypeError: # pragma: no cover 

544 self.max_value = base.UnknownLength 

545 

546 self._iterable = iter(iterable) 

547 return self 

548 

549 def __iter__(self): 

550 return self 

551 

552 def __next__(self): 

553 try: 

554 value = next(self._iterable) 

555 if self.start_time is None: 

556 self.start() 

557 else: 

558 self.update(self.value + 1) 

559 return value 

560 except StopIteration: 

561 self.finish() 

562 raise 

563 except GeneratorExit: # pragma: no cover 

564 self.finish(dirty=True) 

565 raise 

566 

567 def __exit__(self, exc_type, exc_value, traceback): 

568 self.finish(dirty=bool(exc_type)) 

569 

570 def __enter__(self): 

571 return self 

572 

573 # Create an alias so that Python 2.x won't complain about not being 

574 # an iterator. 

575 next = __next__ 

576 

577 def __iadd__(self, value): 

578 'Updates the ProgressBar by adding a new value.' 

579 return self.increment(value) 

580 

581 def increment(self, value, *args, **kwargs): 

582 self.update(self.value + value, *args, **kwargs) 

583 return self 

584 

585 def _format_widgets(self): 

586 result = [] 

587 expanding = [] 

588 width = self.term_width 

589 data = self.data() 

590 

591 for index, widget in enumerate(self.widgets): 

592 if isinstance(widget, widgets.WidgetBase) \ 

593 and not widget.check_size(self): 

594 continue 

595 elif isinstance(widget, widgets.AutoWidthWidgetBase): 

596 result.append(widget) 

597 expanding.insert(0, index) 

598 elif isinstance(widget, str): 

599 result.append(widget) 

600 width -= self.custom_len(widget) 

601 else: 

602 widget_output = converters.to_unicode(widget(self, data)) 

603 result.append(widget_output) 

604 width -= self.custom_len(widget_output) 

605 

606 count = len(expanding) 

607 while expanding: 

608 portion = max(int(math.ceil(width * 1. / count)), 0) 

609 index = expanding.pop() 

610 widget = result[index] 

611 count -= 1 

612 

613 widget_output = widget(self, data, portion) 

614 width -= self.custom_len(widget_output) 

615 result[index] = widget_output 

616 

617 return result 

618 

619 @classmethod 

620 def _to_unicode(cls, args): 

621 for arg in args: 

622 yield converters.to_unicode(arg) 

623 

624 def _format_line(self): 

625 'Joins the widgets and justifies the line' 

626 

627 widgets = ''.join(self._to_unicode(self._format_widgets())) 

628 

629 if self.left_justify: 

630 return widgets.ljust(self.term_width) 

631 else: 

632 return widgets.rjust(self.term_width) 

633 

634 def _needs_update(self): 

635 'Returns whether the ProgressBar should redraw the line.' 

636 delta = timeit.default_timer() - self._last_update_timer 

637 if delta < self.min_poll_interval: 

638 # Prevent updating too often 

639 return False 

640 elif self.poll_interval and delta > self.poll_interval: 

641 # Needs to redraw timers and animations 

642 return True 

643 

644 # Update if value increment is not large enough to 

645 # add more bars to progressbar (according to current 

646 # terminal width) 

647 try: 

648 divisor = self.max_value / self.term_width # float division 

649 if self.value // divisor != self.previous_value // divisor: 

650 return True 

651 except Exception: 

652 # ignore any division errors 

653 pass 

654 

655 # No need to redraw yet 

656 return False 

657 

658 def update(self, value=None, force=False, **kwargs): 

659 'Updates the ProgressBar to a new value.' 

660 if self.start_time is None: 

661 self.start() 

662 return self.update(value, force=force, **kwargs) 

663 

664 if value is not None and value is not base.UnknownLength: 

665 if self.max_value is base.UnknownLength: 

666 # Can't compare against unknown lengths so just update 

667 pass 

668 elif self.min_value <= value <= self.max_value: # pragma: no cover 

669 # Correct value, let's accept 

670 pass 

671 elif self.max_error: 

672 raise ValueError( 

673 'Value %s is out of range, should be between %s and %s' 

674 % (value, self.min_value, self.max_value)) 

675 else: 

676 self.max_value = value 

677 

678 self.previous_value = self.value 

679 self.value = value 

680 

681 # Save the updated values for dynamic messages 

682 variables_changed = False 

683 for key in kwargs: 

684 if key not in self.variables: 

685 raise TypeError( 

686 'update() got an unexpected keyword ' + 

687 'argument {0!r}'.format(key)) 

688 elif self.variables[key] != kwargs[key]: 

689 self.variables[key] = kwargs[key] 

690 variables_changed = True 

691 

692 if self._needs_update() or variables_changed or force: 

693 self.updates += 1 

694 ResizableMixin.update(self, value=value) 

695 ProgressBarBase.update(self, value=value) 

696 StdRedirectMixin.update(self, value=value) 

697 

698 # Only flush if something was actually written 

699 self.fd.flush() 

700 

701 def start(self, max_value=None, init=True): 

702 '''Starts measuring time, and prints the bar at 0%. 

703 

704 It returns self so you can use it like this: 

705 

706 Args: 

707 max_value (int): The maximum value of the progressbar 

708 reinit (bool): Initialize the progressbar, this is useful if you 

709 wish to reuse the same progressbar but can be disabled if 

710 data needs to be passed along to the next run 

711 

712 >>> pbar = ProgressBar().start() 

713 >>> for i in range(100): 

714 ... # do something 

715 ... pbar.update(i+1) 

716 ... 

717 >>> pbar.finish() 

718 ''' 

719 if init: 

720 self.init() 

721 

722 # Prevent multiple starts 

723 if self.start_time is not None: # pragma: no cover 

724 return self 

725 

726 if max_value is not None: 

727 self.max_value = max_value 

728 

729 if self.max_value is None: 

730 self.max_value = self._DEFAULT_MAXVAL 

731 

732 StdRedirectMixin.start(self, max_value=max_value) 

733 ResizableMixin.start(self, max_value=max_value) 

734 ProgressBarBase.start(self, max_value=max_value) 

735 

736 # Constructing the default widgets is only done when we know max_value 

737 if self.widgets is None: 

738 self.widgets = self.default_widgets() 

739 

740 if self.prefix: 

741 self.widgets.insert(0, widgets.FormatLabel( 

742 self.prefix, new_style=True)) 

743 # Unset the prefix variable after applying so an extra start() 

744 # won't keep copying it 

745 self.prefix = None 

746 

747 if self.suffix: 

748 self.widgets.append(widgets.FormatLabel( 

749 self.suffix, new_style=True)) 

750 # Unset the suffix variable after applying so an extra start() 

751 # won't keep copying it 

752 self.suffix = None 

753 

754 for widget in self.widgets: 

755 interval = getattr(widget, 'INTERVAL', None) 

756 if interval is not None: 

757 interval = utils.deltas_to_seconds(interval) 

758 

759 self.poll_interval = min( 

760 self.poll_interval or interval, 

761 interval, 

762 ) 

763 

764 self.num_intervals = max(100, self.term_width) 

765 # The `next_update` is kept for compatibility with external libs: 

766 # https://github.com/WoLpH/python-progressbar/issues/207 

767 self.next_update = 0 

768 

769 if self.max_value is not base.UnknownLength and self.max_value < 0: 

770 raise ValueError('max_value out of range, got %r' % self.max_value) 

771 

772 now = datetime.now() 

773 self.start_time = self.initial_start_time or now 

774 self.last_update_time = now 

775 self._last_update_timer = timeit.default_timer() 

776 self.update(self.min_value, force=True) 

777 

778 return self 

779 

780 def finish(self, end='\n', dirty=False): 

781 ''' 

782 Puts the ProgressBar bar in the finished state. 

783 

784 Also flushes and disables output buffering if this was the last 

785 progressbar running. 

786 

787 Args: 

788 end (str): The string to end the progressbar with, defaults to a 

789 newline 

790 dirty (bool): When True the progressbar kept the current state and 

791 won't be set to 100 percent 

792 ''' 

793 

794 if not dirty: 

795 self.end_time = datetime.now() 

796 self.update(self.max_value, force=True) 

797 

798 StdRedirectMixin.finish(self, end=end) 

799 ResizableMixin.finish(self) 

800 ProgressBarBase.finish(self) 

801 

802 @property 

803 def currval(self): 

804 ''' 

805 Legacy method to make progressbar-2 compatible with the original 

806 progressbar package 

807 ''' 

808 warnings.warn('The usage of `currval` is deprecated, please use ' 

809 '`value` instead', DeprecationWarning) 

810 return self.value 

811 

812 

813class DataTransferBar(ProgressBar): 

814 '''A progress bar with sensible defaults for downloads etc. 

815 

816 This assumes that the values its given are numbers of bytes. 

817 ''' 

818 

819 def default_widgets(self): 

820 if self.max_value: 

821 return [ 

822 widgets.Percentage(), 

823 ' of ', widgets.DataSize('max_value'), 

824 ' ', widgets.Bar(), 

825 ' ', widgets.Timer(), 

826 ' ', widgets.AdaptiveETA(), 

827 ] 

828 else: 

829 return [ 

830 widgets.AnimatedMarker(), 

831 ' ', widgets.DataSize(), 

832 ' ', widgets.Timer(), 

833 ] 

834 

835 

836class NullBar(ProgressBar): 

837 ''' 

838 Progress bar that does absolutely nothing. Useful for single verbosity 

839 flags 

840 ''' 

841 

842 def start(self, *args, **kwargs): 

843 return self 

844 

845 def update(self, *args, **kwargs): 

846 return self 

847 

848 def finish(self, *args, **kwargs): 

849 return self