Repository navigation
Inkscape shell mode. - #8248
Inkscape shell mode.#8248
Conversation
1dee9b7 to
f44aaf1
Compare
|
That's a significant speed up! |
| # set to `None`, which make `proc.communicate` or `proc.terminate` | ||
| # fail. By relying on `atexit` we ensure the destructor runs before | ||
| # `None`-setting occurs. | ||
| atexit.register(self.__del__) |
There was a problem hiding this comment.
Would __del__ not be called a second time when this object is deleted as well?
There was a problem hiding this comment.
Yes, but __del__ can safely be executed twice (a file stream can be closed multiple times).
| if window == terminator: | ||
| break | ||
| else: | ||
| continue |
| next(itertools.islice(it, i, i), None) # Advance `it` by `i`. | ||
| while True: | ||
| window = tuple(map(next, its)) | ||
| if len(window) == n: |
There was a problem hiding this comment.
Could be flattened to:
while True:
window = tuple(map(next, its))
if len(window) != n or window == terminator:
breakor
while True:
window = tuple(map(next, its))
if len(window) != n:
# Ran out of data.
break
elif window == terminator:
# Found terminator.
breakThere was a problem hiding this comment.
went for the first version
| stream = iter(functools.partial(self._proc.stdout.read, 1), b"") | ||
| terminator = (b"\n", b">") | ||
| n = len(terminator) | ||
| its = itertools.tee(stream, n) |
There was a problem hiding this comment.
If I understand this correctly, can you use the example from the itertools documentation?
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)There was a problem hiding this comment.
I wrote a slightly more general version that can handle arbitrary long terminators. I would rather not have the implementation tied to the fact that we're looking exactly for a two-character terminator.
| break | ||
|
|
||
| def __call__(self, orig, dest): | ||
| if not self._proc: |
There was a problem hiding this comment.
I think you also need to handle if inkscape crashed in the background.
f44aaf1 to
d67380d
Compare
|
Related to that last comment, what happens when Inkscape fails to convert the image? Do we raise a something like FileNotFoundError, instead of something more specific? |
|
I don't know, but I don't think this PR changes anything about that... |
| assert expectedImage.ndim == actualImage.ndim | ||
| assert expectedImage.shape == actualImage.shape | ||
| absDiffImage = abs(expectedImage - actualImage) | ||
| assert expectedImage.shape[:2] == actualImage.shape[:2] |
| assert expectedImage.shape == actualImage.shape | ||
| absDiffImage = abs(expectedImage - actualImage) | ||
| assert expectedImage.shape[:2] == actualImage.shape[:2] | ||
| absDiffImage = np.abs(expectedImage[:, :, :3] - actualImage[:, :, :3]) |
There was a problem hiding this comment.
and why not check the alpha channel?
|
👍 but a bit concerned about not testing the alpha channel. |
d67380d to
c3fab50
Compare
|
I was having some issues locally where in some cases the alpha channel would be dropped by the converter (leading to a shape mismatch on comparison) but they seem not to occur anymore. Restored the full comparison. |
Before, with Now, checking for the file occurs much later in the comparison, and any inkscape output is either gobbled up or always printed (but not necessarily at the right place, depends on pytest capture mode, probably.) |
|
Good point. Looks like this will raise In either case, perhaps it would be better to reraise as an ImageComparisonError? (but it's kind of an unrelated issue). |
|
Yes, it raises the same type of error, but my point is that you've no longer got the output from Inkscape coupled with the exception. |
Edit: inkscape uses |
c3fab50 to
2a25286
Compare
2a25286 to
9276eea
Compare
|
There seem to be some timeout problems with the Inkscape process. |
|
Yup, looking into it. |
9276eea to
bd7865c
Compare
|
Temporarily closing to avoid spamming the build queue while I try to fix the timeout... |
d55b6ed to
dc3cfd8
Compare
7dcc42a to
3ab7b71
Compare
|
Looks like I got it to work. Main failure point was that old versions of inkscape (0.48.3, which is used on Travis) would block when stderr was redirected to a pipe and the pipe was not being read from (this is no longer the case on the most recent Inkscape). Instead, I just redirected stderr to a temporary file. Unless I am mistaken, the Appveyor build is 20min(!!) faster -- perhaps because repeatedly spawning inkscape was much slower on Windows? |
|
It's seems indeed much faster on windows now. Good job! I think this is going to be a real relief in our CI pipeline! |
|
PS: Please review carefully, the patch is not completely trivial (and was changed quite a bit since @tacaswell's original review, so I dismissed it). |
QuLogic
left a comment
There was a problem hiding this comment.
Holy speedups, Batman!
I think this looks good.
| raise OSError("Failed to start Inkscape") | ||
|
|
||
| # Inkscape uses glib's `g_shell_parse_argv`, which has a consistent | ||
| # behavior across platforms, so we can just use `shlex.quote`. |
There was a problem hiding this comment.
Isn't this comment related to the next section (L210)?
There was a problem hiding this comment.
Yes. Moving it down. Will also clarify the comment regarding \n (inkscape uses fgets so there's no choice).
| # right... | ||
| self._stderr.seek(0) | ||
| raise ImageComparisonFailure( | ||
| self._stderr.read().decode( |
There was a problem hiding this comment.
I'm fine as long as you've tried this out.
There was a problem hiding this comment.
In [1]: from matplotlib.testing.compare import _SVGConverter; _SVGConverter()("/tmp/quux.svg", "/tmp/foo.png")
---------------------------------------------------------------------------
ImageComparisonFailure Traceback (most recent call last)
<ipython-input-2-c896ab68b2ce> in <module>()
----> 1 from matplotlib.testing.compare import _SVGConverter; _SVGConverter()("/tmp/quux.svg", "/tmp/foo.png")
/home/antony/src/extern/matplotlib/lib/matplotlib/testing/compare.py in __call__(self, orig, dest)
225 raise ImageComparisonFailure(
226 self._stderr.read().decode(
--> 227 sys.getfilesystemencoding(), "replace"))
228
229 def __del__(self):
ImageComparisonFailure: ** Message: /dev/null is not a valid directory.
** Message: Inkscape will run with default settings, and new settings will not be saved.
** (inkscape:25211): WARNING **: Could not create extension error log file '/dev/null/extension-errors.log'
** (inkscape:25211): WARNING **: Can't open file: /tmp/quux.svg (doesn't exist)
** (inkscape:25211): WARNING **: Can't open file: /tmp/quux.svg (doesn't exist)
** (inkscape:25211): WARNING **: Specified document /tmp/quux.svg cannot be opened (does not exist or not a valid SVG file)
The first few warnings should be spurious (due to intentionally setting INKSCAPE_PROFILE_DIR to a nonexistent folder) and I could technically crop them out, but I'd rather have the full info available just in case...
3ab7b71 to
8c66203
Compare
|
The appveyor failure is conda related, the travis failure is one of the flakey ones. Going to go ahead and merge this as is. |
xref #8242.
Using inkscape in shell mode (i.e., having to start it only once) makes the entire test suite run in 624s on my laptop, compared to 787s currently (and 594s using rsvg, which has its own issues mentioned in #8242).
attn @tacaswell @NelleV
Edit: This seems to shave 1-2min per build on Travis, which is a bit less but not inconsistent with my local testing.