Coverage for python / lsst / sphinxutils / ext / utils.py: 44%
35 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:29 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:29 +0000
1"""Utilities for Sphinx extensions."""
3from __future__ import annotations
5import re
6from dataclasses import dataclass
7from typing import Self
9from docutils import nodes
11__all__ = [
12 "RoleContent",
13 "make_section",
14]
16ROLE_DISPLAY_PATTERN = re.compile(r"(?P<display>.+)<(?P<reference>.+)>")
19@dataclass
20class RoleContent:
21 """Interpreted content of a Sphinx role."""
23 last_component: bool
24 """If `True`, the display should show only the last component of a
25 namespace.
26 """
28 display: str | None
29 """Custom display content."""
31 ref: str
32 """The reference content. If the role doesn't have a custom display, the
33 reference will be the role's content. The ``ref`` never includes a ``~``
34 prefix.
35 """
37 @classmethod
38 def parse(cls, role_rawsource: str) -> Self:
39 """Split the ``rawsource`` of a role into standard components.
41 Parameters
42 ----------
43 role_rawsource : `str`
44 The content of the role: its ``rawsource`` attribute.
46 Returns
47 -------
48 RoleContent
49 The parsed role content.
51 Examples
52 --------
53 >>> RoleContent.parse("Tables <lsst.afw.table.Table>")
54 RoleContent(last_component=False, display=Tables',
55 ref='lsst.afw.table.Table')
57 >>> RoleContent.parse("~lsst.afw.table.Table")
58 RoleContent(last_component=True, display=None,
59 ref='lsst.afw.table.Table')
60 """
61 if role_rawsource.startswith("~"):
62 # Only the last part of a namespace should be shown.
63 last_component = True
64 # Strip that marker off
65 role_rawsource = role_rawsource.lstrip("~")
66 else:
67 last_component = False
69 match = ROLE_DISPLAY_PATTERN.match(role_rawsource)
70 if match:
71 display = match.group("display").strip()
72 ref = match.group("reference").strip()
73 else:
74 # No suggested display
75 display = None
76 ref = role_rawsource.strip()
78 return cls(last_component=last_component, display=display, ref=ref)
81def make_section(section_id: str, contents: list[nodes.Node] | None = None) -> nodes.section:
82 """Make a docutils section node.
84 Parameters
85 ----------
86 section_id
87 Section identifier, which is appended to both the ``ids`` and ``names``
88 attributes.
89 contents
90 List of docutils nodes that are inserted into the section.
92 Returns
93 -------
94 docutils.nodes.section
95 Docutils section node.
96 """
97 section = nodes.section()
98 section["ids"].append(nodes.make_id(section_id))
99 section["names"].append(section_id)
100 if contents is not None:
101 section.extend(contents)
102 return section