Skip to content

Commit 78843dc

Browse files
authored
tests: Move ray under toxgen (#4810)
### Description - moving the Ray test suite under toxgen - removed `@pytest.mark.forked` and instead: - added a fixture to ensure ray is shut down after each test - gave each test that was looking at logs its own temporary directory to ensure logs don't get mixed up - we were only testing Ray on Python 3.10 and 3.11 -- fixing that #### Issues Ref #4506 Closes #4811 #### Reminders - Please add tests to validate your changes, and lint your code using `tox -e linters`. - Add GH Issue ID _&_ Linear ID (if applicable) - PR title should use [conventional commit](https://develop.sentry.dev/engineering-practices/commit-messages/#type) style (`feat:`, `fix:`, `ref:`, `meta:`) - For external contributors: [CONTRIBUTING.md](https://github.com/getsentry/sentry-python/blob/master/CONTRIBUTING.md), [Sentry SDK development docs](https://develop.sentry.dev/sdk/), [Discord community](https://discord.gg/Ww9hbqr)
1 parent e110151 commit 78843dc

File tree

6 files changed

+105
-81
lines changed

6 files changed

+105
-81
lines changed

.github/workflows/test-integrations-tasks.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
strategy:
3030
fail-fast: false
3131
matrix:
32-
python-version: ["3.7","3.10","3.11","3.12","3.13"]
32+
python-version: ["3.7","3.12","3.13"]
3333
# python3.6 reached EOL and is no longer being supported on
3434
# new versions of hosted runners on Github Actions
3535
# ubuntu-20.04 is the last version that supported python3.6

scripts/populate_tox/config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@
253253
"py3.8": ["taskgroup==0.0.0a4"],
254254
},
255255
},
256+
"ray": {
257+
"package": "ray",
258+
"python": ">=3.9",
259+
"num_versions": 2,
260+
},
256261
"redis_py_cluster_legacy": {
257262
"package": "redis-py-cluster",
258263
},

scripts/populate_tox/populate_tox.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@
7070
# of these from the IGNORE list
7171
"gcp",
7272
"httpx",
73-
"ray",
7473
"redis",
7574
"requests",
7675
"rq",

scripts/populate_tox/tox.jinja

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,6 @@ envlist =
6161
# OpenTelemetry Experimental (POTel)
6262
{py3.8,py3.9,py3.10,py3.11,py3.12,py3.13}-potel
6363

64-
# Ray
65-
{py3.10,py3.11}-ray-v{2.34}
66-
{py3.10,py3.11}-ray-latest
67-
6864
# Redis
6965
{py3.6,py3.8}-redis-v{3}
7066
{py3.7,py3.8,py3.11}-redis-v{4}
@@ -168,10 +164,6 @@ deps =
168164
# OpenTelemetry Experimental (POTel)
169165
potel: -e .[opentelemetry-experimental]
170166
171-
# Ray
172-
ray-v2.34: ray~=2.34.0
173-
ray-latest: ray
174-
175167
# Redis
176168
redis: fakeredis!=1.7.4
177169
redis: pytest<8.0.0

tests/integrations/ray/test_ray.py

Lines changed: 92 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import json
22
import os
33
import pytest
4+
import shutil
5+
import uuid
46

57
import ray
68

@@ -10,6 +12,12 @@
1012
from tests.conftest import TestTransport
1113

1214

15+
@pytest.fixture(autouse=True)
16+
def shutdown_ray(tmpdir):
17+
yield
18+
ray.shutdown()
19+
20+
1321
class RayTestTransport(TestTransport):
1422
def __init__(self):
1523
self.envelopes = []
@@ -20,9 +28,6 @@ def capture_envelope(self, envelope: Envelope) -> None:
2028

2129

2230
class RayLoggingTransport(TestTransport):
23-
def __init__(self):
24-
super().__init__()
25-
2631
def capture_envelope(self, envelope: Envelope) -> None:
2732
print(envelope.serialize().decode("utf-8", "replace"))
2833

@@ -39,13 +44,24 @@ def setup_sentry(transport=None):
3944
)
4045

4146

42-
def read_error_from_log(job_id):
43-
log_dir = "/tmp/ray/session_latest/logs/"
47+
def read_error_from_log(job_id, ray_temp_dir):
48+
# Find the actual session directory that Ray created
49+
session_dirs = [d for d in os.listdir(ray_temp_dir) if d.startswith("session_")]
50+
if not session_dirs:
51+
raise FileNotFoundError(f"No session directory found in {ray_temp_dir}")
52+
53+
session_dir = os.path.join(ray_temp_dir, session_dirs[0])
54+
log_dir = os.path.join(session_dir, "logs")
55+
56+
if not os.path.exists(log_dir):
57+
raise FileNotFoundError(f"No logs directory found at {log_dir}")
58+
4459
log_file = [
4560
f
4661
for f in os.listdir(log_dir)
4762
if "worker" in f and job_id in f and f.endswith(".out")
4863
][0]
64+
4965
with open(os.path.join(log_dir, log_file), "r") as file:
5066
lines = file.readlines()
5167

@@ -58,7 +74,6 @@ def read_error_from_log(job_id):
5874
return error
5975

6076

61-
@pytest.mark.forked
6277
@pytest.mark.parametrize(
6378
"task_options", [{}, {"num_cpus": 0, "memory": 1024 * 1024 * 10}]
6479
)
@@ -124,40 +139,47 @@ def example_task():
124139
)
125140

126141

127-
@pytest.mark.forked
128142
def test_errors_in_ray_tasks():
129143
setup_sentry_with_logging_transport()
130144

131-
ray.init(
132-
runtime_env={
133-
"worker_process_setup_hook": setup_sentry_with_logging_transport,
134-
"working_dir": "./",
135-
}
136-
)
145+
ray_temp_dir = os.path.join("/tmp", f"ray_test_{uuid.uuid4().hex[:8]}")
146+
os.makedirs(ray_temp_dir, exist_ok=True)
137147

138-
# Setup ray task
139-
@ray.remote
140-
def example_task():
141-
1 / 0
148+
try:
149+
ray.init(
150+
runtime_env={
151+
"worker_process_setup_hook": setup_sentry_with_logging_transport,
152+
"working_dir": "./",
153+
},
154+
_temp_dir=ray_temp_dir,
155+
)
142156

143-
with sentry_sdk.start_transaction(op="task", name="ray test transaction"):
144-
with pytest.raises(ZeroDivisionError):
145-
future = example_task.remote()
146-
ray.get(future)
157+
# Setup ray task
158+
@ray.remote
159+
def example_task():
160+
1 / 0
147161

148-
job_id = future.job_id().hex()
149-
error = read_error_from_log(job_id)
162+
with sentry_sdk.start_transaction(op="task", name="ray test transaction"):
163+
with pytest.raises(ZeroDivisionError):
164+
future = example_task.remote()
165+
ray.get(future)
150166

151-
assert error["level"] == "error"
152-
assert (
153-
error["transaction"]
154-
== "tests.integrations.ray.test_ray.test_errors_in_ray_tasks.<locals>.example_task"
155-
)
156-
assert error["exception"]["values"][0]["mechanism"]["type"] == "ray"
157-
assert not error["exception"]["values"][0]["mechanism"]["handled"]
167+
job_id = future.job_id().hex()
168+
error = read_error_from_log(job_id, ray_temp_dir)
169+
170+
assert error["level"] == "error"
171+
assert (
172+
error["transaction"]
173+
== "tests.integrations.ray.test_ray.test_errors_in_ray_tasks.<locals>.example_task"
174+
)
175+
assert error["exception"]["values"][0]["mechanism"]["type"] == "ray"
176+
assert not error["exception"]["values"][0]["mechanism"]["handled"]
177+
178+
finally:
179+
if os.path.exists(ray_temp_dir):
180+
shutil.rmtree(ray_temp_dir, ignore_errors=True)
158181

159182

160-
@pytest.mark.forked
161183
def test_tracing_in_ray_actors():
162184
setup_sentry()
163185

@@ -194,37 +216,45 @@ def increment(self):
194216
assert worker_envelopes == []
195217

196218

197-
@pytest.mark.forked
198219
def test_errors_in_ray_actors():
199220
setup_sentry_with_logging_transport()
200221

201-
ray.init(
202-
runtime_env={
203-
"worker_process_setup_hook": setup_sentry_with_logging_transport,
204-
"working_dir": "./",
205-
}
206-
)
207-
208-
# Setup ray actor
209-
@ray.remote
210-
class Counter:
211-
def __init__(self):
212-
self.n = 0
213-
214-
def increment(self):
215-
with sentry_sdk.start_span(op="task", name="example actor execution"):
216-
1 / 0
217-
218-
return sentry_sdk.get_client().transport.envelopes
219-
220-
with sentry_sdk.start_transaction(op="task", name="ray test transaction"):
221-
with pytest.raises(ZeroDivisionError):
222-
counter = Counter.remote()
223-
future = counter.increment.remote()
224-
ray.get(future)
225-
226-
job_id = future.job_id().hex()
227-
error = read_error_from_log(job_id)
228-
229-
# We do not capture errors in ray actors yet
230-
assert error is None
222+
ray_temp_dir = os.path.join("/tmp", f"ray_test_{uuid.uuid4().hex[:8]}")
223+
os.makedirs(ray_temp_dir, exist_ok=True)
224+
225+
try:
226+
ray.init(
227+
runtime_env={
228+
"worker_process_setup_hook": setup_sentry_with_logging_transport,
229+
"working_dir": "./",
230+
},
231+
_temp_dir=ray_temp_dir,
232+
)
233+
234+
# Setup ray actor
235+
@ray.remote
236+
class Counter:
237+
def __init__(self):
238+
self.n = 0
239+
240+
def increment(self):
241+
with sentry_sdk.start_span(op="task", name="example actor execution"):
242+
1 / 0
243+
244+
return sentry_sdk.get_client().transport.envelopes
245+
246+
with sentry_sdk.start_transaction(op="task", name="ray test transaction"):
247+
with pytest.raises(ZeroDivisionError):
248+
counter = Counter.remote()
249+
future = counter.increment.remote()
250+
ray.get(future)
251+
252+
job_id = future.job_id().hex()
253+
error = read_error_from_log(job_id, ray_temp_dir)
254+
255+
# We do not capture errors in ray actors yet
256+
assert error is None
257+
258+
finally:
259+
if os.path.exists(ray_temp_dir):
260+
shutil.rmtree(ray_temp_dir, ignore_errors=True)

tox.ini

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# The file (and all resulting CI YAMLs) then need to be regenerated via
1111
# "scripts/generate-test-files.sh".
1212
#
13-
# Last generated: 2025-09-18T08:05:49.500134+00:00
13+
# Last generated: 2025-09-18T10:26:22.484602+00:00
1414

1515
[tox]
1616
requires =
@@ -61,10 +61,6 @@ envlist =
6161
# OpenTelemetry Experimental (POTel)
6262
{py3.8,py3.9,py3.10,py3.11,py3.12,py3.13}-potel
6363

64-
# Ray
65-
{py3.10,py3.11}-ray-v{2.34}
66-
{py3.10,py3.11}-ray-latest
67-
6864
# Redis
6965
{py3.6,py3.8}-redis-v{3}
7066
{py3.7,py3.8,py3.11}-redis-v{4}
@@ -233,6 +229,9 @@ envlist =
233229
{py3.6,py3.7}-huey-v2.3.2
234230
{py3.6,py3.11,py3.12}-huey-v2.5.3
235231

232+
{py3.9,py3.10}-ray-v2.7.2
233+
{py3.9,py3.12,py3.13}-ray-v2.49.1
234+
236235
{py3.8,py3.9}-spark-v3.0.3
237236
{py3.8,py3.10,py3.11}-spark-v3.5.6
238237
{py3.9,py3.12,py3.13}-spark-v4.0.1
@@ -396,10 +395,6 @@ deps =
396395
# OpenTelemetry Experimental (POTel)
397396
potel: -e .[opentelemetry-experimental]
398397

399-
# Ray
400-
ray-v2.34: ray~=2.34.0
401-
ray-latest: ray
402-
403398
# Redis
404399
redis: fakeredis!=1.7.4
405400
redis: pytest<8.0.0
@@ -631,6 +626,9 @@ deps =
631626
huey-v2.3.2: huey==2.3.2
632627
huey-v2.5.3: huey==2.5.3
633628

629+
ray-v2.7.2: ray==2.7.2
630+
ray-v2.49.1: ray==2.49.1
631+
634632
spark-v3.0.3: pyspark==3.0.3
635633
spark-v3.5.6: pyspark==3.5.6
636634
spark-v4.0.1: pyspark==4.0.1

0 commit comments

Comments
 (0)