Skip to content

Commit 71dff0d

Browse files
TomAugspurgerjavadnoorb
authored andcommitted
DOC: add guide on shared docstrings (pandas-dev#20016)
1 parent 3e91555 commit 71dff0d

File tree

6 files changed

+90
-11
lines changed

6 files changed

+90
-11
lines changed

doc/source/contributing.rst

-1
Original file line numberDiff line numberDiff line change
@@ -1088,5 +1088,4 @@ The branch will still exist on GitHub, so to delete it there do::
10881088
10891089
git push origin --delete shiny-new-feature
10901090
1091-
10921091
.. _Gitter: https://gitter.im/pydata/pandas

doc/source/contributing_docstring.rst

+79
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ about reStructuredText can be found in:
8282
- `Quick reStructuredText reference <http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_
8383
- `Full reStructuredText specification <http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html>`_
8484

85+
Pandas has some helpers for sharing docstrings between related classes, see
86+
:ref:`docstring.sharing`.
87+
8588
The rest of this document will summarize all the above guides, and will
8689
provide additional convention specific to the pandas project.
8790

@@ -916,3 +919,79 @@ plot will be generated automatically when building the documentation.
916919
>>> s.plot()
917920
"""
918921
pass
922+
923+
.. _docstring.sharing:
924+
925+
Sharing Docstrings
926+
------------------
927+
928+
Pandas has a system for sharing docstrings, with slight variations, between
929+
classes. This helps us keep docstrings consistent, while keeping things clear
930+
for the user reading. It comes at the cost of some complexity when writing.
931+
932+
Each shared docstring will have a base template with variables, like
933+
``%(klass)s``. The variables filled in later on using the ``Substitution``
934+
decorator. Finally, docstrings can be appended to with the ``Appender``
935+
decorator.
936+
937+
In this example, we'll create a parent docstring normally (this is like
938+
``pandas.core.generic.NDFrame``. Then we'll have two children (like
939+
``pandas.core.series.Series`` and ``pandas.core.frame.DataFrame``). We'll
940+
substitute the children's class names in this docstring.
941+
942+
.. code-block:: python
943+
944+
class Parent:
945+
def my_function(self):
946+
"""Apply my function to %(klass)s."""
947+
...
948+
949+
class ChildA(Parent):
950+
@Substitution(klass="ChildA")
951+
@Appender(Parent.my_function.__doc__)
952+
def my_function(self):
953+
...
954+
955+
class ChildB(Parent):
956+
@Substitution(klass="ChildB")
957+
@Appender(Parent.my_function.__doc__)
958+
def my_function(self):
959+
...
960+
961+
The resulting docstrings are
962+
963+
.. code-block:: python
964+
965+
>>> print(Parent.my_function.__doc__)
966+
Apply my function to %(klass)s.
967+
>>> print(ChildA.my_function.__doc__)
968+
Apply my function to ChildA.
969+
>>> print(ChildB.my_function.__doc__)
970+
Apply my function to ChildB.
971+
972+
Notice two things:
973+
974+
1. We "append" the parent docstring to the children docstrings, which are
975+
initially empty.
976+
2. Python decorators are applied inside out. So the order is Append then
977+
Substitution, even though Substitution comes first in the file.
978+
979+
Our files will often contain a module-level ``_shared_doc_kwargs`` with some
980+
common substitution values (things like ``klass``, ``axes``, etc).
981+
982+
You can substitute and append in one shot with something like
983+
984+
.. code-block:: python
985+
986+
@Appender(template % _shared_doc_kwargs)
987+
def my_function(self):
988+
...
989+
990+
where ``template`` may come from a module-level ``_shared_docs`` dictionary
991+
mapping function names to docstrings. Wherever possible, we prefer using
992+
``Appender`` and ``Substitution``, since the docstring-writing processes is
993+
slightly closer to normal.
994+
995+
See ``pandas.core.generic.NDFrame.fillna`` for an example template, and
996+
``pandas.core.series.Series.fillna`` and ``pandas.core.generic.frame.fillna``
997+
for the filled versions.

pandas/core/frame.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -3696,7 +3696,8 @@ def rename(self, *args, **kwargs):
36963696
kwargs.pop('mapper', None)
36973697
return super(DataFrame, self).rename(**kwargs)
36983698

3699-
@Appender(_shared_docs['fillna'] % _shared_doc_kwargs)
3699+
@Substitution(**_shared_doc_kwargs)
3700+
@Appender(NDFrame.fillna.__doc__)
37003701
def fillna(self, value=None, method=None, axis=None, inplace=False,
37013702
limit=None, downcast=None, **kwargs):
37023703
return super(DataFrame,

pandas/core/generic.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -5252,7 +5252,9 @@ def infer_objects(self):
52525252
# ----------------------------------------------------------------------
52535253
# Filling NA's
52545254

5255-
_shared_docs['fillna'] = ("""
5255+
def fillna(self, value=None, method=None, axis=None, inplace=False,
5256+
limit=None, downcast=None):
5257+
"""
52565258
Fill NA/NaN values using the specified method
52575259
52585260
Parameters
@@ -5343,11 +5345,7 @@ def infer_objects(self):
53435345
1 3.0 4.0 NaN 1
53445346
2 NaN 1.0 NaN 5
53455347
3 NaN 3.0 NaN 4
5346-
""")
5347-
5348-
@Appender(_shared_docs['fillna'] % _shared_doc_kwargs)
5349-
def fillna(self, value=None, method=None, axis=None, inplace=False,
5350-
limit=None, downcast=None):
5348+
"""
53515349
inplace = validate_bool_kwarg(inplace, 'inplace')
53525350
value, method = validate_fillna_kwargs(value, method)
53535351

pandas/core/panel.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
create_block_manager_from_blocks)
3232
from pandas.core.series import Series
3333
from pandas.core.reshape.util import cartesian_product
34-
from pandas.util._decorators import Appender
34+
from pandas.util._decorators import Appender, Substitution
3535
from pandas.util._validators import validate_axis_style_args
3636

3737
_shared_doc_kwargs = dict(
@@ -1254,7 +1254,8 @@ def transpose(self, *args, **kwargs):
12541254

12551255
return super(Panel, self).transpose(*axes, **kwargs)
12561256

1257-
@Appender(_shared_docs['fillna'] % _shared_doc_kwargs)
1257+
@Substitution(**_shared_doc_kwargs)
1258+
@Appender(NDFrame.fillna.__doc__)
12581259
def fillna(self, value=None, method=None, axis=None, inplace=False,
12591260
limit=None, downcast=None, **kwargs):
12601261
return super(Panel, self).fillna(value=value, method=method, axis=axis,

pandas/core/series.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -3384,7 +3384,8 @@ def drop(self, labels=None, axis=0, index=None, columns=None,
33843384
columns=columns, level=level,
33853385
inplace=inplace, errors=errors)
33863386

3387-
@Appender(generic._shared_docs['fillna'] % _shared_doc_kwargs)
3387+
@Substitution(**_shared_doc_kwargs)
3388+
@Appender(generic.NDFrame.fillna.__doc__)
33883389
def fillna(self, value=None, method=None, axis=None, inplace=False,
33893390
limit=None, downcast=None, **kwargs):
33903391
return super(Series, self).fillna(value=value, method=method,

0 commit comments

Comments
 (0)