File: misc.py

package info (click to toggle)
python-docutils 0.22%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 11,448 kB
  • sloc: python: 53,302; lisp: 14,475; xml: 1,807; javascript: 1,032; makefile: 102; sh: 96
file content (151 lines) | stat: -rw-r--r-- 5,328 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# $Id: misc.py 10166 2025-06-16 09:45:10Z milde $
# Author: David Goodger <goodger@python.org>
# Copyright: This module has been placed in the public domain.

"""
Miscellaneous transforms.
"""

from __future__ import annotations

__docformat__ = 'reStructuredText'

from docutils import nodes
from docutils.transforms import Transform


class CallBack(Transform):

    """
    Inserts a callback into a document.  The callback is called when the
    transform is applied, which is determined by its priority.

    For use with `nodes.pending` elements.  Requires a ``details['callback']``
    entry, a bound method or function which takes one parameter: the pending
    node.  Other data can be stored in the ``details`` attribute or in the
    object hosting the callback method.
    """

    default_priority = 990

    def apply(self) -> None:
        pending = self.startnode
        pending.details['callback'](pending)
        pending.parent.remove(pending)


class ClassAttribute(Transform):

    """
    Move the "class" attribute specified in the "pending" node into the
    next visible element.
    """

    default_priority = 210

    def apply(self) -> None:
        pending = self.startnode
        parent = pending.parent
        child = pending
        while parent:
            # Check for appropriate following siblings:
            for index in range(parent.index(child) + 1, len(parent)):
                element = parent[index]
                if (isinstance(element, nodes.Invisible)
                    or isinstance(element, nodes.system_message)):
                    continue
                element['classes'] += pending.details['class']
                pending.parent.remove(pending)
                return
            else:
                # At end of section or container; apply to sibling
                child = parent
                parent = parent.parent
        error = self.document.reporter.error(
            'No suitable element following "%s" directive'
            % pending.details['directive'],
            nodes.literal_block(pending.rawsource, pending.rawsource),
            line=pending.line)
        pending.replace_self(error)


class Transitions(Transform):

    """
    Move transitions at the end of sections up the tree.  Complain
    on transitions after a title, subtitle, meta, or decoration element,
    at the beginning or end of the document, and after another transition.

    For example, transform this::

        <section>
            ...
            <transition>
        <section>
            ...

    into this::

        <section>
            ...
        <transition>
        <section>
            ...
    """

    default_priority = 830

    def apply(self) -> None:
        for node in self.document.findall(nodes.transition):
            self.visit_transition(node)

    def visit_transition(self, node) -> None:
        index = node.parent.index(node)
        previous_sibling = node.previous_sibling()
        msg = ''
        if not isinstance(node.parent, (nodes.document, nodes.section)):
            msg = 'Transition must be child of <document> or <section>.'
        elif index == 0 or isinstance(previous_sibling, (nodes.title,
                                                         nodes.subtitle,
                                                         nodes.meta,
                                                         nodes.decoration)):
            msg = 'Document or section may not begin with a transition.'
        elif isinstance(previous_sibling, nodes.transition):
            msg = ('At least one body element must separate transitions; '
                   'adjacent transitions are not allowed.')
        if msg:
            warning = self.document.reporter.warning(msg, base_node=node)
            # Check, if it is valid to insert a body element
            node.parent[index] = nodes.paragraph()
            try:
                node.parent.validate(recursive=False)
            except nodes.ValidationError:
                node.parent[index] = node
            else:
                node.parent[index] = node
                node.parent.insert(index+1, warning)
                index += 1
        if not isinstance(node.parent, (nodes.document, nodes.section)):
            return
        assert index < len(node.parent)
        if index != len(node.parent) - 1:
            # No need to move the node.
            return
        # Node behind which the transition is to be moved.
        sibling = node
        # While sibling is the last node of its parent.
        while index == len(sibling.parent) - 1:
            sibling = sibling.parent
            if sibling.parent is None:  # sibling is the top node (document)
                # Transition at the end of document.  Do not move the
                # transition up, and place a warning behind.
                warning = self.document.reporter.warning(
                              'Document may not end with a transition.',
                              base_node=node)
                node.parent.append(warning)
                return
            index = sibling.parent.index(sibling)
        # Remove the original transition node.
        node.parent.remove(node)
        # Insert the transition after the sibling.
        sibling.parent.insert(index + 1, node)