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

Skip to content

Commit 328ee38

Browse files
authored
Merge pull request #31503 from QuLogic/popen-contexts
TST: Harden handling of Popen subprocesses
2 parents e6640db + eebd854 commit 328ee38

1 file changed

Lines changed: 53 additions & 56 deletions

File tree

lib/matplotlib/tests/test_backends_interactive.py

Lines changed: 53 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import json
66
import os
77
import platform
8+
import re
89
import signal
910
import subprocess
1011
import sys
@@ -16,7 +17,6 @@
1617

1718
import pytest
1819

19-
import matplotlib as mpl
2020
from matplotlib import _c_internal_utils
2121
from matplotlib.backend_tools import ToolToggleBase
2222
from matplotlib.testing import subprocess_run_helper as _run_helper, is_ci_environment
@@ -46,7 +46,7 @@ def wait_for(self, terminator):
4646
f'Subprocess died before emitting expected {terminator!r}')
4747
buf += c
4848
if buf.endswith(terminator):
49-
return
49+
return buf
5050

5151

5252
# Minimal smoke-testing of the backends for which the dependencies are
@@ -160,7 +160,6 @@ def _test_interactive_impl():
160160
from matplotlib.backend_bases import KeyEvent, FigureCanvasBase
161161
mpl.rcParams.update({
162162
"webagg.open_in_browser": False,
163-
"webagg.port_retries": 1,
164163
})
165164

166165
mpl.rcParams.update(json.loads(sys.argv[1]))
@@ -484,32 +483,32 @@ def test_cross_Qt_imports(host, mpl):
484483
@pytest.mark.skipif(sys.platform == "win32", reason="Cannot send SIGINT on Windows.")
485484
def test_webagg():
486485
pytest.importorskip("tornado")
487-
proc = subprocess.Popen(
488-
[sys.executable, "-c",
489-
inspect.getsource(_test_interactive_impl)
490-
+ "\n_test_interactive_impl()", "{}"],
491-
env={**os.environ, "MPLBACKEND": "webagg", "SOURCE_DATE_EPOCH": "0"})
492-
url = f'http://{mpl.rcParams["webagg.address"]}:{mpl.rcParams["webagg.port"]}'
493-
timeout = time.perf_counter() + _test_timeout
494-
try:
495-
while True:
496-
try:
497-
retcode = proc.poll()
498-
# check that the subprocess for the server is not dead
499-
assert retcode is None
500-
conn = urllib.request.urlopen(url)
501-
break
502-
except urllib.error.URLError:
503-
if time.perf_counter() > timeout:
504-
pytest.fail("Failed to connect to the webagg server.")
505-
else:
506-
continue
507-
conn.close()
508-
proc.send_signal(signal.SIGINT)
509-
assert proc.wait(timeout=_test_timeout) == 0
510-
finally:
511-
if proc.poll() is None:
512-
proc.kill()
486+
source = (inspect.getsource(_test_interactive_impl) +
487+
"\n_test_interactive_impl()")
488+
rc = '{"backend": "webagg"}'
489+
with _WaitForStringPopen([sys.executable, "-c", source, rc]) as proc:
490+
try:
491+
buf = proc.wait_for('Press Ctrl+C')
492+
url = re.search(r'visit (https?:\/\/\S+)', buf).group(1)
493+
timeout = time.perf_counter() + _test_timeout
494+
while True:
495+
try:
496+
retcode = proc.poll()
497+
# check that the subprocess for the server is not dead
498+
assert retcode is None
499+
with urllib.request.urlopen(url):
500+
# Do nothing; we've just confirmed that we can connect.
501+
break
502+
except urllib.error.URLError:
503+
if time.perf_counter() > timeout:
504+
pytest.fail("Failed to connect to the webagg server.")
505+
else:
506+
continue
507+
proc.send_signal(signal.SIGINT)
508+
assert proc.wait(timeout=_test_timeout) == 0
509+
finally:
510+
if proc.poll() is None:
511+
proc.kill()
513512

514513

515514
def _lazy_headless():
@@ -737,18 +736,17 @@ def test_sigint(env, target, kwargs):
737736
backend = env.get("MPLBACKEND")
738737
if not backend.startswith(("qt", "macosx")):
739738
pytest.skip("SIGINT currently only tested on qt and macosx")
740-
proc = _WaitForStringPopen(
741-
[sys.executable, "-c",
742-
inspect.getsource(_test_sigint_impl) +
743-
f"\n_test_sigint_impl({backend!r}, {target!r}, {kwargs!r})"])
744-
try:
745-
proc.wait_for('DRAW')
746-
stdout, _ = proc.communicate(timeout=_test_timeout)
747-
except Exception:
748-
proc.kill()
749-
stdout, _ = proc.communicate()
750-
raise
751-
assert 'SUCCESS' in stdout
739+
source = (inspect.getsource(_test_sigint_impl) +
740+
f"\n_test_sigint_impl({backend!r}, {target!r}, {kwargs!r})")
741+
with _WaitForStringPopen([sys.executable, "-c", source]) as proc:
742+
try:
743+
proc.wait_for('DRAW')
744+
stdout, _ = proc.communicate(timeout=_test_timeout)
745+
except Exception:
746+
proc.kill()
747+
stdout, _ = proc.communicate()
748+
raise
749+
assert 'SUCCESS' in stdout
752750

753751

754752
def _test_other_signal_before_sigint_impl(backend, target_name, kwargs):
@@ -796,20 +794,19 @@ def test_other_signal_before_sigint(env, target, kwargs, request):
796794
# https://github.com/matplotlib/matplotlib/issues/27984
797795
request.node.add_marker(
798796
pytest.mark.xfail(reason="Qt backend is buggy on macOS"))
799-
proc = _WaitForStringPopen(
800-
[sys.executable, "-c",
801-
inspect.getsource(_test_other_signal_before_sigint_impl) +
802-
"\n_test_other_signal_before_sigint_impl("
803-
f"{backend!r}, {target!r}, {kwargs!r})"])
804-
try:
805-
proc.wait_for('DRAW')
806-
os.kill(proc.pid, signal.SIGUSR1)
807-
proc.wait_for('SIGUSR1')
808-
os.kill(proc.pid, signal.SIGINT)
809-
stdout, _ = proc.communicate(timeout=_test_timeout)
810-
except Exception:
811-
proc.kill()
812-
stdout, _ = proc.communicate()
813-
raise
797+
source = (inspect.getsource(_test_other_signal_before_sigint_impl) +
798+
"\n_test_other_signal_before_sigint_impl("
799+
f"{backend!r}, {target!r}, {kwargs!r})")
800+
with _WaitForStringPopen([sys.executable, "-c", source]) as proc:
801+
try:
802+
proc.wait_for('DRAW')
803+
os.kill(proc.pid, signal.SIGUSR1)
804+
proc.wait_for('SIGUSR1')
805+
os.kill(proc.pid, signal.SIGINT)
806+
stdout, _ = proc.communicate(timeout=_test_timeout)
807+
except Exception:
808+
proc.kill()
809+
stdout, _ = proc.communicate()
810+
raise
814811
print(stdout)
815812
assert 'SUCCESS' in stdout

0 commit comments

Comments
 (0)