Skip to content

Commit 14b743b

Browse files
DOC/BLD: add custom sphinx autodoc AccessorAttributeDocumenter
Accessor attributes/methods like Series.str.match or Series.dt.hour cannot be processed by the default autosummary/autodoc machinery. So added: - autosummary template - autodoc custom Documenter that fixes the recognition of the correct module and object Related to GH9322 (reimplement Series delegates/accessors using descriptors) to be able to document the accessors as eg pandas.Series.str.match and not pandas.core.strings.StringMethods.match
1 parent b7f5775 commit 14b743b

File tree

4 files changed

+86
-0
lines changed

4 files changed

+86
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{{ fullname }}
2+
{{ underline }}
3+
4+
.. currentmodule:: {{ module.split('.')[0] }}
5+
6+
.. autoaccessorattribute:: {{ [module.split('.')[1], objname]|join('.') }}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{{ fullname }}
2+
{{ underline }}
3+
4+
.. currentmodule:: {{ module.split('.')[0] }}
5+
6+
.. autoaccessormethod:: {{ [module.split('.')[1], objname]|join('.') }}

doc/source/api.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ These can be accessed like ``Series.dt.<property>``.
455455

456456
.. autosummary::
457457
:toctree: generated/
458+
:template: autosummary/accessor_attribute.rst
458459

459460
Series.dt.date
460461
Series.dt.time
@@ -483,6 +484,7 @@ These can be accessed like ``Series.dt.<property>``.
483484

484485
.. autosummary::
485486
:toctree: generated/
487+
:template: autosummary/accessor_method.rst
486488

487489
Series.dt.to_period
488490
Series.dt.to_pydatetime
@@ -493,6 +495,7 @@ These can be accessed like ``Series.dt.<property>``.
493495

494496
.. autosummary::
495497
:toctree: generated/
498+
:template: autosummary/accessor_attribute.rst
496499

497500
Series.dt.days
498501
Series.dt.seconds
@@ -504,6 +507,7 @@ These can be accessed like ``Series.dt.<property>``.
504507

505508
.. autosummary::
506509
:toctree: generated/
510+
:template: autosummary/accessor_method.rst
507511

508512
Series.dt.to_pytimedelta
509513

@@ -515,6 +519,7 @@ strings and apply several methods to it. These can be acccessed like
515519

516520
.. autosummary::
517521
:toctree: generated/
522+
:template: autosummary/accessor_method.rst
518523

519524
Series.str.cat
520525
Series.str.center

doc/source/conf.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,73 @@
297297
'pd.options.display.encoding="utf8"'
298298
]
299299

300+
301+
# Add custom Documenter to handle attributes/methods of an AccessorProperty
302+
# eg pandas.Series.str and pandas.Series.dt (see GH9322)
303+
304+
from sphinx.util import rpartition
305+
from sphinx.ext.autodoc import Documenter, MethodDocumenter, AttributeDocumenter
306+
307+
308+
class AccessorLevelDocumenter(Documenter):
309+
"""
310+
Specialized Documenter subclass for objects on accessor level (methods,
311+
attributes).
312+
"""
313+
314+
# This is the simple straightforward version
315+
# modname is None, base the last elements (eg 'hour')
316+
# and path the part before (eg 'Series.dt')
317+
# def resolve_name(self, modname, parents, path, base):
318+
# modname = 'pandas'
319+
# mod_cls = path.rstrip('.')
320+
# mod_cls = mod_cls.split('.')
321+
#
322+
# return modname, mod_cls + [base]
323+
324+
def resolve_name(self, modname, parents, path, base):
325+
if modname is None:
326+
if path:
327+
mod_cls = path.rstrip('.')
328+
else:
329+
mod_cls = None
330+
# if documenting a class-level object without path,
331+
# there must be a current class, either from a parent
332+
# auto directive ...
333+
mod_cls = self.env.temp_data.get('autodoc:class')
334+
# ... or from a class directive
335+
if mod_cls is None:
336+
mod_cls = self.env.temp_data.get('py:class')
337+
# ... if still None, there's no way to know
338+
if mod_cls is None:
339+
return None, []
340+
# HACK: this is added in comparison to ClassLevelDocumenter
341+
# mod_cls still exists of class.accessor, so an extra
342+
# rpartition is needed
343+
modname, accessor = rpartition(mod_cls, '.')
344+
modname, cls = rpartition(modname, '.')
345+
parents = [cls, accessor]
346+
# if the module name is still missing, get it like above
347+
if not modname:
348+
modname = self.env.temp_data.get('autodoc:module')
349+
if not modname:
350+
modname = self.env.temp_data.get('py:module')
351+
# ... else, it stays None, which means invalid
352+
return modname, parents + [base]
353+
354+
355+
class AccessorAttributeDocumenter(AccessorLevelDocumenter, AttributeDocumenter):
356+
357+
objtype = 'accessorattribute'
358+
directivetype = 'attribute'
359+
360+
361+
class AccessorMethodDocumenter(AccessorLevelDocumenter, MethodDocumenter):
362+
363+
objtype = 'accessormethod'
364+
directivetype = 'method'
365+
366+
300367
# remove the docstring of the flags attribute (inherited from numpy ndarray)
301368
# because these give doc build errors (see GH issue 5331)
302369
def remove_flags_docstring(app, what, name, obj, options, lines):
@@ -305,3 +372,5 @@ def remove_flags_docstring(app, what, name, obj, options, lines):
305372

306373
def setup(app):
307374
app.connect("autodoc-process-docstring", remove_flags_docstring)
375+
app.add_autodocumenter(AccessorAttributeDocumenter)
376+
app.add_autodocumenter(AccessorMethodDocumenter)

0 commit comments

Comments
 (0)