Repository: pandas-dev/pandas

Issue:
Consider directives when validating docstrings parameters
When running the docstring validation script, errors are mistakenly reported if parameters have a `deprecated`, `versionadded` or `versionchanged` directive.

For example, when running `./scripts/validate_docstrings.py pandas.DataFrame.mode`, the next error is returned by the script:

```
################################################################################
################################## Validation ##################################
################################################################################

Errors found:
	Errors in parameters section
		Parameter "dropna" description should finish with "."
```
But the `dropna` parameter description does finish with a period:

```
dropna : bool, default True
    Don't consider counts of NaN/NaT.

    .. versionadded:: 0.24.0
```

The problem is that the `.. versionadded::` directive is not considered.

The script should ignore the directive when validating, and shouldn't report the error in this case.


Discussion:

Resolving patch:
diff --git a/scripts/validate_docstrings.py b/scripts/validate_docstrings.py
--- a/scripts/validate_docstrings.py
+++ b/scripts/validate_docstrings.py
@@ -42,6 +42,7 @@
 
 
 PRIVATE_CLASSES = ['NDFrame', 'IndexOpsMixin']
+DIRECTIVES = ['versionadded', 'versionchanged', 'deprecated']
 
 
 def _load_obj(obj_name):
@@ -234,7 +235,14 @@ def parameter_type(self, param):
         return self.doc_parameters[param][0]
 
     def parameter_desc(self, param):
-        return self.doc_parameters[param][1]
+        desc = self.doc_parameters[param][1]
+        # Find and strip out any sphinx directives
+        for directive in DIRECTIVES:
+            full_directive = '.. {}'.format(directive)
+            if full_directive in desc:
+                # Only retain any description before the directive
+                desc = desc[:desc.index(full_directive)]
+        return desc
 
     @property
     def see_also(self):


Test patch:
diff --git a/pandas/tests/scripts/test_validate_docstrings.py b/pandas/tests/scripts/test_validate_docstrings.py
--- a/pandas/tests/scripts/test_validate_docstrings.py
+++ b/pandas/tests/scripts/test_validate_docstrings.py
@@ -193,6 +193,27 @@ def contains(self, pat, case=True, na=np.nan):
         """
         pass
 
+    def mode(self, axis, numeric_only):
+        """
+        Ensure sphinx directives don't affect checks for trailing periods.
+
+        Parameters
+        ----------
+        axis : str
+            Sentence ending in period, followed by single directive.
+
+            .. versionchanged:: 0.1.2
+
+        numeric_only : boolean
+            Sentence ending in period, followed by multiple directives.
+
+            .. versionadded:: 0.1.2
+            .. deprecated:: 0.00.0
+                A multiline description,
+                which spans another line.
+        """
+        pass
+
 
 class BadGenericDocStrings(object):
     """Everything here has a bad docstring
@@ -374,6 +395,31 @@ def no_description_period(self, kind):
            Doesn't end with a dot
         """
 
+    def no_description_period_with_directive(self, kind):
+        """
+        Forgets to add a period, and also includes a directive.
+
+        Parameters
+        ----------
+        kind : str
+           Doesn't end with a dot
+
+           .. versionadded:: 0.00.0
+        """
+
+    def no_description_period_with_directives(self, kind):
+        """
+        Forgets to add a period, and also includes multiple directives.
+
+        Parameters
+        ----------
+        kind : str
+           Doesn't end with a dot
+
+           .. versionchanged:: 0.00.0
+           .. deprecated:: 0.00.0
+        """
+
     def parameter_capitalization(self, kind):
         """
         Forgets to capitalize the description.
@@ -495,7 +541,7 @@ def test_good_class(self):
 
     @pytest.mark.parametrize("func", [
         'plot', 'sample', 'random_letters', 'sample_values', 'head', 'head1',
-        'contains'])
+        'contains', 'mode'])
     def test_good_functions(self, func):
         assert validate_one(self._import_path(   # noqa: F821
             klass='GoodDocStrings', func=func)) == 0
@@ -531,6 +577,8 @@ def test_bad_generic_functions(self, func):
           'Parameter "kind: str" has no type')),
         ('BadParameters', 'no_description_period',
          ('Parameter "kind" description should finish with "."',)),
+        ('BadParameters', 'no_description_period_with_directive',
+         ('Parameter "kind" description should finish with "."',)),
         ('BadParameters', 'parameter_capitalization',
          ('Parameter "kind" description should start with a capital letter',)),
         pytest.param('BadParameters', 'blank_lines', ('No error yet?',),