Merge branch 'mauro' into docs-mw

Mauro's work to include documentation from our Python modules.  His cover
letter follows:

This is an extended version of:
    https://lore.kernel.org/linux-doc/cover.1768488832.git.mchehab+huawei@kernel.org/

It basically adds everything we currently have inside libs/tool/python
to "tools" book inside documentation.

This version should be independent of the other series yet to be merged,
(including the jobserver one).

The vast amount of changes here are docstring cleanups and additions.
They mainly consists on:

- ensuring that every phrase will end with a period, making it uniform
  along all files;
- cleaning ups to better uniform docstrings;
- variable descriptions now use "#:" markup, as it allows autodoc to
  add them inside the documentation;
- added some missing docstrings;
- some new blank lines at comments to make ReST syntax parser happy;
- add a couple of sphinx markups (mainly, code blocks).

Most of those are minor changes, affecting only comments.

It also has one patch per libarary type, adding them to docs.

For kernel-doc, I did the cleanups first, as there is one code block
inside tools/lib/python/kdoc/latex_fonts.py that would cause a Sphinx
crash without such markups.

The series actually starts with 3 fixes:

- avoid "*" markups on indexes with deep> 3 to override text
- a variable rename to stop abusing doctree name
- don't rely on cwd to get Documentation/ location

patch 4 adds support to document scripts either at:
    - tools/
    - scripts/

patch 5 contains a CSS to better display autodoc html output.

For those who want to play with documentation, documenting a python
file is very simple. All it takes is to use:

    .. automodule:: lib.python.<dir+name>

Usually, we add a couple of control members to it to adjust
the desired documentation scope (add/remove members, showing class
inheritance, showing members that currently don't have
docstrings, etc). That's why we're using:

    .. automodule:: lib.python.kdoc.enrich_formatter
       :members:
       :show-inheritance:
       :undoc-members:

(and similar) inside tools/kdoc*.rst.

autodoc allows filtering in/out members, file docstrings, etc.

It also allows documenting just some members or functions with
directives like:

    ..autofunction:
    ..automember:

Sphinx also has a helper script to generate .rst files with
documentation:

    $ sphinx-apidoc -o foobar tools/lib/python/

which can be helpful to discover what should be documented,
although changes are needed to use what it produces.
This commit is contained in:
Jonathan Corbet
2026-01-23 11:46:08 -07:00
30 changed files with 607 additions and 264 deletions

View File

@@ -5,11 +5,8 @@
# pylint: disable=C0301,C0302,R0904,R0912,R0913,R0914,R0915,R0917,R1702
"""
kdoc_parser
===========
Read a C language source or header FILE and extract embedded
documentation comments
Classes and functions related to reading a C language source or header FILE
and extract embedded documentation comments from it.
"""
import sys
@@ -195,25 +192,28 @@ function_xforms = [
]
#
# Apply a set of transforms to a block of text.
# Ancillary functions
#
def apply_transforms(xforms, text):
"""
Apply a set of transforms to a block of text.
"""
for search, subst in xforms:
text = search.sub(subst, text)
return text
#
# A little helper to get rid of excess white space
#
multi_space = KernRe(r'\s\s+')
def trim_whitespace(s):
"""
A little helper to get rid of excess white space.
"""
return multi_space.sub(' ', s.strip())
#
# Remove struct/enum members that have been marked "private".
#
def trim_private_members(text):
#
"""
Remove ``struct``/``enum`` members that have been marked "private".
"""
# First look for a "public:" block that ends a private region, then
# handle the "private until the end" case.
#
@@ -226,20 +226,21 @@ def trim_private_members(text):
class state:
"""
State machine enums
States used by the parser's state machine.
"""
# Parser states
NORMAL = 0 # normal code
NAME = 1 # looking for function name
DECLARATION = 2 # We have seen a declaration which might not be done
BODY = 3 # the body of the comment
SPECIAL_SECTION = 4 # doc section ending with a blank line
PROTO = 5 # scanning prototype
DOCBLOCK = 6 # documentation block
INLINE_NAME = 7 # gathering doc outside main block
INLINE_TEXT = 8 # reading the body of inline docs
NORMAL = 0 #: Normal code.
NAME = 1 #: Looking for function name.
DECLARATION = 2 #: We have seen a declaration which might not be done.
BODY = 3 #: The body of the comment.
SPECIAL_SECTION = 4 #: Doc section ending with a blank line.
PROTO = 5 #: Scanning prototype.
DOCBLOCK = 6 #: Documentation block.
INLINE_NAME = 7 #: Gathering doc outside main block.
INLINE_TEXT = 8 #: Reading the body of inline docs.
#: Names for each parser state.
name = [
"NORMAL",
"NAME",
@@ -253,9 +254,12 @@ class state:
]
SECTION_DEFAULT = "Description" # default section
SECTION_DEFAULT = "Description" #: Default section.
class KernelEntry:
"""
Encapsulates a Kernel documentation entry.
"""
def __init__(self, config, fname, ln):
self.config = config
@@ -288,9 +292,11 @@ class KernelEntry:
# Management of section contents
#
def add_text(self, text):
"""Add a new text to the entry contents list."""
self._contents.append(text)
def contents(self):
"""Returns a string with all content texts that were added."""
return '\n'.join(self._contents) + '\n'
# TODO: rename to emit_message after removal of kernel-doc.pl
@@ -309,10 +315,10 @@ class KernelEntry:
self.warnings.append(log_msg)
return
#
# Begin a new section.
#
def begin_section(self, line_no, title = SECTION_DEFAULT, dump = False):
"""
Begin a new section.
"""
if dump:
self.dump_section(start_new = True)
self.section = title
@@ -366,11 +372,13 @@ class KernelDoc:
documentation comments.
"""
# Section names
#: Name of context section.
section_context = "Context"
#: Name of return section.
section_return = "Return"
#: String to write when a parameter is not described.
undescribed = "-- undescribed --"
def __init__(self, config, fname):
@@ -416,7 +424,7 @@ class KernelDoc:
def dump_section(self, start_new=True):
"""
Dumps section contents to arrays/hashes intended for that purpose.
Dump section contents to arrays/hashes intended for that purpose.
"""
if self.entry:
@@ -425,9 +433,9 @@ class KernelDoc:
# TODO: rename it to store_declaration after removal of kernel-doc.pl
def output_declaration(self, dtype, name, **args):
"""
Stores the entry into an entry array.
Store the entry into an entry array.
The actual output and output filters will be handled elsewhere
The actual output and output filters will be handled elsewhere.
"""
item = KdocItem(name, self.fname, dtype,
@@ -682,10 +690,12 @@ class KernelDoc:
self.emit_msg(ln,
f"No description found for return value of '{declaration_name}'")
#
# Split apart a structure prototype; returns (struct|union, name, members) or None
#
def split_struct_proto(self, proto):
"""
Split apart a structure prototype; returns (struct|union, name,
members) or ``None``.
"""
type_pattern = r'(struct|union)'
qualifiers = [
"__attribute__",
@@ -704,21 +714,26 @@ class KernelDoc:
if r.search(proto):
return (r.group(1), r.group(3), r.group(2))
return None
#
# Rewrite the members of a structure or union for easier formatting later on.
# Among other things, this function will turn a member like:
#
# struct { inner_members; } foo;
#
# into:
#
# struct foo; inner_members;
#
def rewrite_struct_members(self, members):
"""
Process ``struct``/``union`` members from the most deeply nested
outward.
Rewrite the members of a ``struct`` or ``union`` for easier formatting
later on. Among other things, this function will turn a member like::
struct { inner_members; } foo;
into::
struct foo; inner_members;
"""
#
# Process struct/union members from the most deeply nested outward. The
# trick is in the ^{ below - it prevents a match of an outer struct/union
# until the inner one has been munged (removing the "{" in the process).
# The trick is in the ``^{`` below - it prevents a match of an outer
# ``struct``/``union`` until the inner one has been munged
# (removing the ``{`` in the process).
#
struct_members = KernRe(r'(struct|union)' # 0: declaration type
r'([^\{\};]+)' # 1: possible name
@@ -796,11 +811,12 @@ class KernelDoc:
tuples = struct_members.findall(members)
return members
#
# Format the struct declaration into a standard form for inclusion in the
# resulting docs.
#
def format_struct_decl(self, declaration):
"""
Format the ``struct`` declaration into a standard form for inclusion
in the resulting docs.
"""
#
# Insert newlines, get rid of extra spaces.
#
@@ -834,7 +850,7 @@ class KernelDoc:
def dump_struct(self, ln, proto):
"""
Store an entry for a struct or union
Store an entry for a ``struct`` or ``union``
"""
#
# Do the basic parse to get the pieces of the declaration.
@@ -876,7 +892,7 @@ class KernelDoc:
def dump_enum(self, ln, proto):
"""
Stores an enum inside self.entries array.
Store an ``enum`` inside self.entries array.
"""
#
# Strip preprocessor directives. Note that this depends on the
@@ -1023,7 +1039,7 @@ class KernelDoc:
def dump_declaration(self, ln, prototype):
"""
Stores a data declaration inside self.entries array.
Store a data declaration inside self.entries array.
"""
if self.entry.decl_type == "enum":
@@ -1040,7 +1056,7 @@ class KernelDoc:
def dump_function(self, ln, prototype):
"""
Stores a function or function macro inside self.entries array.
Store a function or function macro inside self.entries array.
"""
found = func_macro = False
@@ -1141,7 +1157,7 @@ class KernelDoc:
def dump_typedef(self, ln, proto):
"""
Stores a typedef inside self.entries array.
Store a ``typedef`` inside self.entries array.
"""
#
# We start by looking for function typedefs.
@@ -1195,7 +1211,7 @@ class KernelDoc:
@staticmethod
def process_export(function_set, line):
"""
process EXPORT_SYMBOL* tags
process ``EXPORT_SYMBOL*`` tags
This method doesn't use any variable from the class, so declare it
with a staticmethod decorator.
@@ -1226,7 +1242,7 @@ class KernelDoc:
def process_normal(self, ln, line):
"""
STATE_NORMAL: looking for the /** to begin everything.
STATE_NORMAL: looking for the ``/**`` to begin everything.
"""
if not doc_start.match(line):
@@ -1316,10 +1332,10 @@ class KernelDoc:
else:
self.emit_msg(ln, f"Cannot find identifier on line:\n{line}")
#
# Helper function to determine if a new section is being started.
#
def is_new_section(self, ln, line):
"""
Helper function to determine if a new section is being started.
"""
if doc_sect.search(line):
self.state = state.BODY
#
@@ -1351,10 +1367,10 @@ class KernelDoc:
return True
return False
#
# Helper function to detect (and effect) the end of a kerneldoc comment.
#
def is_comment_end(self, ln, line):
"""
Helper function to detect (and effect) the end of a kerneldoc comment.
"""
if doc_end.search(line):
self.dump_section()
@@ -1373,7 +1389,7 @@ class KernelDoc:
def process_decl(self, ln, line):
"""
STATE_DECLARATION: We've seen the beginning of a declaration
STATE_DECLARATION: We've seen the beginning of a declaration.
"""
if self.is_new_section(ln, line) or self.is_comment_end(ln, line):
return
@@ -1402,7 +1418,7 @@ class KernelDoc:
def process_special(self, ln, line):
"""
STATE_SPECIAL_SECTION: a section ending with a blank line
STATE_SPECIAL_SECTION: a section ending with a blank line.
"""
#
# If we have hit a blank line (only the " * " marker), then this
@@ -1492,7 +1508,7 @@ class KernelDoc:
def syscall_munge(self, ln, proto): # pylint: disable=W0613
"""
Handle syscall definitions
Handle syscall definitions.
"""
is_void = False
@@ -1531,7 +1547,7 @@ class KernelDoc:
def tracepoint_munge(self, ln, proto):
"""
Handle tracepoint definitions
Handle tracepoint definitions.
"""
tracepointname = None
@@ -1567,7 +1583,7 @@ class KernelDoc:
return proto
def process_proto_function(self, ln, line):
"""Ancillary routine to process a function prototype"""
"""Ancillary routine to process a function prototype."""
# strip C99-style comments to end of line
line = KernRe(r"//.*$", re.S).sub('', line)
@@ -1612,7 +1628,9 @@ class KernelDoc:
self.reset_state(ln)
def process_proto_type(self, ln, line):
"""Ancillary routine to process a type"""
"""
Ancillary routine to process a type.
"""
# Strip C99-style comments and surrounding whitespace
line = KernRe(r"//.*$", re.S).sub('', line).strip()
@@ -1666,7 +1684,7 @@ class KernelDoc:
self.process_proto_type(ln, line)
def process_docblock(self, ln, line):
"""STATE_DOCBLOCK: within a DOC: block."""
"""STATE_DOCBLOCK: within a ``DOC:`` block."""
if doc_end.search(line):
self.dump_section()
@@ -1678,7 +1696,7 @@ class KernelDoc:
def parse_export(self):
"""
Parses EXPORT_SYMBOL* macros from a single Kernel source file.
Parses ``EXPORT_SYMBOL*`` macros from a single Kernel source file.
"""
export_table = set()
@@ -1695,10 +1713,7 @@ class KernelDoc:
return export_table
#
# The state/action table telling us which function to invoke in
# each state.
#
#: The state/action table telling us which function to invoke in each state.
state_actions = {
state.NORMAL: process_normal,
state.NAME: process_name,