Repository: pytorch/ignite

Issue:
Automatically generated toctree for methods and classes
## 🚀 Feature

Idea is to replace our manually created toctree for [metrics](https://github.com/pytorch/ignite/blob/master/docs/source/metrics.rst#complete-list-of-metrics), [handlers](https://github.com/pytorch/ignite/blob/master/docs/source/handlers.rst#complete-list-of-handlers), [regression metrics](https://github.com/pytorch/ignite/blob/master/docs/source/contrib/metrics.rst#regression-metrics) etc.

How to do that : 
- check `.. autosummary:: ` tag in Sphinx
- add it and configure for each listed above .rst file : metrics.rst, handlers.rst etc

Example of usage:
- https://numpy.org/devdocs/reference/arrays.ndarray.html#id1
- https://github.com/numpy/numpy/blob/master/doc/source/reference/arrays.rst (edited) 

This issue maybe or maybe not blocked by #1272 


For Hacktoberfest contributors, feel free to ask questions for details if any and say that you would like to tackle the issue.
Please, take a look at [CONTRIBUTING guide](https://github.com/pytorch/ignite/blob/master/CONTRIBUTING.md).


Discussion:
Hi @vfdev-5, could I work on this one? If possible in the context of the Hacktoberfest, so I guess I'll have to wait until October before submitting a PR!
Hi @theodumont sure, you can work on this issue ! However, it can be possible that exposed idea can not be doable with Sphinx version we have... Anyway, it worth to check.
About the Hacktoberfest, 

>    September is Preptember – a full month for maintainers to groom your repositories and **for contributors to learn about making quality pull requests.**

>    Contributions during September don't count toward Hacktoberfest. Only pull requests submitted between October 1st to 31st will count.

So, technically, everyone can do a warm-up in September to understand better the project and its standard, guidelines etc and thus, have more chances to get October PRs accepted. 

Concerning this issue, you can try thing locally and report here about how it is going. Then if everything seem to work, you can send the PR in October.

I'll assign you this issue such that others could know.
Ok, great!
I'll keep you updated about how it goes locally.
Hi @vfdev-5, I tried to make it work locally.

Replacing the actual
```rst
Complete list of metrics
------------------------

    - :class:`~ignite.metrics.Accuracy`
    - :class:`~ignite.metrics.Average`
    - [...]
```
by
```rst
Complete list of metrics
------------------------

.. currentmodule:: ignite.metrics

.. autosummary::
    Accuracy
    Average
    [...]
```
produces this output when built:

![image](https://user-images.githubusercontent.com/59149044/94849281-73eac980-0425-11eb-9aee-13fc32782f45.png)
and we could specify a `:template:` option for the `autosummary` to customize the rendering of the `.rst` file.

But we still need to manually specify the classes and functions we want to appear in the list.
Is that what you wanted? I read about the `:recursive:` option ([here](https://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html)), which may be what you're looking for, but I'm not sure. In any case, it is not available for sphinx versions below `3.1`, but [this](https://stackoverflow.com/questions/48074094/use-sphinx-autosummary-recursively-to-generate-api-documentation) may be a good way to simulate this?


Please tell me if I'm going in the wrong direction.
Thanks!
@theodumont sorry for delay. Thanks for working on this issue! Yes, initial idea is to replace manually added items to automatically generated things. 

> In any case, it is not available for sphinx versions below 3.1

Can you check if we can easily upgrade to 3.1 version ? 



Running
```bash
pip install Sphinx==3.1
make html
```
raises no error, and the docs are generated. Is it enough to say that upgrading to 3.1 version is doable?
@theodumont this sounds good ! Can you then check if we can generate the docs like we do it in travis:
https://github.com/pytorch/ignite/blob/3f8daafa7ea119bcf2845c7d042d576f2b9ec122/.travis.yml#L70-L75

but with Sphix==3.1

If everything is OK, you can send a PR and once https://github.com/pytorch/ignite/issues/1270#issuecomment-703180980 "Nice to have" part is done, we can even preview newer version.
Great, I just run these commands and except a few minor `rst` syntax warnings that were already there before, it seems to work.

A few warning lines that weren't written when running the travis scripts with the old version of Sphinx caught my attention though:
```bash
C:\Users\...\tmpmi9anx3rsphinxcontrib_versioning\61b425286ba89092ba0390b9c2d5a26d3d579ef2\ignite\engine\engine.py:docstring of ignite.engine.engine.Engine:1: WARNING: duplicate object description of ignite.engine.engine.Engine, other instance in engine, use :noindex: for one of them
C:\Users\...\tmpmi9anx3rsphinxcontrib_versioning\61b425286ba89092ba0390b9c2d5a26d3d579ef2\ignite\engine\engine.py:docstring of ignite.engine.engine.Engine:7: WARNING: duplicate object description of ignite.engine.engine.Engine.state, other instance in engine, use :noindex: for one of them
# etc...
C:\Users\...\tmpmi9anx3rsphinxcontrib_versioning\61b425286ba89092ba0390b9c2d5a26d3d579ef2\ignite\engine\engine.py:docstring of ignite.engine.engine.Engine.terminate_epoch:1: WARNING: duplicate object description of ignite.engine.engine.Engine.terminate_epoch, other instance in engine, use :noindex: for one of them
```
Do you think this is a big deal?

Awesome for the preview thanks to [#1270 (comment)](https://github.com/pytorch/ignite/issues/1270#issuecomment-703180980)!
Thanks for the update @theodumont ! 

> Do you think this is a big deal?

Good question, I do not know. I'd say if all links and text are present, let's ignore the warning. Otherwise, maybe we should google them...

> Awesome for the preview thanks to #1270 (comment)!

Yeah, I'd like to have preview working to see how it is with sphinx 3.1. 

Anyway, please send a PR to close #1272 issue with just sphinx version update. Thanks !
Done!
`:recursive:` doesn't work for me (`sphinx==3.1`):

```
.. autosummary::
    :toctree: generated
    :nosignatures:
    :recursive:

    ignite.metrics
```

![recursice-0](https://user-images.githubusercontent.com/30748114/95374399-f4f30680-08e6-11eb-9a64-7a271d8cf709.png)
@theodumont any updates on this issue ?
@vfdev-5 If there is no one interested, I can continue work on this
@trsvchn sure, let me assign it to you.
Sorry for the late response. If @trsvchn can continue working on it it is ok for me!

Resolving patch:
diff --git a/docs/source/conf.py b/docs/source/conf.py
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -205,3 +205,98 @@
 
 # replaces pending_xref node with desc_type for type annotations
 sphinx.domains.python.type_to_xref = lambda t, e=None: addnodes.desc_type("", nodes.Text(t))
+
+# -- Autosummary patch to get list of a classes, funcs automatically ----------
+
+from importlib import import_module
+from inspect import getmembers, isclass, isfunction
+import sphinx.ext.autosummary
+from sphinx.ext.autosummary import Autosummary
+from docutils.parsers.rst import directives
+from docutils.statemachine import StringList
+
+
+class BetterAutosummary(Autosummary):
+    """Autosummary with autolisting for modules.
+
+    By default it tries to import all public names (__all__),
+    otherwise import all classes and/or functions in a module.
+
+    Options:
+    - :autolist: option to get list of classes and functions from currentmodule.
+    - :autolist-classes: option to get list of classes from currentmodule.
+    - :autolist-functions: option to get list of functions from currentmodule.
+
+    Example Usage:
+
+    .. currentmodule:: ignite.metrics
+
+    .. autosummary::
+        :nosignatures:
+        :autolist:
+    """
+
+    # Add new option
+    _option_spec = Autosummary.option_spec.copy()
+    _option_spec.update(
+        {
+            "autolist": directives.unchanged,
+            "autolist-classes": directives.unchanged,
+            "autolist-functions": directives.unchanged,
+        }
+    )
+    option_spec = _option_spec
+
+    def run(self):
+        for auto in ("autolist", "autolist-classes", "autolist-functions"):
+            if auto in self.options:
+                # Get current module name
+                module_name = self.env.ref_context.get("py:module")
+                # Import module
+                module = import_module(module_name)
+
+                # Get public names (if possible)
+                try:
+                    names = getattr(module, "__all__")
+                except AttributeError:
+                    # Get classes defined in the module
+                    cls_names = [
+                        name[0]
+                        for name in getmembers(module, isclass)
+                        if name[-1].__module__ == module_name and not (name[0].startswith("_"))
+                    ]
+                    # Get functions defined in the module
+                    fn_names = [
+                        name[0]
+                        for name in getmembers(module, isfunction)
+                        if (name[-1].__module__ == module_name) and not (name[0].startswith("_"))
+                    ]
+                    names = cls_names + fn_names
+                    # It may happen that module doesn't have any defined class or func
+                    if not names:
+                        names = [name[0] for name in getmembers(module)]
+
+                if auto == "autolist":
+                    # Get list of all classes and functions inside module
+                    names = [
+                        name for name in names if (isclass(getattr(module, name)) or isfunction(getattr(module, name)))
+                    ]
+                else:
+                    if auto == "autolist-classes":
+                        # Get only classes
+                        check = isclass
+                    elif auto == "autolist-functions":
+                        # Get only functions
+                        check = isfunction
+                    else:
+                        raise NotImplementedError
+
+                    names = [name for name in names if check(getattr(module, name))]
+
+                # Update content
+                self.content = StringList(names)
+        return super().run()
+
+
+# Patch original Autosummary
+sphinx.ext.autosummary.Autosummary = BetterAutosummary


Test patch: