Repository navigation
savefig has sideeffects #6899
Description
In my investigation of image comparison fails with pytest and xdist plugin I have discovered strange behavior. By changing the extensions order of the image comparison tests you will receive different output (and of course test failures).
I made extensions shuffle like this and got these results. From the log you can see that mostly affected are legend tests on svg backend. So any baseline image from the tests with legend call is spoiled by pdf backend, except the legend + tightbox.
I tried to compare figure state before and after savefig call but run into issue #6870.
Here is the way to reproduce the problem. You can compare output of the snippet with baseline images of matplotlib.tests.test_legend.test_legend_auto1 test, if you want.
# python savefig_sideeffects.py svg svg pdf svg
# diff -u 0.svg 1.svg
# diff -u 1.svg 3.svg
# SOURCE_DATE_EPOCH=0 python savefig_sideeffects.py pdf pdf svg pdf
import numpy as np
import matplotlib
matplotlib.use('agg')
import matplotlib.pylab as plt
# matplotlib.testing.decorators.ImageComparisonTest.remove_text
def remove_text(figure):
from matplotlib import ticker
figure.suptitle("")
for ax in figure.get_axes():
ax.set_title("")
ax.xaxis.set_major_formatter(ticker.NullFormatter())
ax.xaxis.set_minor_formatter(ticker.NullFormatter())
ax.yaxis.set_major_formatter(ticker.NullFormatter())
ax.yaxis.set_minor_formatter(ticker.NullFormatter())
try:
ax.zaxis.set_major_formatter(ticker.NullFormatter())
ax.zaxis.set_minor_formatter(ticker.NullFormatter())
except AttributeError:
pass
# matplotlib.tests.test_legend.test_legend_auto1
def test_legend_auto1():
'Test automatic legend placement'
fig = plt.figure()
ax = fig.add_subplot(111)
x = np.arange(100)
ax.plot(x, 50 - x, 'o', label='y=1')
ax.plot(x, x - 50, 'o', label='y=-1')
ax.legend(loc=0)
return fig
def prepare():
import matplotlib.testing
matplotlib._init_tests()
# matplotlib.testing.decorators.ImageComparisonTest.setup_class
matplotlib.testing.setup()
matplotlib.style.use('classic')
matplotlib.testing.set_font_settings_for_testing()
fig = test_legend_auto1()
remove_text(fig)
return fig
def test(exts):
fig = prepare()
# this will stick svg path ids with the same values
matplotlib.rc('svg', hashsalt='asdf')
for p in enumerate(exts):
fig.savefig('%d.%s' % p)
if __name__ == '__main__':
import sys
test(sys.argv[1:])This is the demonstration of the issue, where savefig with pdf backend has sideeffect on the next savefig with svg backend:
>python savefig_sideeffects.py svg svg pdf svg
>diff -u 0.svg 1.svg
>diff -u 1.svg 3.svg
--- 1.svg 2016-08-04 00:29:52.429050200 +0300
+++ 3.svg 2016-08-04 00:29:52.537061000 +0300
@@ -462,18 +462,18 @@
</g>
<g id="legend_1">
<g id="patch_7">
- <path d="M 424.413 239.2965
-L 511.2 239.2965
-L 511.2 192.7035
-L 424.413 192.7035
+ <path d="M 424.469375 239.25225
+L 511.2 239.25225
+L 511.2 192.74775
+L 424.469375 192.74775
z
" style="fill:#ffffff;stroke:#000000;stroke-linejoin:miter;"/>
</g>
<g id="line2d_29"/>
<g id="line2d_30">
<g>
- <use style="fill:#0000ff;stroke:#000000;stroke-width:0.5;" x="434.493" xlink:href="#mb9df33688a" y="204.36525"/>
- <use style="fill:#0000ff;stroke:#000000;stroke-width:0.5;" x="454.653" xlink:href="#mb9df33688a" y="204.36525"/>
+ <use style="fill:#0000ff;stroke:#000000;stroke-width:0.5;" x="434.549375" xlink:href="#mb9df33688a" y="204.4095"/>
+ <use style="fill:#0000ff;stroke:#000000;stroke-width:0.5;" x="454.709375" xlink:href="#mb9df33688a" y="204.4095"/>
</g>
</g>
<g id="text_1">
@@ -520,7 +520,7 @@
z
" id="DejaVuSans-31"/>
</defs>
- <g transform="translate(470.493 209.40525)scale(0.144 -0.144)">
+ <g transform="translate(470.549375 209.4495)scale(0.144 -0.144)">
<use xlink:href="#DejaVuSans-79"/>
<use x="59.179688" xlink:href="#DejaVuSans-3d"/>
<use x="142.96875" xlink:href="#DejaVuSans-31"/>
@@ -529,8 +529,8 @@
<g id="line2d_31"/>
<g id="line2d_32">
<g>
- <use style="fill:#008000;stroke:#000000;stroke-width:0.5;" x="434.493" xlink:href="#m3d71661f95" y="225.50175"/>
- <use style="fill:#008000;stroke:#000000;stroke-width:0.5;" x="454.653" xlink:href="#m3d71661f95" y="225.50175"/>
+ <use style="fill:#008000;stroke:#000000;stroke-width:0.5;" x="434.549375" xlink:href="#m3d71661f95" y="225.546"/>
+ <use style="fill:#008000;stroke:#000000;stroke-width:0.5;" x="454.709375" xlink:href="#m3d71661f95" y="225.546"/>
</g>
</g>
<g id="text_2">
@@ -543,7 +543,7 @@
z
" id="DejaVuSans-2d"/>
</defs>
- <g transform="translate(470.493 230.54175)scale(0.144 -0.144)">
+ <g transform="translate(470.549375 230.586)scale(0.144 -0.144)">
<use xlink:href="#DejaVuSans-79"/>
<use x="59.179688" xlink:href="#DejaVuSans-3d"/>
<use x="142.96875" xlink:href="#DejaVuSans-2d"/>Same happens to pdf after svg.
By accident, while fixing issue #6870, it seems that I have fixed this too. Test above now passes but there are 505 SVG baseline images affected and need update. I will open PR for this soon.
Activity
tacaswell commentedon Aug 4, 2016
.... what?
Impressive work tracking this down.
dopplershift commentedon Aug 4, 2016
😱
jrbrodie77 commentedon Sep 22, 2016
I was just getting ready to create an issue with test cases. I'm seeing savefig side effects with 2.0.0b4
Is this fix in that version?
Kojoley commentedon Sep 22, 2016
No, the issue is still open. It is a complex problem and I have not updated the ticket with my findings because fixing it reveals other bugs. In the current state it affects only automatic legend placement and the difference is about 0.3-0.5px, so it is not a major for the most of the user.
Currently I have only a workaround but it requires regeneration of ~700 baseline images what is a no-go until I fully understand the roots of the problem.
Fix Text layout cache lookup.