Skip to content

Commit 63447d3

Browse files
authored
fix ci
1 parent 005098d commit 63447d3

5 files changed

Lines changed: 51 additions & 50 deletions

File tree

Lib/profiling/sampling/_control.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ def _close_listener(self):
9090
created_stat.st_dev,
9191
):
9292
os.unlink(self._path)
93-
except FileNotFoundError:
94-
pass
9593
except OSError:
9694
pass
9795

Lib/profiling/sampling/sample.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,10 @@ def wait(timeout):
225225
running_time_sec = time.perf_counter() - start_time
226226
except KeyboardInterrupt:
227227
interrupted = True
228-
running_time_sec = time.perf_counter() - start_time
228+
now = time.perf_counter()
229+
running_time_sec = now - start_time
229230
if enabled_since is not None:
230-
enabled_time_sec += time.perf_counter() - enabled_since
231+
enabled_time_sec += now - enabled_since
231232
print("Interrupted by user.")
232233
finally:
233234
flush_pending()

Lib/test/test_profiling/test_sampling_profiler/test_control.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,34 @@
22

33
import io
44
import os
5-
import shutil
65
import socket
7-
import tempfile
86
import time
97
import unittest
108
from unittest import mock
119

10+
from test.support import SHORT_TIMEOUT, os_helper, socket_helper
11+
1212
try:
1313
from profiling.sampling._control import (
1414
ControlServer,
1515
_MAX_INBUF_BYTES,
1616
parse_control_uri,
1717
)
18-
from profiling.sampling.cli import main
18+
from profiling.sampling.cli import LiveStatsCollector, main
1919
from profiling.sampling.errors import ControlError, ControlURIError
2020
except ImportError:
2121
raise unittest.SkipTest(
2222
"Test only runs when profiling.sampling is available"
2323
)
2424

2525

26+
@socket_helper.skip_unless_bind_unix_socket
2627
class ControlServerTests(unittest.TestCase):
2728
"""Tests for ControlServer protocol, lifecycle and CLI integration."""
2829

2930
def setUp(self):
30-
tmpdir = tempfile.mkdtemp()
31-
self.addCleanup(shutil.rmtree, tmpdir, ignore_errors=True)
32-
self.path = os.path.join(tmpdir, "control.sock")
31+
self.path = socket_helper.create_unix_domain_name()
32+
self.addCleanup(os_helper.unlink, self.path)
3333

3434
def start_server(self):
3535
server = ControlServer(f"unix:{self.path}")
@@ -46,7 +46,7 @@ def connect(self):
4646

4747
def request(self, server, client, command):
4848
client.sendall(command)
49-
deadline = time.monotonic() + 1.0
49+
deadline = time.monotonic() + SHORT_TIMEOUT
5050
while time.monotonic() < deadline:
5151
server.poll(timeout=0.05)
5252
try:
@@ -88,7 +88,7 @@ def test_start_creates_and_stop_unlinks_socket(self):
8888
def test_start_fails_on_occupied_path(self):
8989
"""start() raises ControlError when the path is already bound."""
9090
squatter = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
91-
squatter.bind(self.path)
91+
socket_helper.bind_unix_socket(squatter, self.path)
9292
self.addCleanup(squatter.close)
9393
with self.assertRaisesRegex(ControlError, "failed to start"):
9494
ControlServer(f"unix:{self.path}").start()
@@ -124,7 +124,7 @@ def test_dispatch_quit_sets_running_and_closes(self):
124124
client = self.connect()
125125
self.assertEqual(self.request(server, client, b"quit\n"), b"ok\n")
126126
self.assertFalse(server.control.running)
127-
deadline = time.monotonic() + 1.0
127+
deadline = time.monotonic() + SHORT_TIMEOUT
128128
while time.monotonic() < deadline:
129129
server.poll(timeout=0.05)
130130
try:
@@ -142,7 +142,7 @@ def test_inbuf_overflow_drops_connection(self):
142142
client.connect(self.path)
143143
self.addCleanup(client.close)
144144
client.sendall(b"x" * (_MAX_INBUF_BYTES + 1))
145-
deadline = time.monotonic() + 1.0
145+
deadline = time.monotonic() + SHORT_TIMEOUT
146146
while time.monotonic() < deadline:
147147
server.poll(timeout=0.05)
148148
if not server._connections:
@@ -154,7 +154,7 @@ def test_close_listener_preserves_replaced_path(self):
154154
server = self.start_server()
155155
os.unlink(self.path)
156156
replacement = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
157-
replacement.bind(self.path)
157+
socket_helper.bind_unix_socket(replacement, self.path)
158158
self.addCleanup(replacement.close)
159159
server.stop()
160160
self.assertTrue(os.path.exists(self.path))
@@ -198,6 +198,8 @@ def test_cli_rejects_bad_uri(self):
198198
self.assertEqual(cm.exception.code, 2)
199199
self.assertIn("unsupported control URI scheme", stderr.getvalue())
200200

201+
@unittest.skipUnless(LiveStatsCollector is not None,
202+
"requires curses for --live")
201203
def test_cli_accepts_control_with_live(self):
202204
"""--control and --live coexist after the mutex was dropped."""
203205
argv = [

Lib/test/test_profiling/test_sampling_profiler/test_integration.py

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import subprocess
1010
import sys
1111
import tempfile
12+
import threading
1213
import time
1314
import unittest
1415
from unittest import mock
@@ -17,6 +18,7 @@
1718
import _remote_debugging
1819
import profiling.sampling
1920
import profiling.sampling.sample
21+
from profiling.sampling._control import ControlServer
2022
from profiling.sampling.pstats_collector import PstatsCollector
2123
from profiling.sampling.stack_collector import CollapsedStackCollector
2224
from profiling.sampling.sample import SampleProfiler, _is_process_running
@@ -29,12 +31,13 @@
2931
from test.support import (
3032
requires_remote_subprocess_debugging,
3133
SHORT_TIMEOUT,
34+
os_helper,
35+
socket_helper,
3236
)
3337

3438
from .helpers import (
3539
test_subprocess,
3640
close_and_unlink,
37-
_cleanup_process,
3841
)
3942
from .mocks import MockFrameInfo, MockThreadInfo, MockInterpreterInfo
4043

@@ -959,19 +962,20 @@ def test_all_stacks_share_same_base_frame(self):
959962

960963

961964
@requires_remote_subprocess_debugging()
965+
@socket_helper.skip_unless_bind_unix_socket
962966
@unittest.skipIf(
963967
sys.platform == "darwin" and os.geteuid() != 0,
964968
"macOS profiling requires elevated permissions",
965969
)
966970
class TestControlSocketIntegration(unittest.TestCase):
967-
"""End-to-end tests for the --control socket via a real profiler subprocess."""
971+
"""End-to-end tests for the --control socket via in-process sample()."""
968972

969973
def _send_recv(self, client, request, expected_reply):
970974
client.sendall(request)
971975
self.assertEqual(client.recv(4096), expected_reply)
972976

973977
def test_control_socket_disable_enable_quit_cycle(self):
974-
"""Drive disable/enable/quit through a real ControlServer subprocess."""
978+
"""Drive disable/enable/quit through a real ControlServer."""
975979
script = '''
976980
import time
977981
_test_sock.sendall(b"working")
@@ -980,27 +984,31 @@ def test_control_socket_disable_enable_quit_cycle(self):
980984
time.sleep(0.05)
981985
'''
982986
with test_subprocess(script, wait_for_working=True) as target:
983-
tmpdir = tempfile.mkdtemp()
984-
self.addCleanup(shutil.rmtree, tmpdir, ignore_errors=True)
985-
socket_path = os.path.join(tmpdir, "tachyon.sock")
986-
987-
profiler = subprocess.Popen(
988-
[sys.executable, "-m", "profiling.sampling", "attach",
989-
"--control", f"unix:{socket_path}",
990-
"-d", "30",
991-
str(target.process.pid)],
992-
stdout=subprocess.PIPE,
993-
stderr=subprocess.PIPE,
994-
)
995-
self.addCleanup(_cleanup_process, profiler)
987+
socket_path = socket_helper.create_unix_domain_name()
988+
self.addCleanup(os_helper.unlink, socket_path)
996989

997-
deadline = time.monotonic() + SHORT_TIMEOUT
998-
while time.monotonic() < deadline:
999-
if os.path.exists(socket_path):
1000-
break
1001-
time.sleep(0.05)
1002-
else:
1003-
self.fail("profiler did not create the control socket")
990+
server = ControlServer(f"unix:{socket_path}")
991+
server.start()
992+
self.addCleanup(server.stop)
993+
994+
exception = [None]
995+
996+
collector = PstatsCollector(sample_interval_usec=1000, skip_idle=False)
997+
998+
def sample_worker():
999+
try:
1000+
with contextlib.redirect_stdout(io.StringIO()):
1001+
profiling.sampling.sample.sample(
1002+
target.process.pid,
1003+
collector,
1004+
duration_sec=SHORT_TIMEOUT,
1005+
control_server=server,
1006+
)
1007+
except Exception as exc:
1008+
exception[0] = exc
1009+
1010+
thread = threading.Thread(target=sample_worker, daemon=True)
1011+
thread.start()
10041012

10051013
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
10061014
client.settimeout(SHORT_TIMEOUT)
@@ -1010,9 +1018,7 @@ def test_control_socket_disable_enable_quit_cycle(self):
10101018
self._send_recv(client, b"enable\n", b"ok\n")
10111019
self._send_recv(client, b"quit\n", b"ok\n")
10121020

1013-
stdout, stderr = profiler.communicate(timeout=SHORT_TIMEOUT)
1014-
self.assertEqual(
1015-
profiler.returncode, 0,
1016-
msg=f"stdout={stdout!r} stderr={stderr!r}",
1017-
)
1018-
self.assertFalse(os.path.exists(socket_path))
1021+
thread.join(timeout=SHORT_TIMEOUT)
1022+
self.assertFalse(thread.is_alive(), "sample() did not exit on quit")
1023+
if exception[0] is not None:
1024+
raise exception[0]

Lib/test/test_profiling/test_sampling_profiler/test_profiler.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@ def test_sample_profiler_does_not_buffer_non_aggregating_collectors(self):
210210

211211
stack_frames = [mock.sentinel.stack_frames]
212212
mock_collector = mock.MagicMock()
213-
mock_collector.aggregating = False
214213

215214
with self._patched_unwinder() as u:
216215
u.instance.get_stack_trace.return_value = stack_frames
@@ -445,7 +444,6 @@ def test_sample_polls_control_server_periodically(self):
445444
"""Test that sample() drives control_server.poll() during the loop."""
446445
control_server = self._fake_control_server()
447446
mock_collector = mock.MagicMock()
448-
mock_collector.aggregating = False
449447
times = [1000.0 + i * 0.01 for i in range(60)]
450448
with self._patched_unwinder() as u:
451449
u.instance.get_stack_trace.return_value = []
@@ -466,7 +464,6 @@ def test_sample_breaks_when_control_running_false(self):
466464
"""Test that sample() exits immediately when control.running is False."""
467465
control_server = self._fake_control_server(running=False)
468466
mock_collector = mock.MagicMock()
469-
mock_collector.aggregating = False
470467
with self._patched_unwinder() as u:
471468
u.instance.get_stack_trace.return_value = []
472469
profiler = SampleProfiler(
@@ -485,7 +482,6 @@ def test_sample_pauses_when_control_disabled(self):
485482
"""Test that disabled state stops sample collection."""
486483
control_server = self._fake_control_server(enabled=False)
487484
mock_collector = mock.MagicMock()
488-
mock_collector.aggregating = False
489485
times = [1000.0 + i * 0.01 for i in range(60)]
490486
with self._patched_unwinder() as u:
491487
u.instance.get_stack_trace.return_value = []
@@ -514,7 +510,6 @@ def poll_side_effect(timeout=0):
514510
control_server.poll.side_effect = poll_side_effect
515511

516512
mock_collector = mock.MagicMock()
517-
mock_collector.aggregating = False
518513
times = [1000.0 + i * 0.01 for i in range(80)]
519514
with self._patched_unwinder() as u:
520515
u.instance.get_stack_trace.return_value = [
@@ -545,7 +540,6 @@ def poll_side_effect(timeout=0):
545540
control_server.poll.side_effect = poll_side_effect
546541

547542
mock_collector = mock.MagicMock()
548-
mock_collector.aggregating = False
549543
times = [1000.0 + i * 0.01 for i in range(120)]
550544
with self._patched_unwinder() as u:
551545
u.instance.get_stack_trace.return_value = [

0 commit comments

Comments
 (0)