Repository: pytorch/ignite

Issue:
Add usage name in MetricUsage
## 🚀 Feature

Following @ericspod comment, usage name should be embedded in usage classes.

```python
class EpochWise(MetricUsage):
    usage_name = "epoch_wise"
    ...
```

https://github.com/pytorch/ignite/pull/1018#issuecomment-630506840

Discussion:
vfdev-5: @sdesrozis we now again start to receive coverage reports. We need to workout codecov/patch...
sdesrozis: Ok, I can add tests to cover uncovered code at the moment 😉 

Patch:
diff --git a/ignite/metrics/metric.py b/ignite/metrics/metric.py
index c3146d05..645f1ddc 100644
--- a/ignite/metrics/metric.py
+++ b/ignite/metrics/metric.py
@@ -58,6 +58,8 @@ class EpochWise(MetricUsage):
     - :meth:`~ignite.metrics.Metric.completed` on every :attr:`~ignite.engine.Events.EPOCH_COMPLETED`.
     """
 
+    usage_name = "epoch_wise"
+
     def __init__(self):
         super(EpochWise, self).__init__(
             started=Events.EPOCH_STARTED,
@@ -77,6 +79,8 @@ class BatchWise(MetricUsage):
     - :meth:`~ignite.metrics.Metric.completed` on every :attr:`~ignite.engine.Events.ITERATION_COMPLETED`.
     """
 
+    usage_name = "batch_wise"
+
     def __init__(self):
         super(BatchWise, self).__init__(
             started=Events.ITERATION_STARTED,
@@ -266,12 +270,14 @@ class Metric(metaclass=ABCMeta):
 
     def _check_usage(self, usage: Union[str, MetricUsage]) -> MetricUsage:
         if isinstance(usage, str):
-            if usage == "epoch_wise":
+            if usage == EpochWise.usage_name:
                 usage = EpochWise()
-            elif usage == "batch_wise":
+            elif usage == BatchWise.usage_name:
                 usage = BatchWise()
             else:
-                raise ValueError("usage should be 'epoch_wise' or 'batch_wise', get {}".format(usage))
+                raise ValueError(
+                    "usage should be 'EpochWise.usage_name' or 'BatchWise.usage_name', get {}".format(usage)
+                )
         if not isinstance(usage, MetricUsage):
             raise TypeError("Unhandled usage type {}".format(type(usage)))
         return usage
@@ -285,7 +291,7 @@ class Metric(metaclass=ABCMeta):
             engine (Engine): the engine to which the metric must be attached
             name (str): the name of the metric to attach
             usage (str or MetricUsage, optional): the usage of the metric. Valid string values should be
-                'epoch_wise' (default) or 'batch_wise'.
+                'EpochWise.usage_name' (default) or 'BatchWise.usage_name'.
 
         Example:
 
@@ -303,11 +309,11 @@ class Metric(metaclass=ABCMeta):
         .. code-block:: python
 
             metric = ...
-            metric.attach(engine, "mymetric", usage="batch_wise")
+            metric.attach(engine, "mymetric", usage=BatchWise.usage_name)
 
             assert "mymetric" in engine.run(data).metrics
 
-            assert metric.is_attached(engine, usage="batch_wise")
+            assert metric.is_attached(engine, usage=BatchWise.usage_name)
         """
         usage = self._check_usage(usage)
         if not engine.has_event_handler(self.started, usage.STARTED):


Test patch:
diff --git a/tests/ignite/metrics/test_metric.py b/tests/ignite/metrics/test_metric.py
index 89def6b6..50c5d428 100644
--- a/tests/ignite/metrics/test_metric.py
+++ b/tests/ignite/metrics/test_metric.py
@@ -11,7 +11,7 @@ from sklearn.metrics import confusion_matrix, f1_score, precision_score, recall_
 
 from ignite.engine import Engine, Events, State
 from ignite.metrics import ConfusionMatrix, Precision, Recall
-from ignite.metrics.metric import BatchFiltered, Metric, reinit__is_reduced
+from ignite.metrics.metric import BatchFiltered, BatchWise, EpochWise, Metric, reinit__is_reduced
 
 
 class DummyMetric1(Metric):
@@ -667,6 +667,51 @@ def test_completed():
     assert engine.state.metrics == {"metric": "foo"}
 
 
+def test_usage_exception():
+    engine = Engine(lambda e, b: b)
+    m = DummyMetric2()
+    with pytest.raises(TypeError, match=r"Unhandled usage type"):
+        m.attach(engine, "dummy", usage=1)
+    with pytest.raises(ValueError, match=r"usage should be 'EpochWise.usage_name' or 'BatchWise.usage_name'"):
+        m.attach(engine, "dummy", usage="fake")
+
+
+def test_epochwise_usage():
+    class MyMetric(Metric):
+        def __init__(self):
+            super(MyMetric, self).__init__()
+            self.value = []
+
+        def reset(self):
+            self.value = []
+
+        def compute(self):
+            return self.value
+
+        def update(self, output):
+            self.value.append(output)
+
+    def test(usage):
+        engine = Engine(lambda e, b: b)
+
+        m = MyMetric()
+
+        m.attach(engine, "ewm", usage=usage)
+
+        @engine.on(Events.EPOCH_COMPLETED)
+        def _():
+            ewm = engine.state.metrics["ewm"]
+            assert len(ewm) == 3
+            assert ewm == [0, 1, 2]
+
+        engine.run([0, 1, 2], max_epochs=10)
+        m.detach(engine, usage=usage)
+
+    test("epoch_wise")
+    test(EpochWise.usage_name)
+    test(EpochWise())
+
+
 def test_batchwise_usage():
     class MyMetric(Metric):
         def __init__(self):
@@ -682,19 +727,25 @@ def test_batchwise_usage():
         def update(self, output):
             self.value.append(output)
 
-    engine = Engine(lambda e, b: b)
+    def test(usage):
+        engine = Engine(lambda e, b: b)
 
-    m = MyMetric()
+        m = MyMetric()
 
-    m.attach(engine, "bwm", usage="batch_wise")
+        m.attach(engine, "bwm", usage=usage)
 
-    @engine.on(Events.ITERATION_COMPLETED)
-    def _():
-        bwm = engine.state.metrics["bwm"]
-        assert len(bwm) == 1
-        assert bwm[0] == (engine.state.iteration - 1) % 3
+        @engine.on(Events.ITERATION_COMPLETED)
+        def _():
+            bwm = engine.state.metrics["bwm"]
+            assert len(bwm) == 1
+            assert bwm[0] == (engine.state.iteration - 1) % 3
+
+        engine.run([0, 1, 2], max_epochs=10)
+        m.detach(engine, usage=usage)
 
-    engine.run([0, 1, 2], max_epochs=10)
+    test("batch_wise")
+    test(BatchWise.usage_name)
+    test(BatchWise())
 
 
 def test_batchfiltered_usage():