Repository: pandas-dev/pandas
Pull request: Docstring GL01 GL02 fixes
- [X] closes #24071 and also includes requested fixes from #25324  
- [ ] tests added / passed
- [ ] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [ ] whatsnew entry


Problem statement:
DOC: Fix quotes position in pandas.core
To keep out docstrings consistent, and make them easier to read, we try to keep always the same exact format. In the case of the opening and closing quotes, the right format is:
```
def foo():
    """
    Summary of the docstring.

    Returns
    -------
    str
        Explaining what is being returned.
    """
```

But for historical reasons, we have many docstrings with different format, for example:
```
def foo():
    """Summary of the docstring.

    Returns
    -------
    str
        Explaining what is being returned."""
```

Besides being inconsistent, having docstrings with the wrong format doesn't let us start validating and enforcing automatically the wanted format. So, we need to change all the wrong cases.

For this issue, all the wrong cases of methods of `pandas.core` should be fixed. The list can be obtained with the next command (the command should not report any error when this is fixed):
```
$ ./scripts/validate_docstrings.py --prefix=pandas.core --errors=GL01,GL02
pandas.core.window.Rolling.skew: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.window.Expanding.skew: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.GroupBy.aggregate: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.GroupBy.aggregate: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.GroupBy.transform: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.GroupBy.transform: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.GroupBy.pipe: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.GroupBy.first: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.GroupBy.last: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.GroupBy.max: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.GroupBy.min: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.GroupBy.prod: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.GroupBy.sum: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.corr: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.corr: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.count: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.count: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.cov: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.cov: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.diff: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.diff: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.fillna: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.fillna: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.hist: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.hist: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.idxmax: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.idxmax: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.idxmin: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.idxmin: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.mad: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.mad: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.quantile: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.quantile: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.skew: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.skew: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.take: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.take: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.tshift: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.tshift: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.SeriesGroupBy.nlargest: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.SeriesGroupBy.nlargest: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.SeriesGroupBy.nsmallest: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.SeriesGroupBy.nsmallest: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.SeriesGroupBy.nunique: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.SeriesGroupBy.nunique: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.SeriesGroupBy.unique: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.SeriesGroupBy.unique: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.SeriesGroupBy.value_counts: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.SeriesGroupBy.value_counts: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.SeriesGroupBy.is_monotonic_increasing: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.SeriesGroupBy.is_monotonic_increasing: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.SeriesGroupBy.is_monotonic_decreasing: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.SeriesGroupBy.is_monotonic_decreasing: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.groupby.DataFrameGroupBy.corrwith: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.groupby.DataFrameGroupBy.corrwith: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.resample.Resampler.pipe: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.resample.Resampler.nunique: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.resample.Resampler.nunique: Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
pandas.core.resample.Resampler.first: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.resample.Resampler.last: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.resample.Resampler.max: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.resample.Resampler.min: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.resample.Resampler.prod: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
pandas.core.resample.Resampler.sum: Docstring text (summary) should start in the line immediately after the opening quotes (not in the same line, or leaving a blank line in between)
```

Hints from issue discussion:
Hi @datapythonista Can I work on this issue? 
Absolutely, let me know if you need help. Thanks @giantpanpan 
@datapythonista  Thank you!
Hi @datapythonista I have one question is that when I was reading the contributing instruction, it ask us to install the dependencies using the following command. However, I found that the file "ci/environment-dev.yaml" and "ci/requirements-optional-conda.txt" no longer exist in the ./ci dir. 
Are they moved to other dir? Thank you!

![image](https://user-images.githubusercontent.com/30026355/49523466-d7caf800-f877-11e8-8083-f34b79f93ecc.png)

We updated that document, but you're checking the documentation of the last stable release (which makes sense, but in this case is outdated).

Once you have conda set up, from the pandas project root, you need to simply run `conda env create`. And after it's created activate the environment with `source activate pandas-dev` (not sure if in windows is `conda activate pandas-dev`).
@datapythonista I see! Thanks!
this is the latest version of the contributing page: https://pandas-docs.github.io/pandas-docs-travis/contributing.html
@datapythonista is it okay if I pick up this issue?
@giantpanpan is already working on it. Can you look for an issue that nobody mentioned in the comments that is working on it, or that did it more than 2 weeks ago, but didn't finally work on it?

If you don't find anything, let me know, I'll open some more "good first issue"
@datapythonista awesome, will do, thanks

Commit message: GL01 fix for pandas.Categorical.codes

Patch to review:
diff --git a/pandas/api/__init__.py b/pandas/api/__init__.py
index afff059e7b601..a8a176a06bea7 100644
--- a/pandas/api/__init__.py
+++ b/pandas/api/__init__.py
@@ -1,2 +1,4 @@
-""" public toolkit API """
+"""
+Public toolkit API
+"""
 from . import types, extensions  # noqa
diff --git a/pandas/api/extensions/__init__.py b/pandas/api/extensions/__init__.py
index cb6241016d82f..0e1d66525e16a 100644
--- a/pandas/api/extensions/__init__.py
+++ b/pandas/api/extensions/__init__.py
@@ -1,4 +1,6 @@
-"""Public API for extending pandas objects."""
+"""
+Public API for extending pandas objects.
+"""
 from pandas.core.accessor import (register_dataframe_accessor,  # noqa
                                   register_index_accessor,
                                   register_series_accessor)
diff --git a/pandas/api/types/__init__.py b/pandas/api/types/__init__.py
index 438e4afa3f580..7638377daa24e 100644
--- a/pandas/api/types/__init__.py
+++ b/pandas/api/types/__init__.py
@@ -1,4 +1,6 @@
-""" public toolkit API """
+"""
+Public toolkit API
+"""
 
 from pandas.core.dtypes.api import *  # noqa
 from pandas.core.dtypes.dtypes import (CategoricalDtype,  # noqa
diff --git a/pandas/core/accessor.py b/pandas/core/accessor.py
index 4353e0b3edd08..5fc60a791795f 100644
--- a/pandas/core/accessor.py
+++ b/pandas/core/accessor.py
@@ -1,8 +1,6 @@
 """
-
-accessor.py contains base classes for implementing accessor properties
+Accessor.py contains base classes for implementing accessor properties
 that can be mixed into or pinned onto other pandas classes.
-
 """
 from typing import Set
 import warnings
@@ -196,7 +194,7 @@ def decorator(accessor):
     return decorator
 
 
-_doc = """\
+_doc = """
 Register a custom accessor on %(klass)s objects.
 
 Parameters
diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py
index 0fa705369908a..694d6448c900e 100644
--- a/pandas/core/arrays/categorical.py
+++ b/pandas/core/arrays/categorical.py
@@ -191,7 +191,7 @@ def contains(cat, key, container):
         return any(loc_ in container for loc_ in loc)
 
 
-_codes_doc = """\
+_codes_doc = """
 The category codes of this categorical.
 
 Level codes are an array if integer which are the positions of the real
diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py
index 94b9dc8ebab55..846f38a6e139a 100644
--- a/pandas/core/arrays/interval.py
+++ b/pandas/core/arrays/interval.py
@@ -987,7 +987,7 @@ def __array__(self, dtype=None):
                 result[i] = Interval(left[i], right[i], closed)
         return result
 
-    _interval_shared_docs['to_tuples'] = """\
+    _interval_shared_docs['to_tuples'] = """
         Return an %(return_type)s of tuples of the form (left, right)
 
         Parameters
@@ -1001,7 +1001,7 @@ def __array__(self, dtype=None):
         Returns
         -------
         tuples: %(return_type)s
-        %(examples)s\
+        %(examples)s
     """
 
     @Appender(_interval_shared_docs['to_tuples'] % dict(
diff --git a/pandas/core/base.py b/pandas/core/base.py
index 3f59871fb5b38..66774895c8f13 100644
--- a/pandas/core/base.py
+++ b/pandas/core/base.py
@@ -634,7 +634,8 @@ def _is_builtin_func(self, arg):
 
 
 class IndexOpsMixin:
-    """ common ops mixin to support a unified interface / docs for Series /
+    """
+    Common ops mixin to support a unified interface / docs for Series /
     Index
     """
 
@@ -652,8 +653,10 @@ def transpose(self, *args, **kwargs):
         nv.validate_transpose(args, kwargs)
         return self
 
-    T = property(transpose, doc="Return the transpose, which is by "
-                                "definition self.")
+    T = property(transpose, doc="""
+                                Return the transpose, which is by
+                                definition self.
+                                """)
 
     @property
     def _is_homogeneous_type(self):
diff --git a/pandas/core/computation/eval.py b/pandas/core/computation/eval.py
index 8f6c271af4a58..ef4639a3afe4c 100644
--- a/pandas/core/computation/eval.py
+++ b/pandas/core/computation/eval.py
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 
-"""Top level ``eval`` module.
+"""
+Top level ``eval`` module.
 """
 
 import tokenize
@@ -15,7 +16,8 @@
 
 
 def _check_engine(engine):
-    """Make sure a valid engine is passed.
+    """
+    Make sure a valid engine is passed.
 
     Parameters
     ----------
@@ -31,7 +33,6 @@ def _check_engine(engine):
     Returns
     -------
     string engine
-
     """
     from pandas.core.computation.check import _NUMEXPR_INSTALLED
 
@@ -60,7 +61,8 @@ def _check_engine(engine):
 
 
 def _check_parser(parser):
-    """Make sure a valid parser is passed.
+    """
+    Make sure a valid parser is passed.
 
     Parameters
     ----------
@@ -88,7 +90,8 @@ def _check_resolvers(resolvers):
 
 
 def _check_expression(expr):
-    """Make sure an expression is not an empty string
+    """
+    Make sure an expression is not an empty string
 
     Parameters
     ----------
@@ -105,7 +108,8 @@ def _check_expression(expr):
 
 
 def _convert_expression(expr):
-    """Convert an object to an expression.
+    """
+    Convert an object to an expression.
 
     Thus function converts an object to an expression (a unicode string) and
     checks to make sure it isn't empty after conversion. This is used to
@@ -155,7 +159,8 @@ def _check_for_locals(expr, stack_level, parser):
 def eval(expr, parser='pandas', engine=None, truediv=True,
          local_dict=None, global_dict=None, resolvers=(), level=0,
          target=None, inplace=False):
-    """Evaluate a Python expression as a string using various backends.
+    """
+    Evaluate a Python expression as a string using various backends.
 
     The following arithmetic operations are supported: ``+``, ``-``, ``*``,
     ``/``, ``**``, ``%``, ``//`` (python engine only) along with the following
diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py
index 32047c3fbb5e1..3a55bffcba7e8 100644
--- a/pandas/core/dtypes/dtypes.py
+++ b/pandas/core/dtypes/dtypes.py
@@ -644,12 +644,16 @@ def __init__(self, unit="ns", tz=None):
 
     @property
     def unit(self):
-        """The precision of the datetime data."""
+        """
+        The precision of the datetime data.
+        """
         return self._unit
 
     @property
     def tz(self):
-        """The timezone."""
+        """
+        The timezone.
+        """
         return self._tz
 
     @classmethod
@@ -790,7 +794,9 @@ def __new__(cls, freq=None):
 
     @property
     def freq(self):
-        """The frequency object of this PeriodDtype."""
+        """
+        The frequency object of this PeriodDtype.
+        """
         return self._freq
 
     @classmethod
@@ -957,7 +963,9 @@ def __new__(cls, subtype=None):
 
     @property
     def subtype(self):
-        """The dtype of the Interval bounds."""
+        """
+        The dtype of the Interval bounds.
+        """
         return self._subtype
 
     @classmethod
diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py
index 63cb4d85ca308..02ee777bbe7f3 100644
--- a/pandas/core/dtypes/inference.py
+++ b/pandas/core/dtypes/inference.py
@@ -427,7 +427,8 @@ def is_named_tuple(obj):
 
 
 def is_hashable(obj):
-    """Return True if hash(obj) will succeed, False otherwise.
+    """
+    Return True if hash(obj) will succeed, False otherwise.
 
     Some types will pass a test against collections.abc.Hashable but fail when
     they are actually hashed with hash().
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
index 76c73fc40977c..671bf3ce09e27 100644
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -1862,7 +1862,8 @@ def __iter__(self):
 
     # can we get a better explanation of this?
     def keys(self):
-        """Get the 'info axis' (see Indexing for more)
+        """
+        Get the 'info axis' (see Indexing for more)
 
         This is index for Series, columns for DataFrame.
 
@@ -1874,7 +1875,8 @@ def keys(self):
         return self._info_axis
 
     def iteritems(self):
-        """Iterate over (label, values) on info axis
+        """
+        Iterate over (label, values) on info axis
 
         This is index for Series, columns for DataFrame and so on.
         """
diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py
index aa04b7505afe4..64214adcce098 100644
--- a/pandas/core/groupby/groupby.py
+++ b/pandas/core/groupby/groupby.py
@@ -171,7 +171,7 @@ class providing the base-class of operations.
     {examples}
     """)
 
-_pipe_template = """\
+_pipe_template = """
 Apply a function `func` with arguments to this %(klass)s object and return
 the function's result.
 
@@ -222,7 +222,8 @@ class providing the base-class of operations.
 
 Examples
 --------
-%(examples)s"""
+%(examples)s
+"""
 
 _transform_template = """
 Call function producing a like-indexed %(klass)s on each group and
diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py
index 87216dcc7b957..6bec00b983c2d 100644
--- a/pandas/core/indexes/interval.py
+++ b/pandas/core/indexes/interval.py
@@ -716,7 +716,8 @@ def _find_non_overlapping_monotonic_bounds(self, key):
         return start, stop
 
     def get_loc(self, key, method=None):
-        """Get integer location, slice or boolean mask for requested label.
+        """
+        Get integer location, slice or boolean mask for requested label.
 
         Parameters
         ----------
diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py
index f1553d9db835f..cba2b838b6a75 100644
--- a/pandas/core/indexes/multi.py
+++ b/pandas/core/indexes/multi.py
@@ -1077,7 +1077,9 @@ def _set_names(self, names, level=None, validate=True):
             self.levels[l].rename(name, inplace=True)
 
     names = property(fset=_set_names, fget=_get_names,
-                     doc="Names of levels in MultiIndex")
+                     doc="""
+                     Names of levels in MultiIndex
+                     """)
 
     @Appender(_index_shared_docs['_get_grouper_for_level'])
     def _get_grouper_for_level(self, mapper, level):
@@ -1731,12 +1733,16 @@ def remove_unused_levels(self):
 
     @property
     def nlevels(self):
-        """Integer number of levels in this MultiIndex."""
+        """
+        Integer number of levels in this MultiIndex.
+        """
         return len(self.levels)
 
     @property
     def levshape(self):
-        """A tuple with the length of each level."""
+        """
+        A tuple with the length of each level.
+        """
         return tuple(len(x) for x in self.levels)
 
     def __reduce__(self):
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
index 3c7a286c8a4f8..092bea65355d6 100755
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -1387,7 +1387,8 @@ def _get_slice_axis(self, slice_obj, axis=None):
 
 
 class _IXIndexer(_NDFrameIndexer):
-    """A primarily label-location based indexer, with integer position
+    """
+    A primarily label-location based indexer, with integer position
     fallback.
 
     Warning: Starting in 0.20.0, the .ix indexer is deprecated, in
diff --git a/pandas/core/resample.py b/pandas/core/resample.py
index 874973846a006..d1d99d28e59b6 100644
--- a/pandas/core/resample.py
+++ b/pandas/core/resample.py
@@ -204,8 +204,7 @@ def _assure_grouper(self):
     >>> df.resample('2D').pipe(lambda x: x.max() - x.min())
                 A
     2012-08-02  1
-    2012-08-04  1
-    """)
+    2012-08-04  1""")
     @Appender(_pipe_template)
     def pipe(self, func, *args, **kwargs):
         return super().pipe(func, *args, **kwargs)
diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py
index 1a80b35629356..d21ad58e752c2 100644
--- a/pandas/core/reshape/merge.py
+++ b/pandas/core/reshape/merge.py
@@ -133,7 +133,8 @@ def merge_ordered(left, right, on=None,
                   left_by=None, right_by=None,
                   fill_method=None, suffixes=('_x', '_y'),
                   how='outer'):
-    """Perform merge with optional filling/interpolation designed for ordered
+    """
+    Perform merge with optional filling/interpolation designed for ordered
     data like time series data. Optionally perform group-wise merge (see
     examples)
 
@@ -240,7 +241,8 @@ def merge_asof(left, right, on=None,
                tolerance=None,
                allow_exact_matches=True,
                direction='backward'):
-    """Perform an asof merge. This is similar to a left-join except that we
+    """
+    Perform an asof merge. This is similar to a left-join except that we
     match on nearest key rather than equal keys.
 
     Both DataFrames must be sorted by the key.
diff --git a/pandas/io/html.py b/pandas/io/html.py
index cbdc513cfbbe3..1e779e3e430b2 100644
--- a/pandas/io/html.py
+++ b/pandas/io/html.py
@@ -924,7 +924,8 @@ def read_html(io, match='.+', flavor=None, header=None, index_col=None,
               tupleize_cols=None, thousands=',', encoding=None,
               decimal='.', converters=None, na_values=None,
               keep_default_na=True, displayed_only=True):
-    r"""Read HTML tables into a ``list`` of ``DataFrame`` objects.
+    """
+    Read HTML tables into a ``list`` of ``DataFrame`` objects.
 
     Parameters
     ----------
diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py
index 0f7f6fe399256..e4eab6217a9e1 100644
--- a/pandas/io/pytables.py
+++ b/pandas/io/pytables.py
@@ -1082,7 +1082,8 @@ def create_table_index(self, key, **kwargs):
         s.create_index(**kwargs)
 
     def groups(self):
-        """return a list of all the top-level nodes (that are not themselves a
+        """
+        Return a list of all the top-level nodes (that are not themselves a
         pandas storage object)
 
         Returns
@@ -1101,7 +1102,8 @@ def groups(self):
         ]
 
     def walk(self, where="/"):
-        """ Walk the pytables group hierarchy for pandas objects
+        """
+        Walk the pytables group hierarchy for pandas objects
 
         This generator will yield the group path, subgroups and pandas object
         names for each group.
@@ -1147,7 +1149,9 @@ def walk(self, where="/"):
             yield (g._v_pathname.rstrip('/'), groups, leaves)
 
     def get_node(self, key):
-        """ return the node with the key or None if it does not exist """
+        """
+        Return the node with the key or None if it does not exist
+        """
         self._check_if_open()
         try:
             if not key.startswith('/'):
@@ -1157,7 +1161,9 @@ def get_node(self, key):
             return None
 
     def get_storer(self, key):
-        """ return the storer object for a key, raise if not in the file """
+        """
+        Return the storer object for a key, raise if not in the file
+        """
         group = self.get_node(key)
         if group is None:
             raise KeyError('No object named {key} in the file'.format(key=key))
@@ -1168,7 +1174,8 @@ def get_storer(self, key):
 
     def copy(self, file, mode='w', propindexes=True, keys=None, complib=None,
              complevel=None, fletcher32=False, overwrite=True):
-        """ copy the existing store to a new file, upgrading in place
+        """
+        Copy the existing store to a new file, upgrading in place
 
             Parameters
             ----------
@@ -1181,7 +1188,6 @@ def copy(self, file, mode='w', propindexes=True, keys=None, complib=None,
             Returns
             -------
             open file handle of the new store
-
         """
         new_store = HDFStore(
             file,
@@ -1262,7 +1268,9 @@ def _check_if_open(self):
             raise ClosedFileError("{0} file is not open!".format(self._path))
 
     def _validate_format(self, format, kwargs):
-        """ validate / deprecate formats; return the new kwargs """
+        """
+        Validate / deprecate formats; return the new kwargs
+        """
         kwargs = kwargs.copy()
 
         # validate
@@ -1276,7 +1284,9 @@ def _validate_format(self, format, kwargs):
 
     def _create_storer(self, group, format=None, value=None, append=False,
                        **kwargs):
-        """ return a suitable class to operate """
+        """
+        Return a suitable class to operate
+        """
 
         def error(t):
             raise TypeError(
@@ -1424,7 +1434,8 @@ def _read_group(self, group, **kwargs):
 
 class TableIterator:
 
-    """ define the iteration interface on a table
+    """
+    Define the iteration interface on a table
 
         Parameters
         ----------
@@ -1441,7 +1452,7 @@ class TableIterator:
         auto_close : boolean, automatically close the store at the end of
             iteration, default is False
         kwargs : the passed kwargs
-        """
+    """
 
     def __init__(self, store, s, func, where, nrows, start=None, stop=None,
                  iterator=False, chunksize=None, auto_close=False):
@@ -1521,7 +1532,8 @@ def get_result(self, coordinates=False):
 
 class IndexCol(StringMixin):
 
-    """ an index column description class
+    """
+    An index column description class
 
         Parameters
         ----------
@@ -1532,7 +1544,7 @@ class IndexCol(StringMixin):
         typ    : the pytables type
         pos    : the position in the pytables
 
-        """
+    """
     is_an_indexable = True
     is_data_indexable = True
     _info_fields = ['freq', 'tz', 'index_name']
@@ -1562,7 +1574,9 @@ def __init__(self, values=None, kind=None, typ=None, cname=None,
             self.set_pos(pos)
 
     def set_name(self, name, kind_attr=None):
-        """ set the name of this indexer """
+        """
+        Set the name of this indexer
+        """
         self.name = name
         self.kind_attr = kind_attr or "{name}_kind".format(name=name)
         if self.cname is None:
@@ -1571,13 +1585,17 @@ def set_name(self, name, kind_attr=None):
         return self
 
     def set_axis(self, axis):
-        """ set the axis over which I index """
+        """
+        Set the axis over which I index
+        """
         self.axis = axis
 
         return self
 
     def set_pos(self, pos):
-        """ set the position of this column in the Table """
+        """
+        Set the position of this column in the Table
+        """
         self.pos = pos
         if pos is not None and self.typ is not None:
             self.typ._v_pos = pos
@@ -1600,7 +1618,9 @@ def __str__(self):
             ['name', 'cname', 'axis', 'pos', 'kind'], temp)))
 
     def __eq__(self, other):
-        """ compare 2 col items """
+        """
+        Compare 2 col items
+        """
         return all(getattr(self, a, None) == getattr(other, a, None)
                    for a in ['name', 'cname', 'axis', 'pos'])
 
@@ -1609,7 +1629,9 @@ def __ne__(self, other):
 
     @property
     def is_indexed(self):
-        """ return whether I am an indexed column """
+        """
+        Return whether I am an indexed column
+        """
         try:
             return getattr(self.table.cols, self.cname).is_indexed
         except AttributeError:
@@ -1620,7 +1642,9 @@ def copy(self):
         return new_self
 
     def infer(self, handler):
-        """infer this column from the table: create and return a new object"""
+        """
+        Infer this column from the table: create and return a new object
+        """
         table = handler.table
         new_self = self.copy()
         new_self.set_table(table)
@@ -1629,7 +1653,9 @@ def infer(self, handler):
         return new_self
 
     def convert(self, values, nan_rep, encoding, errors):
-        """ set the values from this selection: take = take ownership """
+        """
+        Set the values from this selection: take = take ownership
+        """
 
         # values is a recarray
         if values.dtype.fields is not None:
@@ -1658,7 +1684,9 @@ def convert(self, values, nan_rep, encoding, errors):
         return self
 
     def take_data(self):
-        """ return the values & release the memory """
+        """
+        Return the values & release the memory
+        """
         self.values, values = None, self.values
         return values
 
@@ -1672,21 +1700,27 @@ def description(self):
 
     @property
     def col(self):
-        """ return my current col description """
+        """
+        Return my current col description
+        """
         return getattr(self.description, self.cname, None)
 
     @property
     def cvalues(self):
-        """ return my cython values """
+        """
+        Return my cython values
+        """
         return self.values
 
     def __iter__(self):
         return iter(self.values)
 
     def maybe_set_size(self, min_itemsize=None):
-        """ maybe set a string col itemsize:
+        """
+        Maybe set a string col itemsize:
                min_itemsize can be an integer or a dict with this columns name
-               with an integer size """
+               with an integer size
+        """
         if _ensure_decoded(self.kind) == 'string':
 
             if isinstance(min_itemsize, dict):
@@ -1711,7 +1745,9 @@ def validate_and_set(self, handler, append):
         self.set_attr()
 
     def validate_col(self, itemsize=None):
-        """ validate this column: return the compared against itemsize """
+        """
+        Validate this column: return the compared against itemsize
+        """
 
         # validate this column for string truncation (or reset to the max size)
         if _ensure_decoded(self.kind) == 'string':
diff --git a/pandas/io/sql.py b/pandas/io/sql.py
index 1e3fe2ade6ab7..e1d5931b9380d 100644
--- a/pandas/io/sql.py
+++ b/pandas/io/sql.py
@@ -246,7 +246,8 @@ def read_sql_table(table_name, con, schema=None, index_col=None,
 
 def read_sql_query(sql, con, index_col=None, coerce_float=True, params=None,
                    parse_dates=None, chunksize=None):
-    """Read SQL query into a DataFrame.
+    """
+    Read SQL query into a DataFrame.
 
     Returns a DataFrame corresponding to the result set of the query
     string. Optionally provide an `index_col` parameter to use one of the
diff --git a/pandas/io/stata.py b/pandas/io/stata.py
index 27ddc4ef6f594..18cbcd49dcca7 100644
--- a/pandas/io/stata.py
+++ b/pandas/io/stata.py
@@ -116,7 +116,7 @@
        _statafile_processing_params2, _chunksize_params,
        _iterator_params)
 
-_data_method_doc = """\
+_data_method_doc = """
 Read observations from Stata file, converting them into a dataframe
 
 .. deprecated::
diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py
index fed4b0d90983c..084b13f560417 100644
--- a/pandas/plotting/_core.py
+++ b/pandas/plotting/_core.py
@@ -2945,7 +2945,8 @@ def pie(self, **kwds):
 
 
 class FramePlotMethods(BasePlotMethods):
-    """DataFrame plotting accessor and method
+    """
+    DataFrame plotting accessor and method
 
     Examples
     --------
diff --git a/pandas/plotting/_misc.py b/pandas/plotting/_misc.py
index f153cdeee268f..710330e0bfc97 100644
--- a/pandas/plotting/_misc.py
+++ b/pandas/plotting/_misc.py
@@ -457,7 +457,8 @@ def parallel_coordinates(frame, class_column, cols=None, ax=None, color=None,
                          use_columns=False, xticks=None, colormap=None,
                          axvlines=True, axvlines_kwds=None, sort_labels=False,
                          **kwds):
-    """Parallel coordinates plotting.
+    """
+    Parallel coordinates plotting.
 
     Parameters
     ----------
@@ -568,7 +569,8 @@ def parallel_coordinates(frame, class_column, cols=None, ax=None, color=None,
 
 
 def lag_plot(series, lag=1, ax=None, **kwds):
-    """Lag plot for time series.
+    """
+    Lag plot for time series.
 
     Parameters
     ----------
diff --git a/pandas/util/testing.py b/pandas/util/testing.py
index 92d450140a891..03d826dc296b0 100644
--- a/pandas/util/testing.py
+++ b/pandas/util/testing.py
@@ -518,7 +518,8 @@ def equalContents(arr1, arr2):
 def assert_index_equal(left, right, exact='equiv', check_names=True,
                        check_less_precise=False, check_exact=True,
                        check_categorical=True, obj='Index'):
-    """Check that left and right Index are equal.
+    """
+    Check that left and right Index are equal.
 
     Parameters
     ----------
@@ -972,7 +973,8 @@ def assert_series_equal(left, right, check_dtype=True,
                         check_datetimelike_compat=False,
                         check_categorical=True,
                         obj='Series'):
-    """Check that left and right Series are equal.
+    """
+    Check that left and right Series are equal.
 
     Parameters
     ----------


Code review comments:
diff_hunk: @@ -1,2 +1,4 @@
-""" public toolkit API """
+"""
path: pandas/api/__init__.py
text: @user1:
Does the docstring validator actually check module-level docstrings like this? Thought it was only class / function

@author:
Reverted this.