Skip to content

Commit 4a192f8

Browse files
committed
Work around httppretty patching
1 parent 509ecfe commit 4a192f8

2 files changed

Lines changed: 50 additions & 14 deletions

File tree

tests/debugging/test_uploader.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
from contextlib import ExitStack
12
from contextlib import contextmanager
23
from http.server import BaseHTTPRequestHandler
34
from http.server import ThreadingHTTPServer
45
import json
56
from queue import Queue
7+
import socket
68
from threading import Thread
79
from unittest.mock import MagicMock
810
from unittest.mock import patch
@@ -25,6 +27,16 @@
2527
# so we use a large enough integer as an approximation instead.
2628
LONG_INTERVAL = 2147483647.0
2729

30+
# httpretty -- reached through tests.internal.remoteconfig.rcm_endpoint, which
31+
# test_debugger.py uses earlier in this suite -- intermittently leaves the socket
32+
# module holding its fakes after being disabled. A server built on a fake socket binds
33+
# a port that never accepts, so the native sender only reports a timeout. These are
34+
# captured at import time, before any test can patch them, and reinstalled for the
35+
# lifetime of the intake server below.
36+
_REAL_SOCKET_ATTRS = {
37+
name: getattr(socket, name) for name in ("socket", "create_connection", "getaddrinfo", "socketpair")
38+
}
39+
2840

2941
class MockSignalUploader(SignalUploader):
3042
def __init__(self, *args, **kwargs):
@@ -212,11 +224,18 @@ def close(self) -> None:
212224

213225
@contextmanager
214226
def _intake_server():
215-
server = _IntakeServer()
216-
try:
217-
yield server
218-
finally:
219-
server.close()
227+
# socket.socket is consulted again on every accept(), so the real
228+
# implementations have to stay installed for as long as the server serves, not
229+
# just while it is constructed.
230+
with ExitStack() as stack:
231+
for name, real in _REAL_SOCKET_ATTRS.items():
232+
stack.enter_context(patch.object(socket, name, real))
233+
234+
server = _IntakeServer()
235+
try:
236+
yield server
237+
finally:
238+
server.close()
220239

221240

222241
def _uploader_to(url):
Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11
from contextlib import contextmanager
2+
import socket
3+
4+
5+
# httpretty replaces these when it patches the socket module, and does not reliably
6+
# put them back: with ``httpretty.is_enabled()`` already False they can still point at
7+
# ``httpretty.core``, which leaves every later test in the process unable to open a
8+
# real socket. Snapshot the real implementations at import time -- before any test can
9+
# enable httpretty -- so rcm_endpoint can restore them itself.
10+
_REAL_SOCKET_ATTRS = {
11+
name: getattr(socket, name) for name in ("socket", "create_connection", "getaddrinfo", "socketpair")
12+
}
213

314

415
@contextmanager
@@ -13,12 +24,18 @@ def rcm_endpoint(port=10126, poll_interval=0.05):
1324
from tests.utils import override_env
1425
from tests.utils import override_global_config
1526

16-
with (
17-
override_env(dict(DD_TRACE_AGENT_URL="http://localhost:%d" % port)),
18-
httpretty.enabled(),
19-
override_global_config(dict(_remote_config_poll_interval=poll_interval)),
20-
):
21-
httpretty.register_uri(
22-
httpretty.GET, "http://localhost:%d/info" % port, body='{"endpoints":["%s"]}' % REMOTE_CONFIG_AGENT_ENDPOINT
23-
)
24-
yield
27+
try:
28+
with (
29+
override_env(dict(DD_TRACE_AGENT_URL="http://localhost:%d" % port)),
30+
httpretty.enabled(),
31+
override_global_config(dict(_remote_config_poll_interval=poll_interval)),
32+
):
33+
httpretty.register_uri(
34+
httpretty.GET,
35+
"http://localhost:%d/info" % port,
36+
body='{"endpoints":["%s"]}' % REMOTE_CONFIG_AGENT_ENDPOINT,
37+
)
38+
yield
39+
finally:
40+
for name, real in _REAL_SOCKET_ATTRS.items():
41+
setattr(socket, name, real)

0 commit comments

Comments
 (0)