parser_yaml.py 3.5 KB

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