Skip to content

Commit

Permalink
[tools] Remove compile-time dependencies on lxml (RobotLocomotion#19027)
Browse files Browse the repository at this point in the history
Programs invoked at compile-time should only use standard Python modules
for maximum portability.

Luckily in this case, Python 3 ships with a (slightly less featureful)
ElementTree implementation that we can use.
  • Loading branch information
jwnimmer-tri authored Mar 19, 2023
1 parent 82d8460 commit ecbc743
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 28 deletions.
4 changes: 2 additions & 2 deletions doc/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import urllib.parse

from bazel_tools.tools.python.runfiles import runfiles
import lxml.etree as ET
import xml.etree.ElementTree as ET

from drake.doc.defs import check_call, main

Expand Down Expand Up @@ -134,9 +134,9 @@ def _build_sitemap(site_dir: str) -> None:
loc = ET.SubElement(url, "loc")
loc.text = location
sitemap = ET.ElementTree(urlset)
ET.indent(sitemap)
sitemap.write(os.path.join(site_dir, "sitemap.xml"),
encoding="utf-8",
pretty_print=True,
xml_declaration=True)


Expand Down
1 change: 0 additions & 1 deletion setup/mac/binary_distribution/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
ipywidgets
lxml
matplotlib
notebook
Pillow
Expand Down
1 change: 0 additions & 1 deletion setup/ubuntu/binary_distribution/packages-focal.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ libxt6
ocl-icd-libopencl1
python3
python3-ipywidgets
python3-lxml
python3-matplotlib
python3-numpy
python3-pil
Expand Down
1 change: 0 additions & 1 deletion setup/ubuntu/binary_distribution/packages-jammy.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ libxt6
ocl-icd-libopencl1
python3
python3-ipywidgets
python3-lxml
python3-matplotlib
python3-munkres
python3-numpy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ llvm-12
python3-dateutil
python3-dbg
python3-flask
python3-lxml-dbg
python3-matplotlib-dbg
python3-nbconvert
python3-nbformat
Expand Down
1 change: 0 additions & 1 deletion tools/wheel/image/provision-python.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ ln -s /usr/local/bin/python /usr/bin/python
# TODO(jwnimmer-tri): Should these be version-pinned? What's the process for
# keeping them up to date if they are?
pip install \
lxml \
matplotlib \
numpy \
pyyaml \
Expand Down
44 changes: 30 additions & 14 deletions tools/workspace/pybind11/pybind_coverage_xml_parser.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
from lxml import etree as ET
import pandas
import os
import logging
import os
import xml.etree.ElementTree as ET

import pandas


XPATHS = {
"class_decl": ".//Node[@kind='CursorKind.CLASS_DECL' or "
"@kind='CursorKind.CLASS_TEMPLATE' or @kind='CursorKind.STRUCT_DECL']"
"[@ignore='0']",
"file_names": ".//Node",
"doc_var_xpath": ".//Node[@ignore='0' and @doc_var]",
"class_decl": [
".//Node[@kind='CursorKind.CLASS_DECL'][@ignore='0']",
".//Node[@kind='CursorKind.CLASS_TEMPLATE'][@ignore='0']",
".//Node[@kind='CursorKind.STRUCT_DECL'][@ignore='0']",
],
"file_names": [
".//Node",
],
"doc_var_xpath": [
".//Node[@ignore='0'][@doc_var]"
],
}

all_kinds = [
Expand Down Expand Up @@ -162,7 +169,9 @@ def write_coverage(self):
"""Writes class-wise coverage to a CSV file.
"""
class_nodes = self.xml_root.xpath(XPATHS["class_decl"])
class_nodes = []
for xpath in XPATHS["class_decl"]:
class_nodes.extend(self.xml_root.findall(xpath))
self.df = pandas.DataFrame(
columns=["ClassName", "Coverage"] + all_kinds)
self.get_class_coverage(class_nodes)
Expand All @@ -181,7 +190,9 @@ def get_class_coverage(self, class_nodes):

for c in class_nodes:
# All nodes which are not ignored and have a doc_var
doc_var_nodes = c.xpath(XPATHS["doc_var_xpath"])
doc_var_nodes = []
for xpath in XPATHS["doc_var_xpath"]:
doc_var_nodes.extend(c.findall(xpath))
row = CommonUtils.get_node_coverage(
doc_var_nodes, self.pybind_strings)

Expand All @@ -191,6 +202,7 @@ def get_class_coverage(self, class_nodes):

self.df_pruned = CommonUtils.prune_dataframe(
self.df, ["ClassName", "Coverage"])
self.df_pruned = self.df_pruned.sort_values(by=["ClassName"])


class FileCoverage:
Expand All @@ -216,7 +228,9 @@ def write_coverage(self):
"""Writes file-wise coverage to a CSV file.
"""
file_nodes = self.xml_root.xpath(XPATHS["doc_var_xpath"])
file_nodes = []
for xpath in XPATHS["doc_var_xpath"]:
file_nodes.extend(self.xml_root.findall(xpath))
self.df = pandas.DataFrame(
columns=["DirCoverage", "FileName", "Coverage"] + all_kinds)

Expand Down Expand Up @@ -300,7 +314,7 @@ def add_directory_coverage(self):
filenames, coverage = df.loc[:, "FileName"], df.loc[:, "Coverage"]
file_coverage = dict(zip(filenames, coverage))
root = self.make_tree(file_coverage)
XP = root.xpath
XP = root.findall
dirname = None
final_row = {}

Expand All @@ -312,10 +326,12 @@ def add_directory_coverage(self):
if dirname != os.path.dirname(str(row["FileName"])):
dirname = os.path.dirname(str(row["FileName"]))
total_num = sum([
int(n) for n in XP('.//{}/*/@num'.format(dirname))
int(n.get("num"))
for n in XP('.//{}/*/[@num]'.format(dirname))
])
total_den = sum([
int(d) for d in XP('.//{}/*/@den'.format(dirname))
int(d.get("den"))
for d in XP('.//{}/*/[@den]'.format(dirname))
])
row["DirCoverage"] = Coverage(total_num, total_den)

Expand Down
15 changes: 8 additions & 7 deletions tools/workspace/sdformat_internal/embed_sdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import sys

from lxml import etree
import xml.etree.ElementTree as ET

assert __name__ == '__main__'

Expand All @@ -11,16 +11,17 @@ def _minified_xml(*, filename):
"""Given a filename for an `*.sdf` schema file, returns a minified xml
string with its contents, to conserve disk space in Drake's library.
"""
parser = etree.XMLParser(remove_blank_text=True, remove_comments=True)
root = etree.parse(filename, parser)
for item in root.xpath("//description"):
item.getparent().remove(item)
for elem in root.iter('*'):
tree = ET.parse(filename)
# Remove all '<description>' elements.
for item in tree.findall(".//description/.."):
item.remove(item.find("description"))
# Discard whitespace.
for elem in tree.iter('*'):
if elem.text is not None:
elem.text = elem.text.strip()
if elem.tail is not None:
elem.tail = elem.tail.strip()
return etree.tostring(root, encoding="utf-8", xml_declaration=False)
return ET.tostring(tree.getroot(), encoding="utf-8", xml_declaration=False)


filenames = sorted(sys.argv[1:])
Expand Down

0 comments on commit ecbc743

Please sign in to comment.