1
1
import functools
2
- import inspect
2
+ import importlib
3
3
import os
4
4
import platform
5
- import re
6
5
import subprocess
7
6
import sys
8
7
9
8
import pytest
10
9
10
+ from matplotlib .testing import subprocess_run_helper
11
+ from matplotlib import _c_internal_utils
12
+
11
13
_test_timeout = 60 # A reasonably safe value for slower architectures.
12
14
13
15
@@ -18,30 +20,33 @@ def _isolated_tk_test(success_count, func=None):
18
20
19
21
TkAgg tests seem to have interactions between tests, so isolate each test
20
22
in a subprocess. See GH#18261
21
-
22
- The decorated function must be fully self-contained, and thus perform
23
- all the imports it needs. Because its source is extracted and run by
24
- itself, coverage will consider it as not being run, so it should be marked
25
- with ``# pragma: no cover``
26
23
"""
27
24
28
25
if func is None :
29
26
return functools .partial (_isolated_tk_test , success_count )
30
27
31
- # Remove decorators.
32
- source = re .search (r"(?ms)^def .*" , inspect .getsource (func )).group (0 )
33
-
28
+ if "MPL_TEST_ESCAPE_HATCH" in os .environ :
29
+ # set in subprocess_run_helper() below
30
+ return func
31
+
32
+ @pytest .mark .skipif (
33
+ not importlib .util .find_spec ('tkinter' ),
34
+ reason = "missing tkinter"
35
+ )
36
+ @pytest .mark .skipif (
37
+ sys .platform == "linux" and not _c_internal_utils .display_is_valid (),
38
+ reason = "$DISPLAY and $WAYLAND_DISPLAY are unset"
39
+ )
34
40
@functools .wraps (func )
35
41
def test_func ():
42
+ # even if the package exists, may not actually be importable this can
43
+ # be the case on some CI systems.
44
+ pytest .importorskip ('tkinter' )
36
45
try :
37
- proc = subprocess .run (
38
- [sys .executable , "-c" , f"{ source } \n { func .__name__ } ()" ],
39
- env = {** os .environ , "MPLBACKEND" : "TkAgg" },
40
- timeout = _test_timeout ,
41
- stdout = subprocess .PIPE ,
42
- stderr = subprocess .PIPE ,
43
- check = True ,
44
- universal_newlines = True ,
46
+ proc = subprocess_run_helper (
47
+ func , timeout = _test_timeout ,
48
+ MPLBACKEND = "TkAgg" ,
49
+ MPL_TEST_ESCAPE_HATCH = "1"
45
50
)
46
51
except subprocess .TimeoutExpired :
47
52
pytest .fail ("Subprocess timed out" )
@@ -59,9 +64,8 @@ def test_func():
59
64
return test_func
60
65
61
66
62
- @pytest .mark .backend ('TkAgg' , skip_on_importerror = True )
63
67
@_isolated_tk_test (success_count = 6 ) # len(bad_boxes)
64
- def test_blit (): # pragma: no cover
68
+ def test_blit ():
65
69
import matplotlib .pyplot as plt
66
70
import numpy as np
67
71
import matplotlib .backends .backend_tkagg # noqa
@@ -88,9 +92,8 @@ def test_blit(): # pragma: no cover
88
92
print ("success" )
89
93
90
94
91
- @pytest .mark .backend ('TkAgg' , skip_on_importerror = True )
92
95
@_isolated_tk_test (success_count = 1 )
93
- def test_figuremanager_preserves_host_mainloop (): # pragma: no cover
96
+ def test_figuremanager_preserves_host_mainloop ():
94
97
import tkinter
95
98
import matplotlib .pyplot as plt
96
99
success = []
@@ -116,10 +119,9 @@ def legitimate_quit():
116
119
@pytest .mark .skipif (platform .python_implementation () != 'CPython' ,
117
120
reason = 'PyPy does not support Tkinter threading: '
118
121
'https://foss.heptapod.net/pypy/pypy/-/issues/1929' )
119
- @pytest .mark .backend ('TkAgg' , skip_on_importerror = True )
120
122
@pytest .mark .flaky (reruns = 3 )
121
123
@_isolated_tk_test (success_count = 1 )
122
- def test_figuremanager_cleans_own_mainloop (): # pragma: no cover
124
+ def test_figuremanager_cleans_own_mainloop ():
123
125
import tkinter
124
126
import time
125
127
import matplotlib .pyplot as plt
@@ -144,10 +146,9 @@ def target():
144
146
thread .join ()
145
147
146
148
147
- @pytest .mark .backend ('TkAgg' , skip_on_importerror = True )
148
149
@pytest .mark .flaky (reruns = 3 )
149
150
@_isolated_tk_test (success_count = 0 )
150
- def test_never_update (): # pragma: no cover
151
+ def test_never_update ():
151
152
import tkinter
152
153
del tkinter .Misc .update
153
154
del tkinter .Misc .update_idletasks
@@ -171,9 +172,8 @@ def test_never_update(): # pragma: no cover
171
172
# checks them.
172
173
173
174
174
- @pytest .mark .backend ('TkAgg' , skip_on_importerror = True )
175
175
@_isolated_tk_test (success_count = 2 )
176
- def test_missing_back_button (): # pragma: no cover
176
+ def test_missing_back_button ():
177
177
import matplotlib .pyplot as plt
178
178
from matplotlib .backends .backend_tkagg import NavigationToolbar2Tk
179
179
@@ -190,7 +190,7 @@ class Toolbar(NavigationToolbar2Tk):
190
190
191
191
@pytest .mark .backend ('TkAgg' , skip_on_importerror = True )
192
192
@_isolated_tk_test (success_count = 1 )
193
- def test_canvas_focus (): # pragma: no cover
193
+ def test_canvas_focus ():
194
194
import tkinter as tk
195
195
import matplotlib .pyplot as plt
196
196
success = []
0 commit comments