Skip to content

Commit b70fc61

Browse files
authored
ci: fix flaky internal core tests (#17745)
## Description There are a few scenarios where we may not properly clean up the core event hub between tests, and where we will fail because of a Python 3.12+ deprecation warning for `os.fork()` ## Testing <!-- Describe your testing strategy or note what tests are included --> ## Risks <!-- Note any risks associated with this change, or "None" if no risks --> ## Additional Notes <!-- Any other information that would be helpful for reviewers --> Co-authored-by: brett.langdon <brett.langdon@datadoghq.com>
1 parent c223f8c commit b70fc61

8 files changed

Lines changed: 34 additions & 15 deletions

File tree

ddtrace/internal/core/__init__.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,20 @@ def __init__(
188188
def __enter__(self) -> "ExecutionContext[EventType]":
189189
if "_CURRENT_CONTEXT" in globals():
190190
self._token = _CURRENT_CONTEXT.set(self)
191-
dispatch("context.started.%s" % self.identifier, (self,))
191+
try:
192+
dispatch("context.started.%s" % self.identifier, (self,))
193+
except BaseException:
194+
# If dispatch raises, __exit__ won't be called — reset the context ourselves
195+
# to avoid leaving _CURRENT_CONTEXT pointing at this partially-entered context.
196+
if self._token is not None:
197+
try:
198+
_CURRENT_CONTEXT.reset(self._token)
199+
except ValueError:
200+
log.debug("Encountered ValueError resetting context in __enter__ error path for %s", self)
201+
except LookupError:
202+
log.debug("Encountered LookupError resetting context in __enter__ error path for %s", self)
203+
self._token = None
204+
raise
192205
return self
193206

194207
def __repr__(self) -> str:

tests/internal/remoteconfig/test_remoteconfig.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,9 @@ def test_remote_config_enable_validate_rc_disabled(remote_config_worker):
182182
assert remoteconfig_poller.status == ServiceStatus.STOPPED
183183

184184

185-
@pytest.mark.skipif(
186-
sys.version_info >= (3, 12, 0),
187-
reason="Python 3.12 subprocess will raise deprecation warning for forking in a multi-threaded process",
185+
@pytest.mark.subprocess(
186+
ddtrace_run=True, env=dict(DD_REMOTE_CONFIGURATION_ENABLED="true", PYTHONWARNINGS="ignore::DeprecationWarning:os")
188187
)
189-
@pytest.mark.subprocess(ddtrace_run=True, env=dict(DD_REMOTE_CONFIGURATION_ENABLED="true"))
190188
def test_remote_config_forksafe():
191189
import os
192190

tests/internal/test_context_events_api.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ def _inner(*args, **kwargs):
2626

2727

2828
class TestContextEventsApi(unittest.TestCase):
29+
def setUp(self):
30+
core.reset_listeners()
31+
core._reset_context()
32+
2933
def tearDown(self):
3034
core.reset_listeners()
3135
core._reset_context()

tests/internal/test_forksafe.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from ddtrace.internal import forksafe
66

77

8-
@pytest.mark.subprocess()
8+
@pytest.mark.subprocess(env={"PYTHONWARNINGS": "ignore::DeprecationWarning:os"})
99
def test_forksafe():
1010
import os
1111

@@ -34,7 +34,7 @@ def my_func():
3434
assert exit_code == 12
3535

3636

37-
@pytest.mark.subprocess()
37+
@pytest.mark.subprocess(env={"PYTHONWARNINGS": "ignore::DeprecationWarning:os"})
3838
def test_registry():
3939
"""This verifies that registered hooks are called after a fork.
4040
@@ -82,7 +82,7 @@ def before_fork():
8282
assert exit_code == 12
8383

8484

85-
@pytest.mark.subprocess()
85+
@pytest.mark.subprocess(env={"PYTHONWARNINGS": "ignore::DeprecationWarning:os"})
8686
def test_duplicates():
8787
import os
8888

@@ -117,7 +117,7 @@ def f3():
117117
assert exit_code == 12
118118

119119

120-
@pytest.mark.subprocess()
120+
@pytest.mark.subprocess(env={"PYTHONWARNINGS": "ignore::DeprecationWarning:os"})
121121
def test_method_usage():
122122
import os
123123

@@ -201,7 +201,7 @@ def test_event_basic():
201201
event.clear()
202202

203203

204-
@pytest.mark.subprocess()
204+
@pytest.mark.subprocess(env={"PYTHONWARNINGS": "ignore::DeprecationWarning:os"})
205205
def test_event_fork():
206206
"""Check that a forksafe.Event is reset after a fork().
207207
@@ -228,7 +228,7 @@ def test_event_fork():
228228
assert exit_code == 12
229229

230230

231-
@pytest.mark.subprocess()
231+
@pytest.mark.subprocess(env={"PYTHONWARNINGS": "ignore::DeprecationWarning:os"})
232232
def test_double_fork():
233233
import os
234234

tests/internal/test_manual_context_events_api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
@pytest.fixture(autouse=True)
1010
def reset_context_and_listeners():
11+
core.reset_listeners()
12+
core._reset_context()
1113
yield
1214
core.reset_listeners()
1315
core._reset_context()

tests/internal/test_periodic.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def periodic(self):
156156
assert queue == list(range(n + 1))
157157

158158

159-
@pytest.mark.subprocess()
159+
@pytest.mark.subprocess(env={"PYTHONWARNINGS": "ignore::DeprecationWarning:os"})
160160
def test_forksafe_awakeable_periodic_service():
161161
import os
162162
from threading import Event
@@ -199,7 +199,7 @@ def periodic(self):
199199

200200

201201
@pytest.mark.skipif(not hasattr(os, "fork"), reason="requires fork")
202-
@pytest.mark.subprocess()
202+
@pytest.mark.subprocess(env={"PYTHONWARNINGS": "ignore::DeprecationWarning:os"})
203203
def test_autorestart_false_service_restarts_in_parent_after_fork():
204204
"""A PeriodicService with autorestart=False must keep running in the parent
205205
process after a fork. The flag means 'do not restart in the child', not
@@ -402,7 +402,7 @@ def _get_native_thread_name():
402402
return None
403403

404404

405-
@pytest.mark.subprocess()
405+
@pytest.mark.subprocess(env={"PYTHONWARNINGS": "ignore::DeprecationWarning:os"})
406406
def test_periodic_thread_stop_without_join_forksafe():
407407
"""
408408
Dropping a PeriodicThread that was stop()'d without join() in a forked child

tests/internal/test_products.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def test_product_manager_start():
103103
assert a.started
104104

105105

106-
@pytest.mark.subprocess()
106+
@pytest.mark.subprocess(env={"PYTHONWARNINGS": "ignore::DeprecationWarning:os"})
107107
def test_product_manager_restart():
108108
import os
109109

tests/internal/test_subscribers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
@pytest.fixture(autouse=True)
2323
def reset_event_hub():
2424
"""Reset event hub after each test to prevent listener leakage between tests."""
25+
core._reset_context()
2526
yield
2627
event_hub.reset()
2728
called.clear()
29+
core._reset_context()
2830

2931

3032
@dataclass

0 commit comments

Comments
 (0)