Skip to content

Ensures the item is processed in the main process when given #145

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 6 commits into from
Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
- `::isValueRequiresQuoting()`
- `::fetchItems()` can now return an `iterable` instead of `array`
- `::getItemName()` can now take `null` for an unknown number of items
- Ensures that if an item is given, then the processing is done in the main
process. An item also cannot be passed to a child process (via the argument).


## New extension points
Expand Down
53 changes: 36 additions & 17 deletions src/Input/ParallelizationInput.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
use Symfony\Component\Console\Input\InputOption;
use Webmozart\Assert\Assert;
use Webmozarts\Console\Parallelization\CpuCoreCounter;
use function get_class;
use function gettype;
use function is_int;
use function is_numeric;
use function is_object;
use function sprintf;

final class ParallelizationInput
Expand Down Expand Up @@ -70,36 +72,30 @@ public static function fromInput(InputInterface $input): self
{
/** @var string|null $numberOfProcesses */
$numberOfProcesses = $input->getOption(self::PROCESSES_OPTION);
/** @var string|null $item */
/** @var mixed|null $item */
$item = $input->getArgument(self::ITEM_ARGUMENT);
$hasItem = null !== $item;
/** @var bool $mainProcess */
$mainProcess = $input->getOption(self::MAIN_PROCESS_OPTION);
/** @var bool $isChild */
$isChild = $input->getOption(self::CHILD_OPTION);

if ($hasItem) {
$item = self::validateItem($item);
// When there is a single item passed, we do not want to spawn
// child processes.
$mainProcess = true;
}

if ($mainProcess) {
// TODO: add this to the logger
$validatedNumberOfProcesses = 1;
} else {
$validatedNumberOfProcesses = null !== $numberOfProcesses
? self::coerceNumberOfProcesses($numberOfProcesses)
: static fn () => CpuCoreCounter::getNumberOfCpuCores();
}

$hasItem = null !== $item;

if ($hasItem && !is_numeric($item)) {
// Safeguard in case an invalid type is accidentally passed in tests when invoking the
// command directly
Assert::string(
$item,
sprintf(
'Invalid item type. Expected a string, got "%s".',
// TODO: change to get_debug_type() once dropping PHP 7.4
gettype($input),
),
);
}

if ($isChild) {
Assert::false(
$hasItem,
Expand All @@ -113,7 +109,7 @@ public static function fromInput(InputInterface $input): self
return new self(
$mainProcess,
$validatedNumberOfProcesses,
$hasItem ? (string) $item : null,
$hasItem ? $item : null,
$isChild,
);
}
Expand Down Expand Up @@ -178,6 +174,29 @@ public function isChildProcess(): bool
return $this->childProcess;
}

/**
* @param mixed $item
*/
private static function validateItem($item): string
{
if (is_numeric($item)) {
return (string) $item;
}

// Safeguard in case an invalid type is accidentally passed in tests when invoking the
// command directly
Assert::string(
$item,
sprintf(
'Invalid item type. Expected a string, got "%s".',
// TODO: use get_debug_type when dropping PHP 7.4 support
is_object($item) ? get_class($item) : gettype($item),
),
);

return $item;
}

/**
* @return positive-int
*/
Expand Down
2 changes: 1 addition & 1 deletion tests/Fixtures/Command/NoSubProcessCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ protected function runSingleCommand(string $item, InputInterface $input, OutputI

protected function getItemName(?int $count): string
{
return 0 === $count ? 'item' : 'items';
return 1 === $count ? 'item' : 'items';
}

protected function createLogger(OutputInterface $output): Logger
Expand Down
14 changes: 7 additions & 7 deletions tests/Input/ParallelizationInputTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function test_it_can_be_instantiated_from_an_input_with_an_invalid_item()
self::bindInput($input);

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid item type. Expected a string, got "object".');
$this->expectExceptionMessage('Invalid item type. Expected a string, got "stdClass".');

ParallelizationInput::fromInput($input);
}
Expand Down Expand Up @@ -196,8 +196,8 @@ public static function inputProvider(): iterable
yield 'item passed' => [
new StringInput('item15'),
new ParallelizationInput(
false,
$findNumberOfProcesses,
true,
1,
'item15',
false,
),
Expand All @@ -206,8 +206,8 @@ public static function inputProvider(): iterable
yield 'integer item passed' => [
new ArrayInput(['item' => 10]),
new ParallelizationInput(
false,
$findNumberOfProcesses,
true,
1,
'10',
false,
),
Expand All @@ -216,8 +216,8 @@ public static function inputProvider(): iterable
yield 'float item passed' => [
new ArrayInput(['item' => -.5]),
new ParallelizationInput(
false,
$findNumberOfProcesses,
true,
1,
'-0.5',
false,
),
Expand Down
30 changes: 30 additions & 0 deletions tests/Integration/ParallelizationIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,36 @@ public function test_it_can_run_the_command_without_sub_processes(): void
self::assertSame($expected, $actual, $actual);
}

public function test_it_processes_the_item_in_the_main_process_if_an_item_is_passed(): void
{
$commandTester = $this->noSubProcessCommandTester;

$commandTester->execute(
[
'command' => 'test:no-subprocess',
'item' => 'item0',
],
['interactive' => true],
);

// TODO: note that the "in 1 process is incorrect here..."
$expected = <<<'EOF'
Processing 1 item, batches of 2, 1 batch

0/1 [>---------------------------] 0% 10 secs/10 secs 10.0 MiB
1/1 [============================] 100% 10 secs/10 secs 10.0 MiB

Processed 1 item.

EOF;

$actual = self::normalizeIntermediateFixedProgressBars(
$this->getOutput($commandTester),
);

self::assertSame($expected, $actual, $actual);
}

public function test_it_uses_a_sub_process_if_only_one_process_is_used(): void
{
$commandTester = $this->noSubProcessCommandTester;
Expand Down