* [PATCH v3 01/13] docs: maintainers_include: keep hidden TOC sorted
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 02/13] docs: maintainers_include: split state machine on multiple funcs Mauro Carvalho Chehab
` (12 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux, Shuah Khan
There's no practical difference on keeping it sorted, but
it helps a lot when checking for differences after patches
to the tool.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <e6b302f2826e6a5c0124bb33cc517e8b5888252b.1777987027.git.mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 436e7ac42ffc..694cdbdc4caf 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -265,7 +265,7 @@ class MaintainersProfile(Include):
output += "\n.. toctree::\n"
output += " :hidden:\n\n"
- for fname in maint_parser.profile_toc:
+ for fname in sorted(maint_parser.profile_toc):
output += f" {fname}\n"
output += "\n"
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 02/13] docs: maintainers_include: split state machine on multiple funcs
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 01/13] docs: maintainers_include: keep hidden TOC sorted Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 03/13] docs: maintainers_include: cleanup the code Mauro Carvalho Chehab
` (11 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux, Shuah Khan
Instead of one big __init__ code, split the MaintainersParser
code in a way that the state machine remains on __init__, but
the actual parser for descriptions and subsystems are moved
to separate functions.
To make parser easier, instead storing parsed results on a list,
place them directly on a string.
That granted 15% of performance increase(*) with Python 3.14 and
made the logic simpler.
(*) measured by creating a new directory under Documentation/,
and placing justmaintainers.rst and an index file there,
building it via sphinx-build-wrapper.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <0b72530cf496ce5e2987ca784058a50f4dc814d2.1777987027.git.mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 297 +++++++++++---------
1 file changed, 158 insertions(+), 139 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 694cdbdc4caf..6d47d55f5b73 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -47,167 +47,186 @@ class MaintainersParser:
self.profile_toc = set()
self.profile_entries = {}
- result = list()
- result.append(".. _maintainers:")
- result.append("")
+ self.output = ".. _maintainers:\n\n"
# Poor man's state machine.
- descriptions = False
- maintainers = False
- subsystems = False
+ self.descriptions = False
+ self.maintainers = False
+ self.subsystems = False
# Field letter to field name mapping.
- field_letter = None
- fields = dict()
+ self.field_letter = None
+ self.fields = dict()
+
+ self.field_prev = ""
+ self.field_content = ""
+ self.subsystem_name = None
+
+ self.app_dir = app_dir
+ self.base_dir, self.doc_dir, self.sphinx_dir = app_dir.partition("Documentation")
+
+ self.re_doc = re.compile(r'(Documentation/([^\s\?\*]*)\.rst)')
prev = None
- field_prev = ""
- field_content = ""
- subsystem_name = None
-
- base_dir, doc_dir, sphinx_dir = app_dir.partition("Documentation")
-
for line in open(path):
- # Have we reached the end of the preformatted Descriptions text?
- if descriptions and line.startswith('Maintainers'):
- descriptions = False
- # Ensure a blank line following the last "|"-prefixed line.
- result.append("")
-
- # Start subsystem processing? This is to skip processing the text
- # between the Maintainers heading and the first subsystem name.
- if maintainers and not subsystems:
+ if self.descriptions:
+ self.parse_descriptions(line)
+ elif self.maintainers and not self.subsystems:
if re.search('^[A-Z0-9]', line):
- subsystems = True
-
- # Drop needless input whitespace.
- line = line.rstrip()
-
- #
- # Handle profile entries - either as files or as https refs
- #
- match = re.match(rf"P:\s*({doc_dir})(/\S+)\.rst", line)
- if match:
- name = "".join(match.groups())
- entry = os.path.relpath(base_dir + name, app_dir)
-
- full_name = os.path.join(base_dir, name)
- path = os.path.relpath(full_name, app_dir)
- #
- # When SPHINXDIRS is used, it will try to reference files
- # outside srctree, causing warnings. To avoid that, point
- # to the latest official documentation
- #
- if path.startswith("../"):
- entry = KERNELDOC_URL + match.group(2) + ".html"
+ self.subsystems = True
+ self.parse_subsystems(line)
else:
- entry = "/" + entry
-
- if "*" in entry:
- for e in glob(entry):
- self.profile_toc.add(e)
- self.profile_entries[subsystem_name] = e
- else:
- self.profile_toc.add(entry)
- self.profile_entries[subsystem_name] = entry
- else:
- match = re.match(r"P:\s*(https?://.*)", line)
- if match:
- entry = match.group(1).strip()
- self.profile_entries[subsystem_name] = entry
-
- # Linkify all non-wildcard refs to ReST files in Documentation/.
- pat = r'(Documentation/([^\s\?\*]*)\.rst)'
- m = re.search(pat, line)
- if m:
- # maintainers.rst is in a subdirectory, so include "../".
- line = re.sub(pat, ':doc:`%s <../%s>`' % (m.group(2), m.group(2)), line)
-
- # Check state machine for output rendering behavior.
- output = None
- if descriptions:
- # Escape the escapes in preformatted text.
- output = "| %s" % (line.replace("\\", "\\\\"))
- # Look for and record field letter to field name mappings:
- # R: Designated *reviewer*: FullName <address@domain>
- m = re.search(r"\s(\S):\s", line)
- if m:
- field_letter = m.group(1)
- if field_letter and not field_letter in fields:
- m = re.search(r"\*([^\*]+)\*", line)
- if m:
- fields[field_letter] = m.group(1)
- elif subsystems:
- # Skip empty lines: subsystem parser adds them as needed.
- if len(line) == 0:
- continue
- # Subsystem fields are batched into "field_content"
- if line[1] != ':':
- # Render a subsystem entry as:
- # SUBSYSTEM NAME
- # ~~~~~~~~~~~~~~
-
- # Flush pending field content.
- output = field_content + "\n\n"
- field_content = ""
-
- subsystem_name = line.title()
-
- # Collapse whitespace in subsystem name.
- heading = re.sub(r"\s+", " ", line)
- output = output + "%s\n%s" % (heading, "~" * len(heading))
- field_prev = ""
- else:
- # Render a subsystem field as:
- # :Field: entry
- # entry...
- field, details = line.split(':', 1)
- details = details.strip()
-
- # Mark paths (and regexes) as literal text for improved
- # readability and to escape any escapes.
- if field in ['F', 'N', 'X', 'K']:
- # But only if not already marked :)
- if not ':doc:' in details:
- details = '``%s``' % (details)
-
- # Comma separate email field continuations.
- if field == field_prev and field_prev in ['M', 'R', 'L']:
- field_content = field_content + ","
-
- # Do not repeat field names, so that field entries
- # will be collapsed together.
- if field != field_prev:
- output = field_content + "\n"
- field_content = ":%s:" % (fields.get(field, field))
- field_content = field_content + "\n\t%s" % (details)
- field_prev = field
+ self.output += line
+ elif self.subsystems:
+ self.parse_subsystems(line)
else:
- output = line
-
- # Re-split on any added newlines in any above parsing.
- if output != None:
- for separated in output.split('\n'):
- result.append(separated)
+ self.output += line
# Update the state machine when we find heading separators.
if line.startswith('----------'):
if prev.startswith('Descriptions'):
- descriptions = True
+ self.descriptions = True
if prev.startswith('Maintainers'):
- maintainers = True
+ self.maintainers = True
# Retain previous line for state machine transitions.
prev = line
# Flush pending field contents.
- if field_content != "":
- for separated in field_content.split('\n'):
- result.append(separated)
+ if self.field_content:
+ self.output += self.field_content + "\n\n"
- self.output = "\n".join(result)
+ self.output = self.output.rstrip()
+
+ def parse_descriptions(self, line):
+ """Handle contents of the descriptions section."""
+
+ # Have we reached the end of the preformatted Descriptions text?
+ if line.startswith('Maintainers'):
+ self.descriptions = False
+ self.output += "\n" + line
+ return
+
+ # Linkify all non-wildcard refs to ReST files in Documentation/.
+ m = self.re_doc.search(line)
+ if m:
+ # maintainers.rst is in a subdirectory, so include "../".
+ line = self.re_doc.sub(':doc:`%s <../%s>`' % (m.group(2), m.group(2)), line)
+
+ # Escape the escapes in preformatted text.
+ output = "| %s" % (line.replace("\\", "\\\\"))
+
+ # Look for and record field letter to field name mappings:
+ # R: Designated *reviewer*: FullName <address@domain>
+ m = re.search(r"\s(\S):\s", line)
+ if m:
+ self.field_letter = m.group(1)
+
+ if self.field_letter and self.field_letter not in self.fields:
+ m = re.search(r"\*([^\*]+)\*", line)
+ if m:
+ self.fields[self.field_letter] = m.group(1)
+
+ # Append parsed content to self.output
+ self.output += output
+
+ def parse_subsystems(self, line):
+ """Handle contents of the per-subsystem sections."""
+
+ # Drop needless input whitespace.
+ line = line.rstrip()
+
+ #
+ # Handle profile entries - either as files or as https refs
+ #
+ match = re.match(rf"P:\s*({self.doc_dir})(/\S+)\.rst", line)
+ if match:
+ name = "".join(match.groups())
+ entry = os.path.relpath(self.base_dir + name, self.app_dir)
+
+ full_name = os.path.join(self.base_dir, name)
+ path = os.path.relpath(full_name, self.app_dir)
+ #
+ # When SPHINXDIRS is used, it will try to reference files
+ # outside srctree, causing warnings. To avoid that, point
+ # to the latest official documentation
+ #
+ if path.startswith("../"):
+ entry = KERNELDOC_URL + match.group(2) + ".html"
+ else:
+ entry = "/" + entry
+
+ if "*" in entry:
+ for e in glob(entry):
+ self.profile_toc.add(e)
+ self.profile_entries[self.subsystem_name] = e
+ else:
+ self.profile_toc.add(entry)
+ self.profile_entries[self.subsystem_name] = entry
+ else:
+ match = re.match(r"P:\s*(https?://.*)", line)
+ if match:
+ entry = match.group(1).strip()
+ self.profile_entries[self.subsystem_name] = entry
+
+ # Linkify all non-wildcard refs to ReST files in Documentation/.
+ m = self.re_doc.search(line)
+ if m:
+ # maintainers.rst is in a subdirectory, so include "../".
+ line = self.re_doc.sub(':doc:`%s <../%s>`' % (m.group(2), m.group(2)), line)
+
+ # Check state machine for output rendering behavior.
+ output = None
+ if self.subsystems:
+ # Skip empty lines: subsystem parser adds them as needed.
+ if len(line) == 0:
+ return
+ # Subsystem fields are batched into "field_content"
+ if line[1] != ':':
+ # Render a subsystem entry as:
+ # SUBSYSTEM NAME
+ # ~~~~~~~~~~~~~~
+ # Flush pending field content.
+ output = self.field_content + "\n\n"
+ self.field_content = ""
+
+ self.subsystem_name = line.title()
+
+ # Collapse whitespace in subsystem name.
+ heading = re.sub(r"\s+", " ", line)
+ output = output + "%s\n%s" % (heading, "~" * len(heading))
+ self.field_prev = ""
+ else:
+ # Render a subsystem field as:
+ # :Field: entry
+ # entry...
+ field, details = line.split(':', 1)
+ details = details.strip()
+
+ # Mark paths (and regexes) as literal text for improved
+ # readability and to escape any escapes.
+ if field in ['F', 'N', 'X', 'K']:
+ # But only if not already marked :)
+ if not ':doc:' in details:
+ details = '``%s``' % (details)
+
+ # Comma separate email field continuations.
+ if field == self.field_prev and self.field_prev in ['M', 'R', 'L']:
+ self.field_content = self.field_content + ","
+
+ # Do not repeat field names, so that field entries
+ # will be collapsed together.
+ if field != self.field_prev:
+ output = self.field_content + "\n"
+ self.field_content = ":%s:" % (self.fields.get(field, field))
+ self.field_content = self.field_content + "\n\t%s" % (details)
+ self.field_prev = field
+ elif not self.descriptions:
+ output = line
+
+ if output is not None:
+ self.output += output + "\n"
- # Create a TOC class
class MaintainersInclude(Include):
"""MaintainersInclude (``maintainers-include``) directive"""
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 03/13] docs: maintainers_include: cleanup the code
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 01/13] docs: maintainers_include: keep hidden TOC sorted Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 02/13] docs: maintainers_include: split state machine on multiple funcs Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 04/13] docs: maintainers_include: clean most SPHINXDIRS=process warnings Mauro Carvalho Chehab
` (10 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux, Shuah Khan
Simplify the logic without affecting the output result.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <24a83995778de8710cac40a3089c2f2fe5c38dbd.1777987027.git.mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 184 ++++++++++----------
1 file changed, 91 insertions(+), 93 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 6d47d55f5b73..615af227a8f8 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -44,10 +44,6 @@ class MaintainersParser:
def __init__(self, app_dir, path):
self.path = path
- self.profile_toc = set()
- self.profile_entries = {}
-
- self.output = ".. _maintainers:\n\n"
# Poor man's state machine.
self.descriptions = False
@@ -67,6 +63,13 @@ class MaintainersParser:
self.re_doc = re.compile(r'(Documentation/([^\s\?\*]*)\.rst)')
+ #
+ # Output variables with maintainers content to be stored
+ #
+ self.profile_toc = set()
+ self.profile_entries = {}
+ self.output = ".. _maintainers:\n\n"
+
prev = None
for line in open(path):
if self.descriptions:
@@ -98,6 +101,16 @@ class MaintainersParser:
self.output = self.output.rstrip()
+
+ def linkify(self, text):
+ """Linkify all non-wildcard refs to ReST files in Documentation/"""
+ m = self.re_doc.search(text)
+ if m:
+ # maintainers.rst is in a subdirectory, so include "../".
+ text = self.re_doc.sub(':doc:`%s <../%s>`' % (m.group(2), m.group(2)), text)
+
+ return text
+
def parse_descriptions(self, line):
"""Handle contents of the descriptions section."""
@@ -107,14 +120,8 @@ class MaintainersParser:
self.output += "\n" + line
return
- # Linkify all non-wildcard refs to ReST files in Documentation/.
- m = self.re_doc.search(line)
- if m:
- # maintainers.rst is in a subdirectory, so include "../".
- line = self.re_doc.sub(':doc:`%s <../%s>`' % (m.group(2), m.group(2)), line)
-
# Escape the escapes in preformatted text.
- output = "| %s" % (line.replace("\\", "\\\\"))
+ self.output += "| " + self.linkify(line).replace("\\", "\\\\")
# Look for and record field letter to field name mappings:
# R: Designated *reviewer*: FullName <address@domain>
@@ -127,105 +134,96 @@ class MaintainersParser:
if m:
self.fields[self.field_letter] = m.group(1)
- # Append parsed content to self.output
- self.output += output
-
def parse_subsystems(self, line):
"""Handle contents of the per-subsystem sections."""
# Drop needless input whitespace.
line = line.rstrip()
+ # Skip empty lines: subsystem parser adds them as needed.
+ if not line:
+ return
+
+ # Subsystem fields are batched into "field_content"
+ if line[1] != ':':
+ line = self.linkify(line)
+
+ # Render a subsystem entry as:
+ # SUBSYSTEM NAME
+ # ~~~~~~~~~~~~~~
+ # Flush pending field content.
+ self.output += self.field_content + "\n\n"
+ self.field_content = ""
+
+ self.subsystem_name = line.title()
+
+ # Collapse whitespace in subsystem name.
+ heading = re.sub(r"\s+", " ", line)
+ self.output += "%s\n%s" % (heading, "~" * len(heading)) + "\n"
+ self.field_prev = ""
+
+ return
+
+ # Render a subsystem field as:
+ # :Field: entry
+ # entry...
+ field, details = line.split(':', 1)
+ details = details.strip()
+
#
# Handle profile entries - either as files or as https refs
#
- match = re.match(rf"P:\s*({self.doc_dir})(/\S+)\.rst", line)
- if match:
- name = "".join(match.groups())
- entry = os.path.relpath(self.base_dir + name, self.app_dir)
-
- full_name = os.path.join(self.base_dir, name)
- path = os.path.relpath(full_name, self.app_dir)
- #
- # When SPHINXDIRS is used, it will try to reference files
- # outside srctree, causing warnings. To avoid that, point
- # to the latest official documentation
- #
- if path.startswith("../"):
- entry = KERNELDOC_URL + match.group(2) + ".html"
- else:
- entry = "/" + entry
-
- if "*" in entry:
- for e in glob(entry):
- self.profile_toc.add(e)
- self.profile_entries[self.subsystem_name] = e
- else:
- self.profile_toc.add(entry)
- self.profile_entries[self.subsystem_name] = entry
- else:
- match = re.match(r"P:\s*(https?://.*)", line)
+ if field == "P":
+ match = self.re_doc.match(details)
if match:
- entry = match.group(1).strip()
- self.profile_entries[self.subsystem_name] = entry
+ name = "".join(match.groups())
+ entry = os.path.relpath(self.base_dir + name, self.app_dir)
- # Linkify all non-wildcard refs to ReST files in Documentation/.
- m = self.re_doc.search(line)
- if m:
- # maintainers.rst is in a subdirectory, so include "../".
- line = self.re_doc.sub(':doc:`%s <../%s>`' % (m.group(2), m.group(2)), line)
+ full_name = os.path.join(self.base_dir, name)
+ path = os.path.relpath(full_name, self.app_dir)
+ #
+ # When SPHINXDIRS is used, it will try to reference files
+ # outside srctree, causing warnings. To avoid that, point
+ # to the latest official documentation
+ #
+ if path.startswith("../"):
+ entry = KERNELDOC_URL + "/" + match.group(2) + ".html"
+ else:
+ entry = "/" + entry
- # Check state machine for output rendering behavior.
- output = None
- if self.subsystems:
- # Skip empty lines: subsystem parser adds them as needed.
- if len(line) == 0:
- return
- # Subsystem fields are batched into "field_content"
- if line[1] != ':':
- # Render a subsystem entry as:
- # SUBSYSTEM NAME
- # ~~~~~~~~~~~~~~
- # Flush pending field content.
- output = self.field_content + "\n\n"
- self.field_content = ""
-
- self.subsystem_name = line.title()
-
- # Collapse whitespace in subsystem name.
- heading = re.sub(r"\s+", " ", line)
- output = output + "%s\n%s" % (heading, "~" * len(heading))
- self.field_prev = ""
+ if "*" in entry:
+ for e in glob(entry):
+ self.profile_toc.add(e)
+ self.profile_entries[self.subsystem_name] = e
+ else:
+ self.profile_toc.add(entry)
+ self.profile_entries[self.subsystem_name] = entry
else:
- # Render a subsystem field as:
- # :Field: entry
- # entry...
- field, details = line.split(':', 1)
- details = details.strip()
+ match = re.match(r"(https?://.*)", details)
+ if match:
+ entry = match.group(1).strip()
+ self.profile_entries[self.subsystem_name] = entry
- # Mark paths (and regexes) as literal text for improved
- # readability and to escape any escapes.
- if field in ['F', 'N', 'X', 'K']:
- # But only if not already marked :)
- if not ':doc:' in details:
- details = '``%s``' % (details)
+ details = self.linkify(details)
- # Comma separate email field continuations.
- if field == self.field_prev and self.field_prev in ['M', 'R', 'L']:
- self.field_content = self.field_content + ","
+ # Mark paths (and regexes) as literal text for improved
+ # readability and to escape any escapes.
+ if field in ['F', 'N', 'X', 'K']:
+ # But only if not already marked :)
+ if not ':doc:' in details:
+ details = '``%s``' % (details)
- # Do not repeat field names, so that field entries
- # will be collapsed together.
- if field != self.field_prev:
- output = self.field_content + "\n"
- self.field_content = ":%s:" % (self.fields.get(field, field))
- self.field_content = self.field_content + "\n\t%s" % (details)
- self.field_prev = field
- elif not self.descriptions:
- output = line
+ # Comma separate email field continuations.
+ if field == self.field_prev and self.field_prev in ['M', 'R', 'L']:
+ self.field_content = self.field_content + ","
- if output is not None:
- self.output += output + "\n"
+ # Do not repeat field names, so that field entries
+ # will be collapsed together.
+ if field != self.field_prev:
+ self.output += self.field_content + "\n\n"
+ self.field_content = ":%s:" % (self.fields.get(field, field))
+ self.field_content = self.field_content + "\n\t%s" % (details)
+ self.field_prev = field
class MaintainersInclude(Include):
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 04/13] docs: maintainers_include: clean most SPHINXDIRS=process warnings
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
` (2 preceding siblings ...)
2026-05-09 6:56 ` [PATCH v3 03/13] docs: maintainers_include: cleanup the code Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 05/13] docs: maintainers_include: do some coding style cleanups Mauro Carvalho Chehab
` (9 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux, Shuah Khan
building docs with SPHINXDIRS=process is too noisy, as it
generates lots of undefined refs. Fixing it is easy: just let
linkify generate html URLs for the broken links when SPHINXDIRS
is used.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <b57d83081c28aa52683b403f8836d098fcdd8530.1777987027.git.mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 44 +++++++++++++++------
1 file changed, 32 insertions(+), 12 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 615af227a8f8..d3ad01e5309e 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -58,8 +58,8 @@ class MaintainersParser:
self.field_content = ""
self.subsystem_name = None
- self.app_dir = app_dir
- self.base_dir, self.doc_dir, self.sphinx_dir = app_dir.partition("Documentation")
+ self.app_dir = os.path.abspath(app_dir)
+ self.base_dir, _, self.sphinx_dir = self.app_dir.partition("Documentation")
self.re_doc = re.compile(r'(Documentation/([^\s\?\*]*)\.rst)')
@@ -104,10 +104,25 @@ class MaintainersParser:
def linkify(self, text):
"""Linkify all non-wildcard refs to ReST files in Documentation/"""
+
m = self.re_doc.search(text)
if m:
- # maintainers.rst is in a subdirectory, so include "../".
- text = self.re_doc.sub(':doc:`%s <../%s>`' % (m.group(2), m.group(2)), text)
+ fname = m.group(1)
+ ename = m.group(2)
+
+ entry = os.path.relpath(self.base_dir + fname, self.app_dir)
+ entry = entry.removesuffix(".rst")
+
+ #
+ # When SPHINXDIRS is used, it will try to reference files
+ # outside srctree, causing warnings. To avoid that, point
+ # to the latest official documentation
+ #
+ if entry.startswith("../"):
+ html = KERNELDOC_URL + ename + ".html"
+ text = self.re_doc.sub(f'`{ename} <{html}>`_', text)
+ else:
+ text = self.re_doc.sub(f':doc:`{ename} </{entry}>`', text)
return text
@@ -176,27 +191,32 @@ class MaintainersParser:
if field == "P":
match = self.re_doc.match(details)
if match:
- name = "".join(match.groups())
- entry = os.path.relpath(self.base_dir + name, self.app_dir)
+ fname = match.group(1)
+ ename = match.group(2)
- full_name = os.path.join(self.base_dir, name)
- path = os.path.relpath(full_name, self.app_dir)
+ entry = os.path.relpath(self.base_dir + fname, self.app_dir)
+ entry = entry.removesuffix(".rst")
#
# When SPHINXDIRS is used, it will try to reference files
# outside srctree, causing warnings. To avoid that, point
# to the latest official documentation
#
- if path.startswith("../"):
- entry = KERNELDOC_URL + "/" + match.group(2) + ".html"
+
+ if entry.startswith("../"):
+ entry = KERNELDOC_URL + ename + ".html"
else:
entry = "/" + entry
if "*" in entry:
for e in glob(entry):
- self.profile_toc.add(e)
+ if "html" not in e:
+ self.profile_toc.add(e)
+
self.profile_entries[self.subsystem_name] = e
else:
- self.profile_toc.add(entry)
+ if "html" not in entry:
+ self.profile_toc.add(entry)
+
self.profile_entries[self.subsystem_name] = entry
else:
match = re.match(r"(https?://.*)", details)
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 05/13] docs: maintainers_include: do some coding style cleanups
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
` (3 preceding siblings ...)
2026-05-09 6:56 ` [PATCH v3 04/13] docs: maintainers_include: clean most SPHINXDIRS=process warnings Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 06/13] docs: maintainers_include: store maintainers entries on a dict Mauro Carvalho Chehab
` (8 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux, Shuah Khan
Minor coding style adjustments to use the style most python
doc scripts are following.
No functional changes.
Assisted-by: pylint, black
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <460aabd0518f080b34e12fdc0beb7ec7685d5866.1777987027.git.mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 101 ++++++++++----------
1 file changed, 51 insertions(+), 50 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index d3ad01e5309e..345eb28804ff 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -1,30 +1,25 @@
#!/usr/bin/env python
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8; mode: python -*-
-# pylint: disable=R0903, C0330, R0914, R0912, E0401
+# pylint: disable=C0209, C0301, E0401, R0022, R0902, R0903, R0912, R0914
"""
- maintainers-include
- ~~~~~~~~~~~~~~~~~~~
+Implementation of the ``maintainers-include`` reST-directive.
- Implementation of the ``maintainers-include`` reST-directive.
+:copyright: Copyright (C) 2019 Kees Cook <keescook@chromium.org>
+:license: GPL Version 2, June 1991 see linux/COPYING for details.
- :copyright: Copyright (C) 2019 Kees Cook <keescook@chromium.org>
- :license: GPL Version 2, June 1991 see linux/COPYING for details.
-
- The ``maintainers-include`` reST-directive performs extensive parsing
- specific to the Linux kernel's standard "MAINTAINERS" file, in an
- effort to avoid needing to heavily mark up the original plain text.
+The ``maintainers-include`` reST-directive performs extensive parsing
+specific to the Linux kernel's standard "MAINTAINERS" file, in an
+effort to avoid needing to heavily mark up the original plain text.
"""
-import sys
-import re
import os.path
+import re
from glob import glob
from docutils import statemachine
-from docutils.parsers.rst import Directive
from docutils.parsers.rst.directives.misc import Include
#
@@ -32,12 +27,14 @@ from docutils.parsers.rst.directives.misc import Include
#
KERNELDOC_URL = "https://docs.kernel.org/"
-def ErrorString(exc): # Shamelessly stolen from docutils
- return f'{exc.__class__.__name}: {exc}'
+__version__ = "1.0"
-__version__ = '1.0'
+maint_parser = None # pylint: disable=C0103
-maint_parser = None
+
+# Shamelessly stolen from docutils
+def ErrorString(exc): # pylint: disable=C0103, C0116
+ return f"{exc.__class__.__name}: {exc}" # pylint: disable=W0212
class MaintainersParser:
"""Parse MAINTAINERS file(s) content"""
@@ -52,7 +49,7 @@ class MaintainersParser:
# Field letter to field name mapping.
self.field_letter = None
- self.fields = dict()
+ self.fields = {}
self.field_prev = ""
self.field_content = ""
@@ -71,29 +68,30 @@ class MaintainersParser:
self.output = ".. _maintainers:\n\n"
prev = None
- for line in open(path):
- if self.descriptions:
- self.parse_descriptions(line)
- elif self.maintainers and not self.subsystems:
- if re.search('^[A-Z0-9]', line):
- self.subsystems = True
+ with open(path, "r", encoding="utf-8") as fp:
+ for line in fp:
+ if self.descriptions:
+ self.parse_descriptions(line)
+ elif self.maintainers and not self.subsystems:
+ if re.search('^[A-Z0-9]', line):
+ self.subsystems = True
+ self.parse_subsystems(line)
+ else:
+ self.output += line
+ elif self.subsystems:
self.parse_subsystems(line)
else:
self.output += line
- elif self.subsystems:
- self.parse_subsystems(line)
- else:
- self.output += line
- # Update the state machine when we find heading separators.
- if line.startswith('----------'):
- if prev.startswith('Descriptions'):
- self.descriptions = True
- if prev.startswith('Maintainers'):
- self.maintainers = True
+ # Update the state machine when we find heading separators.
+ if line.startswith("----------"):
+ if prev.startswith("Descriptions"):
+ self.descriptions = True
+ if prev.startswith("Maintainers"):
+ self.maintainers = True
- # Retain previous line for state machine transitions.
- prev = line
+ # Retain previous line for state machine transitions.
+ prev = line
# Flush pending field contents.
if self.field_content:
@@ -130,7 +128,7 @@ class MaintainersParser:
"""Handle contents of the descriptions section."""
# Have we reached the end of the preformatted Descriptions text?
- if line.startswith('Maintainers'):
+ if line.startswith("Maintainers"):
self.descriptions = False
self.output += "\n" + line
return
@@ -182,7 +180,7 @@ class MaintainersParser:
# Render a subsystem field as:
# :Field: entry
# entry...
- field, details = line.split(':', 1)
+ field, details = line.split(":", 1)
details = details.strip()
#
@@ -248,12 +246,11 @@ class MaintainersParser:
class MaintainersInclude(Include):
"""MaintainersInclude (``maintainers-include``) directive"""
+
required_arguments = 0
def emit(self):
"""Parse all the MAINTAINERS lines into ReST for human-readability"""
- global maint_parser
-
path = maint_parser.path
output = maint_parser.output
@@ -269,20 +266,21 @@ class MaintainersInclude(Include):
raise self.warning('"%s" directive disabled.' % self.name)
try:
- lines = self.emit()
+ self.emit()
except IOError as error:
raise self.severe('Problems with "%s" directive path:\n%s.' %
(self.name, ErrorString(error)))
return []
+
class MaintainersProfile(Include):
+ """Generate a list with all maintainer's profiles"""
+
required_arguments = 0
def emit(self):
"""Parse all the MAINTAINERS lines looking for profile entries"""
- global maint_parser
-
path = maint_parser.path
#
@@ -316,15 +314,17 @@ class MaintainersProfile(Include):
raise self.warning('"%s" directive disabled.' % self.name)
try:
- lines = self.emit()
+ self.emit()
except IOError as error:
raise self.severe('Problems with "%s" directive path:\n%s.' %
(self.name, ErrorString(error)))
return []
+
def setup(app):
- global maint_parser
+ """Setup Sphinx extension"""
+ global maint_parser # pylint: disable=W0603
#
# NOTE: we're using os.fspath() here because of a Sphinx warning:
@@ -338,8 +338,9 @@ def setup(app):
app.add_directive("maintainers-include", MaintainersInclude)
app.add_directive("maintainers-profile-toc", MaintainersProfile)
- return dict(
- version = __version__,
- parallel_read_safe = True,
- parallel_write_safe = True
- )
+
+ return {
+ "version": __version__,
+ "parallel_read_safe": True,
+ "parallel_write_safe": True,
+ }
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 06/13] docs: maintainers_include: store maintainers entries on a dict
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
` (4 preceding siblings ...)
2026-05-09 6:56 ` [PATCH v3 05/13] docs: maintainers_include: do some coding style cleanups Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 07/13] docs: maintainers_include: properly handle file patterns Mauro Carvalho Chehab
` (7 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux, Shuah Khan
Instead of creating just a big output data, store entries inside
a dictionary. Doing that simplifies the parser a little bit
and make the code clearer.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <4ad88179e03436984f29780ae380d50591f86c67.1777987027.git.mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 70 +++++++++------------
1 file changed, 28 insertions(+), 42 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 345eb28804ff..ed9cd7b3dc66 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -49,10 +49,7 @@ class MaintainersParser:
# Field letter to field name mapping.
self.field_letter = None
- self.fields = {}
- self.field_prev = ""
- self.field_content = ""
self.subsystem_name = None
self.app_dir = os.path.abspath(app_dir)
@@ -65,7 +62,9 @@ class MaintainersParser:
#
self.profile_toc = set()
self.profile_entries = {}
- self.output = ".. _maintainers:\n\n"
+ self.header = ".. _maintainers:\n\n"
+ self.maint_entries = {}
+ self.fields = {}
prev = None
with open(path, "r", encoding="utf-8") as fp:
@@ -77,11 +76,11 @@ class MaintainersParser:
self.subsystems = True
self.parse_subsystems(line)
else:
- self.output += line
+ self.header += line
elif self.subsystems:
self.parse_subsystems(line)
else:
- self.output += line
+ self.header += line
# Update the state machine when we find heading separators.
if line.startswith("----------"):
@@ -93,13 +92,6 @@ class MaintainersParser:
# Retain previous line for state machine transitions.
prev = line
- # Flush pending field contents.
- if self.field_content:
- self.output += self.field_content + "\n\n"
-
- self.output = self.output.rstrip()
-
-
def linkify(self, text):
"""Linkify all non-wildcard refs to ReST files in Documentation/"""
@@ -130,11 +122,11 @@ class MaintainersParser:
# Have we reached the end of the preformatted Descriptions text?
if line.startswith("Maintainers"):
self.descriptions = False
- self.output += "\n" + line
+ self.header += "\n" + line
return
# Escape the escapes in preformatted text.
- self.output += "| " + self.linkify(line).replace("\\", "\\\\")
+ self.header += "| " + self.linkify(line).replace("\\", "\\\\")
# Look for and record field letter to field name mappings:
# R: Designated *reviewer*: FullName <address@domain>
@@ -157,24 +149,8 @@ class MaintainersParser:
if not line:
return
- # Subsystem fields are batched into "field_content"
if line[1] != ':':
- line = self.linkify(line)
-
- # Render a subsystem entry as:
- # SUBSYSTEM NAME
- # ~~~~~~~~~~~~~~
- # Flush pending field content.
- self.output += self.field_content + "\n\n"
- self.field_content = ""
-
- self.subsystem_name = line.title()
-
- # Collapse whitespace in subsystem name.
- heading = re.sub(r"\s+", " ", line)
- self.output += "%s\n%s" % (heading, "~" * len(heading)) + "\n"
- self.field_prev = ""
-
+ self.subsystem_name = re.sub(r"\s+", " ", self.linkify(line))
return
# Render a subsystem field as:
@@ -224,23 +200,23 @@ class MaintainersParser:
details = self.linkify(details)
+ #
# Mark paths (and regexes) as literal text for improved
# readability and to escape any escapes.
+ #
if field in ['F', 'N', 'X', 'K']:
# But only if not already marked :)
if not ':doc:' in details:
details = '``%s``' % (details)
- # Comma separate email field continuations.
- if field == self.field_prev and self.field_prev in ['M', 'R', 'L']:
- self.field_content = self.field_content + ","
+ if self.subsystem_name not in self.maint_entries:
+ self.maint_entries[self.subsystem_name] = {}
+
+ if field not in self.maint_entries[self.subsystem_name]:
+ self.maint_entries[self.subsystem_name][field] = []
+
+ self.maint_entries[self.subsystem_name][field].append(details)
- # Do not repeat field names, so that field entries
- # will be collapsed together.
- if field != self.field_prev:
- self.output += self.field_content + "\n\n"
- self.field_content = ":%s:" % (self.fields.get(field, field))
- self.field_content = self.field_content + "\n\t%s" % (details)
self.field_prev = field
@@ -252,7 +228,15 @@ class MaintainersInclude(Include):
def emit(self):
"""Parse all the MAINTAINERS lines into ReST for human-readability"""
path = maint_parser.path
- output = maint_parser.output
+ output = maint_parser.header
+
+ for name, fields in sorted(maint_parser.maint_entries.items()):
+ output += "\n" + name + "\n"
+ output += "~" * len(name) + "\n"
+
+ for field, lines in fields.items():
+ field_name = maint_parser.fields.get(field, field)
+ output += f":{field_name}:\n\t" + ",\n\t".join(lines) + "\n\n"
# For debugging the pre-rendered results...
#print(output, file=open("/tmp/MAINTAINERS.rst", "w"))
@@ -288,6 +272,8 @@ class MaintainersProfile(Include):
#
output = ""
for profile, entry in sorted(maint_parser.profile_entries.items()):
+ profile = profile.title()
+
if entry.startswith("http"):
output += f"- `{profile} <{entry}>`_\n"
else:
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 07/13] docs: maintainers_include: properly handle file patterns
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
` (5 preceding siblings ...)
2026-05-09 6:56 ` [PATCH v3 06/13] docs: maintainers_include: store maintainers entries on a dict Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 08/13] docs: maintainers_include: add a filtering javascript Mauro Carvalho Chehab
` (6 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux,
Andrew Morton, Joe Perches, Matteo Croce, Shuah Khan,
Matteo Croce
handling asterisks inside file patterns atdescription part is
problematic, as ReST has special meaning for them. Due to
that, convert such patterns to literal strings.
Reported-by: Matteo Croce <teknoraver@meta.com>
Fixes: 420849332f9f ("get_maintainer: add ** glob pattern support")
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <89127706fb3493d00ecb21e528c8a27081e5ed40.1777987027.git.mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 26 ++++++++++-----------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index ed9cd7b3dc66..5361774b61bc 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -47,9 +47,6 @@ class MaintainersParser:
self.maintainers = False
self.subsystems = False
- # Field letter to field name mapping.
- self.field_letter = None
-
self.subsystem_name = None
self.app_dir = os.path.abspath(app_dir)
@@ -125,19 +122,22 @@ class MaintainersParser:
self.header += "\n" + line
return
- # Escape the escapes in preformatted text.
- self.header += "| " + self.linkify(line).replace("\\", "\\\\")
-
# Look for and record field letter to field name mappings:
# R: Designated *reviewer*: FullName <address@domain>
- m = re.search(r"\s(\S):\s", line)
+ m = re.match(r"\s+(\S):\s+(\S+)", line)
if m:
- self.field_letter = m.group(1)
+ field = m.group(1)
+ details = m.group(2)
+
+ if field not in self.fields:
+ m = re.search(r"\*([^\*]+)\*", line)
+ if m:
+ self.fields[field] = m.group(1)
+ elif field in ['F', 'N', 'X', 'K']:
+ line = line.replace(details, f'``{details}``')
+
+ self.header += "| " + self.linkify(line)
- if self.field_letter and self.field_letter not in self.fields:
- m = re.search(r"\*([^\*]+)\*", line)
- if m:
- self.fields[self.field_letter] = m.group(1)
def parse_subsystems(self, line):
"""Handle contents of the per-subsystem sections."""
@@ -206,7 +206,7 @@ class MaintainersParser:
#
if field in ['F', 'N', 'X', 'K']:
# But only if not already marked :)
- if not ':doc:' in details:
+ if ':doc:' not in details and "http" not in details:
details = '``%s``' % (details)
if self.subsystem_name not in self.maint_entries:
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 08/13] docs: maintainers_include: add a filtering javascript
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
` (6 preceding siblings ...)
2026-05-09 6:56 ` [PATCH v3 07/13] docs: maintainers_include: properly handle file patterns Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 09/13] docs: maintainers_include: don't ignore invalid profile entries Mauro Carvalho Chehab
` (5 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux, Shuah Khan
The maintainers table is big. Add a javascript to allow filtering
it. Such script is only added at the page which contains the
maintainers-include tag.
I opted to keep the search case-sensitive, as, this way,
upper case searches at subsystem.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <c435ef150f5d6ed16570969f43d92ba6fb857842.1777987027.git.mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 78 +++++++++++++++++++--
1 file changed, 72 insertions(+), 6 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 5361774b61bc..7035754a1c66 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -31,6 +31,49 @@ __version__ = "1.0"
maint_parser = None # pylint: disable=C0103
+JS_FILTER = """
+(function() {
+ function filterTable(table) {
+ const filter = document.getElementById("filter-table").value.trim();
+ const rows = table.querySelectorAll("tbody tr");
+ for (let i = 0; i < rows.length; i++) {
+ const tds = rows[i].getElementsByTagName("td");
+ let match = false;
+ for (let j = 0; j < tds.length; j++) {
+ const cellText = (tds[j].textContent || tds[j].innerText);
+ if (cellText.includes(filter)) {
+ match = true;
+ break;
+ }
+ }
+ rows[i].style.display = match ? "table-row" : "none";
+ }
+ }
+ function addInput() {
+ const table = document.getElementById("maintainers-table");
+ if (!table) return;
+ let input = document.getElementById("filter-table");
+ if (!input) {
+ const filt_div = document.createElement('div');
+ filt_div.innerHTML = `
+ <p>Filter:
+ <input type="search" id="filter-table" placeholder="search string"/>
+ subsystem or property (case-sensitive)
+ </p>
+ `;
+ table.parentNode.insertBefore(filt_div, table);
+ const input = document.getElementById("filter-table")
+ input.addEventListener('input', () => filterTable(table));
+ }
+ }
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', addInput);
+ } else {
+ addInput();
+ }
+})();
+"""
+
# Shamelessly stolen from docutils
def ErrorString(exc): # pylint: disable=C0103, C0116
@@ -59,7 +102,7 @@ class MaintainersParser:
#
self.profile_toc = set()
self.profile_entries = {}
- self.header = ".. _maintainers:\n\n"
+ self.header = ""
self.maint_entries = {}
self.fields = {}
@@ -228,15 +271,28 @@ class MaintainersInclude(Include):
def emit(self):
"""Parse all the MAINTAINERS lines into ReST for human-readability"""
path = maint_parser.path
- output = maint_parser.header
+ output = ".. _maintainers:\n\n"
+ output += maint_parser.header
+
+ output += ".. _maintainers_table:\n\n"
+ output += ".. flat-table::\n"
+ output += " :header-rows: 1\n\n"
+ output += " * - Subsystem\n"
+ output += " - Properties\n\n"
+
+ self.state.document['maintainers_included'] = True
for name, fields in sorted(maint_parser.maint_entries.items()):
- output += "\n" + name + "\n"
- output += "~" * len(name) + "\n"
-
+ output += f" * - {name}\n"
+ tag = "-"
for field, lines in fields.items():
field_name = maint_parser.fields.get(field, field)
- output += f":{field_name}:\n\t" + ",\n\t".join(lines) + "\n\n"
+
+ output += f" {tag} :{field_name}:\n "
+ output += ",\n ".join(lines) + "\n"
+ tag = " "
+
+ output += "\n"
# For debugging the pre-rendered results...
#print(output, file=open("/tmp/MAINTAINERS.rst", "w"))
@@ -308,6 +364,14 @@ class MaintainersProfile(Include):
return []
+# pylint: disable=W0613
+def add_filter_script(app, pagename, templatename, context, doctree):
+ """Add Filter javascript only to maintainers page"""
+
+ if doctree and doctree.get('maintainers_included'):
+ app.add_js_file(None, body=JS_FILTER)
+
+
def setup(app):
"""Setup Sphinx extension"""
global maint_parser # pylint: disable=W0603
@@ -325,6 +389,8 @@ def setup(app):
app.add_directive("maintainers-include", MaintainersInclude)
app.add_directive("maintainers-profile-toc", MaintainersProfile)
+ app.connect("html-page-context", add_filter_script)
+
return {
"version": __version__,
"parallel_read_safe": True,
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 09/13] docs: maintainers_include: don't ignore invalid profile entries
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
` (7 preceding siblings ...)
2026-05-09 6:56 ` [PATCH v3 08/13] docs: maintainers_include: add a filtering javascript Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 10/13] docs: maintainers_include: better handle directories Mauro Carvalho Chehab
` (4 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux,
Björn Roy Baron, Alice Ryhl, Andreas Hindborg, Benno Lossin,
Boqun Feng, Danilo Krummrich, Gary Guo, Miguel Ojeda, Shuah Khan,
Trevor Gross
Currently, there is a "P" entry for Rust pin-init that is
neither a valid ReST file inside Documentation nor an URL.
A proper fix is to either convert/move the file or point to
a URL. Yet, the parser should be able to pick what's there and
show on its output.
Add a logic to produce a warning when this happens.
Message-ID: <63228e005fcf3dc4583cee06905341e8bce84181.1777987027.git.mchehab+huawei@kernel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 7035754a1c66..073a10575872 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -240,6 +240,8 @@ class MaintainersParser:
if match:
entry = match.group(1).strip()
self.profile_entries[self.subsystem_name] = entry
+ else:
+ self.profile_entries[self.subsystem_name] = f"``{details}``"
details = self.linkify(details)
@@ -328,12 +330,15 @@ class MaintainersProfile(Include):
#
output = ""
for profile, entry in sorted(maint_parser.profile_entries.items()):
- profile = profile.title()
+ name = profile.title()
if entry.startswith("http"):
- output += f"- `{profile} <{entry}>`_\n"
+ output += f"- `{name} <{entry}>`_\n"
+ elif entry.startswith("`"):
+ output += f"- {name}: {entry}\n"
+ self.warning(f"{profile}: Invalid 'P' tag: {entry}\n")
else:
- output += f"- :doc:`{profile} <{entry}>`\n"
+ output += f"- :doc:`{name} <{entry}>`\n"
#
# Create a hidden TOC table with all profiles. That allows adding
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 10/13] docs: maintainers_include: better handle directories
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
` (8 preceding siblings ...)
2026-05-09 6:56 ` [PATCH v3 09/13] docs: maintainers_include: don't ignore invalid profile entries Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 11/13] docs: maintainers_include: better handle doc wildcards Mauro Carvalho Chehab
` (3 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux, Shuah Khan
The TOC tree needs to use paths relative to the document containing
the maintainers-profile-toc directive. Fix it.
While here, address a warning from sashiko-bot, which points
that using partition can be problematic if the root Linux path
ends being something like:
foo/Documentation/linux/
causing the documentation dir to be at:
foo/Documentation/linux/Documentation
Very unlikely, but fixing it is trivial: just use regex to
pick the last one.
Notice that I dropped the comment about using os.fspath() as
the logic already uses os.path.abspath() which should work
equally well.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 28 ++++++++++++---------
1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 073a10575872..8c7b79721edd 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -82,7 +82,7 @@ def ErrorString(exc): # pylint: disable=C0103, C0116
class MaintainersParser:
"""Parse MAINTAINERS file(s) content"""
- def __init__(self, app_dir, path):
+ def __init__(self, base_dir, app_dir, path):
self.path = path
# Poor man's state machine.
@@ -92,8 +92,8 @@ class MaintainersParser:
self.subsystem_name = None
- self.app_dir = os.path.abspath(app_dir)
- self.base_dir, _, self.sphinx_dir = self.app_dir.partition("Documentation")
+ self.base_dir = base_dir
+ self.app_dir = app_dir
self.re_doc = re.compile(r'(Documentation/([^\s\?\*]*)\.rst)')
@@ -323,6 +323,8 @@ class MaintainersProfile(Include):
def emit(self):
"""Parse all the MAINTAINERS lines looking for profile entries"""
+ env = self.state.document.settings.env
+ docdir = os.path.dirname(os.path.join(env.srcdir, env.docname))
path = maint_parser.path
#
@@ -347,7 +349,9 @@ class MaintainersProfile(Include):
output += "\n.. toctree::\n"
output += " :hidden:\n\n"
- for fname in sorted(maint_parser.profile_toc):
+ for f in sorted(maint_parser.profile_toc):
+ fname = os.path.join(maint_parser.base_dir, "Documentation", f)
+ fname = os.path.relpath(fname, docdir)
output += f" {fname}\n"
output += "\n"
@@ -381,15 +385,15 @@ def setup(app):
"""Setup Sphinx extension"""
global maint_parser # pylint: disable=W0603
- #
- # NOTE: we're using os.fspath() here because of a Sphinx warning:
- # RemovedInSphinx90Warning: Sphinx 9 will drop support for representing paths as strings. Use "pathlib.Path" or "os.fspath" instead.
- #
- app_dir = os.fspath(app.srcdir)
- srctree = os.path.abspath(os.environ["srctree"])
- path = os.path.join(srctree, "MAINTAINERS")
+ app_dir = os.path.abspath(app.srcdir)
+ match = re.match(r"(.*/)Documentation", app_dir)
+ if not match:
+ raise ValueError('Documentation directory not found.')
- maint_parser = MaintainersParser(app_dir, path)
+ base_dir = match.group(1)
+ path = os.path.join(base_dir, "MAINTAINERS")
+
+ maint_parser = MaintainersParser(base_dir, app_dir, path)
app.add_directive("maintainers-include", MaintainersInclude)
app.add_directive("maintainers-profile-toc", MaintainersProfile)
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 11/13] docs: maintainers_include: better handle doc wildcards
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
` (9 preceding siblings ...)
2026-05-09 6:56 ` [PATCH v3 10/13] docs: maintainers_include: better handle directories Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 12/13] MAINTAINERS: make clearer about what's expected for "P" field Mauro Carvalho Chehab
` (2 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Mauro Carvalho Chehab
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux, Shuah Khan
As warned by sashiko-bot, the new logic doesn't handle wildcards
on Documentation/.
Change the logic to properly handle it, cleaning up the code
to remove some code duplication.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
Documentation/sphinx/maintainers_include.py | 103 ++++++++++----------
1 file changed, 53 insertions(+), 50 deletions(-)
diff --git a/Documentation/sphinx/maintainers_include.py b/Documentation/sphinx/maintainers_include.py
index 8c7b79721edd..be8e566e0363 100755
--- a/Documentation/sphinx/maintainers_include.py
+++ b/Documentation/sphinx/maintainers_include.py
@@ -95,7 +95,7 @@ class MaintainersParser:
self.base_dir = base_dir
self.app_dir = app_dir
- self.re_doc = re.compile(r'(Documentation/([^\s\?\*]*)\.rst)')
+ self.re_doc = re.compile(r'(Documentation/(\S*)\.rst)')
#
# Output variables with maintainers content to be stored
@@ -132,29 +132,47 @@ class MaintainersParser:
# Retain previous line for state machine transitions.
prev = line
+ def get_entries(self, text):
+ """Generate refs to ReST files in Documentation/"""
+
+ if "Documentation/" not in text:
+ return None
+
+ if "*" in text or "?" in text:
+ m = self.re_doc.search(text)
+ if not m:
+ return None
+
+ doc_list = glob(m.group(1), root_dir=self.base_dir)
+ else:
+ doc_list = [text]
+
+ entries = {}
+ for doc in doc_list:
+ m = self.re_doc.search(doc)
+ if m:
+ fname = m.group(1)
+ ename = m.group(2)
+
+ entry = os.path.relpath(self.base_dir + fname, self.app_dir)
+ entry = entry.removesuffix(".rst")
+
+ if entry.startswith("../"):
+ html = KERNELDOC_URL + ename + ".html"
+ entries[entry] = f'`{ename} <{html}>`_'
+ else:
+ entries[entry] = f':doc:`{ename} </{entry}>`'
+
+ return entries
+
def linkify(self, text):
- """Linkify all non-wildcard refs to ReST files in Documentation/"""
+ """Return a list of doc files converted to cross-references"""
- m = self.re_doc.search(text)
- if m:
- fname = m.group(1)
- ename = m.group(2)
+ entries = self.get_entries(text)
+ if not entries:
+ return text
- entry = os.path.relpath(self.base_dir + fname, self.app_dir)
- entry = entry.removesuffix(".rst")
-
- #
- # When SPHINXDIRS is used, it will try to reference files
- # outside srctree, causing warnings. To avoid that, point
- # to the latest official documentation
- #
- if entry.startswith("../"):
- html = KERNELDOC_URL + ename + ".html"
- text = self.re_doc.sub(f'`{ename} <{html}>`_', text)
- else:
- text = self.re_doc.sub(f':doc:`{ename} </{entry}>`', text)
-
- return text
+ return self.re_doc.sub(", ".join(entries.values()), text)
def parse_descriptions(self, line):
"""Handle contents of the descriptions section."""
@@ -206,35 +224,15 @@ class MaintainersParser:
# Handle profile entries - either as files or as https refs
#
if field == "P":
- match = self.re_doc.match(details)
- if match:
- fname = match.group(1)
- ename = match.group(2)
+ entries = self.get_entries(details)
+ if entries:
+ for e, link in entries.items():
+ if "html" not in link:
+ self.profile_toc.add(e)
- entry = os.path.relpath(self.base_dir + fname, self.app_dir)
- entry = entry.removesuffix(".rst")
- #
- # When SPHINXDIRS is used, it will try to reference files
- # outside srctree, causing warnings. To avoid that, point
- # to the latest official documentation
- #
+ self.profile_entries[self.subsystem_name] = link
- if entry.startswith("../"):
- entry = KERNELDOC_URL + ename + ".html"
- else:
- entry = "/" + entry
-
- if "*" in entry:
- for e in glob(entry):
- if "html" not in e:
- self.profile_toc.add(e)
-
- self.profile_entries[self.subsystem_name] = e
- else:
- if "html" not in entry:
- self.profile_toc.add(entry)
-
- self.profile_entries[self.subsystem_name] = entry
+ details = ", ".join(entries.values())
else:
match = re.match(r"(https?://.*)", details)
if match:
@@ -243,7 +241,9 @@ class MaintainersParser:
else:
self.profile_entries[self.subsystem_name] = f"``{details}``"
- details = self.linkify(details)
+ details = self.linkify(details)
+ else:
+ details = self.linkify(details)
#
# Mark paths (and regexes) as literal text for improved
@@ -340,7 +340,7 @@ class MaintainersProfile(Include):
output += f"- {name}: {entry}\n"
self.warning(f"{profile}: Invalid 'P' tag: {entry}\n")
else:
- output += f"- :doc:`{name} <{entry}>`\n"
+ output += f"- {entry}\n"
#
# Create a hidden TOC table with all profiles. That allows adding
@@ -356,6 +356,9 @@ class MaintainersProfile(Include):
output += "\n"
+ # For debugging the pre-rendered results...
+ #print(output, file=open("/tmp/profiles.rst", "w"))
+
self.state.document.settings.record_dependencies.add(path)
self.state_machine.insert_input(statemachine.string2lines(output), path)
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 12/13] MAINTAINERS: make clearer about what's expected for "P" field
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
` (10 preceding siblings ...)
2026-05-09 6:56 ` [PATCH v3 11/13] docs: maintainers_include: better handle doc wildcards Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 6:56 ` [PATCH v3 13/13] MAINTAINERS: use a URL for pin-init maintainer's profile entry Mauro Carvalho Chehab
2026-05-09 17:53 ` [PATCH v3 00/13] Improve process/maintainers output Joe Perches
13 siblings, 0 replies; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux,
Mauro Carvalho Chehab
The "P" field is meant to point to a subsystem maintainer's
profile, stored either at the Kernel documentation or on an
external site. Make it clearer.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <921e5e6a074f9d8cf77483d73e6801f49254bbb8.1777987027.git.mchehab+huawei@kernel.org>
---
MAINTAINERS | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 2fb1c75afd16..77244b7f9545 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25,9 +25,9 @@ Descriptions of section entries and preferred order
C: URI for *chat* protocol, server and channel where developers
usually hang out, for example irc://server/channel.
P: *Subsystem Profile* document for more details submitting
- patches to the given subsystem. This is either an in-tree file,
- or a URI. See Documentation/maintainer/maintainer-entry-profile.rst
- for details.
+ patches to the given subsystem. This is either an in-tree .rst file
+ inside Documentation/, or a URI.
+ See Documentation/maintainer/maintainer-entry-profile.rst for details.
T: *SCM* tree type and location.
Type is one of: git, hg, quilt, stgit, topgit
F: *Files* and directories wildcard patterns.
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v3 13/13] MAINTAINERS: use a URL for pin-init maintainer's profile entry
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
` (11 preceding siblings ...)
2026-05-09 6:56 ` [PATCH v3 12/13] MAINTAINERS: make clearer about what's expected for "P" field Mauro Carvalho Chehab
@ 2026-05-09 6:56 ` Mauro Carvalho Chehab
2026-05-09 12:11 ` Gary Guo
2026-05-09 17:53 ` [PATCH v3 00/13] Improve process/maintainers output Joe Perches
13 siblings, 1 reply; 16+ messages in thread
From: Mauro Carvalho Chehab @ 2026-05-09 6:56 UTC (permalink / raw)
To: Jonathan Corbet, Linux Doc Mailing List, Miguel Ojeda
Cc: Mauro Carvalho Chehab, linux-kernel, rust-for-linux,
Björn Roy Baron, Alice Ryhl, Andreas Hindborg, Benno Lossin,
Boqun Feng, Danilo Krummrich, Gary Guo, Mauro Carvalho Chehab,
Trevor Gross
This maintainer's entry is not inside documentation nor is
ReST, preventing Sphinx to create a hyperlink to it.
Change it to point to the already-formatted URL.
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Message-ID: <1bceee886b9027d66bbb48d9d6c8d1250ce8dbcb.1777987028.git.mchehab+huawei@kernel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
MAINTAINERS | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 77244b7f9545..dd424a4f9f3b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23402,7 +23402,7 @@ S: Maintained
W: https://rust-for-linux.com/pin-init
B: https://github.com/Rust-for-Linux/pin-init/issues
C: zulip://rust-for-linux.zulipchat.com
-P: rust/pin-init/CONTRIBUTING.md
+P: https://github.com/Rust-for-Linux/pin-init/blob/main/CONTRIBUTING.md
T: git https://github.com/Rust-for-Linux/linux.git pin-init-next
F: rust/kernel/init.rs
F: rust/pin-init/
--
2.54.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v3 13/13] MAINTAINERS: use a URL for pin-init maintainer's profile entry
2026-05-09 6:56 ` [PATCH v3 13/13] MAINTAINERS: use a URL for pin-init maintainer's profile entry Mauro Carvalho Chehab
@ 2026-05-09 12:11 ` Gary Guo
0 siblings, 0 replies; 16+ messages in thread
From: Gary Guo @ 2026-05-09 12:11 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Jonathan Corbet, Linux Doc Mailing List,
Miguel Ojeda
Cc: linux-kernel, rust-for-linux, Björn Roy Baron, Alice Ryhl,
Andreas Hindborg, Benno Lossin, Boqun Feng, Danilo Krummrich,
Gary Guo, Mauro Carvalho Chehab, Trevor Gross
On Sat May 9, 2026 at 7:56 AM BST, Mauro Carvalho Chehab wrote:
> This maintainer's entry is not inside documentation nor is
> ReST, preventing Sphinx to create a hyperlink to it.
>
> Change it to point to the already-formatted URL.
>
> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> Message-ID: <1bceee886b9027d66bbb48d9d6c8d1250ce8dbcb.1777987028.git.mchehab+huawei@kernel.org>
> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Acked-by: Gary Guo <gary@garyguo.net>
> ---
> MAINTAINERS | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v3 00/13] Improve process/maintainers output
2026-05-09 6:56 [PATCH v3 00/13] Improve process/maintainers output Mauro Carvalho Chehab
` (12 preceding siblings ...)
2026-05-09 6:56 ` [PATCH v3 13/13] MAINTAINERS: use a URL for pin-init maintainer's profile entry Mauro Carvalho Chehab
@ 2026-05-09 17:53 ` Joe Perches
13 siblings, 0 replies; 16+ messages in thread
From: Joe Perches @ 2026-05-09 17:53 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Jonathan Corbet, Mauro Carvalho Chehab,
Miguel Ojeda
Cc: linux-doc, linux-kernel, rust-for-linux, Björn Roy Baron,
Alice Ryhl, Andreas Hindborg, Andrew Morton, Benno Lossin,
Boqun Feng, Danilo Krummrich, Gary Guo, Matteo Croce, Shuah Khan,
Trevor Gross
On Sat, 2026-05-09 at 08:56 +0200, Mauro Carvalho Chehab wrote:
> This series improve the output at process/maintainers: instead of a
> pure enriched text, the maintainer's file content is now converted
> to a table, and has gained a javascript to allow filtering entries.
[]
> (*) Currently, MAINTAINERS file has several entries not sorted.
> One has to run:
>
> scripts/parse-maintainers.pl --input MAINTAINERS --output MAINTAINERS.new
Maybe add --order
^ permalink raw reply [flat|nested] 16+ messages in thread