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

Skip to content

Commit 9e6b9b4

Browse files
committed
Fix running the test suite with inkscape>=1.
1 parent b5d4a6c commit 9e6b9b4

2 files changed

Lines changed: 51 additions & 45 deletions

File tree

lib/matplotlib/__init__.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -331,18 +331,14 @@ def impl(args, regex, min_ver=None, ignore_exit_code=False):
331331
raise ExecutableNotFoundError(message)
332332
elif name == "inkscape":
333333
try:
334-
# use headless option first (works with inkscape version < 1.0):
335-
info = impl(["inkscape", "--without-gui", "-V"],
334+
# Try headless option first (needed for Inkscape version < 1.0):
335+
return impl(["inkscape", "--without-gui", "-V"],
336336
"Inkscape ([^ ]*)")
337337
except ExecutableNotFoundError:
338-
# if --without-gui is not accepted, we're using Inkscape v > 1.0
339-
# so try without the headless option:
340-
info = impl(["inkscape", "-V"], "Inkscape ([^ ]*)")
341-
if info and info.version >= "1.0":
342-
raise ExecutableNotFoundError(
343-
f"You have Inkscape version {info.version} but Matplotlib "
344-
f"only supports Inkscape<1.0")
345-
return info
338+
pass # Suppress exception chaining.
339+
# If --without-gui is not accepted, we may be using Inkscape >= 1.0 so
340+
# try without it:
341+
return impl(["inkscape", "-V"], "Inkscape ([^ ]*)")
346342
elif name == "magick":
347343
path = None
348344
if sys.platform == "win32":

lib/matplotlib/testing/compare.py

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import shutil
1111
import subprocess
1212
import sys
13-
from tempfile import TemporaryFile
13+
from tempfile import TemporaryDirectory, TemporaryFile
1414

1515
import numpy as np
1616
import PIL
@@ -158,53 +158,56 @@ def encode_and_escape(name):
158158

159159
class _SVGConverter(_Converter):
160160
def __call__(self, orig, dest):
161+
old_inkscape = mpl._get_executable_info("inkscape").version < "1"
162+
terminator = b"\n>" if old_inkscape else b"> "
163+
if not hasattr(self, "_tmpdir"):
164+
self._tmpdir = TemporaryDirectory()
161165
if (not self._proc # First run.
162166
or self._proc.poll() is not None): # Inkscape terminated.
163-
env = os.environ.copy()
164-
# If one passes e.g. a png file to Inkscape, it will try to
165-
# query the user for conversion options via a GUI (even with
166-
# `--without-gui`). Unsetting `DISPLAY` prevents this (and causes
167-
# GTK to crash and Inkscape to terminate, but that'll just be
168-
# reported as a regular exception below).
169-
env.pop("DISPLAY", None) # May already be unset.
170-
# Do not load any user options.
171-
env["INKSCAPE_PROFILE_DIR"] = os.devnull
172-
# Old versions of Inkscape (0.48.3.1, used on Travis as of now)
173-
# seem to sometimes deadlock when stderr is redirected to a pipe,
174-
# so we redirect it to a temporary file instead. This is not
175-
# necessary anymore as of Inkscape 0.92.1.
167+
env = {
168+
**os.environ,
169+
# If one passes e.g. a png file to Inkscape, it will try to
170+
# query the user for conversion options via a GUI (even with
171+
# `--without-gui`). Unsetting `DISPLAY` prevents this (and
172+
# causes GTK to crash and Inkscape to terminate, but that'll
173+
# just be reported as a regular exception below).
174+
"DISPLAY": "",
175+
# Do not load any user options.
176+
"INKSCAPE_PROFILE_DIR": os.devnull,
177+
}
178+
# Old versions of Inkscape (e.g. 0.48.3.1) seem to sometimes
179+
# deadlock when stderr is redirected to a pipe, so we redirect it
180+
# to a temporary file instead. This is not necessary anymore as of
181+
# Inkscape 0.92.1.
176182
stderr = TemporaryFile()
177183
self._proc = subprocess.Popen(
178-
["inkscape", "--without-gui", "--shell"],
179-
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
180-
stderr=stderr, env=env)
184+
["inkscape", "--without-gui", "--shell"] if old_inkscape else
185+
["inkscape", "--shell"],
186+
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=stderr,
187+
env=env, cwd=self._tmpdir.name)
181188
# Slight abuse, but makes shutdown handling easier.
182189
self._proc.stderr = stderr
183190
try:
184-
self._read_until(b"\n>")
191+
self._read_until(terminator)
185192
except _ConverterError as err:
186193
raise OSError("Failed to start Inkscape in interactive "
187194
"mode") from err
188195

189-
# Inkscape uses glib's `g_shell_parse_argv`, which has a consistent
190-
# behavior across platforms, so we can just use `shlex.quote`.
191-
orig_b, dest_b = map(_shlex_quote_bytes,
192-
map(os.fsencode, [orig, dest]))
193-
if b"\n" in orig_b or b"\n" in dest_b:
194-
# Who knows whether the current folder name has a newline, or if
195-
# our encoding is even ASCII compatible... Just fall back on the
196-
# slow solution (Inkscape uses `fgets` so it will always stop at a
197-
# newline).
198-
cbook.warn_deprecated(
199-
"3.3", message="Support for converting files from paths "
200-
"containing a newline is deprecated since %(since)s and "
201-
"support will be removed %(removal)s")
202-
return make_external_conversion_command(lambda old, new: [
203-
'inkscape', '-z', old, '--export-png', new])(orig, dest)
204-
self._proc.stdin.write(orig_b + b" --export-png=" + dest_b + b"\n")
196+
# Inkscape's shell mode does not support escaping metacharacters in the
197+
# filename ("\n", and ":;" for inkscape>=1). Avoid any problems by
198+
# running from a temporary directory and using fixed filenames.
199+
inkscape_orig = Path(self._tmpdir.name, os.fsdecode(b"f.svg"))
200+
inkscape_dest = Path(self._tmpdir.name, os.fsdecode(b"f.png"))
201+
try:
202+
inkscape_orig.symlink_to(Path(orig).resolve())
203+
except OSError:
204+
shutil.copyfile(orig, inkscape_orig)
205+
self._proc.stdin.write(
206+
b"f.svg --export-png=f.png\n" if old_inkscape else
207+
b"file-open:f.svg;export-filename:f.png;export-do;file-close\n")
205208
self._proc.stdin.flush()
206209
try:
207-
self._read_until(b"\n>")
210+
self._read_until(terminator)
208211
except _ConverterError as err:
209212
# Inkscape's output is not localized but gtk's is, so the output
210213
# stream probably has a mixed encoding. Using the filesystem
@@ -213,6 +216,13 @@ def __call__(self, orig, dest):
213216
raise ImageComparisonFailure(
214217
self._proc.stderr.read().decode(
215218
sys.getfilesystemencoding(), "replace")) from err
219+
os.remove(inkscape_orig)
220+
shutil.move(inkscape_dest, dest)
221+
222+
def __del__(self):
223+
super().__del__()
224+
if hasattr(self, "_tmpdir"):
225+
self._tmpdir.cleanup()
216226

217227

218228
def _update_converter():

0 commit comments

Comments
 (0)