Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 27fea5f

Browse files
committed
Simplify tmpdir handling in backend_pgf.
backend_pgf uses a complicated way to handle temporary directories, in particular because (on top of the normal calls to tex) it runs a separate, long-standing tex instance to compute text bounding boxes. I'm not sure this is actually needed, but at least, for the places where we just run tex locally, we can certainly just use normal TemporaryDirectories with automatic cleanup. Do so in _print_pdf_to_fh (also with some pathlibification), _print_png_to_fh, and PdfPages._run_latex (where all the temporary files can be set up in `_run_latex`, rather than in `__init__`, which saves the need for a bunch of attributes).
1 parent dd15dc0 commit 27fea5f

File tree

2 files changed

+38
-65
lines changed

2 files changed

+38
-65
lines changed

lib/matplotlib/backends/backend_pgf.py

Lines changed: 31 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import codecs
33
import datetime
44
import functools
5+
from io import BytesIO
56
import logging
67
import math
78
import os
@@ -11,6 +12,7 @@
1112
import subprocess
1213
import sys
1314
import tempfile
15+
from tempfile import TemporaryDirectory
1416
import weakref
1517

1618
from PIL import Image
@@ -866,18 +868,12 @@ def _print_pdf_to_fh(self, fh, *args, metadata=None, **kwargs):
866868
hyperref_options = ','.join(
867869
_metadata_to_str(k, v) for k, v in info_dict.items())
868870

869-
try:
870-
# create temporary directory for compiling the figure
871-
tmpdir = tempfile.mkdtemp(prefix="mpl_pgf_")
872-
fname_pgf = os.path.join(tmpdir, "figure.pgf")
873-
fname_tex = os.path.join(tmpdir, "figure.tex")
874-
fname_pdf = os.path.join(tmpdir, "figure.pdf")
871+
with TemporaryDirectory() as tmpdir:
872+
tmppath = pathlib.Path(tmpdir)
875873

876874
# print figure to pgf and compile it with latex
877-
self.print_pgf(fname_pgf, *args, **kwargs)
875+
self.print_pgf(tmppath / "figure.pgf", *args, **kwargs)
878876

879-
latex_preamble = get_preamble()
880-
latex_fontspec = get_fontspec()
881877
latexcode = """
882878
\\PassOptionsToPackage{pdfinfo={%s}}{hyperref}
883879
\\RequirePackage{hyperref}
@@ -890,22 +886,16 @@ def _print_pdf_to_fh(self, fh, *args, metadata=None, **kwargs):
890886
\\begin{document}
891887
\\centering
892888
\\input{figure.pgf}
893-
\\end{document}""" % (hyperref_options, w, h, latex_preamble, latex_fontspec)
894-
pathlib.Path(fname_tex).write_text(latexcode, encoding="utf-8")
889+
\\end{document}""" % (hyperref_options, w, h, get_preamble(), get_fontspec())
890+
(tmppath / "figure.tex").write_text(latexcode, encoding="utf-8")
895891

896892
texcommand = mpl.rcParams["pgf.texsystem"]
897893
cbook._check_and_log_subprocess(
898894
[texcommand, "-interaction=nonstopmode", "-halt-on-error",
899895
"figure.tex"], _log, cwd=tmpdir)
900896

901-
# copy file contents to target
902-
with open(fname_pdf, "rb") as fh_src:
903-
shutil.copyfileobj(fh_src, fh)
904-
finally:
905-
try:
906-
shutil.rmtree(tmpdir)
907-
except:
908-
TmpDirCleaner.add(tmpdir)
897+
with (tmppath / "figure.pdf").open("rb") as fh_src:
898+
shutil.copyfileobj(fh_src, fh) # copy file contents to target
909899

910900
def print_pdf(self, fname_or_fh, *args, **kwargs):
911901
"""Use LaTeX to compile a Pgf generated figure to PDF."""
@@ -914,23 +904,14 @@ def print_pdf(self, fname_or_fh, *args, **kwargs):
914904

915905
def _print_png_to_fh(self, fh, *args, **kwargs):
916906
converter = make_pdf_to_png_converter()
917-
918-
try:
919-
# create temporary directory for pdf creation and png conversion
920-
tmpdir = tempfile.mkdtemp(prefix="mpl_pgf_")
921-
fname_pdf = os.path.join(tmpdir, "figure.pdf")
922-
fname_png = os.path.join(tmpdir, "figure.png")
923-
# create pdf and try to convert it to png
924-
self.print_pdf(fname_pdf, *args, **kwargs)
925-
converter(fname_pdf, fname_png, dpi=self.figure.dpi)
926-
# copy file contents to target
927-
with open(fname_png, "rb") as fh_src:
928-
shutil.copyfileobj(fh_src, fh)
929-
finally:
930-
try:
931-
shutil.rmtree(tmpdir)
932-
except:
933-
TmpDirCleaner.add(tmpdir)
907+
with TemporaryDirectory() as tmpdir:
908+
tmppath = pathlib.Path(tmpdir)
909+
pdf_path = tmppath / "figure.pdf"
910+
png_path = tmppath / "figure.png"
911+
self.print_pdf(pdf_path, *args, **kwargs)
912+
converter(pdf_path, png_path, dpi=self.figure.dpi)
913+
with png_path.open("rb") as fh_src:
914+
shutil.copyfileobj(fh_src, fh) # copy file contents to target
934915

935916
def print_png(self, fname_or_fh, *args, **kwargs):
936917
"""Use LaTeX to compile a pgf figure to pdf and convert it to png."""
@@ -973,12 +954,8 @@ class PdfPages:
973954
... pdf.savefig()
974955
"""
975956
__slots__ = (
976-
'_outputfile',
957+
'_output_name',
977958
'keep_empty',
978-
'_tmpdir',
979-
'_basename',
980-
'_fname_tex',
981-
'_fname_pdf',
982959
'_n_figures',
983960
'_file',
984961
'_info_dict',
@@ -1009,7 +986,7 @@ def __init__(self, filename, *, keep_empty=True, metadata=None):
1009986
'Trapped'. Values have been predefined for 'Creator', 'Producer'
1010987
and 'CreationDate'. They can be removed by setting them to `None`.
1011988
"""
1012-
self._outputfile = filename
989+
self._output_name = filename
1013990
self._n_figures = 0
1014991
self.keep_empty = keep_empty
1015992
self._metadata = (metadata or {}).copy()
@@ -1027,13 +1004,7 @@ def __init__(self, filename, *, keep_empty=True, metadata=None):
10271004
f'set {canonical} instead of {key}.')
10281005
self._metadata[canonical] = self._metadata.pop(key)
10291006
self._info_dict = _create_pdf_info_dict('pgf', self._metadata)
1030-
1031-
# create temporary directory for compiling the figure
1032-
self._tmpdir = tempfile.mkdtemp(prefix="mpl_pgf_pdfpages_")
1033-
self._basename = 'pdf_pages'
1034-
self._fname_tex = os.path.join(self._tmpdir, self._basename + ".tex")
1035-
self._fname_pdf = os.path.join(self._tmpdir, self._basename + ".pdf")
1036-
self._file = open(self._fname_tex, 'wb')
1007+
self._file = BytesIO()
10371008

10381009
@cbook.deprecated('3.3')
10391010
@property
@@ -1085,27 +1056,22 @@ def close(self):
10851056
and moving the final pdf file to *filename*.
10861057
"""
10871058
self._file.write(rb'\end{document}\n')
1088-
self._file.close()
1089-
10901059
if self._n_figures > 0:
1091-
try:
1092-
self._run_latex()
1093-
finally:
1094-
try:
1095-
shutil.rmtree(self._tmpdir)
1096-
except:
1097-
TmpDirCleaner.add(self._tmpdir)
1060+
self._run_latex()
10981061
elif self.keep_empty:
1099-
open(self._outputfile, 'wb').close()
1062+
open(self._output_name, 'wb').close()
1063+
self._file.close()
11001064

11011065
def _run_latex(self):
11021066
texcommand = mpl.rcParams["pgf.texsystem"]
1103-
cbook._check_and_log_subprocess(
1104-
[texcommand, "-interaction=nonstopmode", "-halt-on-error",
1105-
os.path.basename(self._fname_tex)],
1106-
_log, cwd=self._tmpdir)
1107-
# copy file contents to target
1108-
shutil.copyfile(self._fname_pdf, self._outputfile)
1067+
with TemporaryDirectory() as tmpdir:
1068+
tex_source = pathlib.Path(tmpdir, "pdf_pages.tex")
1069+
tex_source.write_bytes(self._file.getvalue())
1070+
cbook._check_and_log_subprocess(
1071+
[texcommand, "-interaction=nonstopmode", "-halt-on-error",
1072+
tex_source],
1073+
_log, cwd=tmpdir)
1074+
shutil.move(tex_source.with_suffix(".pdf"), self._output_name)
11091075

11101076
def savefig(self, figure=None, **kwargs):
11111077
"""

lib/matplotlib/tests/test_backend_pgf.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,10 @@ def test_bbox_inches_tight(tmpdir):
321321
ax.imshow([[0, 1], [2, 3]])
322322
fig.savefig(os.path.join(tmpdir, "test.pdf"), backend="pgf",
323323
bbox_inches="tight")
324+
325+
326+
@needs_xelatex
327+
def test_png():
328+
# Just a smoketest.
329+
fig, ax = plt.subplots()
330+
fig.savefig(BytesIO(), format="png", backend="pgf")

0 commit comments

Comments
 (0)