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

Skip to content

Commit d67380d

Browse files
committed
Inkscape shell mode.
1 parent 8a77cfb commit d67380d

File tree

1 file changed

+58
-6
lines changed

1 file changed

+58
-6
lines changed

lib/matplotlib/testing/compare.py

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77

88
import six
99

10+
import atexit
11+
import functools
1012
import hashlib
13+
import itertools
1114
import os
1215
import shutil
16+
import sys
1317

1418
import numpy as np
1519

@@ -128,6 +132,57 @@ def convert(old, new):
128132
return convert
129133

130134

135+
class _SVGConverter(object):
136+
def __init__(self):
137+
self._proc = None
138+
# We cannot rely on the GC to trigger `__del__` at exit because
139+
# other modules (e.g. `subprocess`) may already have their globals
140+
# set to `None`, which make `proc.communicate` or `proc.terminate`
141+
# fail. By relying on `atexit` we ensure the destructor runs before
142+
# `None`-setting occurs.
143+
atexit.register(self.__del__)
144+
145+
def _read_to_terminator(self):
146+
stream = iter(functools.partial(self._proc.stdout.read, 1), b"")
147+
terminator = (b"\n", b">")
148+
n = len(terminator)
149+
its = itertools.tee(stream, n)
150+
for i, it in enumerate(its):
151+
next(itertools.islice(it, i, i), None) # Advance `it` by `i`.
152+
while True:
153+
window = tuple(map(next, its))
154+
if (len(window) != n or window == terminator):
155+
# First case is to running out of data -- one of the `next(it)`
156+
# raises StopIteration, so the tuple is shorter.
157+
# Second case is to successful reading until terminator.
158+
break
159+
160+
def __call__(self, orig, dest):
161+
if (not self._proc # First run.
162+
or self._proc.poll()): # Inkscape terminated, e.g. crashed.
163+
self._proc = subprocess.Popen([str("inkscape"), "--shell"],
164+
stdin=subprocess.PIPE,
165+
stdout=subprocess.PIPE)
166+
self._read_to_terminator()
167+
try:
168+
fsencode = os.fsencode
169+
except AttributeError: # Py2.
170+
def fsencode(s):
171+
return s.encode(sys.getfilesystemencoding())
172+
self._proc.stdin.write(
173+
fsencode("{} --export-png={}\n".format(orig, dest)))
174+
self._proc.stdin.flush()
175+
self._read_to_terminator()
176+
177+
def __del__(self):
178+
if self._proc:
179+
if self._proc.poll() is None: # Not exited yet.
180+
self._proc.communicate(b"quit\n")
181+
self._proc.wait()
182+
self._proc.stdin.close()
183+
self._proc.stdout.close()
184+
185+
131186
def _update_converter():
132187
gs, gs_v = matplotlib.checkdep_ghostscript()
133188
if gs_v is not None:
@@ -138,9 +193,7 @@ def cmd(old, new):
138193
converter['eps'] = make_external_conversion_command(cmd)
139194

140195
if matplotlib.checkdep_inkscape() is not None:
141-
def cmd(old, new):
142-
return [str('inkscape'), '-z', old, '--export-png', new]
143-
converter['svg'] = make_external_conversion_command(cmd)
196+
converter['svg'] = _SVGConverter()
144197

145198

146199
#: A dictionary that maps filename extensions to functions which
@@ -363,9 +416,8 @@ def save_diff_image(expected, actual, output):
363416
actual, actualImage, expected, expectedImage)
364417
expectedImage = np.array(expectedImage).astype(float)
365418
actualImage = np.array(actualImage).astype(float)
366-
assert expectedImage.ndim == actualImage.ndim
367-
assert expectedImage.shape == actualImage.shape
368-
absDiffImage = abs(expectedImage - actualImage)
419+
assert expectedImage.shape[:2] == actualImage.shape[:2]
420+
absDiffImage = np.abs(expectedImage[:, :, :3] - actualImage[:, :, :3])
369421

370422
# expand differences in luminance domain
371423
absDiffImage *= 255 * 10

0 commit comments

Comments
 (0)