Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
Reproducible PS/PDF output (master)#6597
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
c6a4660
4e477dc
946ae45
22f71a2
ad660d7
37c28b4
2e1c773
541f97e
2a6ebc8
6ee8967
a3185e6
8f095f6
c007f49
ecbdd55
65ec88e
5b405cc
d10a21e
da55bb6
c56dae7
995173d
eef6b12
fb529da
ebff832
f6301c2
1786555
af4213e
bf7387e
2cdc577
76bec02
bbab0c5
1a5ada6
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Reproducible PS and PDF output | ||
------------------------------ | ||
The ``SOURCE_DATE_EPOCH`` environment variable can now be used to set | ||
the timestamp value in the PS and PDF outputs. See | ||
https://reproducible-builds.org/specs/source-date-epoch/ | ||
The reproducibility of the output from the PS and PDF backends has so | ||
far been tested using various plot elements but only default values of | ||
options such as ``{ps,pdf}.fonttype`` that can affect the output at a | ||
low level, and not with the mathtext or usetex features. When | ||
matplotlib calls external tools (such as PS distillers or LaTeX) their | ||
versions need to be kept constant for reproducibility, and they may | ||
add sources of nondeterminism outside the control of matplotlib. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
""" | ||
Provides utilities to test output reproducibility. | ||
""" | ||
from __future__ import (absolute_import, division, print_function, | ||
unicode_literals) | ||
import six | ||
import io | ||
import os | ||
import re | ||
import sys | ||
from subprocess import check_output | ||
import matplotlib | ||
from matplotlib import pyplot as plt | ||
from nose.plugins.skip import SkipTest | ||
def _determinism_save(objects='mhi', format="pdf", usetex=False): | ||
# save current value of SOURCE_DATE_EPOCH and set it | ||
# to a constant value, so that time difference is not | ||
# taken into account | ||
sde = os.environ.pop('SOURCE_DATE_EPOCH', None) | ||
os.environ['SOURCE_DATE_EPOCH'] = "946684800" | ||
matplotlib.rcParams['text.usetex'] = usetex | ||
fig = plt.figure() | ||
if 'm' in objects: | ||
# use different markers... | ||
ax1 = fig.add_subplot(1, 6, 1) | ||
x = range(10) | ||
ax1.plot(x, [1] * 10, marker=u'D') | ||
ax1.plot(x, [2] * 10, marker=u'x') | ||
ax1.plot(x, [3] * 10, marker=u'^') | ||
ax1.plot(x, [4] * 10, marker=u'H') | ||
ax1.plot(x, [5] * 10, marker=u'v') | ||
if 'h' in objects: | ||
# also use different hatch patterns | ||
ax2 = fig.add_subplot(1, 6, 2) | ||
bars = ax2.bar(range(1, 5), range(1, 5)) + \ | ||
ax2.bar(range(1, 5), [6] * 4, bottom=range(1, 5)) | ||
ax2.set_xticks([1.5, 2.5, 3.5, 4.5]) | ||
patterns = ('-', '+', 'x', '\\', '*', 'o', 'O', '.') | ||
for bar, pattern in zip(bars, patterns): | ||
bar.set_hatch(pattern) | ||
if 'i' in objects: | ||
# also use different images | ||
A = [[1, 2, 3], [2, 3, 1], [3, 1, 2]] | ||
fig.add_subplot(1, 6, 3).imshow(A, interpolation='nearest') | ||
A = [[1, 3, 2], [1, 2, 3], [3, 1, 2]] | ||
fig.add_subplot(1, 6, 4).imshow(A, interpolation='bilinear') | ||
A = [[2, 3, 1], [1, 2, 3], [2, 1, 3]] | ||
fig.add_subplot(1, 6, 5).imshow(A, interpolation='bicubic') | ||
x = range(5) | ||
fig.add_subplot(1, 6, 6).plot(x, x) | ||
if six.PY2 and format == 'ps': | ||
stdout = io.StringIO() | ||
else: | ||
stdout = getattr(sys.stdout, 'buffer', sys.stdout) | ||
fig.savefig(stdout, format=format) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Why not just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. It does not work with the PS backend, which tries to wrap the input in | ||
if six.PY2 and format == 'ps': | ||
sys.stdout.write(stdout.getvalue()) | ||
# Restores SOURCE_DATE_EPOCH | ||
if sde is None: | ||
os.environ.pop('SOURCE_DATE_EPOCH', None) | ||
else: | ||
os.environ['SOURCE_DATE_EPOCH'] = sde | ||
def _determinism_check(objects='mhi', format="pdf", usetex=False): | ||
""" | ||
Output three times the same graphs and checks that the outputs are exactly | ||
the same. | ||
Parameters | ||
---------- | ||
objects : str | ||
contains characters corresponding to objects to be included in the test | ||
document: 'm' for markers, 'h' for hatch patterns, 'i' for images. The | ||
default value is "mhi", so that the test includes all these objects. | ||
format : str | ||
format string. The default value is "pdf". | ||
""" | ||
from nose.tools import assert_equal | ||
plots = [] | ||
for i in range(3): | ||
result = check_output([sys.executable, '-R', '-c', | ||
'import matplotlib; ' | ||
'matplotlib.use(%r); ' | ||
'from matplotlib.testing.determinism ' | ||
'import _determinism_save;' | ||
'_determinism_save(%r,%r,%r)' | ||
% (format, objects, format, usetex)]) | ||
plots.append(result) | ||
for p in plots[1:]: | ||
if usetex: | ||
if p != plots[0]: | ||
raise SkipTest("failed, maybe due to ghostscript timestamps") | ||
else: | ||
assert_equal(p, plots[0]) | ||
def _determinism_source_date_epoch(format, string, keyword=b"CreationDate"): | ||
""" | ||
Test SOURCE_DATE_EPOCH support. Output a document with the envionment | ||
variable SOURCE_DATE_EPOCH set to 2000-01-01 00:00 UTC and check that the | ||
document contains the timestamp that corresponds to this date (given as an | ||
argument). | ||
Parameters | ||
---------- | ||
format : str | ||
format string, such as "pdf". | ||
string : str | ||
timestamp string for 2000-01-01 00:00 UTC. | ||
keyword : bytes | ||
a string to look at when searching for the timestamp in the document | ||
(used in case the test fails). | ||
""" | ||
buff = check_output([sys.executable, '-R', '-c', | ||
'import matplotlib; ' | ||
'matplotlib.use(%r); ' | ||
'from matplotlib.testing.determinism ' | ||
'import _determinism_save;' | ||
'_determinism_save(%r,%r)' | ||
% (format, "", format)]) | ||
find_keyword = re.compile(b".*" + keyword + b".*") | ||
key = find_keyword.search(buff) | ||
if key: | ||
print(key.group()) | ||
else: | ||
print("Timestamp keyword (%s) not found!" % keyword) | ||
assert string in buff |
Uh oh!
There was an error while loading.Please reload this page.