Repository: Lightning-AI/pytorch-lightning
Pull request: DummyLogger can be called with unknown methods
## What does this PR do?

This PR overrides the `__getattr__` method of the `DummyLogger` class, such that an unkown method calls due to external 
loggers do not cause an `AttributeError` but return a dummy method and print information about the call to a non-existing method.

Fixes #13203 

### Does your PR introduce any breaking changes? If yes, please list them.

-

## 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?
- [x] 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 minor internal changes/refactors)

<!-- In the CHANGELOG, separate each item in the unreleased section by a blank line to reduce collisions -->

## PR review

Anyone in the community is welcome to review the PR.
Before you start reviewing, make sure you have read the [review guidelines](https://github.com/PyTorchLightning/pytorch-lightning/wiki/Review-guidelines). In short, see the following bullet-list:

- [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

## Did you have fun?

Make sure you had fun coding 🙃


Problem statement:
DummyLogger AttributeError when calling WandbLogger methods with fast_dev_run
## 🐛 Bug
When using the `fast_dev_run` flag `pytorch-lightning` replaces the logger with a `DummyLogger` object. But with a WandbLogger I also log media with `self.logger.log_text` in my pl module.  I therefore get an AttributeError that the DummyLogger does not have the attribute `log_text`. 

### To Reproduce

```python
import os

import torch
from torch.utils.data import DataLoader, Dataset

from pytorch_lightning import LightningModule, Trainer
from pytorch_lightning.loggers import WandbLogger


class RandomDataset(Dataset):
    def __init__(self, size, length):
        self.len = length
        self.data = torch.randn(length, size)

    def __getitem__(self, index):
        return self.data[index]

    def __len__(self):
        return self.len


class BoringModel(LightningModule):
    def __init__(self):
        super().__init__()
        self.layer = torch.nn.Linear(32, 2)

    def forward(self, x):
        return self.layer(x)

    def training_step(self, batch, batch_idx):
        loss = self(batch).sum()
        self.log("train_loss", loss)
        self.logger.log_text(
            "samples",
            columns=["epoch", "temperatue", "text"],
            data=[[1, 0.7, "generated_text"]],
        )
        return {"loss": loss}

    def validation_step(self, batch, batch_idx):
        loss = self(batch).sum()
        self.log("valid_loss", loss)

    def test_step(self, batch, batch_idx):
        loss = self(batch).sum()
        self.log("test_loss", loss)

    def configure_optimizers(self):
        return torch.optim.SGD(self.layer.parameters(), lr=0.1)


def run():
    wandb_logger = WandbLogger(project="myproject", offline=False)

    train_data = DataLoader(RandomDataset(32, 64), batch_size=2)
    val_data = DataLoader(RandomDataset(32, 64), batch_size=2)
    test_data = DataLoader(RandomDataset(32, 64), batch_size=2)

    model = BoringModel()
    trainer = Trainer(
        logger=wandb_logger,
        default_root_dir=os.getcwd(),
        limit_train_batches=1,
        limit_val_batches=1,
        limit_test_batches=1,
        num_sanity_val_steps=0,
        max_epochs=1,
        enable_model_summary=False,
        fast_dev_run=True
    )
    trainer.fit(model, train_dataloaders=train_data, val_dataloaders=val_data)
    trainer.test(model, dataloaders=test_data)


if __name__ == "__main__":
    run()

```

### Expected behavior

The `DummyLogger` ignores attribute access of external loggers. 

### Environment


```bash

* CUDA:
	- GPU:
	- available:         False
	- version:           None
* Packages:
	- numpy:             1.22.3
	- pyTorch_debug:     False
	- pyTorch_version:   1.11.0
	- pytorch-lightning: 1.6.3
	- tqdm:              4.64.0
* System:
	- OS:                Darwin
	- architecture:
		- 64bit
		-
	- processor:         i386
	- python:            3.9.10
	- version:           Darwin Kernel Version 21.4.0: Fri Mar 18 00:45:05 PDT 2022; root:xnu-8020.101.4~15/RELEASE_X86_64
```

### Additional context

Requires probably a `wandb` setup.


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

Hints from issue discussion:
This suggestion makes sense to me. But it should still print a warning that "The DummyLogger is being used and the method `log_text` was called on it but it does not exist."
I could take over this, if it is okay

Commit message: [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

Patch to review:
diff --git a/pytorch_lightning/loggers/logger.py b/pytorch_lightning/loggers/logger.py
index e9f7c165d07ed..d19f377b67f42 100644
--- a/pytorch_lightning/loggers/logger.py
+++ b/pytorch_lightning/loggers/logger.py
@@ -26,7 +26,7 @@
 
 import pytorch_lightning as pl
 from pytorch_lightning.callbacks.model_checkpoint import ModelCheckpoint
-from pytorch_lightning.utilities.rank_zero import rank_zero_deprecation, rank_zero_only
+from pytorch_lightning.utilities.rank_zero import rank_zero_deprecation, rank_zero_info, rank_zero_only
 
 
 def rank_zero_experiment(fn: Callable) -> Callable:
@@ -346,6 +346,16 @@ def __iter__(self):
         # if DummyLogger is substituting a logger collection, pretend it is empty
         yield from ()
 
+    def __getattr__(self, name):
+        """Allows the DummyLogger to be called with arbitrary methods, to avoid AttributeErrors."""
+
+        def method(*args, **kwargs):
+            rank_zero_info(
+                f"The DummyLogger is being used and the method `{name}` was called on it but it does not exist"
+            )
+
+        return method
+
 
 def merge_dicts(
     dicts: Sequence[Mapping],
diff --git a/tests/loggers/test_logger.py b/tests/loggers/test_logger.py
index 250ec7861c5b6..545c0fe962503 100644
--- a/tests/loggers/test_logger.py
+++ b/tests/loggers/test_logger.py
@@ -257,6 +257,13 @@ def test_dummylogger_noop_method_calls():
     logger.log_metrics("1", 2, three="three")
 
 
+def test_dummlogger_arbitrary_method_calls():
+    """Test that the DummyLogger can be called with non existing methods."""
+    logger = DummyLogger()
+    # Example method from WandbLogger
+    logger.log_text("1", columns=["1", "2", "3"], data=[[1, 2, 3]])
+
+
 def test_dummyexperiment_support_item_assignment():
     """Test that the DummyExperiment supports item assignment."""
     experiment = DummyExperiment()


Code review comments:
diff_hunk: @@ -346,6 +346,16 @@ def __iter__(self):
         # if DummyLogger is substituting a logger collection, pretend it is empty
         yield from ()
 
+    def __getattr__(self, name):
+        """Allows the DummyLogger to be called with arbitrary methods, to avoid AttributeErrors."""
+
+        def method(*args, **kwargs):
+            rank_zero_info(
+                f"The DummyLogger is being used and the method `{name}` was called on it but it does not exist"
path: pytorch_lightning/loggers/logger.py
text: @user1:
Let's mention fast_dev_run
```suggestion
                f"The `DummyLogger` is being used and the method `{name}` was called on it but it does not exist."
                " This is the case when `fast_dev_run` is enabled"
```

diff_hunk: @@ -346,6 +346,16 @@ def __iter__(self):
         # if DummyLogger is substituting a logger collection, pretend it is empty
         yield from ()
 
+    def __getattr__(self, name):
path: pytorch_lightning/loggers/logger.py
text: @user1:
Can you add typing?