_internal_utils.py 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. """
  2. Internal debugging utilities, that are not expected to be used in the rest of
  3. the codebase.
  4. WARNING: Code in this module may change without prior notice!
  5. """
  6. from io import StringIO
  7. from pathlib import Path
  8. import subprocess
  9. from matplotlib.transforms import TransformNode
  10. def graphviz_dump_transform(transform, dest, *, highlight=None):
  11. """
  12. Generate a graphical representation of the transform tree for *transform*
  13. using the :program:`dot` program (which this function depends on). The
  14. output format (png, dot, etc.) is determined from the suffix of *dest*.
  15. Parameters
  16. ----------
  17. transform : `~matplotlib.transform.Transform`
  18. The represented transform.
  19. dest : str
  20. Output filename. The extension must be one of the formats supported
  21. by :program:`dot`, e.g. png, svg, dot, ...
  22. (see https://www.graphviz.org/doc/info/output.html).
  23. highlight : list of `~matplotlib.transform.Transform` or None
  24. The transforms in the tree to be drawn in bold.
  25. If *None*, *transform* is highlighted.
  26. """
  27. if highlight is None:
  28. highlight = [transform]
  29. seen = set()
  30. def recurse(root, buf):
  31. if id(root) in seen:
  32. return
  33. seen.add(id(root))
  34. props = {}
  35. label = type(root).__name__
  36. if root._invalid:
  37. label = f'[{label}]'
  38. if root in highlight:
  39. props['style'] = 'bold'
  40. props['shape'] = 'box'
  41. props['label'] = '"%s"' % label
  42. props = ' '.join(map('{0[0]}={0[1]}'.format, props.items()))
  43. buf.write(f'{id(root)} [{props}];\n')
  44. for key, val in vars(root).items():
  45. if isinstance(val, TransformNode) and id(root) in val._parents:
  46. buf.write(f'"{id(root)}" -> "{id(val)}" '
  47. f'[label="{key}", fontsize=10];\n')
  48. recurse(val, buf)
  49. buf = StringIO()
  50. buf.write('digraph G {\n')
  51. recurse(transform, buf)
  52. buf.write('}\n')
  53. subprocess.run(
  54. ['dot', '-T', Path(dest).suffix[1:], '-o', dest],
  55. input=buf.getvalue().encode('utf-8'), check=True)