Repository: numpy/numpydoc

Issue:
Anonymous return values have their types populated in the name slot of the tuple. 
I noticed an inconsistency, when using numpydoc version 0.6.0 in python2.7 on Ubuntu. The parsed return section information returns different styles of tuple depending on if the return value is anoymous or not. 

Here is a minimal working example: 

```python

def mwe():
        from numpydoc.docscrape import NumpyDocString
        docstr = (
            'Returns\n'
            '----------\n'
            'int\n'
            '    can return an anoymous integer\n'
            'out : ndarray\n'
            '    can return a named value\n'
        )
        doc = NumpyDocString(docstr)
        returns = doc._parsed_data['Returns']
        print(returns)
```

This results in 

```python
[(u'int', '', [u'can return an anoymous integer']),
 (u'out', u'ndarray', [u'can return a named value'])]
```

However judging by tests (due to lack of docs), I believe it was indented that each value in the returns list should be a tuple of `(arg, arg_type, arg_desc)`. Therefore we should see this instead: 

```python
[('', u'int', [u'can return an anoymous integer']),
 (u'out', u'ndarray', [u'can return a named value'])]
```

My current workaround is this:

```python
        for p_name, p_type, p_descr in returns:
            if not p_type:
                p_name = ''
                p_type = p_name
```


Comments:

Patch:
diff --git a/numpydoc/docscrape.py b/numpydoc/docscrape.py
index 02afd88..32245a9 100644
--- a/numpydoc/docscrape.py
+++ b/numpydoc/docscrape.py
@@ -220,7 +220,7 @@ class NumpyDocString(Mapping):
             else:
                 yield name, self._strip(data[2:])
 
-    def _parse_param_list(self, content):
+    def _parse_param_list(self, content, single_element_is_type=False):
         r = Reader(content)
         params = []
         while not r.eof():
@@ -228,7 +228,10 @@ class NumpyDocString(Mapping):
             if ' : ' in header:
                 arg_name, arg_type = header.split(' : ')[:2]
             else:
-                arg_name, arg_type = header, ''
+                if single_element_is_type:
+                    arg_name, arg_type = '', header
+                else:
+                    arg_name, arg_type = header, ''
 
             desc = r.read_to_next_unindented_line()
             desc = dedent_lines(desc)
@@ -393,10 +396,12 @@ class NumpyDocString(Mapping):
                     self._error_location("The section %s appears twice"
                                          % section)
 
-            if section in ('Parameters', 'Returns', 'Yields', 'Receives',
-                           'Raises', 'Warns', 'Other Parameters', 'Attributes',
+            if section in ('Parameters', 'Other Parameters', 'Attributes',
                            'Methods'):
                 self[section] = self._parse_param_list(content)
+            elif section in ('Returns', 'Yields', 'Raises', 'Warns', 'Receives'):
+                self[section] = self._parse_param_list(
+                    content, single_element_is_type=True)
             elif section.startswith('.. index::'):
                 self['index'] = self._parse_index(section, content)
             elif section == 'See Also':
@@ -452,10 +457,12 @@ class NumpyDocString(Mapping):
         if self[name]:
             out += self._str_header(name)
             for param in self[name]:
+                parts = []
+                if param.name:
+                    parts.append(param.name)
                 if param.type:
-                    out += ['%s : %s' % (param.name, param.type)]
-                else:
-                    out += [param.name]
+                    parts.append(param.type)
+                out += [' : '.join(parts)]
                 if param.desc and ''.join(param.desc).strip():
                     out += self._str_indent(param.desc)
             out += ['']
@@ -637,7 +644,7 @@ class ClassDoc(NumpyDocString):
         if _members is ALL:
             _members = None
         _exclude = config.get('exclude-members', [])
-        
+
         if config.get('show_class_members', True) and _exclude is not ALL:
             def splitlines_x(s):
                 if not s:
@@ -649,7 +656,7 @@ class ClassDoc(NumpyDocString):
                 if not self[field]:
                     doc_list = []
                     for name in sorted(items):
-                        if (name in _exclude or 
+                        if (name in _exclude or
                                 (_members and name not in _members)):
                             continue
                         try:
diff --git a/numpydoc/docscrape_sphinx.py b/numpydoc/docscrape_sphinx.py
index 9b23235..aad64c7 100644
--- a/numpydoc/docscrape_sphinx.py
+++ b/numpydoc/docscrape_sphinx.py
@@ -70,19 +70,19 @@ class SphinxDocString(NumpyDocString):
         return self['Extended Summary'] + ['']
 
     def _str_returns(self, name='Returns'):
-        typed_fmt = '**%s** : %s'
-        untyped_fmt = '**%s**'
+        named_fmt = '**%s** : %s'
+        unnamed_fmt = '%s'
 
         out = []
         if self[name]:
             out += self._str_field_list(name)
             out += ['']
             for param in self[name]:
-                if param.type:
-                    out += self._str_indent([typed_fmt % (param.name.strip(),
+                if param.name:
+                    out += self._str_indent([named_fmt % (param.name.strip(),
                                                           param.type)])
                 else:
-                    out += self._str_indent([untyped_fmt % param.name.strip()])
+                    out += self._str_indent([unnamed_fmt % param.type.strip()])
                 if not param.desc:
                     out += self._str_indent(['..'], 8)
                 else:
@@ -209,12 +209,13 @@ class SphinxDocString(NumpyDocString):
                 display_param, desc = self._process_param(param.name,
                                                           param.desc,
                                                           fake_autosummary)
-
+                parts = []
+                if display_param:
+                    parts.append(display_param)
                 if param.type:
-                    out += self._str_indent(['%s : %s' % (display_param,
-                                                          param.type)])
-                else:
-                    out += self._str_indent([display_param])
+                    parts.append(param.type)
+                out += self._str_indent([' : '.join(parts)])
+
                 if desc and self.use_blockquotes:
                     out += ['']
                 elif not desc:
@@ -376,8 +377,8 @@ class SphinxDocString(NumpyDocString):
             'yields': self._str_returns('Yields'),
             'receives': self._str_returns('Receives'),
             'other_parameters': self._str_param_list('Other Parameters'),
-            'raises': self._str_param_list('Raises'),
-            'warns': self._str_param_list('Warns'),
+            'raises': self._str_returns('Raises'),
+            'warns': self._str_returns('Warns'),
             'warnings': self._str_warnings(),
             'see_also': self._str_see_also(func_role),
             'notes': self._str_section('Notes'),


Test patch:
diff --git a/numpydoc/tests/test_docscrape.py b/numpydoc/tests/test_docscrape.py
index b4b7e03..e5e3f1f 100644
--- a/numpydoc/tests/test_docscrape.py
+++ b/numpydoc/tests/test_docscrape.py
@@ -211,14 +211,14 @@ def test_returns():
     assert desc[-1].endswith('distribution.')
 
     arg, arg_type, desc = doc['Returns'][1]
-    assert arg == 'list of str'
-    assert arg_type == ''
+    assert arg == ''
+    assert arg_type == 'list of str'
     assert desc[0].startswith('This is not a real')
     assert desc[-1].endswith('anonymous return values.')
 
     arg, arg_type, desc = doc['Returns'][2]
-    assert arg == 'no_description'
-    assert arg_type == ''
+    assert arg == ''
+    assert arg_type == 'no_description'
     assert not ''.join(desc).strip()
 
 
@@ -227,7 +227,7 @@ def test_yields():
     assert len(section) == 3
     truth = [('a', 'int', 'apples.'),
              ('b', 'int', 'bananas.'),
-             ('int', '', 'unknowns.')]
+             ('', 'int', 'unknowns.')]
     for (arg, arg_type, desc), (arg_, arg_type_, end) in zip(section, truth):
         assert arg == arg_
         assert arg_type == arg_type_
@@ -594,11 +594,11 @@ of the one-dimensional normal distribution to higher dimensions.
         In other words, each entry ``out[i,j,...,:]`` is an N-dimensional
         value drawn from the distribution.
 
-    **list of str**
+    list of str
         This is not a real return value.  It exists to test
         anonymous return values.
 
-    **no_description**
+    no_description
         ..
 
 :Other Parameters:
@@ -608,12 +608,12 @@ of the one-dimensional normal distribution to higher dimensions.
 
 :Raises:
 
-    **RuntimeError**
+    RuntimeError
         Some error
 
 :Warns:
 
-    **RuntimeWarning**
+    RuntimeWarning
         Some warning
 
 .. warning::
@@ -687,7 +687,7 @@ def test_sphinx_yields_str():
     **b** : int
         The number of bananas.
 
-    **int**
+    int
         The number of unknowns.
 """)
 
@@ -754,16 +754,18 @@ doc5 = NumpyDocString(
 
 def test_raises():
     assert len(doc5['Raises']) == 1
-    name, _, desc = doc5['Raises'][0]
-    assert name == 'LinAlgException'
-    assert desc == ['If array is singular.']
+    param = doc5['Raises'][0]
+    assert param.name == ''
+    assert param.type == 'LinAlgException'
+    assert param.desc == ['If array is singular.']
 
 
 def test_warns():
     assert len(doc5['Warns']) == 1
-    name, _, desc = doc5['Warns'][0]
-    assert name == 'SomeWarning'
-    assert desc == ['If needed']
+    param = doc5['Warns'][0]
+    assert param.name == ''
+    assert param.type == 'SomeWarning'
+    assert param.desc == ['If needed']
 
 
 def test_see_also():
@@ -995,7 +997,7 @@ def test_use_blockquotes():
 
             GHI
 
-        **JKL**
+        JKL
 
             MNO
     ''')