Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cc14f4e

Browse files
committed
Move the debug-mode TransformNode.write_graphviz out.
It was only accessible by modifying the source of transforms.py to set DEBUG to True (before the module is imported), and anyways broken because transforms are not hashable in Py3 (so the call to hash() fails). Instead move it to a private module. Example use: ``` from matplotlib._internal_utils import graphviz_dump_transform graphviz_dump_transform(plt.gca().transAxes, "/tmp/test.png") ``` Also, stop tracking transform node children even in debug mode, as it was only used when dumping the transform but that's not even necessary -- one can just inspect `vars(node)` to look for children. Also fix `AffineBase.__eq__` to check that the "other" object has a get_matrix method, to not raise an AttributeError when comparing with Bboxes (which don't).
1 parent db06a34 commit cc14f4e

File tree

2 files changed

+53
-61
lines changed

2 files changed

+53
-61
lines changed

lib/matplotlib/_internal_utils.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
Internal debugging utilities, that are not expected to be used in the rest of
3+
the codebase.
4+
5+
WARNING: Code in this module may change without prior notice!
6+
"""
7+
8+
from io import StringIO
9+
from pathlib import Path
10+
import subprocess
11+
12+
from matplotlib.transforms import TransformNode
13+
14+
15+
def graphviz_dump_transform(transform, dest, *, highlight=None):
16+
"""
17+
Generate a graphical representation of the transform tree for *transform*
18+
using the :program:`dot` program (which this function depends on). The
19+
output format (png, dot, etc.) is determined from the suffix of *dest*.
20+
"""
21+
22+
if highlight is None:
23+
highlight = [transform]
24+
seen = set()
25+
26+
def recurse(root, buf):
27+
if id(root) in seen:
28+
return
29+
seen.add(id(root))
30+
props = {}
31+
label = type(root).__name__
32+
if root._invalid:
33+
label = f'[{label}]'
34+
if root in highlight:
35+
props['style'] = 'bold'
36+
props['shape'] = 'box'
37+
props['label'] = '"%s"' % label
38+
props = ' '.join(map('{0[0]}={0[1]}'.format, props.items()))
39+
buf.write(f'{id(root)} [{props}];\n')
40+
for key, val in vars(root).items():
41+
if isinstance(val, TransformNode) and id(root) in val._parents:
42+
buf.write(f'"{id(root)}" -> "{id(val)}" '
43+
f'[label="{key}", fontsize=10];\n')
44+
recurse(val, buf)
45+
46+
buf = StringIO()
47+
buf.write('digraph G {\n')
48+
recurse(transform, buf)
49+
buf.write('}\n')
50+
subprocess.run(
51+
['dot', '-T', Path(dest).suffix[1:], '-o', dest],
52+
input=buf.getvalue().encode('utf-8'), check=True)

lib/matplotlib/transforms.py

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -178,14 +178,6 @@ def set_children(self, *children):
178178
target=child._parents: target.pop(sid))
179179
child._parents[id(self)] = ref
180180

181-
if DEBUG:
182-
_set_children = set_children
183-
184-
def set_children(self, *children):
185-
self._set_children(*children)
186-
self._children = children
187-
set_children.__doc__ = _set_children.__doc__
188-
189181
def frozen(self):
190182
"""
191183
Returns a frozen copy of this transform node. The frozen copy
@@ -195,58 +187,6 @@ def frozen(self):
195187
"""
196188
return self
197189

198-
if DEBUG:
199-
def write_graphviz(self, fobj, highlight=[]):
200-
"""
201-
For debugging purposes.
202-
203-
Writes the transform tree rooted at 'self' to a graphviz "dot"
204-
format file. This file can be run through the "dot" utility
205-
to produce a graph of the transform tree.
206-
207-
Affine transforms are marked in blue. Bounding boxes are
208-
marked in yellow.
209-
210-
*fobj*: A Python file-like object
211-
212-
Once the "dot" file has been created, it can be turned into a
213-
png easily with::
214-
215-
$> dot -Tpng -o $OUTPUT_FILE $DOT_FILE
216-
217-
"""
218-
seen = set()
219-
220-
def recurse(root):
221-
if root in seen:
222-
return
223-
seen.add(root)
224-
props = {}
225-
label = root.__class__.__name__
226-
if root._invalid:
227-
label = '[%s]' % label
228-
if root in highlight:
229-
props['style'] = 'bold'
230-
props['shape'] = 'box'
231-
props['label'] = '"%s"' % label
232-
props = ' '.join(map('{0[0]}={0[1]}'.format, props.items()))
233-
234-
fobj.write('%s [%s];\n' % (hash(root), props))
235-
236-
if hasattr(root, '_children'):
237-
for child in root._children:
238-
name = next((key for key, val in root.__dict__.items()
239-
if val is child), '?')
240-
fobj.write('"%s" -> "%s" [label="%s", fontsize=10];\n'
241-
% (hash(root),
242-
hash(child),
243-
name))
244-
recurse(child)
245-
246-
fobj.write("digraph G {\n")
247-
recurse(self)
248-
fobj.write("}\n")
249-
250190

251191
class BboxBase(TransformNode):
252192
"""
@@ -1667,7 +1607,7 @@ def __array__(self, *args, **kwargs):
16671607
return self.get_matrix()
16681608

16691609
def __eq__(self, other):
1670-
if getattr(other, "is_affine", False):
1610+
if getattr(other, "is_affine", False) and hasattr(other, "get_matrix"):
16711611
return np.all(self.get_matrix() == other.get_matrix())
16721612
return NotImplemented
16731613

0 commit comments

Comments
 (0)