Repository: sphinx-gallery/sphinx-gallery

Issue:
SG incorrectly grabbing the description when a label is defined in docstring
I noticed that SG is incorrectly grabbing the description when you define a label in the docstring before the title. E.g.:

```
"""
.. my_label:

=======
Title
=======

etc etc.
"""
```

SG will correctly find title, but when it tries to pull the description it shows up as `=====Title=====` because it's splitting on the wrong line.

For example see my matplotlib (new) tutorials build here: http://predictablynoisy.com/matplotlib/tutorials/index.html

Discussion:

Patch:
diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py
index e1f4c0e..8e45375 100644
--- a/sphinx_gallery/gen_rst.py
+++ b/sphinx_gallery/gen_rst.py
@@ -208,11 +208,11 @@ def extract_intro_and_title(filename, docstring):
     # lstrip is just in case docstring has a '\n\n' at the beginning
     paragraphs = docstring.lstrip().split('\n\n')
     # remove comments and other syntax like `.. _link:`
-    paragraphs = [p for p in paragraphs if not p.startswith('.. ')]
-    if len(paragraphs) <= 1:
+    paragraphs = [p for p in paragraphs
+                  if not p.startswith('.. ') and len(p) > 0]
+    if len(paragraphs) == 0:
         raise ValueError(
-            "Example docstring should have a header for the example title "
-            "and at least a paragraph explaining what the example is about. "
+            "Example docstring should have a header for the example title. "
             "Please check the example file:\n {}\n".format(filename))
     # Title is the first paragraph with any ReSTructuredText title chars
     # removed, i.e. lines that consist of (all the same) 7-bit non-ASCII chars.
@@ -220,8 +220,13 @@ def extract_intro_and_title(filename, docstring):
     title = paragraphs[0].strip().split('\n')
     title = ' '.join(t for t in title if len(t) > 0 and
                      (ord(t[0]) >= 128 or t[0].isalnum()))
+    if len(title) == 0:
+        raise ValueError('Empty title detected from first paragraph:\n%s'
+                         % (paragraphs[0],))
+    # Use the title if no other paragraphs are provided
+    first_paragraph = title if len(paragraphs) < 2 else paragraphs[1]
     # Concatenate all lines of the first paragraph and truncate at 95 chars
-    first_paragraph = re.sub('\n', ' ', paragraphs[1])
+    first_paragraph = re.sub('\n', ' ', first_paragraph)
     first_paragraph = (first_paragraph[:95] + '...'
                        if len(first_paragraph) > 95 else first_paragraph)
 


Test patch:
diff --git a/sphinx_gallery/tests/test_gen_rst.py b/sphinx_gallery/tests/test_gen_rst.py
index e8919bc..7269149 100644
--- a/sphinx_gallery/tests/test_gen_rst.py
+++ b/sphinx_gallery/tests/test_gen_rst.py
@@ -15,7 +15,6 @@ import re
 import os
 import shutil
 import zipfile
-import sys
 
 import pytest
 
@@ -121,6 +120,26 @@ def test_extract_intro_and_title():
     assert intro == 'This is the description of the example which goes on and on, Óscar'  # noqa
     assert 'second paragraph' not in intro
 
+    # SG incorrectly grabbing description when a label is defined (gh-232)
+    intro_label, title_label = sg.extract_intro_and_title(
+        '<string>', '\n'.join(['.. my_label', ''] + CONTENT[1:10]))
+    assert intro_label == intro
+    assert title_label == title
+
+    intro_whitespace, title_whitespace = sg.extract_intro_and_title(
+        '<string>', '\n'.join(CONTENT[1:4] + [''] + CONTENT[5:10]))
+    assert intro_whitespace == intro
+    assert title_whitespace == title
+
+    # Make example title optional (gh-222)
+    intro, title = sg.extract_intro_and_title('<string', 'Title\n-----')
+    assert intro == title == 'Title'
+
+    with pytest.raises(ValueError, match='should have a header'):
+        sg.extract_intro_and_title('<string>', '')  # no title
+    with pytest.raises(ValueError, match='Empty title'):
+        sg.extract_intro_and_title('<string>', '-')  # no real title
+
 
 def test_md5sums():
     """Test md5sum check functions work on know file content"""