Repository: Lightning-AI/pytorch-lightning
Pull request: [CLI] Support shorthand for loggers
## What does this PR do?

Fixes https://github.com/PyTorchLightning/pytorch-lightning/issues/10574

### Does your PR introduce any breaking changes? If yes, please list them.

Previous calls with `--trainer.logger=pytorch_lightning.loggers.WandbLogger` will need to update to `--trainer.logger=WandbLogger`

## Before submitting

- [x] Was this **discussed/approved** via a GitHub issue? (not for typos and docs)
- [x] Did you read the [contributor guideline](https://github.com/PyTorchLightning/pytorch-lightning/blob/master/.github/CONTRIBUTING.md), **Pull Request** section?
- [x] Did you make sure your **PR does only one thing**, instead of bundling different changes together?
- [ ] Did you make sure to **update the documentation** with your changes? (if necessary)
- [x] Did you write any **new necessary tests**? (not for typos and docs)
- [x] Did you verify new and **existing tests pass** locally with your changes?
- [x] Did you list all the **breaking changes** introduced by this pull request?
- [x] Did you **update the [CHANGELOG](https://github.com/PyTorchLightning/pytorch-lightning/blob/master/CHANGELOG.md)**? (not for typos, docs, test updates, or internal minor changes/refactorings)

## PR review

- [x] Is this pull request ready for review? (if not, please submit in draft mode)
- [x] Check that all items from **Before submitting** are resolved
- [x] Make sure the title is self-explanatory and the description concisely explains the PR
- [x] Add labels and milestones (and optionally projects) to the PR so it can be classified

cc @borda @awaelchli @edward-io @ananthsub @rohitgr7 @kamil-kaczmarek @Raalsky @Blaizzy @carmocca @mauvilsa

Problem statement:
Add ability to pass logger to LightningCLI
## 🚀 Feature

```
python trainer. py fit --trainer.logger=WandbLogger
```

### Motivation

If I use LightningCLI, I don't want to abandon my favourite logger, WandbLogger 😏. With the new way to reference class names, this would be very nice.

### Pitch

Allow users to pass their favourite logger via the command line (and in a config) using LightningCLI like 

```
python trainer. py fit --trainer.logger=WandbLogger
```

______________________________________________________________________

cc @borda @awaelchli @edward-io @ananthsub @rohitgr7 @kamil-kaczmarek @Raalsky @Blaizzy @carmocca @mauvilsa

Hints from issue discussion:
This issue has been automatically marked as stale because it hasn't had any recent activity. This issue will be closed in 7 days if no further activity occurs. Thank you for your contributions, Pytorch Lightning Team!

This issue has been automatically marked as stale because it hasn't had any recent activity. This issue will be closed in 7 days if no further activity occurs. Thank you for your contributions, Pytorch Lightning Team!

Commit message: whitespace

Patch to review:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b203856de1ec4..679e0c6976e70 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -60,6 +60,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
 - Added `console_kwargs` for `RichProgressBar` to initialize inner Console ([#10875](https://github.com/PyTorchLightning/pytorch-lightning/pull/10875))
 
 
+- Added support for shorthand notation to instantiate loggers with the `LightningCLI` ([#11533](https://github.com/PyTorchLightning/pytorch-lightning/pull/11533))
+
+
 - Added a `PrecisionPlugin.teardown` method ([#10990](https://github.com/PyTorchLightning/pytorch-lightning/pull/10990))
 
 
diff --git a/pytorch_lightning/utilities/cli.py b/pytorch_lightning/utilities/cli.py
index 6466fb95674eb..05b7b1d4fd39b 100644
--- a/pytorch_lightning/utilities/cli.py
+++ b/pytorch_lightning/utilities/cli.py
@@ -18,7 +18,7 @@
 import sys
 from functools import partial, update_wrapper
 from types import MethodType, ModuleType
-from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union
+from typing import Any, Callable, Dict, Generator, List, Optional, Set, Tuple, Type, Union
 from unittest import mock
 
 import torch
@@ -64,9 +64,16 @@ def __call__(self, cls: Type, key: Optional[str] = None, override: bool = False)
 
     def register_classes(self, module: ModuleType, base_cls: Type, override: bool = False) -> None:
         """This function is an utility to register all classes from a module."""
-        for _, cls in inspect.getmembers(module, predicate=inspect.isclass):
-            if issubclass(cls, base_cls) and cls != base_cls:
-                self(cls=cls, override=override)
+        for cls in self.get_members(module, base_cls):
+            self(cls=cls, override=override)
+
+    @staticmethod
+    def get_members(module: ModuleType, base_cls: Type) -> Generator[Type, None, None]:
+        return (
+            cls
+            for _, cls in inspect.getmembers(module, predicate=inspect.isclass)
+            if issubclass(cls, base_cls) and cls != base_cls
+        )
 
     @property
     def names(self) -> List[str]:
@@ -302,7 +309,7 @@ def _convert_argv_issue_84(classes: Tuple[Type, ...], nested_key: str, argv: Lis
             init_args_key = f"{argv_key}.init_args"
             init_args = {k[len(init_args_key) + 1 :]: v for k, v in passed_args.items() if k.startswith(init_args_key)}
             config = str({"class_path": class_path, "init_args": init_args})
-        elif argv_class.startswith("{"):
+        elif argv_class.startswith("{") or argv_class in ("None", "True", "False"):
             # the user passed a config as a dict
             config = argv_class
         else:
@@ -579,6 +586,7 @@ def add_core_arguments_to_parser(self, parser: LightningArgumentParser) -> None:
         """Adds arguments from the core classes to the parser."""
         parser.add_lightning_class_args(self.trainer_class, "trainer")
         parser.set_choices("trainer.callbacks", CALLBACK_REGISTRY.classes, is_list=True)
+        parser.set_choices("trainer.logger", tuple(_Registry.get_members(pl.loggers, pl.loggers.LightningLoggerBase)))
         trainer_defaults = {"trainer." + k: v for k, v in self.trainer_defaults.items() if k != "callbacks"}
         parser.set_defaults(trainer_defaults)
 
diff --git a/tests/utilities/test_cli.py b/tests/utilities/test_cli.py
index fac6c89b5fe76..e6d4791255c2d 100644
--- a/tests/utilities/test_cli.py
+++ b/tests/utilities/test_cli.py
@@ -33,6 +33,7 @@
 
 from pytorch_lightning import Callback, LightningDataModule, LightningModule, Trainer
 from pytorch_lightning.callbacks import LearningRateMonitor, ModelCheckpoint
+from pytorch_lightning.loggers import TensorBoardLogger
 from pytorch_lightning.plugins.environments import SLURMEnvironment
 from pytorch_lightning.trainer.states import TrainerFn
 from pytorch_lightning.utilities import _TPU_AVAILABLE
@@ -1435,3 +1436,17 @@ def __init__(self, activation: torch.nn.Module = lazy_instance(torch.nn.LeakyReL
         assert isinstance(cli.model.activation, torch.nn.LeakyReLU)
         assert cli.model.activation.negative_slope == 0.05
         assert cli.model.activation is not model.activation
+
+
+def test_cli_logger_shorthand():
+    with mock.patch("sys.argv", ["any.py"]):
+        cli = LightningCLI(TestModel, run=False, trainer_defaults={"logger": False})
+    assert cli.trainer.logger is None
+
+    with mock.patch("sys.argv", ["any.py", "--trainer.logger=TensorBoardLogger", "--trainer.logger.save_dir=foo"]):
+        cli = LightningCLI(TestModel, run=False, trainer_defaults={"logger": False})
+    assert isinstance(cli.trainer.logger, TensorBoardLogger)
+
+    with mock.patch("sys.argv", ["any.py", "--trainer.logger=False"]):
+        cli = LightningCLI(TestModel, run=False)
+    assert cli.trainer.logger is None


Code review comments:
diff_hunk: @@ -579,6 +586,7 @@ def add_core_arguments_to_parser(self, parser: LightningArgumentParser) -> None:
         """Adds arguments from the core classes to the parser."""
         parser.add_lightning_class_args(self.trainer_class, "trainer")
         parser.set_choices("trainer.callbacks", CALLBACK_REGISTRY.classes, is_list=True)
+        parser.set_choices("trainer.logger", tuple(_Registry.get_members(pl.loggers, pl.loggers.LightningLoggerBase)))
path: pytorch_lightning/utilities/cli.py
text: @user1:
Can we create a proper registry for logger ? 

@author:
I don't agree with that. It's adding bloat considering there are no logger implementations outside of the project (that I know of at least).

I'm totally open to adding it when the community requests it, but adding it is unrelated to supporting shorthand (this PR). There's no need to do both things in the same PR.

@user1:
The point of adding a registry is for users to easily add any external library such as the aim logger or any other and benefit from shorthand without adding them within Lightning. IMO, this needs to be added :)