Repository: mne-tools/mne-python

Issue:
DOC: Installation troubleshooting
Talking to @jasmainak about an upcoming workshop and getting emails, I think we would benefit from a dedicated page about troubleshooting installation issues. A page like this could be nice:

http://ryan-leung.github.io/PHYS4650_Python_Tutorial/installing-on-linux.html

Basically something with green/checkboxes saying "this is good" and red/X's saying "this is bad" at different stages, with links to "how to fix bad" would be helpful. I'm not sure how it should be structured. @drammock do you want to try, or would you rather I take a stab at it?

Maybe this should be broken into:

1. Ensure conda is installed and accessible from your terminal
2. Ensure the `mne` environment is active
3. Ensure the dependencies are installed (`sys_info`, maybe `mlab.test_plot3d`)
4. Ensure the dependencies work (esp. `mayavi`, with interactivity / OpenGL issues)
5. Ensure Spyder works (?) if we want to push people that way

Example diagnostics that should appear somewhere, with what they imply:

- `which conda` (good: correct path, bad: no output)
- `conda 
- `which python` (good: Anaconda, bad: anything else e.g. system python)
- `which pip` (good: Anaconda pip, bad: anything else e.g. system pip)
- `echo $PATH` (good: Anaconda on path; bad: not on path)
- `python -c "import mne"` (good: works, bad: fails)
- `echo $PYTHONPATH` (good: empty, bad: could point somewhere)

Other things:
- tcsh vs bash (
- old way of doing conda (PATH modification) vs new way (sourcing their script); the corresponding `conda activate` vs `source activate`

If others have installation tips let me know and we'll try to add them.


Discussion:
depends on when your workshop is; I likely can't get to this until next week.
Okay cool. I'll see if I can do it tomorrow morning. Hopefully I can get most content in this week, and you can work some readability / incorporation magic on it :)
I like the idea of giving a list of diagnostic commands with a good and bad. It's how I usually troubleshoot myself. another command I would add to the list is:

```sh
$ pip list | grep mne
```

This is really useful to figure out which version of mne is being used and if it's the latest one
just a note to say that there is a vestigial bit of the installation docs [here](https://mne-tools.github.io/stable/install_mne_python.html#troubleshooting-mne-python-installation) about troubleshooting. That should be revised once there is a dedicated troubleshooting page.

Resolving patch:
diff --git a/doc/sphinxext/sphinx_bootstrap_divs/__init__.py b/doc/sphinxext/sphinx_bootstrap_divs/__init__.py
--- a/doc/sphinxext/sphinx_bootstrap_divs/__init__.py
+++ b/doc/sphinxext/sphinx_bootstrap_divs/__init__.py
@@ -2,7 +2,9 @@
 
 import os.path as op
 from docutils import nodes
-from docutils.parsers.rst.directives import flag
+from docutils.parsers.rst.directives import flag, class_option
+from docutils.parsers.rst.roles import set_classes
+from docutils.statemachine import StringList
 
 from sphinx.locale import _
 from sphinx.util.docutils import SphinxDirective
@@ -16,7 +18,7 @@
 ###############################################################################
 # Super classes
 
-class DivNode(nodes.General, nodes.Element):
+class DivNode(nodes.Body, nodes.Element):
     """Generic DivNode class."""
 
     def __init__(self, **options):
@@ -25,15 +27,43 @@ def __init__(self, **options):
         self.options = options
         super().__init__()
 
-    @staticmethod
     def visit_node(self, node):
         """Visit the node."""
-        self.body.append(node.HEADER.format(**node.options))
+        atts = {}
+        if node.BASECLASS:
+            atts['class'] = node.BASECLASS
+            if node.options.get('class'):
+                atts['class'] += \
+                    ' {}-{}'.format(node.BASECLASS, node.options['class'])
+        self.body.append(self.starttag(node, node.ELEMENT, **atts))
 
-    @staticmethod
     def depart_node(self, node):
         """Depart the node."""
-        self.body.append(node.FOOTER)
+        self.body.append('</{}>'.format(node.ELEMENT))
+
+
+def _assemble(node, directive):
+    title_text = directive.arguments[0]
+    directive.add_name(node)
+    header = node.HEADER_PRETITLE.format(**node.options).split('\n')
+    directive.state.nested_parse(
+        StringList(header), directive.content_offset, node)
+
+    textnodes, messages = directive.state.inline_text(
+        title_text, directive.lineno)
+    node += textnodes
+    node += messages
+
+    header = node.HEADER_POSTTITLE.format(**node.options).split('\n')
+    directive.state.nested_parse(
+        StringList(header), directive.content_offset, node)
+
+    directive.state.nested_parse(
+        directive.content, directive.content_offset, node)
+
+    footer = node.FOOTER.format(**node.options).split('\n')
+    directive.state.nested_parse(
+        StringList(footer), directive.content_offset, node)
 
 
 ###############################################################################
@@ -42,16 +72,21 @@ def depart_node(self, node):
 class CollapseNode(DivNode):
     """Class for .. collapse:: directive."""
 
-    OPTION_KEYS = ('title', 'id_', 'extra', 'class_')
-    HEADER = """
-    <div class="panel panel-{class_}">
+    OPTION_KEYS = ('title', 'id_', 'extra', 'class')
+    ELEMENT = 'div'
+    BASECLASS = 'panel'
+    HEADER_PRETITLE = """.. raw:: html
+
     <div class="panel-heading"><h4 class="panel-title">
-    <a data-toggle="collapse" href="#collapse_{id_}">{title}</a>
-    </h4></div>
+    <a data-toggle="collapse" href="#collapse_{id_}">"""
+    HEADER_POSTTITLE = """.. raw:: html
+
+    </a></h4></div>
     <div id="collapse_{id_}" class="panel-collapse collapse{extra}">
-    <div class="panel-body">
-    """
-    FOOTER = "</div></div></div>"
+    <div class="panel-body">"""
+    FOOTER = """.. raw:: html
+
+    </div></div>"""
     KNOWN_CLASSES = (
         'default', 'primary', 'success', 'info', 'warning', 'danger')
 
@@ -75,18 +110,14 @@ class CollapseDirective(SphinxDirective):
 
     def run(self):
         """Parse."""
-        env = self.state.document.settings.env
         self.assert_has_content()
+        title_text = _(self.arguments[0])
         extra = _(' in' if 'open' in self.options else '')
-        title = _(self.arguments[0])
-        class_ = self.options.get('class', 'default')
-        id_ = env.new_serialno('Collapse')
-        collapse_node = CollapseNode(title=title, id_=id_, extra=extra,
-                                     class_=class_)
-        self.add_name(collapse_node)
-        self.state.nested_parse(
-            self.content, self.content_offset, collapse_node)
-        return [collapse_node]
+        class_ = {'class': self.options.get('class', 'default')}
+        id_ = nodes.make_id(title_text)
+        node = CollapseNode(title=title_text, id_=id_, extra=extra, **class_)
+        _assemble(node, self)
+        return [node]
 
 
 ###############################################################################
@@ -95,10 +126,16 @@ def run(self):
 class DetailsNode(DivNode):
     """Class for .. details:: directive."""
 
-    OPTION_KEYS = ('title', 'class_')
-    HEADER = """
-    <details class="{class_}"><summary>{title}</summary>"""
-    FOOTER = "</details>"
+    ELEMENT = 'details'
+    BASECLASS = ''
+    OPTION_KEYS = ('title', 'class')
+    HEADER_PRETITLE = """.. raw:: html
+
+    <summary>"""
+    HEADER_POSTTITLE = """.. raw:: html
+
+    </summary>"""
+    FOOTER = """"""
 
 
 class DetailsDirective(SphinxDirective):
@@ -107,19 +144,18 @@ class DetailsDirective(SphinxDirective):
     required_arguments = 1
     optional_arguments = 0
     final_argument_whitespace = True
-    option_spec = {'class': str}
+    option_spec = {'class': class_option}
     has_content = True
 
     def run(self):
         """Parse."""
+        set_classes(self.options)
         self.assert_has_content()
-        title = _(self.arguments[0])
-        class_ = self.options.get('class', '')
-        details_node = DetailsNode(title=title, class_=class_)
-        self.add_name(details_node)
-        self.state.nested_parse(
-            self.content, self.content_offset, details_node)
-        return [details_node]
+        title_text = _(self.arguments[0])
+        class_ = {'class': self.options.get('class', '')}
+        node = DetailsNode(title=title_text, **class_)
+        _assemble(node, self)
+        return [node]
 
 
 ###############################################################################


Test patch: