Repository: pytorch/ignite

Issue:
Use autodoc_typehints = "description" in conf.py
## 📚 Documentation

EDITED HERE: As a help wanted issue, please, see below comment https://github.com/pytorch/ignite/issues/1665#issuecomment-783240506 on what remains to do here. Thanks 


If there are many arguments in class/function/method, it's a little hard to read/scan all arguments (due to typehints).
Although there is a `Parameters` section, there might be missing some arguments (rarely though).
And we are duplicating type hints work in both signatures and docstring. Writing once can make less work on authors and less type hints mismatch can occur.

If we can remove type hints, it can be a little easier to read, and showing type hints only in `Parameters` section.

For example:
Before:
![Screen Shot 2021-02-21 at 22 52 16](https://user-images.githubusercontent.com/32727188/108631357-ff79fb00-7497-11eb-9409-79f9c7f12424.png)
After:
![Screen Shot 2021-02-21 at 22 51 40](https://user-images.githubusercontent.com/32727188/108631370-11f43480-7498-11eb-935f-c3fd5f69371f.png)

Changing to this docs style needs a little work. It needs to 
- add
```py
autoclass_content = "both"
autodoc_typehints = "description"  # new in sphinx 3.1
```
those lines in `conf.py`
- removing type hints in docstring
- leaving type hints only in signatures.

Let me know what you think.
cc: @vfdev-5 @sdesrozis @trsvchn 


Discussion:
@ydcjeff thanks! Yes, it would be a nice improvement of the docs ! I also found that function's signature is cluttered by type hints which can hard to read. Let's introduce those configs to `conf.py`
@ydcjeff Yes, I like it! BTW, I didn't know that we can do that so easily, I was expecting to use https://github.com/agronholm/sphinx-autodoc-typehints as a new dependency for that.
@vfdev-5 I will send PR tmr

@trsvchn yes, that `description` option is new in sphinx 3.1 so that we can do that
@ydcjeff typehints have to be removed from docstring, haven't they ? If so, It seems to be a huge work that we could scatter between us. 
> @ydcjeff typehints have to be removed from docstring, haven't they ?

@sdesrozis why ? 

As far as I understand, it is just about adding to additional lines to `conf.py`.
Yes, @sdesrozis is correct. We only need to add type hints in signatures, we can remove the type hints added manually in docstring. Sorry not mentioning clearly.

For the attached example, docstring is just:
```rst
to_save: Dictionary with the objects to save. Objects should have implemented ``state_dict`` and
        ``load_state_dict`` methods. If contains objects of type torch `DistributedDataParallel`_ or
        `DataParallel`_, their internal wrapped model is automatically saved (to avoid additional key ``module.`` in
        the state dictionary).
save_handler: Method or callable class to
        use to save engine and other provided objects. Function receives two objects: checkpoint as a dictionary
        and filename. If ``save_handler`` is callable class, it can
        inherit of :class:`~ignite.handlers.checkpoint.BaseSaveHandler` and optionally implement ``remove`` method
        to keep a fixed number of saved checkpoints. In case if user needs to save engine's checkpoint on a disk,
        ``save_handler`` can be defined with :class:`~ignite.handlers.DiskSaver`.
```

Since the docs started with sphinx 2.x, we also need to add type hints in docstring to appear in the description.
But, sphinx 3.x has this option, we would need a migration on that.
And I am willing to take, but as @sdesrozis said, this can be out of sync with current ongoing (feature) PRs.
I will find other solutions in the mean time.
Oh, I see, thanks for the clarification @ydcjeff ! 
I think we can proceed with that in parallel with other PRs and sync between them depending on which one is merged before.


Resolving patch:
diff --git a/ignite/utils.py b/ignite/utils.py
--- a/ignite/utils.py
+++ b/ignite/utils.py
@@ -2,7 +2,6 @@
 import functools
 import logging
 import random
-import sys
 import warnings
 from typing import Any, Callable, Dict, Optional, TextIO, Tuple, Type, TypeVar, Union, cast
 
@@ -12,46 +11,60 @@
 
 
 def convert_tensor(
-    input_: Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes],
+    x: Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes],
     device: Optional[Union[str, torch.device]] = None,
     non_blocking: bool = False,
 ) -> Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes]:
-    """Move tensors to relevant device."""
+    """Move tensors to relevant device.
+
+    Args:
+        x: input tensor or mapping, or sequence of tensors.
+        device: device type to move ``x``.
+        non_blocking: convert a CPU Tensor with pinned memory to a CUDA Tensor
+            asynchronously with respect to the host if possible
+    """
 
     def _func(tensor: torch.Tensor) -> torch.Tensor:
         return tensor.to(device=device, non_blocking=non_blocking) if device is not None else tensor
 
-    return apply_to_tensor(input_, _func)
+    return apply_to_tensor(x, _func)
 
 
 def apply_to_tensor(
-    input_: Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes], func: Callable
+    x: Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes], func: Callable
 ) -> Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes]:
     """Apply a function on a tensor or mapping, or sequence of tensors.
+
+    Args:
+        x: input tensor or mapping, or sequence of tensors.
+        func: the function to apply on ``x``.
     """
-    return apply_to_type(input_, torch.Tensor, func)
+    return apply_to_type(x, torch.Tensor, func)
 
 
 def apply_to_type(
-    input_: Union[Any, collections.Sequence, collections.Mapping, str, bytes],
+    x: Union[Any, collections.Sequence, collections.Mapping, str, bytes],
     input_type: Union[Type, Tuple[Type[Any], Any]],
     func: Callable,
 ) -> Union[Any, collections.Sequence, collections.Mapping, str, bytes]:
-    """Apply a function on a object of `input_type` or mapping, or sequence of objects of `input_type`.
+    """Apply a function on an object of `input_type` or mapping, or sequence of objects of `input_type`.
+
+    Args:
+        x: object or mapping or sequence.
+        input_type: data type of ``x``.
+        func: the function to apply on ``x``.
     """
-    if isinstance(input_, input_type):
-        return func(input_)
-    if isinstance(input_, (str, bytes)):
-        return input_
-    if isinstance(input_, collections.Mapping):
-        return cast(Callable, type(input_))(
-            {k: apply_to_type(sample, input_type, func) for k, sample in input_.items()}
-        )
-    if isinstance(input_, tuple) and hasattr(input_, "_fields"):  # namedtuple
-        return cast(Callable, type(input_))(*(apply_to_type(sample, input_type, func) for sample in input_))
-    if isinstance(input_, collections.Sequence):
-        return cast(Callable, type(input_))([apply_to_type(sample, input_type, func) for sample in input_])
-    raise TypeError((f"input must contain {input_type}, dicts or lists; found {type(input_)}"))
+    if isinstance(x, input_type):
+        return func(x)
+    if isinstance(x, (str, bytes)):
+        return x
+    if isinstance(x, collections.Mapping):
+        return cast(Callable, type(x))({k: apply_to_type(sample, input_type, func) for k, sample in x.items()})
+    if isinstance(x, tuple) and hasattr(x, "_fields"):  # namedtuple
+        return cast(Callable, type(x))(*(apply_to_type(sample, input_type, func) for sample in x))
+    if isinstance(x, collections.Sequence):
+        return cast(Callable, type(x))([apply_to_type(sample, input_type, func) for sample in x])
+    raise TypeError((f"x must contain {input_type}, dicts or lists; found {type(x)}"))
 
 
 def to_onehot(indices: torch.Tensor, num_classes: int) -> torch.Tensor:
@@ -59,6 +72,10 @@ def to_onehot(indices: torch.Tensor, num_classes: int) -> torch.Tensor:
     tensor of one-hot indicators of shape `(N, num_classes, ...) and of type uint8. Output's device is equal to the
     input's device`.
 
+    Args:
+        indices: input tensor to convert.
+        num_classes: number of classes for one-hot tensor.
+
     .. versionchanged:: 0.4.3
         This functions is now torchscriptable.
     """
@@ -78,12 +95,12 @@ def setup_logger(
     """Setups logger: name, level, format etc.
 
     Args:
-        name (str, optional): new name for the logger. If None, the standard logger is used.
-        level (int): logging level, e.g. CRITICAL, ERROR, WARNING, INFO, DEBUG.
-        stream (TextIO, optional): logging stream. If None, the standard stream is used (sys.stderr).
-        format (str): logging format. By default, `%(asctime)s %(name)s %(levelname)s: %(message)s`.
-        filepath (str, optional): Optional logging file path. If not None, logs are written to the file.
-        distributed_rank (int, optional): Optional, rank in distributed configuration to avoid logger setup for workers.
+        name: new name for the logger. If None, the standard logger is used.
+        level: logging level, e.g. CRITICAL, ERROR, WARNING, INFO, DEBUG.
+        stream: logging stream. If None, the standard stream is used (sys.stderr).
+        format: logging format. By default, `%(asctime)s %(name)s %(levelname)s: %(message)s`.
+        filepath: Optional logging file path. If not None, logs are written to the file.
+        distributed_rank: Optional, rank in distributed configuration to avoid logger setup for workers.
             If None, distributed_rank is initialized to the rank of process.
 
     Returns:
@@ -156,7 +173,7 @@ def manual_seed(seed: int) -> None:
     """Setup random state from a seed for `torch`, `random` and optionally `numpy` (if can be imported).
 
     Args:
-        seed (int): Random state seed
+        seed: Random state seed
 
     .. versionchanged:: 0.4.3
         Added ``torch.cuda.manual_seed_all(seed)``.


Test patch: