Repository: Lightning-Universe/lightning-flash

Issue:
Decorator to raise warning when beta feature is used
## 🚀 Feature

Add a decorator that will give a warning that this is a beta feature when the decorated class / method etc. is used.



Discussion:

Resolving patch:
diff --git a/flash/core/utilities/stability.py b/flash/core/utilities/stability.py
new file mode 100644
--- /dev/null
+++ b/flash/core/utilities/stability.py
@@ -0,0 +1,79 @@
+# Copyright The PyTorch Lightning team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import functools
+import inspect
+from typing import Callable, Type, Union
+
+from pytorch_lightning.utilities import rank_zero_warn
+
+
+@functools.lru_cache()  # Trick to only warn once for each message
+def _raise_beta_warning(message: str, stacklevel: int = 6):
+    rank_zero_warn(
+        f"{message} The API and functionality may change without warning in future releases. "
+        "More details: https://lightning-flash.readthedocs.io/en/latest/stability.html",
+        stacklevel=stacklevel,
+        category=UserWarning,
+    )
+
+
+def beta(message: str = "This feature is currently in Beta."):
+    """The beta decorator is used to indicate that a particular feature is in Beta. A callable or type that has
+    been marked as beta will give a ``UserWarning`` when it is called or instantiated. This designation should be
+    used following the description given in :ref:`stability`.
+
+    Args:
+        message: The message to include in the warning.
+
+    Examples
+    ________
+
+    .. testsetup::
+
+        >>> import pytest
+
+    .. doctest::
+
+        >>> from flash.core.utilities.stability import beta
+        >>> @beta()
+        ... class MyBetaFeature:
+        ...     pass
+        ...
+        >>> with pytest.warns(UserWarning, match="This feature is currently in Beta."):
+        ...     MyBetaFeature()  # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+        ...
+        <...>
+        >>> @beta("This feature is currently in Beta with a custom message.")
+        ... class MyBetaFeatureWithCustomMessage:
+        ...     pass
+        ...
+        >>> with pytest.warns(UserWarning, match="This feature is currently in Beta with a custom message."):
+        ...     MyBetaFeatureWithCustomMessage()  # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+        ...
+        <...>
+    """
+
+    def decorator(callable: Union[Callable, Type]):
+        if inspect.isclass(callable):
+            callable.__init__ = decorator(callable.__init__)
+            return callable
+
+        @functools.wraps(callable)
+        def wrapper(*args, **kwargs):
+            _raise_beta_warning(message)
+            return callable(*args, **kwargs)
+
+        return wrapper
+
+    return decorator
diff --git a/flash/pointcloud/detection/data.py b/flash/pointcloud/detection/data.py
--- a/flash/pointcloud/detection/data.py
+++ b/flash/pointcloud/detection/data.py
@@ -18,6 +18,7 @@
 from flash.core.data.data_module import DataModule
 from flash.core.data.io.input import BaseDataFormat, Input
 from flash.core.data.io.input_transform import InputTransform
+from flash.core.utilities.stability import beta
 from flash.core.utilities.stages import RunningStage
 from flash.core.utilities.types import INPUT_TRANSFORM_TYPE
 from flash.pointcloud.detection.input import PointCloudObjectDetectorDatasetInput
@@ -27,6 +28,7 @@
 )
 
 
+@beta("Point cloud object detection is currently in Beta.")
 class PointCloudObjectDetectorData(DataModule):
 
     input_transform_cls = InputTransform
diff --git a/flash/pointcloud/detection/model.py b/flash/pointcloud/detection/model.py
--- a/flash/pointcloud/detection/model.py
+++ b/flash/pointcloud/detection/model.py
@@ -22,12 +22,14 @@
 from flash.core.model import Task
 from flash.core.registry import FlashRegistry
 from flash.core.utilities.apply_func import get_callable_dict
+from flash.core.utilities.stability import beta
 from flash.core.utilities.types import LOSS_FN_TYPE, LR_SCHEDULER_TYPE, METRICS_TYPE, OPTIMIZER_TYPE
 from flash.pointcloud.detection.backbones import POINTCLOUD_OBJECT_DETECTION_BACKBONES
 
 __FILE_EXAMPLE__ = "pointcloud_detection"
 
 
+@beta("Point cloud object detection is currently in Beta.")
 class PointCloudObjectDetector(Task):
     """The ``PointCloudObjectDetector`` is a :class:`~flash.core.classification.ClassificationTask` that classifies
     pointcloud data.


Test patch:
diff --git a/tests/core/utilities/test_stability.py b/tests/core/utilities/test_stability.py
new file mode 100644
--- /dev/null
+++ b/tests/core/utilities/test_stability.py
@@ -0,0 +1,53 @@
+# Copyright The PyTorch Lightning team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import pytest
+
+from flash.core.utilities.stability import _raise_beta_warning, beta
+
+
+@beta()
+class _BetaType:
+    pass
+
+
+@beta("_BetaTypeCustomMessage is currently in Beta.")
+class _BetaTypeCustomMessage:
+    pass
+
+
+@beta()
+def _beta_func():
+    pass
+
+
+@beta("_beta_func_custom_message is currently in Beta.")
+def _beta_func_custom_message():
+    pass
+
+
+@pytest.mark.parametrize(
+    "callable, match",
+    [
+        (_BetaType, "This feature is currently in Beta."),
+        (_BetaTypeCustomMessage, "_BetaTypeCustomMessage is currently in Beta."),
+        (_beta_func, "This feature is currently in Beta."),
+        (_beta_func_custom_message, "_beta_func_custom_message is currently in Beta."),
+    ],
+)
+def test_beta(callable, match):
+    # Clear warning cache
+    _raise_beta_warning.cache_clear()
+
+    with pytest.warns(UserWarning, match=match):
+        callable()