8
8
import tempfile
9
9
import codecs
10
10
import subprocess
11
+ import atexit
12
+ import weakref
11
13
12
14
import matplotlib as mpl
13
15
from matplotlib .backend_bases import RendererBase , GraphicsContextBase ,\
@@ -221,13 +223,31 @@ def get_latex_manager():
221
223
LatexManagerFactory .previous_instance = new_inst
222
224
return new_inst
223
225
226
+ class WeakSet :
227
+ # TODO: Poor man's weakref.WeakSet.
228
+ # Remove this once python 2.6 support is dropped from matplotlib.
229
+
230
+ def __init__ (self ):
231
+ self .weak_key_dict = weakref .WeakKeyDictionary ()
232
+
233
+ def add (self , item ):
234
+ self .weak_key_dict [item ] = None
235
+
236
+ def discard (self , item ):
237
+ if item in self .weak_key_dict :
238
+ del self .weak_key_dict [item ]
239
+
240
+ def __iter__ (self ):
241
+ return self .weak_key_dict .iterkeys ()
242
+
224
243
225
244
class LatexManager :
226
245
"""
227
246
The LatexManager opens an instance of the LaTeX application for
228
247
determining the metrics of text elements. The LaTeX environment can be
229
248
modified by setting fonts and/or a custem preamble in the rc parameters.
230
249
"""
250
+ _unclean_instances = WeakSet ()
231
251
232
252
@staticmethod
233
253
def _build_latex_header ():
@@ -244,6 +264,12 @@ def _build_latex_header():
244
264
r"\typeout{pgf_backend_query_start}" ]
245
265
return "\n " .join (latex_header )
246
266
267
+ @staticmethod
268
+ def _cleanup_remaining_instances ():
269
+ unclean_instances = list (LatexManager ._unclean_instances )
270
+ for latex_manager in unclean_instances :
271
+ latex_manager ._cleanup ()
272
+
247
273
def _stdin_writeln (self , s ):
248
274
self .latex_stdin_utf8 .write (s )
249
275
self .latex_stdin_utf8 .write ("\n " )
@@ -265,23 +291,26 @@ def _expect_prompt(self):
265
291
return self ._expect ("\n *" )
266
292
267
293
def __init__ (self ):
294
+ # create a tmp directory for running latex, remember to cleanup
295
+ self .tmpdir = tempfile .mkdtemp (prefix = "mpl_pgf_lm_" )
296
+ LatexManager ._unclean_instances .add (self )
297
+
298
+ # test the LaTeX setup to ensure a clean startup of the subprocess
268
299
self .texcommand = get_texcommand ()
269
300
self .latex_header = LatexManager ._build_latex_header ()
270
301
latex_end = "\n \\ makeatletter\n \\ @@end\n "
271
-
272
- # test the LaTeX setup to ensure a clean startup of the subprocess
273
302
latex = subprocess .Popen ([self .texcommand , "-halt-on-error" ],
274
- stdin = subprocess .PIPE ,
275
- stdout = subprocess . PIPE )
303
+ stdin = subprocess .PIPE , stdout = subprocess . PIPE ,
304
+ cwd = self . tmpdir )
276
305
test_input = self .latex_header + latex_end
277
306
stdout , stderr = latex .communicate (test_input .encode ("utf-8" ))
278
307
if latex .returncode != 0 :
279
308
raise LatexError ("LaTeX returned an error, probably missing font or error in preamble:\n %s" % stdout )
280
309
281
310
# open LaTeX process for real work
282
311
latex = subprocess .Popen ([self .texcommand , "-halt-on-error" ],
283
- stdin = subprocess .PIPE ,
284
- stdout = subprocess . PIPE )
312
+ stdin = subprocess .PIPE , stdout = subprocess . PIPE ,
313
+ cwd = self . tmpdir )
285
314
self .latex = latex
286
315
self .latex_stdin_utf8 = codecs .getwriter ("utf8" )(self .latex .stdin )
287
316
# write header with 'pgf_backend_query_start' token
@@ -293,19 +322,25 @@ def __init__(self):
293
322
# cache for strings already processed
294
323
self .str_cache = {}
295
324
296
- def __del__ (self ):
297
- if rcParams . get ( "pgf.debug" , False ):
298
- print "deleting LatexManager"
325
+ def _cleanup (self ):
326
+ if not os . path . isdir ( self . tmpdir ):
327
+ return
299
328
try :
300
329
self .latex_stdin_utf8 .close ()
301
330
self .latex .communicate ()
331
+ self .latex .wait ()
302
332
except :
303
333
pass
304
334
try :
305
- os . remove ( "texput.log" )
306
- os . remove ( "texput.aux" )
335
+ shutil . rmtree ( self . tmpdir )
336
+ LatexManager . _unclean_instances . discard ( self )
307
337
except :
308
- pass
338
+ sys .stderr .write ("error deleting tmp directory %s\n " % self .tmpdir )
339
+
340
+ def __del__ (self ):
341
+ if rcParams .get ("pgf.debug" , False ):
342
+ print "deleting LatexManager"
343
+ self ._cleanup ()
309
344
310
345
def get_width_height_descent (self , text , prop ):
311
346
"""
@@ -643,6 +678,22 @@ def new_figure_manager_given_figure(num, figure):
643
678
return manager
644
679
645
680
681
+ class TmpDirCleaner :
682
+ remaining_tmpdirs = set ()
683
+
684
+ @staticmethod
685
+ def add (tmpdir ):
686
+ TmpDirCleaner .remaining_tmpdirs .add (tmpdir )
687
+
688
+ @staticmethod
689
+ def cleanup_remaining_tmpdirs ():
690
+ for tmpdir in TmpDirCleaner .remaining_tmpdirs :
691
+ try :
692
+ shutil .rmtree (tmpdir )
693
+ except :
694
+ sys .stderr .write ("error deleting tmp directory %s\n " % tmpdir )
695
+
696
+
646
697
class FigureCanvasPgf (FigureCanvasBase ):
647
698
filetypes = {"pgf" : "LaTeX PGF picture" ,
648
699
"pdf" : "LaTeX compiled PGF picture" ,
@@ -723,13 +774,14 @@ def _print_pdf_to_fh(self, fh):
723
774
w , h = self .figure .get_figwidth (), self .figure .get_figheight ()
724
775
725
776
try :
726
- # create and switch to temporary directory
727
- tmpdir = tempfile .mkdtemp ()
728
- cwd = os .getcwd ()
729
- os .chdir (tmpdir )
777
+ # create temporary directory for compiling the figure
778
+ tmpdir = tempfile .mkdtemp (prefix = "mpl_pgf_" )
779
+ fname_pgf = os .path .join (tmpdir , "figure.pgf" )
780
+ fname_tex = os .path .join (tmpdir , "figure.tex" )
781
+ fname_pdf = os .path .join (tmpdir , "figure.pdf" )
730
782
731
783
# print figure to pgf and compile it with latex
732
- self .print_pgf ("figure.pgf" )
784
+ self .print_pgf (fname_pgf )
733
785
734
786
latex_preamble = get_preamble ()
735
787
latex_fontspec = get_fontspec ()
@@ -744,26 +796,25 @@ def _print_pdf_to_fh(self, fh):
744
796
\centering
745
797
\input{figure.pgf}
746
798
\end{document}""" % (w , h , latex_preamble , latex_fontspec )
747
- with codecs .open ("figure.tex" , "w" , "utf-8" ) as fh_tex :
799
+ with codecs .open (fname_tex , "w" , "utf-8" ) as fh_tex :
748
800
fh_tex .write (latexcode )
749
801
750
802
texcommand = get_texcommand ()
751
803
cmdargs = [texcommand , "-interaction=nonstopmode" ,
752
804
"-halt-on-error" , "figure.tex" ]
753
805
try :
754
- check_output (cmdargs , stderr = subprocess .STDOUT )
806
+ check_output (cmdargs , stderr = subprocess .STDOUT , cwd = tmpdir )
755
807
except subprocess .CalledProcessError as e :
756
808
raise RuntimeError ("%s was not able to process your file.\n \n Full log:\n %s" % (texcommand , e .output ))
757
809
758
810
# copy file contents to target
759
- with open ("figure.pdf" , "rb" ) as fh_src :
811
+ with open (fname_pdf , "rb" ) as fh_src :
760
812
shutil .copyfileobj (fh_src , fh )
761
813
finally :
762
- os .chdir (cwd )
763
814
try :
764
815
shutil .rmtree (tmpdir )
765
816
except :
766
- sys . stderr . write ( "could not delete tmp directory %s \n " % tmpdir )
817
+ TmpDirCleaner . add ( tmpdir )
767
818
768
819
def print_pdf (self , fname_or_fh , * args , ** kwargs ):
769
820
"""
@@ -782,22 +833,21 @@ def _print_png_to_fh(self, fh):
782
833
converter = make_pdf_to_png_converter ()
783
834
784
835
try :
785
- # create and switch to temporary directory
786
- tmpdir = tempfile .mkdtemp ()
787
- cwd = os .getcwd ( )
788
- os .chdir (tmpdir )
836
+ # create temporary directory for pdf creation and png conversion
837
+ tmpdir = tempfile .mkdtemp (prefix = "mpl_pgf_" )
838
+ fname_pdf = os .path . join ( tmpdir , "figure.pdf" )
839
+ fname_png = os .path . join (tmpdir , "figure.png" )
789
840
# create pdf and try to convert it to png
790
- self .print_pdf ("figure.pdf" )
791
- converter ("figure.pdf" , "figure.png" , dpi = self .figure .dpi )
841
+ self .print_pdf (fname_pdf )
842
+ converter (fname_pdf , fname_png , dpi = self .figure .dpi )
792
843
# copy file contents to target
793
- with open ("figure.png" , "rb" ) as fh_src :
844
+ with open (fname_png , "rb" ) as fh_src :
794
845
shutil .copyfileobj (fh_src , fh )
795
846
finally :
796
- os .chdir (cwd )
797
847
try :
798
848
shutil .rmtree (tmpdir )
799
849
except :
800
- sys . stderr . write ( "could not delete tmp directory %s \n " % tmpdir )
850
+ TmpDirCleaner . add ( tmpdir )
801
851
802
852
def print_png (self , fname_or_fh , * args , ** kwargs ):
803
853
"""
@@ -869,3 +919,9 @@ def __init__(self, *args):
869
919
########################################################################
870
920
871
921
FigureManager = FigureManagerPgf
922
+
923
+ def _cleanup_all ():
924
+ LatexManager ._cleanup_remaining_instances ()
925
+ TmpDirCleaner .cleanup_remaining_tmpdirs ()
926
+
927
+ atexit .register (_cleanup_all )
0 commit comments