Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c3fab50

Browse filesBrowse files
committed
Inkscape shell mode.
1 parent 8a77cfb commit c3fab50
Copy full SHA for c3fab50

File tree

Expand file treeCollapse file tree

1 file changed

+57
-5
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+57
-5
lines changed

‎lib/matplotlib/testing/compare.py

Copy file name to clipboardExpand all lines: lib/matplotlib/testing/compare.py
+57-5Lines changed: 57 additions & 5 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
367419
assert expectedImage.shape == actualImage.shape
368-
absDiffImage = abs(expectedImage - actualImage)
420+
absDiffImage = np.abs(expectedImage - actualImage)
369421

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

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.