From cb7c722fb8f7e9555031b2d2074ea3e6528192c0 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Mon, 14 Dec 2020 03:02:20 +0100 Subject: [PATCH 1/4] Backport PR #19108: Fix failing animation test with pytest 6.2. --- lib/matplotlib/tests/test_image.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index 6ffb2e2d0554..71c7f8e16d01 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -718,7 +718,8 @@ def test_load_from_url(): + ('///' if sys.platform == 'win32' else '') + path.resolve().as_posix()) plt.imread(url) - plt.imread(urllib.request.urlopen(url)) + with urllib.request.urlopen(url) as file: + plt.imread(file) @image_comparison(['log_scale_image'], remove_text=True) From 400ea30016ba24bc98d36f228e4461bcc2449a5c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 16 Dec 2020 16:26:11 -0500 Subject: [PATCH 2/4] FIX: Explicitly clean up temporary files in HTMLWriter - HTMLWriter inherits from FileMovieWriter. - FileMovieWriter create a non-context manager TemporayDirectory on init. - The TemporayDirectory is cleaned up in the cleaup method of FileMovieWriter. - However this method (and its call chain through finish) try to either call a subprocess to merge a bunch of single-frame files into a movie (not relevant) or to clean up such a process (not relevant). - The least disruptive fix is to duplicate the file clean up code. --- lib/matplotlib/animation.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index 2568d455de39..472de0f8dc5b 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -891,6 +891,22 @@ def finish(self): interval=interval, **mode_dict)) + # duplicate the temporary file clean up logic from + # FileMovieWriter.cleanup. We can not call the inherited + # versions of finished or cleanup because both assume that + # there is a subprocess that we either need to call to merge + # many frames together or that there is a subprocess call that + # we need to clean up. + if self._tmpdir: + _log.debug('MovieWriter: clearing temporary path=%s', self._tmpdir) + self._tmpdir.cleanup() + else: + if self._clear_temp: + _log.debug('MovieWriter: clearing temporary paths=%s', + self._temp_paths) + for path in self._temp_paths: + path.unlink() + class Animation: """ From 1c9518b14d560cec4624132e68b6cfa1020424dd Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Thu, 8 Oct 2020 21:53:04 +0200 Subject: [PATCH 3/4] Merge pull request #18679 from anntzer/pgftmpdir Further simplify pgf tmpdir cleanup. Backports #18679 but drops the deprecation steps. --- lib/matplotlib/backends/backend_pgf.py | 67 +++++++++----------------- 1 file changed, 24 insertions(+), 43 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 8da4abca6bba..55e1b44b8501 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -11,6 +11,7 @@ import subprocess import sys import tempfile +from tempfile import TemporaryDirectory import weakref from PIL import Image @@ -208,7 +209,6 @@ class LatexManager: determining the metrics of text elements. The LaTeX environment can be modified by setting fonts and/or a custom preamble in `.rcParams`. """ - _unclean_instances = weakref.WeakSet() @staticmethod def _build_latex_header(): @@ -245,12 +245,6 @@ def _get_cached_or_new(cls): def _get_cached_or_new_impl(cls, header): # Helper for _get_cached_or_new. return cls() - @staticmethod - def _cleanup_remaining_instances(): - unclean_instances = list(LatexManager._unclean_instances) - for latex_manager in unclean_instances: - latex_manager._cleanup() - def _stdin_writeln(self, s): if self.latex is None: self._setup_latex_process() @@ -276,13 +270,10 @@ def _expect_prompt(self): return self._expect("\n*") def __init__(self): - # store references for __del__ - self._os_path = os.path - self._shutil = shutil - - # create a tmp directory for running latex, remember to cleanup - self.tmpdir = tempfile.mkdtemp(prefix="mpl_pgf_lm_") - LatexManager._unclean_instances.add(self) + # create a tmp directory for running latex, register it for deletion + self._tmpdir = TemporaryDirectory() + self.tmpdir = self._tmpdir.name + self._finalize_tmpdir = weakref.finalize(self, self._tmpdir.cleanup) # test the LaTeX setup to ensure a clean startup of the subprocess self.texcommand = mpl.rcParams["pgf.texsystem"] @@ -311,11 +302,21 @@ def __init__(self): self.str_cache = {} # cache for strings already processed def _setup_latex_process(self): - # open LaTeX process for real work + # Open LaTeX process for real work; register it for deletion. On + # Windows, we must ensure that the subprocess has quit before being + # able to delete the tmpdir in which it runs; in order to do so, we + # must first `kill()` it, and then `communicate()` with it. self.latex = subprocess.Popen( [self.texcommand, "-halt-on-error"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding="utf-8", cwd=self.tmpdir) + + def finalize_latex(latex): + latex.kill() + latex.communicate() + + self._finalize_latex = weakref.finalize( + self, finalize_latex, self.latex) # write header with 'pgf_backend_query_start' token self._stdin_writeln(self._build_latex_header()) # read all lines until our 'pgf_backend_query_start' token appears @@ -326,23 +327,6 @@ def _setup_latex_process(self): def latex_stdin_utf8(self): return self.latex.stdin - def _cleanup(self): - if not self._os_path.isdir(self.tmpdir): - return - try: - self.latex.communicate() - except Exception: - pass - try: - self._shutil.rmtree(self.tmpdir) - LatexManager._unclean_instances.discard(self) - except Exception: - sys.stderr.write("error deleting tmp directory %s\n" % self.tmpdir) - - def __del__(self): - _log.debug("deleting LatexManager") - self._cleanup() - def get_width_height_descent(self, text, prop): """ Get the width, total height and descent for a text typeset by the @@ -779,15 +763,20 @@ class GraphicsContextPgf(GraphicsContextBase): class TmpDirCleaner: - remaining_tmpdirs = set() + _remaining_tmpdirs = set() + + @cbook._classproperty + def remaining_tmpdirs(cls): + return cls._remaining_tmpdirs @staticmethod def add(tmpdir): - TmpDirCleaner.remaining_tmpdirs.add(tmpdir) + TmpDirCleaner._remaining_tmpdirs.add(tmpdir) @staticmethod + @atexit.register def cleanup_remaining_tmpdirs(): - for tmpdir in TmpDirCleaner.remaining_tmpdirs: + for tmpdir in TmpDirCleaner._remaining_tmpdirs: error_message = "error deleting tmp directory {}".format(tmpdir) shutil.rmtree( tmpdir, @@ -979,14 +968,6 @@ class _BackendPgf(_Backend): FigureCanvas = FigureCanvasPgf -def _cleanup_all(): - LatexManager._cleanup_remaining_instances() - TmpDirCleaner.cleanup_remaining_tmpdirs() - - -atexit.register(_cleanup_all) - - class PdfPages: """ A multi-page PDF file using the pgf backend From b3ad2b9535bff52b4bb18585aedf3668b7fb4401 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 21 Dec 2020 21:44:02 -0500 Subject: [PATCH 4/4] MNT: backout a deprecation related rename --- lib/matplotlib/backends/backend_pgf.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 55e1b44b8501..fc30e6a7b909 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -763,20 +763,16 @@ class GraphicsContextPgf(GraphicsContextBase): class TmpDirCleaner: - _remaining_tmpdirs = set() - - @cbook._classproperty - def remaining_tmpdirs(cls): - return cls._remaining_tmpdirs + remaining_tmpdirs = set() @staticmethod def add(tmpdir): - TmpDirCleaner._remaining_tmpdirs.add(tmpdir) + TmpDirCleaner.remaining_tmpdirs.add(tmpdir) @staticmethod @atexit.register def cleanup_remaining_tmpdirs(): - for tmpdir in TmpDirCleaner._remaining_tmpdirs: + for tmpdir in TmpDirCleaner.remaining_tmpdirs: error_message = "error deleting tmp directory {}".format(tmpdir) shutil.rmtree( tmpdir,