summaryrefslogtreecommitdiff
path: root/tools/lib/python/kdoc
diff options
context:
space:
mode:
authorJonathan Corbet <corbet@lwn.net>2026-01-23 11:46:08 -0700
committerJonathan Corbet <corbet@lwn.net>2026-01-23 11:46:08 -0700
commit330367bdc176a8f52cc4c5065ba0312277202dee (patch)
tree4e7965522177b29a5f51c3fb85f285e8b69585ff /tools/lib/python/kdoc
parentffb569d59c253399efb2345ddfefe7929cd7e2a8 (diff)
parentef6aa110d8888a14dfb2e843794097263c45a06b (diff)
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.
Diffstat (limited to 'tools/lib/python/kdoc')
-rw-r--r--tools/lib/python/kdoc/enrich_formatter.py20
-rw-r--r--tools/lib/python/kdoc/kdoc_files.py23
-rw-r--r--tools/lib/python/kdoc/kdoc_item.py18
-rw-r--r--tools/lib/python/kdoc/kdoc_output.py60
-rw-r--r--tools/lib/python/kdoc/kdoc_parser.py171
-rw-r--r--tools/lib/python/kdoc/kdoc_re.py18
-rwxr-xr-xtools/lib/python/kdoc/latex_fonts.py95
-rwxr-xr-xtools/lib/python/kdoc/parse_data_structs.py62
-rw-r--r--tools/lib/python/kdoc/python_version.py20
9 files changed, 295 insertions, 192 deletions
diff --git a/tools/lib/python/kdoc/enrich_formatter.py b/tools/lib/python/kdoc/enrich_formatter.py
index bb171567a4ca..d1be4e5e1962 100644
--- a/tools/lib/python/kdoc/enrich_formatter.py
+++ b/tools/lib/python/kdoc/enrich_formatter.py
@@ -26,12 +26,16 @@ class EnrichFormatter(argparse.HelpFormatter):
and how they're used at the __doc__ description.
"""
def __init__(self, *args, **kwargs):
- """Initialize class and check if is TTY"""
+ """
+ Initialize class and check if is TTY.
+ """
super().__init__(*args, **kwargs)
self._tty = sys.stdout.isatty()
def enrich_text(self, text):
- """Handle ReST markups (currently, only ``foo``)"""
+ r"""
+ Handle ReST markups (currently, only \`\`text\`\` markups).
+ """
if self._tty and text:
# Replace ``text`` with ANSI SGR (bold)
return re.sub(r'\`\`(.+?)\`\`',
@@ -39,12 +43,16 @@ class EnrichFormatter(argparse.HelpFormatter):
return text
def _fill_text(self, text, width, indent):
- """Enrich descriptions with markups on it"""
+ """
+ Enrich descriptions with markups on it.
+ """
enriched = self.enrich_text(text)
return "\n".join(indent + line for line in enriched.splitlines())
def _format_usage(self, usage, actions, groups, prefix):
- """Enrich positional arguments at usage: line"""
+ """
+ Enrich positional arguments at usage: line.
+ """
prog = self._prog
parts = []
@@ -63,7 +71,9 @@ class EnrichFormatter(argparse.HelpFormatter):
return usage_text
def _format_action_invocation(self, action):
- """Enrich argument names"""
+ """
+ Enrich argument names.
+ """
if not action.option_strings:
return self.enrich_text(f"``{action.dest.upper()}``")
diff --git a/tools/lib/python/kdoc/kdoc_files.py b/tools/lib/python/kdoc/kdoc_files.py
index bfe02baf1606..022487ea2cc6 100644
--- a/tools/lib/python/kdoc/kdoc_files.py
+++ b/tools/lib/python/kdoc/kdoc_files.py
@@ -5,7 +5,8 @@
# pylint: disable=R0903,R0913,R0914,R0917
"""
-Parse lernel-doc tags on multiple kernel source files.
+Classes for navigating through the files that kernel-doc needs to handle
+to generate documentation.
"""
import argparse
@@ -43,7 +44,7 @@ class GlobSourceFiles:
self.srctree = srctree
def _parse_dir(self, dirname):
- """Internal function to parse files recursively"""
+ """Internal function to parse files recursively."""
with os.scandir(dirname) as obj:
for entry in obj:
@@ -65,7 +66,7 @@ class GlobSourceFiles:
def parse_files(self, file_list, file_not_found_cb):
"""
Define an iterator to parse all source files from file_list,
- handling directories if any
+ handling directories if any.
"""
if not file_list:
@@ -91,18 +92,18 @@ class KernelFiles():
There are two type of parsers defined here:
- self.parse_file(): parses both kernel-doc markups and
- EXPORT_SYMBOL* macros;
- - self.process_export_file(): parses only EXPORT_SYMBOL* macros.
+ ``EXPORT_SYMBOL*`` macros;
+ - self.process_export_file(): parses only ``EXPORT_SYMBOL*`` macros.
"""
def warning(self, msg):
- """Ancillary routine to output a warning and increment error count"""
+ """Ancillary routine to output a warning and increment error count."""
self.config.log.warning(msg)
self.errors += 1
def error(self, msg):
- """Ancillary routine to output an error and increment error count"""
+ """Ancillary routine to output an error and increment error count."""
self.config.log.error(msg)
self.errors += 1
@@ -128,7 +129,7 @@ class KernelFiles():
def process_export_file(self, fname):
"""
- Parses EXPORT_SYMBOL* macros from a single Kernel source file.
+ Parses ``EXPORT_SYMBOL*`` macros from a single Kernel source file.
"""
# Prevent parsing the same file twice if results are cached
@@ -157,7 +158,7 @@ class KernelFiles():
wcontents_before_sections=False,
logger=None):
"""
- Initialize startup variables and parse all files
+ Initialize startup variables and parse all files.
"""
if not verbose:
@@ -213,7 +214,7 @@ class KernelFiles():
def parse(self, file_list, export_file=None):
"""
- Parse all files
+ Parse all files.
"""
glob = GlobSourceFiles(srctree=self.config.src_tree)
@@ -242,7 +243,7 @@ class KernelFiles():
filenames=None, export_file=None):
"""
Interacts over the kernel-doc results and output messages,
- returning kernel-doc markups on each interaction
+ returning kernel-doc markups on each interaction.
"""
self.out_style.set_config(self.config)
diff --git a/tools/lib/python/kdoc/kdoc_item.py b/tools/lib/python/kdoc/kdoc_item.py
index 19805301cb2c..2b8a93f79716 100644
--- a/tools/lib/python/kdoc/kdoc_item.py
+++ b/tools/lib/python/kdoc/kdoc_item.py
@@ -4,7 +4,16 @@
# then pass into the output modules.
#
+"""
+Data class to store a kernel-doc Item.
+"""
+
class KdocItem:
+ """
+ A class that will, eventually, encapsulate all of the parsed data that we
+ then pass into the output modules.
+ """
+
def __init__(self, name, fname, type, start_line, **other_stuff):
self.name = name
self.fname = fname
@@ -24,6 +33,9 @@ class KdocItem:
self.other_stuff = other_stuff
def get(self, key, default = None):
+ """
+ Get a value from optional keys.
+ """
return self.other_stuff.get(key, default)
def __getitem__(self, key):
@@ -33,10 +45,16 @@ class KdocItem:
# Tracking of section and parameter information.
#
def set_sections(self, sections, start_lines):
+ """
+ Set sections and start lines.
+ """
self.sections = sections
self.section_start_lines = start_lines
def set_params(self, names, descs, types, starts):
+ """
+ Set parameter list: names, descriptions, types and start lines.
+ """
self.parameterlist = names
self.parameterdescs = descs
self.parametertypes = types
diff --git a/tools/lib/python/kdoc/kdoc_output.py b/tools/lib/python/kdoc/kdoc_output.py
index d2bf94275d65..4210b91dde5f 100644
--- a/tools/lib/python/kdoc/kdoc_output.py
+++ b/tools/lib/python/kdoc/kdoc_output.py
@@ -5,14 +5,16 @@
# pylint: disable=C0301,R0902,R0911,R0912,R0913,R0914,R0915,R0917
"""
-Implement output filters to print kernel-doc documentation.
+Classes to implement output filters to print kernel-doc documentation.
-The implementation uses a virtual base class (OutputFormat) which
+The implementation uses a virtual base class ``OutputFormat``. It
contains dispatches to virtual methods, and some code to filter
out output messages.
The actual implementation is done on one separate class per each type
-of output. Currently, there are output classes for ReST and man/troff.
+of output, e.g. ``RestFormat`` and ``ManFormat`` classes.
+
+Currently, there are output classes for ReST and man/troff.
"""
import os
@@ -54,16 +56,19 @@ class OutputFormat:
"""
# output mode.
- OUTPUT_ALL = 0 # output all symbols and doc sections
- OUTPUT_INCLUDE = 1 # output only specified symbols
- OUTPUT_EXPORTED = 2 # output exported symbols
- OUTPUT_INTERNAL = 3 # output non-exported symbols
+ OUTPUT_ALL = 0 #: Output all symbols and doc sections.
+ OUTPUT_INCLUDE = 1 #: Output only specified symbols.
+ OUTPUT_EXPORTED = 2 #: Output exported symbols.
+ OUTPUT_INTERNAL = 3 #: Output non-exported symbols.
- # Virtual member to be overridden at the inherited classes
+ #: Highlights to be used in ReST format.
highlights = []
+ #: Blank line character.
+ blankline = ""
+
def __init__(self):
- """Declare internal vars and set mode to OUTPUT_ALL"""
+ """Declare internal vars and set mode to ``OUTPUT_ALL``."""
self.out_mode = self.OUTPUT_ALL
self.enable_lineno = None
@@ -128,7 +133,7 @@ class OutputFormat:
self.config.warning(log_msg)
def check_doc(self, name, args):
- """Check if DOC should be output"""
+ """Check if DOC should be output."""
if self.no_doc_sections:
return False
@@ -177,7 +182,7 @@ class OutputFormat:
def msg(self, fname, name, args):
"""
- Handles a single entry from kernel-doc parser
+ Handles a single entry from kernel-doc parser.
"""
self.data = ""
@@ -220,30 +225,31 @@ class OutputFormat:
# Virtual methods to be overridden by inherited classes
# At the base class, those do nothing.
def set_symbols(self, symbols):
- """Get a list of all symbols from kernel_doc"""
+ """Get a list of all symbols from kernel_doc."""
def out_doc(self, fname, name, args):
- """Outputs a DOC block"""
+ """Outputs a DOC block."""
def out_function(self, fname, name, args):
- """Outputs a function"""
+ """Outputs a function."""
def out_enum(self, fname, name, args):
- """Outputs an enum"""
+ """Outputs an enum."""
def out_var(self, fname, name, args):
- """Outputs a variable"""
+ """Outputs a variable."""
def out_typedef(self, fname, name, args):
- """Outputs a typedef"""
+ """Outputs a typedef."""
def out_struct(self, fname, name, args):
- """Outputs a struct"""
+ """Outputs a struct."""
class RestFormat(OutputFormat):
- """Consts and functions used by ReST output"""
+ """Consts and functions used by ReST output."""
+ #: Highlights to be used in ReST format
highlights = [
(type_constant, r"``\1``"),
(type_constant2, r"``\1``"),
@@ -263,9 +269,13 @@ class RestFormat(OutputFormat):
(type_fallback, r":c:type:`\1`"),
(type_param_ref, r"**\1\2**")
]
+
blankline = "\n"
+ #: Sphinx literal block regex.
sphinx_literal = KernRe(r'^[^.].*::$', cache=False)
+
+ #: Sphinx code block regex.
sphinx_cblock = KernRe(r'^\.\.\ +code-block::', cache=False)
def __init__(self):
@@ -280,7 +290,7 @@ class RestFormat(OutputFormat):
self.lineprefix = ""
def print_lineno(self, ln):
- """Outputs a line number"""
+ """Outputs a line number."""
if self.enable_lineno and ln is not None:
ln += 1
@@ -289,7 +299,7 @@ class RestFormat(OutputFormat):
def output_highlight(self, args):
"""
Outputs a C symbol that may require being converted to ReST using
- the self.highlights variable
+ the self.highlights variable.
"""
input_text = args
@@ -570,7 +580,7 @@ class RestFormat(OutputFormat):
class ManFormat(OutputFormat):
- """Consts and functions used by man pages output"""
+ """Consts and functions used by man pages output."""
highlights = (
(type_constant, r"\1"),
@@ -587,6 +597,7 @@ class ManFormat(OutputFormat):
)
blankline = ""
+ #: Allowed timestamp formats.
date_formats = [
"%a %b %d %H:%M:%S %Z %Y",
"%a %b %d %H:%M:%S %Y",
@@ -653,7 +664,7 @@ class ManFormat(OutputFormat):
self.symbols = symbols
def out_tail(self, fname, name, args):
- """Adds a tail for all man pages"""
+ """Adds a tail for all man pages."""
# SEE ALSO section
self.data += f'.SH "SEE ALSO"' + "\n.PP\n"
@@ -689,7 +700,7 @@ class ManFormat(OutputFormat):
def output_highlight(self, block):
"""
Outputs a C symbol that may require being highlighted with
- self.highlights variable using troff syntax
+ self.highlights variable using troff syntax.
"""
contents = self.highlight_block(block)
@@ -720,7 +731,6 @@ class ManFormat(OutputFormat):
self.output_highlight(text)
def out_function(self, fname, name, args):
- """output function in man"""
out_name = self.arg_name(args, name)
diff --git a/tools/lib/python/kdoc/kdoc_parser.py b/tools/lib/python/kdoc/kdoc_parser.py
index c03505889dc2..fd57944ae907 100644
--- a/tools/lib/python/kdoc/kdoc_parser.py
+++ b/tools/lib/python/kdoc/kdoc_parser.py
@@ -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,
diff --git a/tools/lib/python/kdoc/kdoc_re.py b/tools/lib/python/kdoc/kdoc_re.py
index 2dfa1bf83d64..2816bd9f90f8 100644
--- a/tools/lib/python/kdoc/kdoc_re.py
+++ b/tools/lib/python/kdoc/kdoc_re.py
@@ -51,6 +51,9 @@ class KernRe:
"""
return self.regex.pattern
+ def __repr__(self):
+ return f're.compile("{self.regex.pattern}")'
+
def __add__(self, other):
"""
Allows adding two regular expressions into one.
@@ -61,7 +64,7 @@ class KernRe:
def match(self, string):
"""
- Handles a re.match storing its results
+ Handles a re.match storing its results.
"""
self.last_match = self.regex.match(string)
@@ -69,7 +72,7 @@ class KernRe:
def search(self, string):
"""
- Handles a re.search storing its results
+ Handles a re.search storing its results.
"""
self.last_match = self.regex.search(string)
@@ -77,28 +80,28 @@ class KernRe:
def findall(self, string):
"""
- Alias to re.findall
+ Alias to re.findall.
"""
return self.regex.findall(string)
def split(self, string):
"""
- Alias to re.split
+ Alias to re.split.
"""
return self.regex.split(string)
def sub(self, sub, string, count=0):
"""
- Alias to re.sub
+ Alias to re.sub.
"""
return self.regex.sub(sub, string, count=count)
def group(self, num):
"""
- Returns the group results of the last match
+ Returns the group results of the last match.
"""
return self.last_match.group(num)
@@ -110,7 +113,7 @@ class NestedMatch:
even harder on Python with its normal re module, as there are several
advanced regular expressions that are missing.
- This is the case of this pattern:
+ This is the case of this pattern::
'\\bSTRUCT_GROUP(\\(((?:(?>[^)(]+)|(?1))*)\\))[^;]*;'
@@ -121,6 +124,7 @@ class NestedMatch:
replace nested expressions.
The original approach was suggested by:
+
https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex
Although I re-implemented it to make it more generic and match 3 types
diff --git a/tools/lib/python/kdoc/latex_fonts.py b/tools/lib/python/kdoc/latex_fonts.py
index 29317f8006ea..1d04cbda169f 100755
--- a/tools/lib/python/kdoc/latex_fonts.py
+++ b/tools/lib/python/kdoc/latex_fonts.py
@@ -5,12 +5,13 @@
# Ported to Python by (c) Mauro Carvalho Chehab, 2025
"""
-Detect problematic Noto CJK variable fonts.
+Detect problematic Noto CJK variable fonts
+==========================================
-For "make pdfdocs", reports of build errors of translations.pdf started
-arriving early 2024 [1, 2]. It turned out that Fedora and openSUSE
-tumbleweed have started deploying variable-font [3] format of "Noto CJK"
-fonts [4, 5]. For PDF, a LaTeX package named xeCJK is used for CJK
+For ``make pdfdocs``, reports of build errors of translations.pdf started
+arriving early 2024 [1]_ [2]_. It turned out that Fedora and openSUSE
+tumbleweed have started deploying variable-font [3]_ format of "Noto CJK"
+fonts [4]_ [5]_. For PDF, a LaTeX package named xeCJK is used for CJK
(Chinese, Japanese, Korean) pages. xeCJK requires XeLaTeX/XeTeX, which
does not (and likely never will) understand variable fonts for historical
reasons.
@@ -25,68 +26,77 @@ This script is invoked from the error path of "make pdfdocs" and emits
suggestions if variable-font files of "Noto CJK" fonts are in the list of
fonts accessible from XeTeX.
-References:
-[1]: https://lore.kernel.org/r/8734tqsrt7.fsf@meer.lwn.net/
-[2]: https://lore.kernel.org/r/1708585803.600323099@f111.i.mail.ru/
-[3]: https://en.wikipedia.org/wiki/Variable_font
-[4]: https://fedoraproject.org/wiki/Changes/Noto_CJK_Variable_Fonts
-[5]: https://build.opensuse.org/request/show/1157217
+.. [1] https://lore.kernel.org/r/8734tqsrt7.fsf@meer.lwn.net/
+.. [2] https://lore.kernel.org/r/1708585803.600323099@f111.i.mail.ru/
+.. [3] https://en.wikipedia.org/wiki/Variable_font
+.. [4] https://fedoraproject.org/wiki/Changes/Noto_CJK_Variable_Fonts
+.. [5] https://build.opensuse.org/request/show/1157217
-#===========================================================================
Workarounds for building translations.pdf
-#===========================================================================
+-----------------------------------------
* Denylist "variable font" Noto CJK fonts.
+
- Create $HOME/deny-vf/fontconfig/fonts.conf from template below, with
tweaks if necessary. Remove leading "".
+
- Path of fontconfig/fonts.conf can be overridden by setting an env
variable FONTS_CONF_DENY_VF.
- * Template:
------------------------------------------------------------------
-<?xml version="1.0"?>
-<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
-<fontconfig>
-<!--
- Ignore variable-font glob (not to break xetex)
--->
- <selectfont>
- <rejectfont>
- <!--
- for Fedora
- -->
- <glob>/usr/share/fonts/google-noto-*-cjk-vf-fonts</glob>
- <!--
- for openSUSE tumbleweed
- -->
- <glob>/usr/share/fonts/truetype/Noto*CJK*-VF.otf</glob>
- </rejectfont>
- </selectfont>
-</fontconfig>
------------------------------------------------------------------
+ * Template::
+
+ <?xml version="1.0"?>
+ <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
+ <fontconfig>
+ <!--
+ Ignore variable-font glob (not to break xetex)
+ -->
+ <selectfont>
+ <rejectfont>
+ <!--
+ for Fedora
+ -->
+ <glob>/usr/share/fonts/google-noto-*-cjk-vf-fonts</glob>
+ <!--
+ for openSUSE tumbleweed
+ -->
+ <glob>/usr/share/fonts/truetype/Noto*CJK*-VF.otf</glob>
+ </rejectfont>
+ </selectfont>
+ </fontconfig>
The denylisting is activated for "make pdfdocs".
* For skipping CJK pages in PDF
+
- Uninstall texlive-xecjk.
Denylisting is not needed in this case.
* For printing CJK pages in PDF
+
- Need non-variable "Noto CJK" fonts.
+
* Fedora
+
- google-noto-sans-cjk-fonts
- google-noto-serif-cjk-fonts
+
* openSUSE tumbleweed
+
- Non-variable "Noto CJK" fonts are not available as distro packages
as of April, 2024. Fetch a set of font files from upstream Noto
CJK Font released at:
+
https://github.com/notofonts/noto-cjk/tree/main/Sans#super-otc
+
and at:
+
https://github.com/notofonts/noto-cjk/tree/main/Serif#super-otc
- , then uncompress and deploy them.
+
+ then uncompress and deploy them.
- Remember to update fontconfig cache by running fc-cache.
-!!! Caution !!!
+.. caution::
Uninstalling "variable font" packages can be dangerous.
They might be depended upon by other packages important for your work.
Denylisting should be less invasive, as it is effective only while
@@ -115,10 +125,15 @@ class LatexFontChecker:
self.re_cjk = re.compile(r"([^:]+):\s*Noto\s+(Sans|Sans Mono|Serif) CJK")
def description(self):
+ """
+ Returns module description.
+ """
return __doc__
def get_noto_cjk_vf_fonts(self):
- """Get Noto CJK fonts"""
+ """
+ Get Noto CJK fonts.
+ """
cjk_fonts = set()
cmd = ["fc-list", ":", "file", "family", "variable"]
@@ -143,7 +158,9 @@ class LatexFontChecker:
return sorted(cjk_fonts)
def check(self):
- """Check for problems with CJK fonts"""
+ """
+ Check for problems with CJK fonts.
+ """
fonts = textwrap.indent("\n".join(self.get_noto_cjk_vf_fonts()), " ")
if not fonts:
diff --git a/tools/lib/python/kdoc/parse_data_structs.py b/tools/lib/python/kdoc/parse_data_structs.py
index 25361996cd20..9941cd19032e 100755
--- a/tools/lib/python/kdoc/parse_data_structs.py
+++ b/tools/lib/python/kdoc/parse_data_structs.py
@@ -9,12 +9,12 @@ Parse a source file or header, creating ReStructured Text cross references.
It accepts an optional file to change the default symbol reference or to
suppress symbols from the output.
-It is capable of identifying defines, functions, structs, typedefs,
-enums and enum symbols and create cross-references for all of them.
+It is capable of identifying ``define``, function, ``struct``, ``typedef``,
+``enum`` and ``enum`` symbols and create cross-references for all of them.
It is also capable of distinguish #define used for specifying a Linux
ioctl.
-The optional rules file contains a set of rules like:
+The optional rules file contains a set of rules like::
ignore ioctl VIDIOC_ENUM_FMT
replace ioctl VIDIOC_DQBUF vidioc_qbuf
@@ -34,8 +34,8 @@ class ParseDataStructs:
It is meant to allow having a more comprehensive documentation, where
uAPI headers will create cross-reference links to the code.
- It is capable of identifying defines, functions, structs, typedefs,
- enums and enum symbols and create cross-references for all of them.
+ It is capable of identifying ``define``, function, ``struct``, ``typedef``,
+ ``enum`` and ``enum`` symbols and create cross-references for all of them.
It is also capable of distinguish #define used for specifying a Linux
ioctl.
@@ -43,13 +43,13 @@ class ParseDataStructs:
allows parsing an exception file. Such file contains a set of rules
using the syntax below:
- 1. Ignore rules:
+ 1. Ignore rules::
ignore <type> <symbol>`
Removes the symbol from reference generation.
- 2. Replace rules:
+ 2. Replace rules::
replace <type> <old_symbol> <new_reference>
@@ -58,22 +58,22 @@ class ParseDataStructs:
- A simple symbol name;
- A full Sphinx reference.
- 3. Namespace rules
+ 3. Namespace rules::
namespace <namespace>
Sets C namespace to be used during cross-reference generation. Can
be overridden by replace rules.
- On ignore and replace rules, <type> can be:
- - ioctl: for defines that end with _IO*, e.g. ioctl definitions
- - define: for other defines
- - symbol: for symbols defined within enums;
- - typedef: for typedefs;
- - enum: for the name of a non-anonymous enum;
- - struct: for structs.
+ On ignore and replace rules, ``<type>`` can be:
+ - ``ioctl``: for defines that end with ``_IO*``, e.g. ioctl definitions
+ - ``define``: for other defines
+ - ``symbol``: for symbols defined within enums;
+ - ``typedef``: for typedefs;
+ - ``enum``: for the name of a non-anonymous enum;
+ - ``struct``: for structs.
- Examples:
+ Examples::
ignore define __LINUX_MEDIA_H
ignore ioctl VIDIOC_ENUM_FMT
@@ -83,13 +83,15 @@ class ParseDataStructs:
namespace MC
"""
- # Parser regexes with multiple ways to capture enums and structs
+ #: Parser regex with multiple ways to capture enums.
RE_ENUMS = [
re.compile(r"^\s*enum\s+([\w_]+)\s*\{"),
re.compile(r"^\s*enum\s+([\w_]+)\s*$"),
re.compile(r"^\s*typedef\s*enum\s+([\w_]+)\s*\{"),
re.compile(r"^\s*typedef\s*enum\s+([\w_]+)\s*$"),
]
+
+ #: Parser regex with multiple ways to capture structs.
RE_STRUCTS = [
re.compile(r"^\s*struct\s+([_\w][\w\d_]+)\s*\{"),
re.compile(r"^\s*struct\s+([_\w][\w\d_]+)$"),
@@ -97,11 +99,13 @@ class ParseDataStructs:
re.compile(r"^\s*typedef\s*struct\s+([_\w][\w\d_]+)$"),
]
- # FIXME: the original code was written a long time before Sphinx C
+ # NOTE: the original code was written a long time before Sphinx C
# domain to have multiple namespaces. To avoid to much turn at the
# existing hyperlinks, the code kept using "c:type" instead of the
# right types. To change that, we need to change the types not only
# here, but also at the uAPI media documentation.
+
+ #: Dictionary containing C type identifiers to be transformed.
DEF_SYMBOL_TYPES = {
"ioctl": {
"prefix": "\\ ",
@@ -158,6 +162,10 @@ class ParseDataStructs:
self.symbols[symbol_type] = {}
def read_exceptions(self, fname: str):
+ """
+ Read an optional exceptions file, used to override defaults.
+ """
+
if not fname:
return
@@ -242,9 +250,9 @@ class ParseDataStructs:
def store_type(self, ln, symbol_type: str, symbol: str,
ref_name: str = None, replace_underscores: bool = True):
"""
- Stores a new symbol at self.symbols under symbol_type.
+ Store a new symbol at self.symbols under symbol_type.
- By default, underscores are replaced by "-"
+ By default, underscores are replaced by ``-``.
"""
defs = self.DEF_SYMBOL_TYPES[symbol_type]
@@ -276,12 +284,16 @@ class ParseDataStructs:
self.symbols[symbol_type][symbol] = (f"{prefix}{ref_link}{suffix}", ln)
def store_line(self, line):
- """Stores a line at self.data, properly indented"""
+ """
+ Store a line at self.data, properly indented.
+ """
line = " " + line.expandtabs()
self.data += line.rstrip(" ")
def parse_file(self, file_in: str, exceptions: str = None):
- """Reads a C source file and get identifiers"""
+ """
+ Read a C source file and get identifiers.
+ """
self.data = ""
is_enum = False
is_comment = False
@@ -433,7 +445,7 @@ class ParseDataStructs:
def gen_toc(self):
"""
- Create a list of symbols to be part of a TOC contents table
+ Create a list of symbols to be part of a TOC contents table.
"""
text = []
@@ -464,6 +476,10 @@ class ParseDataStructs:
return "\n".join(text)
def write_output(self, file_in: str, file_out: str, toc: bool):
+ """
+ Write a ReST output file.
+ """
+
title = os.path.basename(file_in)
if toc:
diff --git a/tools/lib/python/kdoc/python_version.py b/tools/lib/python/kdoc/python_version.py
index e83088013db2..4ddb7ead5f56 100644
--- a/tools/lib/python/kdoc/python_version.py
+++ b/tools/lib/python/kdoc/python_version.py
@@ -33,21 +33,31 @@ class PythonVersion:
"""
def __init__(self, version):
- """Ïnitialize self.version tuple from a version string"""
+ """
+ Ïnitialize self.version tuple from a version string.
+ """
self.version = self.parse_version(version)
@staticmethod
def parse_version(version):
- """Convert a major.minor.patch version into a tuple"""
+ """
+ Convert a major.minor.patch version into a tuple.
+ """
return tuple(int(x) for x in version.split("."))
@staticmethod
def ver_str(version):
- """Returns a version tuple as major.minor.patch"""
+ """
+ Returns a version tuple as major.minor.patch.
+ """
return ".".join([str(x) for x in version])
@staticmethod
def cmd_print(cmd, max_len=80):
+ """
+ Outputs a command line, repecting maximum width.
+ """
+
cmd_line = []
for w in cmd:
@@ -66,7 +76,9 @@ class PythonVersion:
return "\n ".join(cmd_line)
def __str__(self):
- """Returns a version tuple as major.minor.patch from self.version"""
+ """
+ Return a version tuple as major.minor.patch from self.version.
+ """
return self.ver_str(self.version)
@staticmethod