File: doxygen_postprocessing.py

package info (click to toggle)
boost1.88 1.88.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 576,932 kB
  • sloc: cpp: 4,149,234; xml: 136,789; ansic: 35,092; python: 33,910; asm: 5,698; sh: 4,604; ada: 1,681; makefile: 1,633; pascal: 1,139; perl: 1,124; sql: 640; yacc: 478; ruby: 271; java: 77; lisp: 24; csh: 6
file content (206 lines) | stat: -rw-r--r-- 5,796 bytes parent folder | download | duplicates (4)
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#            Copyright Hans Dembinski 2018 - 2019.
#   Distributed under the Boost Software License, Version 1.0.
#      (See accompanying file LICENSE_1_0.txt or copy at
#            https://www.boost.org/LICENSE_1_0.txt)

import sys
import xml.etree.ElementTree as ET
import re


def log(*args):
    sys.stdout.write("post-processing:" + " ".join(args) + "\n")


def select(condition, *tags):
    result = []
    for tag in tags:
        for item in root.iter(tag):
            if condition(item):
                result.append(item)
    return result


def is_detail(x):
    if x.text is not None:
        if "detail" in x.text.lower():
            return True
        m = re.match(r"(?:typename)? *([A-Za-z0-9_\:]+)", x.text)
        if m is not None:
            s = m.group(1).lower()
            if s.startswith("detail") or s.endswith("_impl"):
                x.text = s
                return True

    p = x.find("purpose")
    if p is not None:
        return p.text.lower().lstrip().startswith("implementation detail")
    return False


def is_deprecated(x):
    p = x.find("purpose")
    if p is not None:
        return p.text.lower().lstrip().startswith("deprecated")
    return False


def sort_headers_by(x):
    name = x.get("name")
    return name.count("/"), name


def tail_stripper(elem):
    if elem.tail:
        elem.tail = elem.tail.rstrip()
    for item in elem:
        tail_stripper(item)


def item_sorter(elem):
    if elem.tag == "namespace":
        if len(elem) > 1:
            items = list(elem)
            items.sort(key=lambda x: x.get("name"))
            elem[:] = items
        for sub in elem:
            item_sorter(sub)


if "ipykernel" in sys.argv[0]:  # used when run interactively in Atom/Hydrogen
    from pathlib import Path

    for input_file in Path().rglob("reference.xml"):
        input_file = str(input_file)
        break
else:
    input_file = sys.argv[1]

output_file = input_file.replace(".xml", "_pp.xml")

tree = ET.parse(input_file)
root = tree.getroot()

parent_map = {c: p for p in tree.iter() for c in p}

unspecified = ET.Element("emphasis")
unspecified.text = "unspecified"

# - hide all unnamed template parameters, these are used for SFINAE
# - hide all template parameters that start with Detail
for item in select(
    lambda x: x.get("name") == "" or x.get("name").startswith("Detail"),
    "template-type-parameter",
    "template-nontype-parameter",
):
    parent = parent_map[item]
    assert parent.tag == "template"
    parent.remove(item)
    parent = parent_map[parent]
    name = parent.get("name")
    if name is None:
        log("removing unnamed template parameter from", parent.tag)
    else:
        log("removing unnamed template parameter from", parent.tag, name)

# hide macros with detail in the name
for item in select(lambda x: "DETAIL" in x.get("name").split("_"), "macro"):
    parent = parent_map[item]
    parent.remove(item)
    log("removing macro", item.get("name"))

# replace any type with "detail" in its name with "unspecified"
for item in select(is_detail, "type"):
    log("replacing", '"%s"' % item.text, 'with "unspecified"')
    item.clear()
    item.append(unspecified)

# hide everything that's deprecated
for item in select(is_deprecated, "typedef"):
    parent = parent_map[item]
    log(
        "removing deprecated",
        item.tag,
        item.get("name"),
        "from",
        parent.tag,
        parent.get("name"),
    )
    parent.remove(item)

# replace any typedef with "unspecified"
for item in select(is_detail, "typedef"):
    log("replacing typedef", item.get("name"), 'with "unspecified"')
    name = item.get("name")
    item.clear()
    item.tag = "typedef"
    item.set("name", name)
    type = ET.Element("type")
    type.append(unspecified)
    item.append(type)

# hide private member functions
for item in select(
    lambda x: x.get("name") == "private member functions", "method-group"
):
    parent = parent_map[item]
    log("removing private member functions from", parent.tag, parent.get("name"))
    parent.remove(item)

# hide undocumented classes, structs, functions and replace those declared
# "implementation detail" with typedef to unspecified
for item in select(lambda x: True, "class", "struct", "function"):
    purpose = item.find("purpose")
    if purpose is None:
        parent = parent_map[item]
        log(
            "removing undocumented",
            item.tag,
            item.get("name"),
            "from",
            parent.tag,
            parent.get("name"),
        )
        if item in parent_map[item]:
            parent_map[item].remove(item)
    elif purpose.text.strip().lower() == "implementation detail":
        log("replacing", item.tag, item.get("name"), "with unspecified typedef")
        name = item.get("name")
        item.clear()
        item.tag = "typedef"
        item.set("name", name)
        type = ET.Element("type")
        type.append(unspecified)
        item.append(type)

parent_map = {c: p for p in tree.iter() for c in p}

# hide methods and constructors explicitly declared as "implementation detail"
for item in select(is_detail, "constructor", "method"):
    name = item.get("name")
    log(
        "removing",
        (item.tag + " " + name) if name is not None else item.tag,
        "declared as implementation detail",
    )
    parent_map[item].remove(item)


log("sorting headers")
reference = tree.getroot()
assert reference.tag == "library-reference"
reference[:] = sorted(reference, key=sort_headers_by)


log("sorting items in each namespace")
for header in reference:
    namespace = header.find("namespace")
    if namespace is None:
        continue
    item_sorter(namespace)


log("stripping trailing whitespace")
tail_stripper(reference)

tree.write(output_file)