Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9b6dba1

Browse files
committed
feature #46242 [Console] Add support for resuming a ProgressBar (yivi)
This PR was squashed before being merged into the 6.2 branch. Discussion ---------- [Console] Add support for resuming a ProgressBar Add an `(int) $resumeAt` argument for `ProgressBar#start()`, so that the progress bar is initialized at a specific progress level, with a new property so that `getEstimated()` and `getRemaining()` report the right number. | Q | A | ------------- | --- | Branch? | 6.1 | Bug fix? | no | New feature? | yes | Deprecations? | no | License | MIT As pointed out on a recently posted [SO question](https://stackoverflow.com/q/72097256/1426539)), when working on a longish task one may want to create a progress bar for a _resumed_ task. Calling `advance()` works to increase the progress bar counter, but the results of `getRemaining()` and `getEstimated()` will be "broken", since whatever steps one "skipped" will be considered to have taken 0 seconds. For a longer running task, the estimation will be **very** inaccurate, and will remain so for a long while. Using the example code from the linked question: ```php $itemCount = 1_000_000; $startItem = 150_000; $progressBar = new ProgressBar($output, $itemCount); $progressBar->setFormat( $progressBar->getFormatDefinition(ProgressBar::FORMAT_DEBUG) ); $progressBar->start(); if ($startItem !== 0) { // unfortunately taken into account in ETA calculation $progressBar->advance($startItem); } for ($i = $startItem; $i < $itemCount; $i++) { usleep(50_000); $progressBar->advance(); } ``` This will output this to begin with: ```none 150038/1000000 [====>-----------------------] 15% 2 secs/13 secs 20.0 MiB ``` The estimated time will keep growing as the tasks progresses, but the estimation won't be remotely useful for a long while. This PR adds a `$resumeAt` parameter to `ProgresBar#start()`, so that's possible to write: ```php $bar = new ProgressBar($output, 1_200_000, 0); $bar->start(null, 300_000); ``` And calls to `$bar->getEstimated()` and `$bar->getRemaining()` will return sane results (and calling the initial `->advance()` would be no longer necessary). ### TODO: - [ ] submit changes to the documentation (if the PR is well received) Commits ------- 8e1596e [Console] Add support for resuming a ProgressBar
2 parents 7f7d839 + 8e1596e commit 9b6dba1

File tree

3 files changed

+76
-7
lines changed

3 files changed

+76
-7
lines changed

src/Symfony/Component/Console/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ CHANGELOG
1414
* Add method `__toString()` to `InputInterface`
1515
* Deprecate `Command::$defaultName` and `Command::$defaultDescription`, use the `AsCommand` attribute instead
1616
* Add suggested values for arguments and options in input definition, for input completion
17+
* Add `$resumeAt` parameter to `ProgressBar#start()`, so that one can easily 'resume' progress on longer tasks, and still get accurate `getEstimate()` and `getRemaining()` results.
1718

1819
6.0
1920
---

src/Symfony/Component/Console/Helper/ProgressBar.php

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ final class ProgressBar
4949
private float $maxSecondsBetweenRedraws = 1;
5050
private OutputInterface $output;
5151
private int $step = 0;
52+
private int $resumedStep = 0;
5253
private ?int $max = null;
5354
private int $startTime;
5455
private int $stepWidth;
@@ -199,11 +200,11 @@ public function getBarOffset(): float
199200

200201
public function getEstimated(): float
201202
{
202-
if (!$this->step) {
203+
if (0 === $this->step || $this->step === $this->resumedStep) {
203204
return 0;
204205
}
205206

206-
return round((time() - $this->startTime) / $this->step * $this->max);
207+
return round((time() - $this->startTime) / ($this->step - $this->resumedStep) * $this->max);
207208
}
208209

209210
public function getRemaining(): float
@@ -212,7 +213,7 @@ public function getRemaining(): float
212213
return 0;
213214
}
214215

215-
return round((time() - $this->startTime) / $this->step * ($this->max - $this->step));
216+
return round((time() - $this->startTime) / ($this->step - $this->resumedStep) * ($this->max - $this->step));
216217
}
217218

218219
public function setBarWidth(int $size)
@@ -302,13 +303,17 @@ public function iterate(iterable $iterable, int $max = null): iterable
302303
/**
303304
* Starts the progress output.
304305
*
305-
* @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged
306+
* @param int|null $max Number of steps to complete the bar (0 if indeterminate), null to leave unchanged
307+
* @param int $resumeAt when restarting a previously started progress, set on what step we are restarting so that time estimations are calculated correctly, and the
308+
* progress is automatically set to the appropriate step
306309
*/
307-
public function start(int $max = null)
310+
public function start(int $max = null, int $resumeAt = 0): void
308311
{
309312
$this->startTime = time();
310-
$this->step = 0;
311-
$this->percent = 0.0;
313+
$this->step = $resumeAt;
314+
$this->resumedStep = $resumeAt;
315+
316+
$resumeAt > 0 ? $this->setProgress($resumeAt) : $this->percent = 0.0;
312317

313318
if (null !== $max) {
314319
$this->setMaxSteps($max);

src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,69 @@ public function testAdvance()
6666
);
6767
}
6868

69+
public function testResumeNoMax()
70+
{
71+
$bar = new ProgressBar($output = $this->getOutputStream(), 0, 0);
72+
$bar->start(null, 15);
73+
$bar->advance();
74+
75+
rewind($output->getStream());
76+
77+
$this->assertEquals(
78+
' 15 [--------------->------------]'.
79+
$this->generateOutput(' 16 [---------------->-----------]'),
80+
stream_get_contents($output->getStream())
81+
);
82+
}
83+
84+
public function testResumeWithMax()
85+
{
86+
$bar = new ProgressBar($output = $this->getOutputStream(), 5000, 0);
87+
$bar->start(null, 1000);
88+
89+
rewind($output->getStream());
90+
91+
$this->assertEquals(
92+
' 1000/5000 [=====>----------------------] 20%',
93+
stream_get_contents($output->getStream())
94+
);
95+
}
96+
97+
public function testRegularTimeEstimation()
98+
{
99+
$bar = new ProgressBar($output = $this->getOutputStream(), 1_200, 0);
100+
$bar->start();
101+
102+
$bar->advance();
103+
$bar->advance();
104+
105+
sleep(1);
106+
107+
$this->assertEquals(
108+
600.0,
109+
$bar->getEstimated()
110+
);
111+
}
112+
113+
public function testResumedTimeEstimation()
114+
{
115+
$bar = new ProgressBar($output = $this->getOutputStream(), 1_200, 0);
116+
$bar->start(null, 599);
117+
$bar->advance();
118+
119+
sleep(1);
120+
121+
$this->assertEquals(
122+
1_200.0,
123+
$bar->getEstimated()
124+
);
125+
126+
$this->assertEquals(
127+
600.0,
128+
$bar->getRemaining()
129+
);
130+
}
131+
69132
public function testAdvanceWithStep()
70133
{
71134
$bar = new ProgressBar($output = $this->getOutputStream(), 0, 0);

0 commit comments

Comments
 (0)