Skip to content

Commit a0dfa6b

Browse files
authored
Fix recursively transforming collections to JSON (#308)
- and add testing for it
1 parent 0e4a327 commit a0dfa6b

File tree

10 files changed

+104
-29
lines changed

10 files changed

+104
-29
lines changed

Diff for: pyslurm/core/job/job.pyx

+3-1
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,9 @@ cdef class Job:
295295
Returns:
296296
(dict): Job information as dict
297297
"""
298-
return instance_to_dict(self)
298+
cdef dict out = instance_to_dict(self)
299+
out["steps"] = self.steps.to_dict()
300+
return out
299301

300302
def send_signal(self, signal, steps="children", hurry=False):
301303
"""Send a signal to a running Job.

Diff for: pyslurm/core/job/step.pyx

+10-1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ cdef class JobSteps(dict):
143143
cdef JobSteps steps = JobSteps()
144144
return steps._load_data(slurm.NO_VAL, slurm.SHOW_ALL)
145145

146+
def to_dict(self):
147+
return xcollections.dict_recursive(self)
148+
146149

147150
cdef class JobStep:
148151

@@ -329,7 +332,13 @@ cdef class JobStep:
329332
Returns:
330333
(dict): JobStep information as dict
331334
"""
332-
return instance_to_dict(self)
335+
cdef dict out = instance_to_dict(self)
336+
337+
dist = self.distribution
338+
if dist:
339+
out["distribution"] = dist.to_dict()
340+
341+
return out
333342

334343
@property
335344
def id(self):

Diff for: pyslurm/db/job.pyx

+2-5
Original file line numberDiff line numberDiff line change
@@ -524,11 +524,8 @@ cdef class Job:
524524

525525
if self.stats:
526526
out["stats"] = self.stats.to_dict()
527-
528-
steps = out.pop("steps", {})
529-
out["steps"] = {}
530-
for step_id, step in steps.items():
531-
out["steps"][step_id] = step.to_dict()
527+
if self.steps:
528+
out["steps"] = self.steps.to_dict()
532529

533530
return out
534531

Diff for: pyslurm/db/step.pyx

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ from pyslurm.core.error import RPCError
2727
from typing import Union
2828
from pyslurm.utils.uint import *
2929
from pyslurm.utils.ctime import _raw_time
30+
from pyslurm import xcollections
3031
from pyslurm.utils.helpers import (
3132
gid_to_name,
3233
uid_to_name,
@@ -43,6 +44,9 @@ cdef class JobSteps(dict):
4344
data = super().__repr__()
4445
return f'pyslurm.db.{self.__class__.__name__}({data})'
4546

47+
def to_dict(self):
48+
return xcollections.dict_recursive(self)
49+
4650

4751
cdef class JobStep:
4852

Diff for: pyslurm/xcollections.pyx

+3
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,9 @@ cdef class MultiClusterMap:
447447
Returns:
448448
(str): JSON formatted string from `json.dumps()`
449449
"""
450+
if not self.data:
451+
return '{}'
452+
450453
data = multi_dict_recursive(self)
451454
if multi_cluster:
452455
return json.dumps(data)

Diff for: tests/integration/test_db_job.py

+15
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import pyslurm
2525
import time
2626
import util
27+
import json
2728

2829

2930
# TODO: Instead of submitting new Jobs and waiting to test Database API
@@ -55,6 +56,20 @@ def test_parse_all(submit_job):
5556
assert job_dict["steps"]
5657

5758

59+
def test_to_json(submit_job):
60+
job = submit_job()
61+
util.wait()
62+
63+
jfilter = pyslurm.db.JobFilter(ids=[job.id])
64+
jobs = pyslurm.db.Jobs.load(jfilter)
65+
66+
json_data = jobs.to_json()
67+
dict_data = json.loads(json_data)
68+
assert dict_data
69+
assert json_data
70+
assert len(dict_data) == 1
71+
72+
5873
def test_modify(submit_job):
5974
job = submit_job()
6075
util.wait(5)

Diff for: tests/integration/test_job.py

+37-7
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import time
2424
import pytest
2525
import pyslurm
26+
import json
2627
import util
2728
from util import create_simple_job_desc
2829
from pyslurm import (
@@ -64,34 +65,34 @@ def test_cancel(submit_job):
6465
job = submit_job()
6566
job.cancel()
6667
# make sure the job is actually cancelled
67-
time.sleep(util.WAIT_SECS_SLURMCTLD)
68+
util.wait()
6869
assert Job.load(job.id).state == "CANCELLED"
6970

7071

7172
def test_send_signal(submit_job):
7273
job = submit_job()
7374

74-
time.sleep(util.WAIT_SECS_SLURMCTLD)
75+
util.wait()
7576
assert Job.load(job.id).state == "RUNNING"
7677

7778
# Send a SIGKILL (basically cancelling the Job)
7879
job.send_signal(9)
7980

8081
# make sure the job is actually cancelled
81-
time.sleep(util.WAIT_SECS_SLURMCTLD)
82+
util.wait()
8283
assert Job.load(job.id).state == "CANCELLED"
8384

8485

8586
def test_suspend_unsuspend(submit_job):
8687
job = submit_job()
8788

88-
time.sleep(util.WAIT_SECS_SLURMCTLD)
89+
util.wait()
8990
job.suspend()
9091
assert Job.load(job.id).state == "SUSPENDED"
9192

9293
job.unsuspend()
9394
# make sure the job is actually running again
94-
time.sleep(util.WAIT_SECS_SLURMCTLD)
95+
util.wait()
9596
assert Job.load(job.id).state == "RUNNING"
9697

9798

@@ -121,7 +122,7 @@ def test_requeue(submit_job):
121122

122123
assert job.requeue_count == 0
123124

124-
time.sleep(util.WAIT_SECS_SLURMCTLD)
125+
util.wait()
125126
job.requeue()
126127
job = Job.load(job.id)
127128

@@ -130,7 +131,7 @@ def test_requeue(submit_job):
130131

131132
def test_notify(submit_job):
132133
job = submit_job()
133-
time.sleep(util.WAIT_SECS_SLURMCTLD)
134+
util.wait()
134135

135136
# Could check the logfile, but we just assume for now
136137
# that when this function raises no Exception, everything worked.
@@ -155,6 +156,35 @@ def test_get_job_queue(submit_job):
155156
assert isinstance(jobs[job.id], Job)
156157

157158

159+
def test_load_steps(submit_job):
160+
job_list = [submit_job() for i in range(3)]
161+
util.wait()
162+
163+
jobs = Jobs.load()
164+
jobs.load_steps()
165+
166+
for _job in job_list:
167+
job = jobs[_job.id]
168+
assert job.state == "RUNNING"
169+
assert job.steps
170+
assert isinstance(job.steps, pyslurm.JobSteps)
171+
assert job.steps.get("batch")
172+
173+
174+
def test_to_json(submit_job):
175+
job_list = [submit_job() for i in range(3)]
176+
util.wait()
177+
178+
jobs = Jobs.load()
179+
jobs.load_steps()
180+
181+
json_data = jobs.to_json()
182+
dict_data = json.loads(json_data)
183+
assert dict_data
184+
assert json_data
185+
assert len(dict_data) >= 3
186+
187+
158188
def test_get_resource_layout_per_node(submit_job):
159189
# TODO
160190
assert True

Diff for: tests/integration/test_job_steps.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def test_load(submit_job):
5757

5858
# Load the step info, waiting one second to make sure the Step
5959
# actually exists.
60-
time.sleep(util.WAIT_SECS_SLURMCTLD)
60+
util.wait()
6161
step = JobStep.load(job.id, "batch")
6262

6363
assert step.id == "batch"
@@ -101,7 +101,7 @@ def test_load(submit_job):
101101
def test_collection(submit_job):
102102
job = submit_job(script=create_job_script_multi_step())
103103

104-
time.sleep(util.WAIT_SECS_SLURMCTLD)
104+
util.wait()
105105
steps = JobSteps.load(job)
106106

107107
assert steps
@@ -115,7 +115,7 @@ def test_collection(submit_job):
115115
def test_cancel(submit_job):
116116
job = submit_job(script=create_job_script_multi_step())
117117

118-
time.sleep(util.WAIT_SECS_SLURMCTLD)
118+
util.wait()
119119
steps = JobSteps.load(job)
120120
assert len(steps) == 3
121121
assert ("batch" in steps and
@@ -124,7 +124,7 @@ def test_cancel(submit_job):
124124

125125
steps[0].cancel()
126126

127-
time.sleep(util.WAIT_SECS_SLURMCTLD)
127+
util.wait()
128128
steps = JobSteps.load(job)
129129
assert len(steps) == 2
130130
assert ("batch" in steps and
@@ -135,7 +135,7 @@ def test_modify(submit_job):
135135
steps = "srun -t 20 sleep 100"
136136
job = submit_job(script=create_job_script_multi_step(steps))
137137

138-
time.sleep(util.WAIT_SECS_SLURMCTLD)
138+
util.wait()
139139
step = JobStep.load(job, 0)
140140
assert step.time_limit == 20
141141

@@ -150,7 +150,7 @@ def test_send_signal(submit_job):
150150
steps = "srun -t 10 sleep 100"
151151
job = submit_job(script=create_job_script_multi_step(steps))
152152

153-
time.sleep(util.WAIT_SECS_SLURMCTLD)
153+
util.wait()
154154
step = JobStep.load(job, 0)
155155
assert step.state == "RUNNING"
156156

@@ -159,7 +159,7 @@ def test_send_signal(submit_job):
159159

160160
# Make sure the job is actually cancelled.
161161
# If a RPCError is raised, this means the Step got cancelled.
162-
time.sleep(util.WAIT_SECS_SLURMCTLD)
162+
util.wait()
163163
with pytest.raises(RPCError):
164164
step = JobStep.load(job, 0)
165165

@@ -173,5 +173,5 @@ def test_load_with_wrong_step_id(submit_job):
173173

174174
def test_parse_all(submit_job):
175175
job = submit_job()
176-
time.sleep(util.WAIT_SECS_SLURMCTLD)
176+
util.wait()
177177
JobStep.load(job, "batch").to_dict()

Diff for: tests/integration/test_node.py

+11-7
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,9 @@
2020
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2121
"""test_node.py - Test the node api functions."""
2222

23-
import sys
24-
import time
2523
import pytest
2624
import pyslurm
27-
import os
25+
import json
2826
from pyslurm import Node, Nodes, RPCError
2927

3028

@@ -51,10 +49,6 @@ def test_create():
5149
Node("testhostpyslurm2").create("idle")
5250

5351

54-
# def test_delete():
55-
# node = Node("testhost1").delete()
56-
57-
5852
def test_modify():
5953
_, node = Nodes.load().popitem()
6054

@@ -71,3 +65,13 @@ def test_modify():
7165
def test_parse_all():
7266
_, node = Nodes.load().popitem()
7367
assert node.to_dict()
68+
69+
70+
def test_to_json():
71+
nodes = Nodes.load()
72+
json_data = nodes.to_json()
73+
dict_data = json.loads(json_data)
74+
75+
assert dict_data
76+
assert len(dict_data) >= 1
77+
assert json_data

Diff for: tests/integration/test_partition.py

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import pytest
2525
import pyslurm
26+
import json
2627
import util
2728
from pyslurm import Partition, Partitions, RPCError
2829

@@ -72,6 +73,16 @@ def test_parse_all():
7273
assert part.to_dict()
7374

7475

76+
def test_to_json():
77+
parts = Partitions.load()
78+
json_data = parts.to_json()
79+
dict_data = json.loads(json_data)
80+
81+
assert dict_data
82+
assert len(dict_data) >= 1
83+
assert json_data
84+
85+
7586
def test_reload():
7687
_partnames = [util.randstr() for i in range(3)]
7788
_tmp_parts = Partitions(_partnames)

0 commit comments

Comments
 (0)