xref: /linux/Documentation/sphinx/parser_yaml.py (revision d90555ef0603935529837e490d5acc8436297c56)
1# SPDX-License-Identifier: GPL-2.0
2# Copyright 2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
3
4"""
5Sphinx extension for processing YAML files
6"""
7
8import os
9import re
10import sys
11
12from pprint import pformat
13
14from docutils.parsers.rst import Parser as RSTParser
15from docutils.statemachine import ViewList
16
17from sphinx.util import logging
18from sphinx.parsers import Parser
19
20srctree = os.path.abspath(os.environ["srctree"])
21sys.path.insert(0, os.path.join(srctree, "tools/net/ynl/pyynl/lib"))
22
23from doc_generator import YnlDocGenerator        # pylint: disable=C0413
24
25logger = logging.getLogger(__name__)
26
27class YamlParser(Parser):
28    """
29    Kernel parser for YAML files.
30
31    This is a simple sphinx.Parser to handle yaml files inside the
32    Kernel tree that will be part of the built documentation.
33
34    The actual parser function is not contained here: the code was
35    written in a way that parsing yaml for different subsystems
36    can be done from a single dispatcher.
37
38    All it takes to have parse YAML patches is to have an import line:
39
40            from some_parser_code import NewYamlGenerator
41
42    To this module. Then add an instance of the parser with:
43
44            new_parser = NewYamlGenerator()
45
46    and add a logic inside parse() to handle it based on the path,
47    like this:
48
49            if "/foo" in fname:
50                msg = self.new_parser.parse_yaml_file(fname)
51    """
52
53    supported = ('yaml', )
54
55    netlink_parser = YnlDocGenerator()
56
57    re_lineno = re.compile(r"\.\. LINENO ([0-9]+)$")
58
59    def rst_parse(self, inputstring, document, msg):
60        """
61        Receives a ReST content that was previously converted by the
62        YAML parser, adding it to the document tree.
63        """
64
65        self.setup_parse(inputstring, document)
66
67        result = ViewList()
68
69        try:
70            # Parse message with RSTParser
71            lineoffset = 0;
72            for line in msg.split('\n'):
73                match = self.re_lineno.match(line)
74                if match:
75                    lineoffset = int(match.group(1))
76                    continue
77
78                result.append(line, document.current_source, lineoffset)
79
80            # Fix backward compatibility with docutils < 0.17.1
81            if "tab_width" not in vars(document.settings):
82                document.settings.tab_width = 8
83
84            rst_parser = RSTParser()
85            rst_parser.parse('\n'.join(result), document)
86
87        except Exception as e:
88            document.reporter.error("YAML parsing error: %s" % pformat(e))
89
90        self.finish_parse()
91
92    # Overrides docutils.parsers.Parser. See sphinx.parsers.RSTParser
93    def parse(self, inputstring, document):
94        """Check if a YAML is meant to be parsed."""
95
96        fname = document.current_source
97
98        # Handle netlink yaml specs
99        if "/netlink/specs/" in fname:
100            msg = self.netlink_parser.parse_yaml_file(fname)
101            self.rst_parse(inputstring, document, msg)
102
103        # All other yaml files are ignored
104
105def setup(app):
106    """Setup function for the Sphinx extension."""
107
108    # Add YAML parser
109    app.add_source_parser(YamlParser)
110    app.add_source_suffix('.yaml', 'yaml')
111
112    return {
113        'version': '1.0',
114        'parallel_read_safe': True,
115        'parallel_write_safe': True,
116    }
117