# 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: return True m = re.match("(?:typename)? *([A-Za-z0-9_\:]+)", x.text) if m is not None: s = m.group(1) 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) # 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) # 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)