Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Matplotlib savefig() closes BytesIO object when saving in postscript format #1862

Merged
merged 1 commit into from

2 participants

@astrofrog

In Python 3, I am seeing the following bug:

In [1]: from io import BytesIO

In [2]: import matplotlib.pyplot as plt

In [3]: b = BytesIO()

In [4]: fig = plt.figure()

In [5]: ax = fig.add_subplot(1,1,1)

In [6]: ax.plot([1,2,3])
Out[6]: [<matplotlib.lines.Line2D at 0x104a90990>]

In [7]: fig.savefig(b, format='eps')

In [8]: b.seek(0)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-8-22abda41fe70> in <module>()
----> 1 b.seek(0)

ValueError: I/O operation on closed file.

As far as I can tell, this does not occur with StringIO in Python 2.x.

@mdboom - is this related to #1779?

@mdboom
Owner

Nope -- this bug is only on master, and perhaps related to #1826.

@mdboom mdboom Fixes #1862: The PS backend would inadvertently close file handles pa…
…ssed to it on Python 3. The TextIOWrapper that handles the encoding from unicode to ascii needs to be detached before its destructor is called, otherwise it will call close on the underlying file object. Also cleans up how with statements are handled to avoid the sometimes problematic null contextmanager.
820a326
@astrofrog

@mdboom - this works for me - thanks!

@mdboom mdboom merged commit ca476df into matplotlib:master
@HubertHolin HubertHolin referenced this pull request from a commit in HubertHolin/matplotlib
@mdboom mdboom Fixes #1862: The PS backend would inadvertently close file handles pa…
…ssed to it on Python 3. The TextIOWrapper that handles the encoding from unicode to ascii needs to be detached before its destructor is called, otherwise it will call close on the underlying file object. Also cleans up how with statements are handled to avoid the sometimes problematic null contextmanager.
1a81502
@pelson pelson referenced this pull request from a commit in pelson/matplotlib
@mdboom mdboom Fixes #1862: The PS backend would inadvertently close file handles pa…
…ssed to it on Python 3. The TextIOWrapper that handles the encoding from unicode to ascii needs to be detached before its destructor is called, otherwise it will call close on the underlying file object. Also cleans up how with statements are handled to avoid the sometimes problematic null contextmanager.
8a992cc
@mdboom mdboom deleted the mdboom:ps-file-closing branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 27, 2013
  1. @mdboom

    Fixes #1862: The PS backend would inadvertently close file handles pa…

    mdboom authored
    …ssed to it on Python 3. The TextIOWrapper that handles the encoding from unicode to ascii needs to be detached before its destructor is called, otherwise it will call close on the underlying file object. Also cleans up how with statements are handled to avoid the sometimes problematic null contextmanager.
This page is out of date. Refresh to see the latest.
Showing with 19 additions and 21 deletions.
  1. +19 −21 lib/matplotlib/backends/backend_ps.py
View
40 lib/matplotlib/backends/backend_ps.py
@@ -5,7 +5,6 @@
# PY3KTODO: Get rid of "print >>fh" syntax
from __future__ import division, print_function
-import contextlib
import glob, math, os, shutil, sys, time
def _fn_name(): return sys._getframe(1).f_code.co_name
import io
@@ -974,11 +973,6 @@ def print_ps(self, outfile, *args, **kwargs):
def print_eps(self, outfile, *args, **kwargs):
return self._print_ps(outfile, 'eps', *args, **kwargs)
-
-
-
-
-
def _print_ps(self, outfile, format, *args, **kwargs):
papertype = kwargs.pop("papertype", rcParams['ps.papersize'])
papertype = papertype.lower()
@@ -1106,21 +1100,7 @@ def write(self, *kl, **kwargs):
self.figure.set_facecolor(origfacecolor)
self.figure.set_edgecolor(origedgecolor)
- if rcParams['ps.usedistiller']:
- # We are going to use an external program to process the output.
- # Write to a temporary file.
- fd, tmpfile = mkstemp()
- context_manager = io.open(fd, 'wb')
- else:
- # Write directly to outfile.
- if passed_in_file_object:
- @contextlib.contextmanager
- def null_context(value):
- yield value
- context_manager = null_context(outfile)
- else:
- context_manager = open(outfile, 'wb')
- with context_manager as raw_fh:
+ def print_figure_impl():
if sys.version_info[0] >= 3:
fh = io.TextIOWrapper(raw_fh, encoding="ascii")
else:
@@ -1195,6 +1175,24 @@ def null_context(value):
if not isEPSF: print("%%EOF", file=fh)
fh.flush()
+ if sys.version_info[0] >= 3:
+ fh.detach()
+
+ if rcParams['ps.usedistiller']:
+ # We are going to use an external program to process the output.
+ # Write to a temporary file.
+ fd, tmpfile = mkstemp()
+ with io.open(fd, 'wb') as raw_fh:
+ print_figure_impl()
+ else:
+ # Write directly to outfile.
+ if passed_in_file_object:
+ raw_fh = outfile
+ print_figure_impl()
+ else:
+ with open(outfile, 'wb') as raw_fh:
+ print_figure_impl()
+
if rcParams['ps.usedistiller']:
if rcParams['ps.usedistiller'] == 'ghostscript':
gs_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox)
Something went wrong with that request. Please try again.