Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a3d2992

Browse filesBrowse files
committed
Merge pull request #2512 from mdboom/ps_StringIO
Fix saving to in-memory file-like objects in Postscript backend
2 parents 65ce30e + 63f13c8 commit a3d2992
Copy full SHA for a3d2992

File tree

Expand file treeCollapse file tree

6 files changed

+155
-37
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+155
-37
lines changed

‎lib/matplotlib/__init__.py

Copy file name to clipboardExpand all lines: lib/matplotlib/__init__.py
+9-3Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,16 @@
115115
def compare_versions(a, b):
116116
"return True if a is greater than or equal to b"
117117
if a:
118+
if six.PY3:
119+
if isinstance(a, bytes):
120+
a = a.decode('ascii')
121+
if isinstance(b, bytes):
122+
b = b.decode('ascii')
118123
a = distutils.version.LooseVersion(a)
119124
b = distutils.version.LooseVersion(b)
120-
if a>=b: return True
121-
else: return False
122-
else: return False
125+
return a >= b
126+
else:
127+
return False
123128

124129
try:
125130
import pyparsing
@@ -1222,6 +1227,7 @@ def tk_window_focus():
12221227
'matplotlib.tests.test_axes',
12231228
'matplotlib.tests.test_backend_pdf',
12241229
'matplotlib.tests.test_backend_pgf',
1230+
'matplotlib.tests.test_backend_ps',
12251231
'matplotlib.tests.test_backend_qt4',
12261232
'matplotlib.tests.test_backend_svg',
12271233
'matplotlib.tests.test_basic',

‎lib/matplotlib/backends/backend_ps.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_ps.py
+40-31Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
unicode_literals)
77

88
import six
9+
from six.moves import StringIO
910

1011
import glob, math, os, shutil, sys, time
1112
def _fn_name(): return sys._getframe(1).f_code.co_name
@@ -24,7 +25,7 @@ def _fn_name(): return sys._getframe(1).f_code.co_name
2425
FigureManagerBase, FigureCanvasBase
2526

2627
from matplotlib.cbook import is_string_like, get_realpath_and_stat, \
27-
is_writable_file_like, maxdict
28+
is_writable_file_like, maxdict, file_requires_unicode
2829
from matplotlib.mlab import quad2cubic
2930
from matplotlib.figure import Figure
3031

@@ -1085,7 +1086,7 @@ def write(self, *kl, **kwargs):
10851086

10861087
self._pswriter = NullWriter()
10871088
else:
1088-
self._pswriter = six.moves.cStringIO()
1089+
self._pswriter = io.StringIO()
10891090

10901091

10911092
# mixed mode rendering
@@ -1105,8 +1106,6 @@ def write(self, *kl, **kwargs):
11051106
self.figure.set_edgecolor(origedgecolor)
11061107

11071108
def print_figure_impl():
1108-
fh = io.TextIOWrapper(raw_fh, encoding="ascii")
1109-
11101109
# write the PostScript headers
11111110
if isEPSF: print("%!PS-Adobe-3.0 EPSF-3.0", file=fh)
11121111
else: print("%!PS-Adobe-3.0", file=fh)
@@ -1157,7 +1156,7 @@ def print_figure_impl():
11571156
fh.flush()
11581157
convert_ttf_to_ps(
11591158
font_filename.encode(sys.getfilesystemencoding()),
1160-
raw_fh, fonttype, glyph_ids)
1159+
fh, fonttype, glyph_ids)
11611160
print("end", file=fh)
11621161
print("%%EndProlog", file=fh)
11631162

@@ -1181,22 +1180,31 @@ def print_figure_impl():
11811180
if not isEPSF: print("%%EOF", file=fh)
11821181
fh.flush()
11831182

1184-
if six.PY3:
1185-
fh.detach()
1186-
11871183
if rcParams['ps.usedistiller']:
11881184
# We are going to use an external program to process the output.
11891185
# Write to a temporary file.
11901186
fd, tmpfile = mkstemp()
1191-
with io.open(fd, 'wb') as raw_fh:
1187+
with io.open(fd, 'w', encoding='ascii') as fh:
11921188
print_figure_impl()
11931189
else:
11941190
# Write directly to outfile.
11951191
if passed_in_file_object:
1196-
raw_fh = outfile
1192+
requires_unicode = file_requires_unicode(outfile)
1193+
1194+
if (not requires_unicode and
1195+
(six.PY3 or not isinstance(outfile, StringIO))):
1196+
fh = io.TextIOWrapper(outfile, encoding="ascii")
1197+
# Prevent the io.TextIOWrapper from closing the
1198+
# underlying file
1199+
def do_nothing():
1200+
pass
1201+
fh.close = do_nothing
1202+
else:
1203+
fh = outfile
1204+
11971205
print_figure_impl()
11981206
else:
1199-
with io.open(outfile, 'wb') as raw_fh:
1207+
with io.open(outfile, 'w', encoding='ascii') as fh:
12001208
print_figure_impl()
12011209

12021210
if rcParams['ps.usedistiller']:
@@ -1206,8 +1214,12 @@ def print_figure_impl():
12061214
xpdf_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox)
12071215

12081216
if passed_in_file_object:
1209-
with io.open(tmpfile, 'rb') as fh:
1210-
outfile.write(fh.read())
1217+
if file_requires_unicode(outfile):
1218+
with io.open(tmpfile, 'rb') as fh:
1219+
outfile.write(fh.read().decode('ascii'))
1220+
else:
1221+
with io.open(tmpfile, 'rb') as fh:
1222+
outfile.write(fh.read())
12111223
else:
12121224
with io.open(outfile, 'w') as fh:
12131225
pass
@@ -1224,7 +1236,12 @@ def _print_figure_tex(self, outfile, format, dpi, facecolor, edgecolor,
12241236
package. These files are processed to yield the final ps or eps file.
12251237
"""
12261238
isEPSF = format == 'eps'
1227-
title = outfile
1239+
if is_string_like(outfile):
1240+
title = outfile
1241+
elif is_writable_file_like(outfile):
1242+
title = None
1243+
else:
1244+
raise ValueError("outfile must be a path or a file-like object")
12281245

12291246
self.figure.dpi = 72 # ignore the dpi kwarg
12301247
width, height = self.figure.get_size_inches()
@@ -1252,7 +1269,7 @@ def write(self, *kl, **kwargs):
12521269

12531270
self._pswriter = NullWriter()
12541271
else:
1255-
self._pswriter = six.moves.cStringIO()
1272+
self._pswriter = io.StringIO()
12561273

12571274

12581275
# mixed mode rendering
@@ -1273,11 +1290,7 @@ def write(self, *kl, **kwargs):
12731290

12741291
# write to a temp file, we'll move it to outfile when done
12751292
fd, tmpfile = mkstemp()
1276-
if six.PY3:
1277-
fh = io.open(fd, 'w', encoding='ascii')
1278-
else:
1279-
fh = io.open(fd, 'wb')
1280-
with fh:
1293+
with io.open(fd, 'w', encoding='ascii') as fh:
12811294
# write the Encapsulated PostScript headers
12821295
print("%!PS-Adobe-3.0 EPSF-3.0", file=fh)
12831296
if title: print("%%Title: "+title, file=fh)
@@ -1357,17 +1370,13 @@ def write(self, *kl, **kwargs):
13571370
else: gs_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox,
13581371
rotated=psfrag_rotated)
13591372

1360-
is_file = False
1361-
if six.PY3:
1362-
if isinstance(outfile, io.IOBase):
1363-
is_file = True
1364-
else:
1365-
if isinstance(outfile, file):
1366-
is_file = True
1367-
1368-
if is_file:
1369-
with io.open(tmpfile, 'rb') as fh:
1370-
outfile.write(fh.read())
1373+
if is_writable_file_like(outfile):
1374+
if file_requires_unicode(outfile):
1375+
with io.open(tmpfile, 'rb') as fh:
1376+
outfile.write(fh.read().decode('ascii'))
1377+
else:
1378+
with io.open(tmpfile, 'rb') as fh:
1379+
outfile.write(fh.read())
13711380
else:
13721381
with io.open(outfile, 'wb') as fh:
13731382
pass

‎lib/matplotlib/cbook.py

Copy file name to clipboardExpand all lines: lib/matplotlib/cbook.py
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,19 @@ def is_writable_file_like(obj):
693693
return hasattr(obj, 'write') and six.callable(obj.write)
694694

695695

696+
def file_requires_unicode(x):
697+
"""
698+
Returns `True` if the given writable file-like object requires Unicode
699+
to be written to it.
700+
"""
701+
try:
702+
x.write(b'')
703+
except TypeError:
704+
return True
705+
else:
706+
return False
707+
708+
696709
def is_scalar(obj):
697710
'return true if *obj* is not string like and is not iterable'
698711
return not is_string_like(obj) and not iterable(obj)
+83Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from __future__ import (absolute_import, division, print_function,
4+
unicode_literals)
5+
6+
import io
7+
import re
8+
9+
import six
10+
from nose.plugins.skip import SkipTest
11+
12+
import matplotlib
13+
import matplotlib.pyplot as plt
14+
from matplotlib.testing.decorators import cleanup
15+
16+
17+
def _test_savefig_to_stringio(format='ps'):
18+
buffers = [
19+
six.moves.StringIO(),
20+
io.StringIO(),
21+
io.BytesIO()]
22+
23+
plt.figure()
24+
plt.plot([0, 1], [0, 1])
25+
plt.title("Déjà vu")
26+
for buffer in buffers:
27+
plt.savefig(buffer, format=format)
28+
29+
values = [x.getvalue() for x in buffers]
30+
31+
if six.PY3:
32+
values = [
33+
values[0].encode('ascii'),
34+
values[1].encode('ascii'),
35+
values[2]]
36+
37+
# Remove comments from the output. This includes things that
38+
# could change from run to run, such as the time.
39+
values = [re.sub(b'%%.*?\n', b'', x) for x in values]
40+
41+
assert values[0] == values[1]
42+
assert values[1] == values[2]
43+
44+
45+
@cleanup
46+
def test_savefig_to_stringio():
47+
_test_savefig_to_stringio()
48+
49+
50+
@cleanup
51+
def test_savefig_to_stringio_with_distiller():
52+
matplotlib.rcParams['ps.usedistiller'] = 'ghostscript'
53+
_test_savefig_to_stringio()
54+
55+
56+
@cleanup
57+
def test_savefig_to_stringio_with_usetex():
58+
if not matplotlib.checkdep_tex():
59+
raise SkipTest("This test requires a TeX installation")
60+
61+
matplotlib.rcParams['text.latex.unicode'] = True
62+
matplotlib.rcParams['text.usetex'] = True
63+
_test_savefig_to_stringio()
64+
65+
66+
@cleanup
67+
def test_savefig_to_stringio_eps():
68+
_test_savefig_to_stringio(format='eps')
69+
70+
71+
@cleanup
72+
def test_savefig_to_stringio_with_usetex_eps():
73+
if not matplotlib.checkdep_tex():
74+
raise SkipTest("This test requires a TeX installation")
75+
76+
matplotlib.rcParams['text.latex.unicode'] = True
77+
matplotlib.rcParams['text.usetex'] = True
78+
_test_savefig_to_stringio(format='eps')
79+
80+
81+
if __name__ == '__main__':
82+
import nose
83+
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)

‎lib/matplotlib/texmanager.py

Copy file name to clipboardExpand all lines: lib/matplotlib/texmanager.py
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,8 @@ def make_dvi(self, tex, fontsize):
413413
raise RuntimeError(
414414
('LaTeX was not able to process the following '
415415
'string:\n%s\nHere is the full report generated by '
416-
'LaTeX: \n\n' % repr(tex)) + report)
416+
'LaTeX: \n\n' % repr(tex.encode('unicode_escape')) +
417+
report))
417418
else:
418419
mpl.verbose.report(report, 'debug')
419420
for fname in glob.glob(basefile + '*'):

‎src/_ttconv.cpp

Copy file name to clipboardExpand all lines: src/_ttconv.cpp
+8-2Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,15 @@ class PythonFileWriter : public TTStreamWriter
4949
if (_write_method)
5050
{
5151
#if PY3K
52-
result = PyObject_CallFunction(_write_method, (char *)"y", a);
53-
#else
5452
result = PyObject_CallFunction(_write_method, (char *)"s", a);
53+
#else
54+
PyObject* decoded = NULL;
55+
decoded = PyUnicode_FromString(a);
56+
if (decoded == NULL) {
57+
throw PythonExceptionOccurred();
58+
}
59+
result = PyObject_CallFunction(_write_method, "O", decoded);
60+
Py_DECREF(decoded);
5561
#endif
5662
if (! result)
5763
{

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.