xref: /linux/tools/lib/python/kdoc/kdoc_yaml_file.py (revision 5181afcdf99527dd92a88f80fc4d0d8013e1b510)
1b37b3cbbSMauro Carvalho Chehab#!/usr/bin/env python3
2b37b3cbbSMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0
3b37b3cbbSMauro Carvalho Chehab# Copyright(c) 2026: Mauro Carvalho Chehab <mchehab@kernel.org>.
4b37b3cbbSMauro Carvalho Chehab
5b37b3cbbSMauro Carvalho Chehabimport os
6b37b3cbbSMauro Carvalho Chehab
7b37b3cbbSMauro Carvalho Chehabfrom kdoc.kdoc_output import ManFormat, RestFormat
8b37b3cbbSMauro Carvalho Chehab
9b37b3cbbSMauro Carvalho Chehab
10b37b3cbbSMauro Carvalho Chehabclass KDocTestFile():
11b37b3cbbSMauro Carvalho Chehab    """
12b37b3cbbSMauro Carvalho Chehab    Handles the logic needed to store kernel‑doc output inside a YAML file.
13b37b3cbbSMauro Carvalho Chehab     Useful for unit tests and regression tests.
14b37b3cbbSMauro Carvalho Chehab    """
15b37b3cbbSMauro Carvalho Chehab
16b37b3cbbSMauro Carvalho Chehab    def __init__(self, config, yaml_file, yaml_content):
17b37b3cbbSMauro Carvalho Chehab        #
18b37b3cbbSMauro Carvalho Chehab        # Bail out early if yaml is not available
19b37b3cbbSMauro Carvalho Chehab        #
20b37b3cbbSMauro Carvalho Chehab        try:
21b37b3cbbSMauro Carvalho Chehab            import yaml
22b37b3cbbSMauro Carvalho Chehab        except ImportError:
23b37b3cbbSMauro Carvalho Chehab            sys.exit("Warning: yaml package not available. Aborting it.")
24b37b3cbbSMauro Carvalho Chehab
25b37b3cbbSMauro Carvalho Chehab        self.config = config
26b37b3cbbSMauro Carvalho Chehab        self.test_file = os.path.expanduser(yaml_file)
27b37b3cbbSMauro Carvalho Chehab        self.yaml_content = yaml_content
28*e786fab2SMauro Carvalho Chehab        self.test_names = set()
29b37b3cbbSMauro Carvalho Chehab
30b37b3cbbSMauro Carvalho Chehab        self.tests = []
31b37b3cbbSMauro Carvalho Chehab
32b37b3cbbSMauro Carvalho Chehab        out_dir = os.path.dirname(self.test_file)
33b37b3cbbSMauro Carvalho Chehab        if out_dir and not os.path.isdir(out_dir):
34b37b3cbbSMauro Carvalho Chehab            sys.exit(f"Directory {out_dir} doesn't exist.")
35b37b3cbbSMauro Carvalho Chehab
36b37b3cbbSMauro Carvalho Chehab        self.out_style = []
37b37b3cbbSMauro Carvalho Chehab
38b37b3cbbSMauro Carvalho Chehab        if "man" in self.yaml_content:
39b37b3cbbSMauro Carvalho Chehab            out_style = ManFormat()
40b37b3cbbSMauro Carvalho Chehab            out_style.set_config(self.config)
41b37b3cbbSMauro Carvalho Chehab
42b37b3cbbSMauro Carvalho Chehab            self.out_style.append(out_style)
43b37b3cbbSMauro Carvalho Chehab
44b37b3cbbSMauro Carvalho Chehab        if "rst" in self.yaml_content:
45b37b3cbbSMauro Carvalho Chehab            out_style = RestFormat()
46b37b3cbbSMauro Carvalho Chehab            out_style.set_config(self.config)
47b37b3cbbSMauro Carvalho Chehab
48b37b3cbbSMauro Carvalho Chehab            self.out_style.append(out_style)
49b37b3cbbSMauro Carvalho Chehab
50b37b3cbbSMauro Carvalho Chehab    def set_filter(self, export, internal, symbol, nosymbol,
51b37b3cbbSMauro Carvalho Chehab                   function_table, enable_lineno, no_doc_sections):
52b37b3cbbSMauro Carvalho Chehab        """
53b37b3cbbSMauro Carvalho Chehab        Set filters at the output classes.
54b37b3cbbSMauro Carvalho Chehab        """
55b37b3cbbSMauro Carvalho Chehab        for out_style in self.out_style:
56b37b3cbbSMauro Carvalho Chehab            out_style.set_filter(export, internal, symbol,
57b37b3cbbSMauro Carvalho Chehab                                 nosymbol, function_table,
58b37b3cbbSMauro Carvalho Chehab                                 enable_lineno, no_doc_sections)
59b37b3cbbSMauro Carvalho Chehab
60b37b3cbbSMauro Carvalho Chehab    @staticmethod
61b37b3cbbSMauro Carvalho Chehab    def get_kdoc_item(arg, start_line=1):
62b37b3cbbSMauro Carvalho Chehab
63b37b3cbbSMauro Carvalho Chehab        d = vars(arg)
64b37b3cbbSMauro Carvalho Chehab
65b37b3cbbSMauro Carvalho Chehab        declaration_start_line = d.get("declaration_start_line")
66b37b3cbbSMauro Carvalho Chehab        if not declaration_start_line:
67b37b3cbbSMauro Carvalho Chehab            return d
68b37b3cbbSMauro Carvalho Chehab
69b37b3cbbSMauro Carvalho Chehab        d["declaration_start_line"] = start_line
70b37b3cbbSMauro Carvalho Chehab
71b37b3cbbSMauro Carvalho Chehab        parameterdesc_start_lines = d.get("parameterdesc_start_lines")
72b37b3cbbSMauro Carvalho Chehab        if parameterdesc_start_lines:
73b37b3cbbSMauro Carvalho Chehab            for key in parameterdesc_start_lines:
74b37b3cbbSMauro Carvalho Chehab                ln = parameterdesc_start_lines[key]
75b37b3cbbSMauro Carvalho Chehab                ln += start_line - declaration_start_line
76b37b3cbbSMauro Carvalho Chehab
77b37b3cbbSMauro Carvalho Chehab                parameterdesc_start_lines[key] = ln
78b37b3cbbSMauro Carvalho Chehab
79b37b3cbbSMauro Carvalho Chehab        sections_start_lines = d.get("sections_start_lines")
80b37b3cbbSMauro Carvalho Chehab        if sections_start_lines:
81b37b3cbbSMauro Carvalho Chehab            for key in sections_start_lines:
82b37b3cbbSMauro Carvalho Chehab                ln = sections_start_lines[key]
83b37b3cbbSMauro Carvalho Chehab                ln += start_line - declaration_start_line
84b37b3cbbSMauro Carvalho Chehab
85b37b3cbbSMauro Carvalho Chehab                sections_start_lines[key] = ln
86b37b3cbbSMauro Carvalho Chehab
87b37b3cbbSMauro Carvalho Chehab        return d
88b37b3cbbSMauro Carvalho Chehab
8999ec67a9SMauro Carvalho Chehab    def output_symbols(self, fname, symbols):
90b37b3cbbSMauro Carvalho Chehab        """
91b37b3cbbSMauro Carvalho Chehab        Store source, symbols and output strings at self.tests.
92b37b3cbbSMauro Carvalho Chehab        """
93b37b3cbbSMauro Carvalho Chehab
94b37b3cbbSMauro Carvalho Chehab        #
95b37b3cbbSMauro Carvalho Chehab        # KdocItem needs to be converted into dicts
96b37b3cbbSMauro Carvalho Chehab        #
97b37b3cbbSMauro Carvalho Chehab        kdoc_item = []
98b37b3cbbSMauro Carvalho Chehab        expected = []
99b37b3cbbSMauro Carvalho Chehab
10099ec67a9SMauro Carvalho Chehab        #
10199ec67a9SMauro Carvalho Chehab        # Source code didn't produce any symbol
10299ec67a9SMauro Carvalho Chehab        #
10399ec67a9SMauro Carvalho Chehab        if not symbols:
104b37b3cbbSMauro Carvalho Chehab            return
105b37b3cbbSMauro Carvalho Chehab
106b37b3cbbSMauro Carvalho Chehab        expected_dict = {}
107b37b3cbbSMauro Carvalho Chehab        start_line=1
108b37b3cbbSMauro Carvalho Chehab
109*e786fab2SMauro Carvalho Chehab        for arg in symbols:
11099ec67a9SMauro Carvalho Chehab            source = arg.get("source", "")
11199ec67a9SMauro Carvalho Chehab
11299ec67a9SMauro Carvalho Chehab            if arg and "KdocItem" in self.yaml_content:
113b37b3cbbSMauro Carvalho Chehab                msg = self.get_kdoc_item(arg)
114b37b3cbbSMauro Carvalho Chehab
11599ec67a9SMauro Carvalho Chehab                other_stuff = msg.get("other_stuff", {})
11699ec67a9SMauro Carvalho Chehab                if "source" in other_stuff:
11799ec67a9SMauro Carvalho Chehab                    del other_stuff["source"]
11899ec67a9SMauro Carvalho Chehab
119b37b3cbbSMauro Carvalho Chehab                expected_dict["kdoc_item"] = msg
120b37b3cbbSMauro Carvalho Chehab
121*e786fab2SMauro Carvalho Chehab            base_name = arg.name
122*e786fab2SMauro Carvalho Chehab            if not base_name:
123*e786fab2SMauro Carvalho Chehab                base_name = fname
124*e786fab2SMauro Carvalho Chehab            base_name = base_name.lower().replace(".", "_").replace("/", "_")
125*e786fab2SMauro Carvalho Chehab
126*e786fab2SMauro Carvalho Chehab
127*e786fab2SMauro Carvalho Chehab            # Don't add duplicated names
128*e786fab2SMauro Carvalho Chehab            i = 0
129*e786fab2SMauro Carvalho Chehab            name = base_name
130*e786fab2SMauro Carvalho Chehab            while name in self.test_names:
131*e786fab2SMauro Carvalho Chehab                i += 1
132*e786fab2SMauro Carvalho Chehab                name = f"{base_name}_{i:03d}"
133*e786fab2SMauro Carvalho Chehab
134*e786fab2SMauro Carvalho Chehab            self.test_names.add(name)
135*e786fab2SMauro Carvalho Chehab
136b37b3cbbSMauro Carvalho Chehab            for out_style in self.out_style:
137b37b3cbbSMauro Carvalho Chehab                if isinstance(out_style, ManFormat):
138b37b3cbbSMauro Carvalho Chehab                    key = "man"
139b37b3cbbSMauro Carvalho Chehab                else:
140b37b3cbbSMauro Carvalho Chehab                    key = "rst"
141b37b3cbbSMauro Carvalho Chehab
1426e0d7b63SMauro Carvalho Chehab                expected_dict[key]= out_style.output_symbols(fname, [arg]).strip()
143b37b3cbbSMauro Carvalho Chehab
144b37b3cbbSMauro Carvalho Chehab            test = {
145b37b3cbbSMauro Carvalho Chehab                "name": name,
14699ec67a9SMauro Carvalho Chehab                "description": f"{fname} line {arg.declaration_start_line}",
147b37b3cbbSMauro Carvalho Chehab                "fname": fname,
14899ec67a9SMauro Carvalho Chehab                "source": source,
149b37b3cbbSMauro Carvalho Chehab                "expected": [expected_dict]
150b37b3cbbSMauro Carvalho Chehab            }
151b37b3cbbSMauro Carvalho Chehab
152b37b3cbbSMauro Carvalho Chehab            self.tests.append(test)
153b37b3cbbSMauro Carvalho Chehab
154b37b3cbbSMauro Carvalho Chehab            expected_dict = {}
155b37b3cbbSMauro Carvalho Chehab
156b37b3cbbSMauro Carvalho Chehab    def write(self):
157b37b3cbbSMauro Carvalho Chehab        """
158b37b3cbbSMauro Carvalho Chehab        Output the content of self.tests to self.test_file.
159b37b3cbbSMauro Carvalho Chehab        """
160b37b3cbbSMauro Carvalho Chehab        import yaml
161b37b3cbbSMauro Carvalho Chehab
1626e0d7b63SMauro Carvalho Chehab        # Helper function to better handle multilines
1636e0d7b63SMauro Carvalho Chehab        def str_presenter(dumper, data):
1646e0d7b63SMauro Carvalho Chehab            if "\n" in data:
1656e0d7b63SMauro Carvalho Chehab                return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
1666e0d7b63SMauro Carvalho Chehab
1676e0d7b63SMauro Carvalho Chehab            return dumper.represent_scalar("tag:yaml.org,2002:str", data)
1686e0d7b63SMauro Carvalho Chehab
1696e0d7b63SMauro Carvalho Chehab        # Register the representer
1706e0d7b63SMauro Carvalho Chehab        yaml.add_representer(str, str_presenter)
1716e0d7b63SMauro Carvalho Chehab
172b37b3cbbSMauro Carvalho Chehab        data = {"tests": self.tests}
173b37b3cbbSMauro Carvalho Chehab
174b37b3cbbSMauro Carvalho Chehab        with open(self.test_file, "w", encoding="utf-8") as fp:
1756e0d7b63SMauro Carvalho Chehab            yaml.dump(data, fp,
1766e0d7b63SMauro Carvalho Chehab                      sort_keys=False, width=120, indent=2,
1776e0d7b63SMauro Carvalho Chehab                      default_flow_style=False, allow_unicode=True,
1786e0d7b63SMauro Carvalho Chehab                      explicit_start=False, explicit_end=False)
179