15
15
import re
16
16
import shutil
17
17
import sys
18
+ from tempfile import TemporaryFile
18
19
19
20
import numpy as np
20
21
@@ -132,8 +133,8 @@ def convert(old, new):
132
133
return convert
133
134
134
135
135
- # Copied from https://bugs.python.org/issue25567
136
- _find_unsafe_bytes = re .compile (rb '[^\w @%+=:,./-]', re . ASCII ).search
136
+ # Modified from https://bugs.python.org/issue25567.
137
+ _find_unsafe_bytes = re .compile (br '[^a-zA-Z0-9_ @%+=:,./-]' ).search
137
138
138
139
139
140
def _shlex_quote_bytes (b ):
@@ -182,13 +183,19 @@ def __call__(self, orig, dest):
182
183
# `--without-gui`). Unsetting `DISPLAY` prevents this (and causes
183
184
# GTK to crash and Inkscape to terminate, but that'll just be
184
185
# reported as a regular exception below).
185
- env [ "DISPLAY" ] = ""
186
+ env . pop ( "DISPLAY" , None ) # May already be unset.
186
187
# Do not load any user options.
187
- env ["INKSCAPE_PROFILE_DIR" ] = os .devnull
188
+ # `os.environ` needs native strings on Py2+Windows.
189
+ env [str ("INKSCAPE_PROFILE_DIR" )] = os .devnull
190
+ # Old versions of Inkscape (0.48.3.1, used on Travis as of now)
191
+ # seem to sometimes deadlock when stderr is redirected to a pipe,
192
+ # so we redirect it to a temporary file instead. This is not
193
+ # necessary anymore as of Inkscape 0.92.1.
194
+ self ._stderr = TemporaryFile ()
188
195
self ._proc = subprocess .Popen (
189
196
[str ("inkscape" ), "--without-gui" , "--shell" ],
190
- env = env , stdin = subprocess .PIPE ,
191
- stdout = subprocess . PIPE , stderr = subprocess . PIPE )
197
+ stdin = subprocess . PIPE , stdout = subprocess .PIPE ,
198
+ stderr = self . _stderr , env = env )
192
199
if not self ._read_to_prompt ():
193
200
raise OSError ("Failed to start Inkscape" )
194
201
@@ -200,16 +207,23 @@ def __call__(self, orig, dest):
200
207
def fsencode (s ):
201
208
return s .encode (sys .getfilesystemencoding ())
202
209
203
- orig , dest = map (_shlex_quote_bytes , map (fsencode , [orig , dest ]))
204
- self ._proc .stdin .write (orig + b" --export-png=" + dest + b"\n " )
210
+ orig_b , dest_b = map (_shlex_quote_bytes , map (fsencode , [orig , dest ]))
211
+ if b"\n " in orig_b or b"\n " in dest_b :
212
+ # Who knows whether the current folder name has a newline, or if
213
+ # our encoding is even ASCII compatible... Just fall back on the
214
+ # slow solution.
215
+ return make_external_conversion_command (lambda old , new : [
216
+ str ('inkscape' ), '-z' , old , '--export-png' , new ])(orig , dest )
217
+ self ._proc .stdin .write (orig_b + b" --export-png=" + dest_b + b"\n " )
205
218
self ._proc .stdin .flush ()
206
219
if not self ._read_to_prompt ():
207
- # I *guess* we should technically decode the stream using
208
- # `getdefaultencoding` *except* for filenames which should be
209
- # decoded using `getfilesystemencoding` but that would be a bit
210
- # overkill.
220
+ # Inkscape's output is not localized but gtk's is, so the
221
+ # output stream probably has a mixed encoding. Using
222
+ # `getfilesystemencoding` should at least get the filenames
223
+ # right...
224
+ self ._stderr .seek (0 )
211
225
raise ImageComparisonFailure (
212
- self ._proc . stderr .read ().decode (
226
+ self ._stderr .read ().decode (
213
227
sys .getfilesystemencoding (), "replace" ))
214
228
215
229
def __del__ (self ):
@@ -219,7 +233,7 @@ def __del__(self):
219
233
self ._proc .wait ()
220
234
self ._proc .stdin .close ()
221
235
self ._proc .stdout .close ()
222
- self ._proc . stderr .close ()
236
+ self ._stderr .close ()
223
237
224
238
225
239
def _update_converter ():
0 commit comments