|  | 
| 2 | 2 | import subprocess | 
| 3 | 3 | import sys | 
| 4 | 4 | 
 | 
| 5 |  | -import numpy as np | 
| 6 | 5 | import pytest | 
| 7 | 6 | 
 | 
| 8 |  | -from matplotlib import pyplot as plt | 
| 9 | 7 | _test_timeout = 10  # Empirically, 1s is not enough on Travis. | 
| 10 | 8 | 
 | 
|  | 9 | +# NOTE: TkAgg tests seem to have interactions between tests, | 
|  | 10 | +# So isolate each test in a subprocess. See GH#18261 | 
|  | 11 | + | 
| 11 | 12 | 
 | 
| 12 | 13 | @pytest.mark.backend('TkAgg', skip_on_importerror=True) | 
| 13 | 14 | def test_blit(): | 
| 14 |  | -    from matplotlib.backends import _tkagg | 
| 15 |  | -    def evil_blit(photoimage, aggimage, offsets, bboxptr): | 
| 16 |  | -        data = np.asarray(aggimage) | 
| 17 |  | -        height, width = data.shape[:2] | 
| 18 |  | -        dataptr = (height, width, data.ctypes.data) | 
| 19 |  | -        _tkagg.blit( | 
| 20 |  | -            photoimage.tk.interpaddr(), str(photoimage), dataptr, offsets, | 
| 21 |  | -            bboxptr) | 
| 22 |  | - | 
| 23 |  | -    fig, ax = plt.subplots() | 
| 24 |  | -    for bad_boxes in ((-1, 2, 0, 2), | 
| 25 |  | -                      (2, 0, 0, 2), | 
| 26 |  | -                      (1, 6, 0, 2), | 
| 27 |  | -                      (0, 2, -1, 2), | 
| 28 |  | -                      (0, 2, 2, 0), | 
| 29 |  | -                      (0, 2, 1, 6)): | 
| 30 |  | -        with pytest.raises(ValueError): | 
| 31 |  | -            evil_blit(fig.canvas._tkphoto, | 
| 32 |  | -                      np.ones((4, 4, 4)), | 
| 33 |  | -                      (0, 1, 2, 3), | 
| 34 |  | -                      bad_boxes) | 
|  | 15 | +    script = """ | 
|  | 16 | +import matplotlib.pyplot as plt | 
|  | 17 | +import numpy as np | 
|  | 18 | +from matplotlib.backends import _tkagg | 
|  | 19 | +def evil_blit(photoimage, aggimage, offsets, bboxptr): | 
|  | 20 | +    data = np.asarray(aggimage) | 
|  | 21 | +    height, width = data.shape[:2] | 
|  | 22 | +    dataptr = (height, width, data.ctypes.data) | 
|  | 23 | +    _tkagg.blit( | 
|  | 24 | +        photoimage.tk.interpaddr(), str(photoimage), dataptr, offsets, | 
|  | 25 | +        bboxptr) | 
|  | 26 | +
 | 
|  | 27 | +fig, ax = plt.subplots() | 
|  | 28 | +bad_boxes = ((-1, 2, 0, 2), | 
|  | 29 | +             (2, 0, 0, 2), | 
|  | 30 | +             (1, 6, 0, 2), | 
|  | 31 | +             (0, 2, -1, 2), | 
|  | 32 | +             (0, 2, 2, 0), | 
|  | 33 | +             (0, 2, 1, 6)) | 
|  | 34 | +for bad_box in bad_boxes: | 
|  | 35 | +    try: | 
|  | 36 | +        evil_blit(fig.canvas._tkphoto, | 
|  | 37 | +                  np.ones((4, 4, 4)), | 
|  | 38 | +                  (0, 1, 2, 3), | 
|  | 39 | +                  bad_box) | 
|  | 40 | +    except ValueError: | 
|  | 41 | +        print("success") | 
|  | 42 | +""" | 
|  | 43 | +    try: | 
|  | 44 | +        proc = subprocess.run( | 
|  | 45 | +            [sys.executable, "-c", script], | 
|  | 46 | +            env={**os.environ, | 
|  | 47 | +                 "MPLBACKEND": "TkAgg", | 
|  | 48 | +                 "SOURCE_DATE_EPOCH": "0"}, | 
|  | 49 | +            timeout=_test_timeout, | 
|  | 50 | +            stdout=subprocess.PIPE, | 
|  | 51 | +            check=True, | 
|  | 52 | +            universal_newlines=True, | 
|  | 53 | +        ) | 
|  | 54 | +    except subprocess.CalledProcessError: | 
|  | 55 | +        pytest.fail("Likely regression on out-of-bounds data access" | 
|  | 56 | +                    " in _tkagg.cpp") | 
|  | 57 | +    else: | 
|  | 58 | +        print(proc.stdout) | 
|  | 59 | +        assert proc.stdout.count("success") == 6  # len(bad_boxes) | 
| 35 | 60 | 
 | 
| 36 | 61 | 
 | 
| 37 | 62 | @pytest.mark.backend('TkAgg', skip_on_importerror=True) | 
| @@ -172,12 +197,32 @@ def test_never_update(): | 
| 172 | 197 | 
 | 
| 173 | 198 | @pytest.mark.backend('TkAgg', skip_on_importerror=True) | 
| 174 | 199 | def test_missing_back_button(): | 
| 175 |  | -    from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk | 
| 176 |  | -    class Toolbar(NavigationToolbar2Tk): | 
| 177 |  | -        # only display the buttons we need | 
| 178 |  | -        toolitems = [t for t in NavigationToolbar2Tk.toolitems if | 
| 179 |  | -                     t[0] in ('Home', 'Pan', 'Zoom')] | 
| 180 |  | - | 
| 181 |  | -    fig = plt.figure() | 
| 182 |  | -    # this should not raise | 
| 183 |  | -    Toolbar(fig.canvas, fig.canvas.manager.window) | 
|  | 200 | +    script = """ | 
|  | 201 | +import matplotlib.pyplot as plt | 
|  | 202 | +from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk | 
|  | 203 | +class Toolbar(NavigationToolbar2Tk): | 
|  | 204 | +    # only display the buttons we need | 
|  | 205 | +    toolitems = [t for t in NavigationToolbar2Tk.toolitems if | 
|  | 206 | +                 t[0] in ('Home', 'Pan', 'Zoom')] | 
|  | 207 | +
 | 
|  | 208 | +fig = plt.figure() | 
|  | 209 | +print("setup complete") | 
|  | 210 | +# this should not raise | 
|  | 211 | +Toolbar(fig.canvas, fig.canvas.manager.window) | 
|  | 212 | +print("success") | 
|  | 213 | +""" | 
|  | 214 | +    try: | 
|  | 215 | +        proc = subprocess.run( | 
|  | 216 | +            [sys.executable, "-c", script], | 
|  | 217 | +            env={**os.environ, | 
|  | 218 | +                 "MPLBACKEND": "TkAgg", | 
|  | 219 | +                 "SOURCE_DATE_EPOCH": "0"}, | 
|  | 220 | +            timeout=_test_timeout, | 
|  | 221 | +            stdout=subprocess.PIPE, | 
|  | 222 | +            universal_newlines=True, | 
|  | 223 | +        ) | 
|  | 224 | +    except subprocess.TimeoutExpired: | 
|  | 225 | +        pytest.fail("Subprocess timed out") | 
|  | 226 | +    else: | 
|  | 227 | +        assert proc.stdout.count("setup complete") == 1 | 
|  | 228 | +        assert proc.stdout.count("success") == 1 | 
0 commit comments