Repository: optuna/optuna

Issue:
`experimental` decorator breaks class documentation.
The `experimental` decorator used on classes break documentation. This could be one manifestation but there is an issue with how the documentation including type hints are propagated to the decorated class. This does not apply for free functions.

See https://github.com/optuna/optuna/pull/1265#issuecomment-633195955 for how it may break.

## Expected behavior

Class documentation should not be altered by applying the experimental decorator.

## Steps to reproduce

1. Apply the experimental decorator to a class.
1. Build the document (`cd docs && make html`) 
1. Open the rendered documentation and note that the class signatures is broken.

## Additional context (optional)

- An issue regarding the indentation https://github.com/optuna/optuna/issues/1213.


Discussion:

Patch:
diff --git a/examples/allennlp/allennlp_jsonnet.py b/examples/allennlp/allennlp_jsonnet.py
index 33f5318fd..976e27f77 100644
--- a/examples/allennlp/allennlp_jsonnet.py
+++ b/examples/allennlp/allennlp_jsonnet.py
@@ -15,7 +15,7 @@ We have the following two ways to execute this example:
 
 (2) Execute through CLI.
     $ STUDY_NAME=`optuna create-study --direction maximize --storage sqlite:///example.db`
-    $ optuna study optimize allennlp_jsonnet.py objective --n-trials=100 --study-name $STUDY_NAME \
+    $ optuna study optimize allennlp_jsonnet.py objective --n-trials=100 --study $STUDY_NAME \
       --storage sqlite:///example.db
 
 """
diff --git a/optuna/_experimental.py b/optuna/_experimental.py
index 8ad355936..dae7c4887 100644
--- a/optuna/_experimental.py
+++ b/optuna/_experimental.py
@@ -18,35 +18,6 @@ _EXPERIMENTAL_DOCSTRING_TEMPLATE = """
 """
 
 
-def _make_func_spec_str(func: Callable[..., Any]) -> str:
-
-    name = func.__name__
-    argspec = inspect.getfullargspec(func)
-
-    n_defaults = len(argspec.defaults) if argspec.defaults is not None else 0
-    offset = int(len(argspec.args) > 0 and argspec.args[0] == "self")
-
-    if n_defaults > 0:
-        args = ", ".join(argspec.args[offset:-n_defaults])
-        with_default_values = ", ".join(
-            [
-                "{}={}".format(a, d)
-                for a, d in zip(argspec.args[-n_defaults:], argspec.defaults)  # type: ignore
-            ]
-        )
-    else:
-        args = ", ".join(argspec.args[offset:])
-        with_default_values = ""
-
-    if len(args) > 0 and len(with_default_values) > 0:
-        args += ", "
-
-    # NOTE(crcrpar): The four spaces are necessary to correctly render documentation.
-    # Different classes or methods require more spaces.
-    str_args_description = "(" + args + with_default_values + ")\n\n    "
-    return name + str_args_description
-
-
 def _validate_version(version: str) -> None:
 
     if not isinstance(version, str) or len(version.split(".")) != 3:
@@ -102,6 +73,7 @@ def experimental(version: str, name: str = None) -> Any:
 
             _original_init = cls.__init__
 
+            @functools.wraps(_original_init)
             def wrapped_init(self, *args, **kwargs) -> None:  # type: ignore
                 warnings.warn(
                     "{} is experimental (supported from v{}). "
@@ -117,11 +89,7 @@ def experimental(version: str, name: str = None) -> Any:
 
             if cls.__doc__ is None:
                 cls.__doc__ = ""
-            cls.__doc__ = (
-                _make_func_spec_str(_original_init)
-                + cls.__doc__
-                + _EXPERIMENTAL_DOCSTRING_TEMPLATE.format(ver=version)
-            )
+            cls.__doc__ += _EXPERIMENTAL_DOCSTRING_TEMPLATE.format(ver=version)
             return cls
 
         return _experimental_class(f) if inspect.isclass(f) else _experimental_func(f)
diff --git a/optuna/integration/allennlp.py b/optuna/integration/allennlp.py
index fc04369db..e2a7b5e6b 100644
--- a/optuna/integration/allennlp.py
+++ b/optuna/integration/allennlp.py
@@ -3,7 +3,6 @@ import os
 from typing import Any
 from typing import Dict
 from typing import List
-from typing import Optional
 from typing import Union
 import warnings
 
@@ -85,7 +84,7 @@ class AllenNLPExecutor(object):
         serialization_dir: str,
         metrics: str = "best_validation_accuracy",
         *,
-        include_package: Optional[Union[str, List[str]]] = None
+        include_package: Union[str, List[str]] = []
     ):
 
         _check_allennlp_availability()
@@ -94,8 +93,6 @@ class AllenNLPExecutor(object):
         self._config_file = config_file
         self._serialization_dir = serialization_dir
         self._metrics = metrics
-        if include_package is None:
-            include_package = []
         if isinstance(include_package, str):
             self._include_package = [include_package]
         else:


Test patch:
diff --git a/tests/test_experimental.py b/tests/test_experimental.py
index 3a56c51fa..b13e4913b 100644
--- a/tests/test_experimental.py
+++ b/tests/test_experimental.py
@@ -11,30 +11,11 @@ def _sample_func(_: Any) -> int:
     return 10
 
 
-def _f() -> None:
-    pass
-
-
-def _g(a: Any, b: Any = None) -> None:
-    pass
-
-
-def _h(a: Any = None, b: int = 10) -> None:
-    pass
-
-
 class _Sample(object):
     def __init__(self, a: Any, b: Any, c: Any) -> None:
         pass
 
 
-def test_str() -> None:
-
-    assert _experimental._make_func_spec_str(_f) == "_f()\n\n    "
-    assert _experimental._make_func_spec_str(_g) == "_g(a, b=None)\n\n    "
-    assert _experimental._make_func_spec_str(_h) == "_h(a=None, b=10)\n\n    "
-
-
 @pytest.mark.parametrize("version", ["1.1.0", "1.1", 100, None])
 def test_experimental_decorator(version: str) -> None:
 
@@ -74,10 +55,9 @@ def test_experimental_class_decorator(version: str) -> None:
 
         decorated_sample = decorator_experimental(_Sample)
         assert decorated_sample.__name__ == "_Sample"
-        assert (
-            decorated_sample.__doc__
-            == "__init__(a, b, c)\n\n    "
-            + _experimental._EXPERIMENTAL_DOCSTRING_TEMPLATE.format(ver=version)
+        assert decorated_sample.__init__.__name__ == "__init__"
+        assert decorated_sample.__doc__ == _experimental._EXPERIMENTAL_DOCSTRING_TEMPLATE.format(
+            ver=version
         )
 
         with pytest.warns(ExperimentalWarning):