Repository: meltano/meltano

Issue:
bug: `--parallelism` flag not working in latest version of meltano
Version: `2.16.1`

```
aj@ajs-macbook-pro jaffle-shop-template % meltano --version
meltano, version 2.16.1
aj@ajs-macbook-pro jaffle-shop-template % pipx upgrade meltano
meltano is already at latest version 2.16.1 (location: /Users/aj/.local/pipx/venvs/meltano)
```

The `meltano install --help` command correctly advertises `--parallelism` and `-p`:

```console
aj@ajs-macbook-pro jaffle-shop-template % meltano install --help              
Usage: meltano install [OPTIONS] [[extractor|extractors|loader|loaders|transfo
                       rm|transforms|orchestrator|orchestrators|transformer|tr
                       ansformers|file|files|utility|utilities|mapper|mappers|
                       mapping|mappings]] [PLUGIN_NAME]...

      Install all the dependencies of your project based on the meltano.yml
      file.

      Read more at https://docs.meltano.com/reference/command-line-interface#install
          

Options:
  --clean                    Completely reinstall a plugin rather than simply
                             upgrading if necessary.
  -p, --parallelism INTEGER  Limit the number of plugins to install in
                             parallel. Defaults to the number of cores.
  -f, --force                Ignore the required Python version declared by
                             the plugins.
  --database-uri TEXT        System database URI.
  --help                     Show this message and exit.
```

Error seems to be: `'PluginInstallService' object has no attribute 'parallelism'`

```console
aj@ajs-macbook-pro jaffle-shop-template % meltano install --parallelism=1
Installing 5 plugins...
Need help fixing this problem? Visit http://melta.no/ for troubleshooting steps, or to
join our friendly Slack community.

'PluginInstallService' object has no attribute 'parallelism'

aj@ajs-macbook-pro jaffle-shop-template % meltano install -p1        
Installing 5 plugins...
Need help fixing this problem? Visit http://melta.no/ for troubleshooting steps, or to
join our friendly Slack community.

'PluginInstallService' object has no attribute 'parallelism'
```

If I spell it wrong, I get a different error:

```
aj@ajs-macbook-pro jaffle-shop-template % meltano install --p=1          
Usage: meltano install [OPTIONS] [[extractor|extractors|loader|loaders|transfo
                       rm|transforms|orchestrator|orchestrators|transformer|tr
                       ansformers|file|files|utility|utilities|mapper|mappers|
                       mapping|mappings]] [PLUGIN_NAME]...
Try 'meltano install --help' for help.

Error: No such option: --p Did you mean --help?
aj@ajs-macbook-pro jaffle-shop-template % meltano install --parall 1
Installing 5 plugins...
Need help fixing this problem? Visit http://melta.no/ for troubleshooting steps, or to
join our friendly Slack community.

'PluginInstallService' object has no attribute 'parallelism'


aj@ajs-macbook-pro jaffle-shop-template % meltano install --parallel 1
Usage: meltano install [OPTIONS] [[extractor|extractors|loader|loaders|transfo
                       rm|transforms|orchestrator|orchestrators|transformer|tr
                       ansformers|file|files|utility|utilities|mapper|mappers|
                       mapping|mappings]] [PLUGIN_NAME]...
Try 'meltano install --help' for help.

Error: No such option: --parallel Did you mean --parallelism?
```


So, this seems to be a regression related to the `PluginInstallService` or its connection with the CLI layer.


Discussion:

Resolving patch:
diff --git a/src/meltano/core/plugin_install_service.py b/src/meltano/core/plugin_install_service.py
--- a/src/meltano/core/plugin_install_service.py
+++ b/src/meltano/core/plugin_install_service.py
@@ -151,13 +151,23 @@ def __init__(
         """
         self.project = project
         self.status_cb = status_cb
-        if parallelism is None:
-            self.parallelism = cpu_count()
-        elif parallelism < 1:
-            self.parallelism = sys.maxsize
+        self._parallelism = parallelism
         self.clean = clean
         self.force = force
 
+    @cached_property
+    def parallelism(self) -> int:
+        """Return the number of parallel installation processes to use.
+
+        Returns:
+            The number of parallel installation processes to use.
+        """
+        if self._parallelism is None:
+            return cpu_count()
+        elif self._parallelism < 1:
+            return sys.maxsize
+        return self._parallelism
+
     @cached_property
     def semaphore(self):
         """An asyncio semaphore with a counter starting at `self.parallelism`.


Test patch:
diff --git a/tests/meltano/core/test_plugin_install_service.py b/tests/meltano/core/test_plugin_install_service.py
--- a/tests/meltano/core/test_plugin_install_service.py
+++ b/tests/meltano/core/test_plugin_install_service.py
@@ -10,8 +10,11 @@
 
 
 class TestPluginInstallService:
-    @pytest.fixture
-    def subject(self, project):
+    @pytest.fixture(
+        params=({}, {"parallelism": -1}, {"parallelism": 2}),
+        ids=("default", "-p=-1", "-p=2"),
+    )
+    def subject(self, project, request):
         with open(project.meltanofile, "w") as file:
             file.write(
                 yaml.dump(
@@ -38,7 +41,7 @@ def subject(self, project):
                 )
             )
         project.refresh()
-        return PluginInstallService(project)
+        return PluginInstallService(project, **request.param)
 
     def test_default_init_should_not_fail(self, subject):
         assert subject