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

326 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-10-18 12:46 +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 

30class ProgressBarMixinBase(object): 

31 

32 def __init__(self, **kwargs): 

33 self._finished = False 

34 

35 def start(self, **kwargs): 

36 pass 

37 

38 def update(self, value=None): 

39 pass 

40 

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

42 self._finished = True 

43 

44 def __del__(self): 

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

46 try: 

47 self.finish() 

48 except Exception: 

49 pass 

50 

51 

52class ProgressBarBase(abc.Iterable, ProgressBarMixinBase): 

53 pass 

54 

55 

56class DefaultFdMixin(ProgressBarMixinBase): 

57 

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

59 is_terminal: bool | None = None, 

60 line_breaks: bool | None = None, 

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

62 if fd is sys.stdout: 

63 fd = utils.streams.original_stdout 

64 

65 elif fd is sys.stderr: 

66 fd = utils.streams.original_stderr 

67 

68 self.fd = fd 

69 self.is_ansi_terminal = utils.is_ansi_terminal(fd) 

70 

71 # Check if this is an interactive terminal 

72 self.is_terminal = utils.is_terminal( 

73 fd, is_terminal or self.is_ansi_terminal) 

74 

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

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

77 if line_breaks is None: 

78 line_breaks = utils.env_flag('PROGRESSBAR_LINE_BREAKS', 

79 not self.is_terminal) 

80 self.line_breaks = line_breaks 

81 

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

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

84 if enable_colors is None: 

85 enable_colors = utils.env_flag('PROGRESSBAR_ENABLE_COLORS', 

86 self.is_ansi_terminal) 

87 

88 self.enable_colors = enable_colors 

89 

90 ProgressBarMixinBase.__init__(self, **kwargs) 

91 

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

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

94 

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

96 if not self.enable_colors: 

97 line = utils.no_color(line) 

98 

99 if self.line_breaks: 

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

101 else: 

102 line = '\r' + line 

103 

104 try: # pragma: no cover 

105 self.fd.write(line) 

106 except UnicodeEncodeError: # pragma: no cover 

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

108 

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

110 if self._finished: 

111 return 

112 

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

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

115 

116 if end and not self.line_breaks: 

117 self.fd.write(end) 

118 

119 self.fd.flush() 

120 

121 

122class ResizableMixin(ProgressBarMixinBase): 

123 

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

125 ProgressBarMixinBase.__init__(self, **kwargs) 

126 

127 self.signal_set = False 

128 if term_width: 

129 self.term_width = term_width 

130 else: # pragma: no cover 

131 try: 

132 self._handle_resize() 

133 import signal 

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

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

136 self.signal_set = True 

137 except Exception: 

138 pass 

139 

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

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

142 

143 w, h = utils.get_terminal_size() 

144 self.term_width = w 

145 

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

147 ProgressBarMixinBase.finish(self) 

148 if self.signal_set: 

149 try: 

150 import signal 

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

152 except Exception: # pragma no cover 

153 pass 

154 

155 

156class StdRedirectMixin(DefaultFdMixin): 

157 

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

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

160 DefaultFdMixin.__init__(self, **kwargs) 

161 self.redirect_stderr = redirect_stderr 

162 self.redirect_stdout = redirect_stdout 

163 self._stdout = self.stdout = sys.stdout 

164 self._stderr = self.stderr = sys.stderr 

165 

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

167 if self.redirect_stdout: 

168 utils.streams.wrap_stdout() 

169 

170 if self.redirect_stderr: 

171 utils.streams.wrap_stderr() 

172 

173 self._stdout = utils.streams.original_stdout 

174 self._stderr = utils.streams.original_stderr 

175 

176 self.stdout = utils.streams.stdout 

177 self.stderr = utils.streams.stderr 

178 

179 utils.streams.start_capturing(self) 

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

181 

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

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

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

185 

186 utils.streams.flush() 

187 DefaultFdMixin.update(self, value=value) 

188 

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

190 DefaultFdMixin.finish(self, end=end) 

191 utils.streams.stop_capturing(self) 

192 if self.redirect_stdout: 

193 utils.streams.unwrap_stdout() 

194 

195 if self.redirect_stderr: 

196 utils.streams.unwrap_stderr() 

197 

198 

199class ProgressBar(StdRedirectMixin, ResizableMixin, ProgressBarBase): 

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

201 

202 Args: 

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

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

205 Defaults to `_DEFAULT_MAXVAL` 

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

207 `default_widget()` 

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

209 `False` 

210 initial_value (int): The value to start with 

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

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

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

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

215 reduced output in logs 

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

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

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

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

220 environment variable PROGRESSBAR_MINIMUM_UPDATE_INTERVAL 

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

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

223 calculated. When using non-latin characters the width 

224 calculation might be off by default 

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

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

227 raised when needed 

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

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

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

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

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

233 used to set initial values for variables' widgets 

234 

235 A common way of using it is like: 

236 

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

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

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

240 ... # do something 

241 ... 

242 >>> progress.finish() 

243 

244 You can also use a ProgressBar as an iterator: 

245 

246 >>> progress = ProgressBar() 

247 >>> some_iterable = range(100) 

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

249 ... # do something 

250 ... pass 

251 ... 

252 

253 Since the progress bar is incredibly customizable you can specify 

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

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

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

257 widgets. 

258 

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

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

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

262 80 columns if the width cannot be determined. 

263 

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

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

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

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

268 

269 Useful methods and attributes include (Public API): 

270 - value: current progress (min_value <= value <= max_value) 

271 - max_value: maximum (and final) value 

272 - end_time: not None if the bar has finished (reached 100%) 

273 - start_time: the time when start() method of ProgressBar was called 

274 - seconds_elapsed: seconds elapsed since start_time and last call to 

275 update 

276 ''' 

277 

278 _DEFAULT_MAXVAL = base.UnknownLength 

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

280 _MINIMUM_UPDATE_INTERVAL = 0.050 

281 

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

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

284 widget_kwargs=None, custom_len=utils.len_color, 

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

286 min_poll_interval=None, **kwargs): 

287 ''' 

288 Initializes a progress bar with sane defaults 

289 ''' 

290 StdRedirectMixin.__init__(self, **kwargs) 

291 ResizableMixin.__init__(self, **kwargs) 

292 ProgressBarBase.__init__(self, **kwargs) 

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

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

295 '`max_value` instead', DeprecationWarning) 

296 max_value = kwargs.get('maxval') 

297 

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

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

300 '`poll_interval` instead', DeprecationWarning) 

301 poll_interval = kwargs.get('poll') 

302 

303 if max_value: 

304 if min_value > max_value: 

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

306 'value') 

307 self.min_value = min_value 

308 self.max_value = max_value 

309 self.max_error = max_error 

310 

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

312 # assume this to be true 

313 if widgets is None: 

314 self.widgets = widgets 

315 else: 

316 self.widgets = [] 

317 for widget in widgets: 

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

319 widget = deepcopy(widget) 

320 self.widgets.append(widget) 

321 

322 self.widgets = widgets 

323 self.prefix = prefix 

324 self.suffix = suffix 

325 self.widget_kwargs = widget_kwargs or {} 

326 self.left_justify = left_justify 

327 self.value = initial_value 

328 self._iterable = None 

329 self.custom_len = custom_len 

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

331 self.init() 

332 

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

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

335 # 1. Backwards compatibility (most important one) 

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

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

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

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

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

341 min_poll_interval = utils.deltas_to_seconds(min_poll_interval, 

342 default=None) 

343 self._MINIMUM_UPDATE_INTERVAL = utils.deltas_to_seconds( 

344 self._MINIMUM_UPDATE_INTERVAL) 

345 

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

347 # low values. 

348 self.poll_interval = poll_interval 

349 self.min_poll_interval = max( 

350 min_poll_interval or self._MINIMUM_UPDATE_INTERVAL, 

351 self._MINIMUM_UPDATE_INTERVAL, 

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

353 ) 

354 

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

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

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

358 if isinstance(widget, widgets_module.VariableMixin): 

359 if widget.name not in self.variables: 

360 self.variables[widget.name] = None 

361 

362 @property 

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

364 return self.variables 

365 

366 @dynamic_messages.setter 

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

368 self.variables = value 

369 

370 def init(self): 

371 ''' 

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

373 used (again) 

374 ''' 

375 self.previous_value = None 

376 self.last_update_time = None 

377 self.start_time = None 

378 self.updates = 0 

379 self.end_time = None 

380 self.extra = dict() 

381 self._last_update_timer = timeit.default_timer() 

382 

383 @property 

384 def percentage(self): 

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

386 

387 >>> progress = ProgressBar() 

388 >>> progress.max_value = 10 

389 >>> progress.min_value = 0 

390 >>> progress.value = 0 

391 >>> progress.percentage 

392 0.0 

393 >>> 

394 >>> progress.value = 1 

395 >>> progress.percentage 

396 10.0 

397 >>> progress.value = 10 

398 >>> progress.percentage 

399 100.0 

400 >>> progress.min_value = -10 

401 >>> progress.percentage 

402 100.0 

403 >>> progress.value = 0 

404 >>> progress.percentage 

405 50.0 

406 >>> progress.value = 5 

407 >>> progress.percentage 

408 75.0 

409 >>> progress.value = -5 

410 >>> progress.percentage 

411 25.0 

412 >>> progress.max_value = None 

413 >>> progress.percentage 

414 ''' 

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

416 return None 

417 elif self.max_value: 

418 todo = self.value - self.min_value 

419 total = self.max_value - self.min_value 

420 percentage = 100.0 * todo / total 

421 else: 

422 percentage = 100.0 

423 

424 return percentage 

425 

426 def get_last_update_time(self): 

427 if self._last_update_time: 

428 return datetime.fromtimestamp(self._last_update_time) 

429 

430 def set_last_update_time(self, value): 

431 if value: 

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

433 else: 

434 self._last_update_time = None 

435 

436 last_update_time = property(get_last_update_time, set_last_update_time) 

437 

438 def data(self): 

439 ''' 

440 

441 Returns: 

442 dict: 

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

444 iterators) 

445 - `start_time`: Start time of the widget 

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

447 - `end_time`: End time of the widget 

448 - `value`: The current value 

449 - `previous_value`: The previous value 

450 - `updates`: The total update count 

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

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

453 60 

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

455 60 

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

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

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

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

460 is available 

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

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

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

464 

465 ''' 

466 self._last_update_time = time.time() 

467 self._last_update_timer = timeit.default_timer() 

468 elapsed = self.last_update_time - self.start_time 

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

470 # want to support older versions as well 

471 total_seconds_elapsed = utils.deltas_to_seconds(elapsed) 

472 return dict( 

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

474 max_value=self.max_value, 

475 # Start time of the widget 

476 start_time=self.start_time, 

477 # Last update time of the widget 

478 last_update_time=self.last_update_time, 

479 # End time of the widget 

480 end_time=self.end_time, 

481 # The current value 

482 value=self.value, 

483 # The previous value 

484 previous_value=self.previous_value, 

485 # The total update count 

486 updates=self.updates, 

487 # The seconds since the bar started 

488 total_seconds_elapsed=total_seconds_elapsed, 

489 # The seconds since the bar started modulo 60 

490 seconds_elapsed=(elapsed.seconds % 60) 

491 + (elapsed.microseconds / 1000000.), 

492 # The minutes since the bar started modulo 60 

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

494 # The hours since the bar started modulo 24 

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

496 # The hours since the bar started 

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

498 # The raw elapsed `datetime.timedelta` object 

499 time_elapsed=elapsed, 

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

501 percentage=self.percentage, 

502 # Dictionary of user-defined 

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

504 variables=self.variables, 

505 # Deprecated alias for `variables` 

506 dynamic_messages=self.variables, 

507 ) 

508 

509 def default_widgets(self): 

510 if self.max_value: 

511 return [ 

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

513 ' ', widgets.SimpleProgress( 

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

515 **self.widget_kwargs), 

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

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

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

519 ] 

520 else: 

521 return [ 

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

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

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

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

526 ] 

527 

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

529 'Use a ProgressBar to iterate through an iterable' 

530 if max_value is not None: 

531 self.max_value = max_value 

532 elif self.max_value is None: 

533 try: 

534 self.max_value = len(iterable) 

535 except TypeError: # pragma: no cover 

536 self.max_value = base.UnknownLength 

537 

538 self._iterable = iter(iterable) 

539 return self 

540 

541 def __iter__(self): 

542 return self 

543 

544 def __next__(self): 

545 try: 

546 value = next(self._iterable) 

547 if self.start_time is None: 

548 self.start() 

549 else: 

550 self.update(self.value + 1) 

551 return value 

552 except StopIteration: 

553 self.finish() 

554 raise 

555 except GeneratorExit: # pragma: no cover 

556 self.finish(dirty=True) 

557 raise 

558 

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

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

561 

562 def __enter__(self): 

563 return self 

564 

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

566 # an iterator. 

567 next = __next__ 

568 

569 def __iadd__(self, value): 

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

571 self.update(self.value + value) 

572 return self 

573 

574 def _format_widgets(self): 

575 result = [] 

576 expanding = [] 

577 width = self.term_width 

578 data = self.data() 

579 

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

581 if isinstance(widget, widgets.WidgetBase) \ 

582 and not widget.check_size(self): 

583 continue 

584 elif isinstance(widget, widgets.AutoWidthWidgetBase): 

585 result.append(widget) 

586 expanding.insert(0, index) 

587 elif isinstance(widget, str): 

588 result.append(widget) 

589 width -= self.custom_len(widget) 

590 else: 

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

592 result.append(widget_output) 

593 width -= self.custom_len(widget_output) 

594 

595 count = len(expanding) 

596 while expanding: 

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

598 index = expanding.pop() 

599 widget = result[index] 

600 count -= 1 

601 

602 widget_output = widget(self, data, portion) 

603 width -= self.custom_len(widget_output) 

604 result[index] = widget_output 

605 

606 return result 

607 

608 @classmethod 

609 def _to_unicode(cls, args): 

610 for arg in args: 

611 yield converters.to_unicode(arg) 

612 

613 def _format_line(self): 

614 'Joins the widgets and justifies the line' 

615 

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

617 

618 if self.left_justify: 

619 return widgets.ljust(self.term_width) 

620 else: 

621 return widgets.rjust(self.term_width) 

622 

623 def _needs_update(self): 

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

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

626 if delta < self.min_poll_interval: 

627 # Prevent updating too often 

628 return False 

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

630 # Needs to redraw timers and animations 

631 return True 

632 

633 # Update if value increment is not large enough to 

634 # add more bars to progressbar (according to current 

635 # terminal width) 

636 try: 

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

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

639 return True 

640 except Exception: 

641 # ignore any division errors 

642 pass 

643 

644 # No need to redraw yet 

645 return False 

646 

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

648 'Updates the ProgressBar to a new value.' 

649 if self.start_time is None: 

650 self.start() 

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

652 

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

654 if self.max_value is base.UnknownLength: 

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

656 pass 

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

658 # Correct value, let's accept 

659 pass 

660 elif self.max_error: 

661 raise ValueError( 

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

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

664 else: 

665 self.max_value = value 

666 

667 self.previous_value = self.value 

668 self.value = value 

669 

670 # Save the updated values for dynamic messages 

671 variables_changed = False 

672 for key in kwargs: 

673 if key not in self.variables: 

674 raise TypeError( 

675 'update() got an unexpected keyword ' + 

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

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

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

679 variables_changed = True 

680 

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

682 self.updates += 1 

683 ResizableMixin.update(self, value=value) 

684 ProgressBarBase.update(self, value=value) 

685 StdRedirectMixin.update(self, value=value) 

686 

687 # Only flush if something was actually written 

688 self.fd.flush() 

689 

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

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

692 

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

694 

695 Args: 

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

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

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

699 data needs to be passed along to the next run 

700 

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

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

703 ... # do something 

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

705 ... 

706 >>> pbar.finish() 

707 ''' 

708 if init: 

709 self.init() 

710 

711 # Prevent multiple starts 

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

713 return self 

714 

715 if max_value is not None: 

716 self.max_value = max_value 

717 

718 if self.max_value is None: 

719 self.max_value = self._DEFAULT_MAXVAL 

720 

721 StdRedirectMixin.start(self, max_value=max_value) 

722 ResizableMixin.start(self, max_value=max_value) 

723 ProgressBarBase.start(self, max_value=max_value) 

724 

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

726 if self.widgets is None: 

727 self.widgets = self.default_widgets() 

728 

729 if self.prefix: 

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

731 self.prefix, new_style=True)) 

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

733 # won't keep copying it 

734 self.prefix = None 

735 

736 if self.suffix: 

737 self.widgets.append(widgets.FormatLabel( 

738 self.suffix, new_style=True)) 

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

740 # won't keep copying it 

741 self.suffix = None 

742 

743 for widget in self.widgets: 

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

745 if interval is not None: 

746 interval = utils.deltas_to_seconds(interval) 

747 

748 self.poll_interval = min( 

749 self.poll_interval or interval, 

750 interval, 

751 ) 

752 

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

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

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

756 self.next_update = 0 

757 

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

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

760 

761 now = datetime.now() 

762 self.start_time = self.initial_start_time or now 

763 self.last_update_time = now 

764 self._last_update_timer = timeit.default_timer() 

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

766 

767 return self 

768 

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

770 ''' 

771 Puts the ProgressBar bar in the finished state. 

772 

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

774 progressbar running. 

775 

776 Args: 

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

778 newline 

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

780 won't be set to 100 percent 

781 ''' 

782 

783 if not dirty: 

784 self.end_time = datetime.now() 

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

786 

787 StdRedirectMixin.finish(self, end=end) 

788 ResizableMixin.finish(self) 

789 ProgressBarBase.finish(self) 

790 

791 

792class DataTransferBar(ProgressBar): 

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

794 

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

796 ''' 

797 

798 def default_widgets(self): 

799 if self.max_value: 

800 return [ 

801 widgets.Percentage(), 

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

803 ' ', widgets.Bar(), 

804 ' ', widgets.Timer(), 

805 ' ', widgets.AdaptiveETA(), 

806 ] 

807 else: 

808 return [ 

809 widgets.AnimatedMarker(), 

810 ' ', widgets.DataSize(), 

811 ' ', widgets.Timer(), 

812 ] 

813 

814 

815class NullBar(ProgressBar): 

816 ''' 

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

818 flags 

819 ''' 

820 

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

822 return self 

823 

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

825 return self 

826 

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

828 return self