Skip to content

Commit 485722b

Browse files
authored
Implement DB Associations API and a little refactoring. (#304)
- All collections inherit from `list` instead of `dict` now (this change is mostly motivated by multi-cluster support that will probably be added in the future) - Added work-in-progress API for database `Associations` - renamed `JobSearchFilter` to `JobFilter`
1 parent 3a603e1 commit 485722b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1541
-317
lines changed

CHANGELOG.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
- failed_node
1919
- Now possible to initialize a pyslurm.db.Jobs collection with existing job
2020
ids or pyslurm.db.Job objects
21+
- Added `as_dict` function to all Collections
2122

2223
### Fixed
2324

@@ -26,6 +27,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2627
- no start/end time was specified
2728
- the Job was older than a day
2829

30+
### Changed
31+
32+
- All Collections (like [pyslurm.Jobs](https://pyslurm.github.io/23.2/reference/job/#pyslurm.Jobs)) inherit from `list` now instead of `dict`
33+
- `JobSearchFilter` has been renamed to `JobFilter`
34+
2935
## [23.2.1](https://github.com/PySlurm/pyslurm/releases/tag/v23.2.1) - 2023-05-18
3036

3137
### Added
@@ -40,7 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4046
- [pyslurm.db.Job](https://pyslurm.github.io/23.2/reference/db/job/#pyslurm.db.Job)
4147
- [pyslurm.db.Jobs](https://pyslurm.github.io/23.2/reference/db/job/#pyslurm.db.Jobs)
4248
- [pyslurm.db.JobStep](https://pyslurm.github.io/23.2/reference/db/jobstep/#pyslurm.db.JobStep)
43-
- [pyslurm.db.JobSearchFilter](https://pyslurm.github.io/23.2/reference/db/jobsearchfilter/#pyslurm.db.JobSearchFilter)
49+
- [pyslurm.db.JobFilter](https://pyslurm.github.io/23.2/reference/db/jobsearchfilter/#pyslurm.db.JobFilter)
4450
- Classes to interact with the Node API
4551
- [pyslurm.Node](https://pyslurm.github.io/23.2/reference/node/#pyslurm.Node)
4652
- [pyslurm.Nodes](https://pyslurm.github.io/23.2/reference/node/#pyslurm.Nodes)
@@ -49,7 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4955
- [pyslurm.RPCError](https://pyslurm.github.io/23.2/reference/exceptions/#pyslurm.RPCError)
5056
- [Utility Functions](https://pyslurm.github.io/23.2/reference/utilities/#pyslurm.utils)
5157

52-
### Changes
58+
### Changed
5359

5460
- Completely overhaul the documentation, switch to mkdocs
5561
- Rework the tests: Split them into unit and integration tests

docs/reference/db/jobfilter.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
title: JobFilter
3+
---
4+
5+
::: pyslurm.db.JobFilter
6+
handler: python

docs/reference/db/jobsearchfilter.md

Lines changed: 0 additions & 6 deletions
This file was deleted.

docs/reference/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The `pyslurm` package is a wrapper around the Slurm C-API
3737
* [pyslurm.db.Job][]
3838
* [pyslurm.db.JobStep][]
3939
* [pyslurm.db.Jobs][]
40-
* [pyslurm.db.JobSearchFilter][]
40+
* [pyslurm.db.JobFilter][]
4141
* Node API
4242
* [pyslurm.Node][]
4343
* [pyslurm.Nodes][]

pyslurm/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@
99

1010
sys.setdlopenflags(sys.getdlopenflags() | ctypes.RTLD_GLOBAL)
1111

12+
# Initialize slurm api
13+
from pyslurm.api import slurm_init, slurm_fini
14+
slurm_init()
15+
1216
from .pyslurm import *
1317
from .__version__ import __version__
1418

15-
from pyslurm import utils
1619
from pyslurm import db
20+
from pyslurm import utils
1721
from pyslurm import constants
1822

1923
from pyslurm.core.job import (
@@ -32,10 +36,6 @@
3236
)
3337
from pyslurm.core import slurmctld
3438

35-
# Initialize slurm api
36-
from pyslurm.api import slurm_init, slurm_fini
37-
slurm_init()
38-
3939

4040
def version():
4141
return __version__

pyslurm/core/job/job.pxd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ from pyslurm.slurm cimport (
6767
)
6868

6969

70-
cdef class Jobs(dict):
70+
cdef class Jobs(list):
7171
"""A collection of [pyslurm.Job][] objects.
7272
7373
Args:

pyslurm/core/job/job.pyx

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ from typing import Union
3434
from pyslurm.utils import cstr, ctime
3535
from pyslurm.utils.uint import *
3636
from pyslurm.core.job.util import *
37+
from pyslurm.db.cluster import LOCAL_CLUSTER
3738
from pyslurm.core.error import (
3839
RPCError,
3940
verify_rpc,
@@ -47,12 +48,14 @@ from pyslurm.utils.helpers import (
4748
_getgrall_to_dict,
4849
_getpwall_to_dict,
4950
instance_to_dict,
51+
collection_to_dict,
52+
group_collection_by_cluster,
5053
_sum_prop,
5154
_get_exit_code,
5255
)
5356

5457

55-
cdef class Jobs(dict):
58+
cdef class Jobs(list):
5659

5760
def __cinit__(self):
5861
self.info = NULL
@@ -63,14 +66,37 @@ cdef class Jobs(dict):
6366
def __init__(self, jobs=None, frozen=False):
6467
self.frozen = frozen
6568

66-
if isinstance(jobs, dict):
67-
self.update(jobs)
68-
elif jobs is not None:
69+
if isinstance(jobs, list):
6970
for job in jobs:
7071
if isinstance(job, int):
71-
self[job] = Job(job)
72+
self.append(Job(job))
7273
else:
73-
self[job.id] = job
74+
self.append(job)
75+
elif isinstance(jobs, str):
76+
joblist = jobs.split(",")
77+
self.extend([Job(int(job)) for job in joblist])
78+
elif isinstance(jobs, dict):
79+
self.extend([job for job in jobs.values()])
80+
elif jobs is not None:
81+
raise TypeError("Invalid Type: {type(jobs)}")
82+
83+
def as_dict(self, recursive=False):
84+
"""Convert the collection data to a dict.
85+
86+
Args:
87+
recursive (bool, optional):
88+
By default, the objects will not be converted to a dict. If
89+
this is set to `True`, then additionally all objects are
90+
converted to dicts.
91+
92+
Returns:
93+
(dict): Collection as a dict.
94+
"""
95+
col = collection_to_dict(self, identifier=Job.id, recursive=recursive)
96+
return col.get(LOCAL_CLUSTER, {})
97+
98+
def group_by_cluster(self):
99+
return group_collection_by_cluster(self)
74100

75101
@staticmethod
76102
def load(preload_passwd_info=False, frozen=False):
@@ -124,7 +150,7 @@ cdef class Jobs(dict):
124150
job.passwd = passwd
125151
job.groups = groups
126152

127-
jobs[job.id] = job
153+
jobs.append(job)
128154

129155
# At this point we memcpy'd all the memory for the Jobs. Setting this
130156
# to 0 will prevent the slurm job free function to deallocate the
@@ -143,28 +169,34 @@ cdef class Jobs(dict):
143169
Raises:
144170
RPCError: When getting the Jobs from the slurmctld failed.
145171
"""
146-
cdef Jobs reloaded_jobs = Jobs.load()
172+
cdef:
173+
Jobs reloaded_jobs
174+
Jobs new_jobs = Jobs()
175+
dict self_dict
147176

148-
for jid in list(self.keys()):
177+
if not self:
178+
return self
179+
180+
reloaded_jobs = Jobs.load().as_dict()
181+
for idx, jid in enumerate(self):
149182
if jid in reloaded_jobs:
150183
# Put the new data in.
151-
self[jid] = reloaded_jobs[jid]
152-
elif not self.frozen:
153-
# Remove this instance from the current collection, as the Job
154-
# doesn't exist anymore.
155-
del self[jid]
184+
new_jobs.append(reloaded_jobs[jid])
156185

157186
if not self.frozen:
187+
self_dict = self.as_dict()
158188
for jid in reloaded_jobs:
159-
if jid not in self:
160-
self[jid] = reloaded_jobs[jid]
189+
if jid not in self_dict:
190+
new_jobs.append(reloaded_jobs[jid])
161191

192+
self.clear()
193+
self.extend(new_jobs)
162194
return self
163195

164196
def load_steps(self):
165197
"""Load all Job steps for this collection of Jobs.
166198
167-
This function fills in the "steps" attribute for all Jobs in the
199+
This function fills in the `steps` attribute for all Jobs in the
168200
collection.
169201
170202
!!! note
@@ -175,21 +207,16 @@ cdef class Jobs(dict):
175207
RPCError: When retrieving the Job information for all the Steps
176208
failed.
177209
"""
178-
cdef dict step_info = JobSteps.load_all()
210+
cdef dict steps = JobSteps.load().as_dict()
179211

180-
for jid in self:
212+
for idx, job in enumerate(self):
181213
# Ignore any Steps from Jobs which do not exist in this
182214
# collection.
183-
if jid in step_info:
184-
self[jid].steps = step_info[jid]
185-
186-
def as_list(self):
187-
"""Format the information as list of Job objects.
188-
189-
Returns:
190-
(list[pyslurm.Job]): List of Job objects
191-
"""
192-
return list(self.values())
215+
jid = job.id
216+
if jid in steps:
217+
job_steps = self[idx].steps
218+
job_steps.clear()
219+
job_steps.extend(steps[jid].values())
193220

194221
@property
195222
def memory(self):
@@ -218,6 +245,7 @@ cdef class Job:
218245
self.ptr.job_id = job_id
219246
self.passwd = {}
220247
self.groups = {}
248+
cstr.fmalloc(&self.ptr.cluster, LOCAL_CLUSTER)
221249
self.steps = JobSteps.__new__(JobSteps)
222250

223251
def _alloc_impl(self):
@@ -234,7 +262,9 @@ cdef class Job:
234262
self._dealloc_impl()
235263

236264
def __eq__(self, other):
237-
return isinstance(other, Job) and self.id == other.id
265+
if isinstance(other, Job):
266+
return self.id == other.id and self.cluster == other.cluster
267+
return NotImplemented
238268

239269
@staticmethod
240270
def load(job_id):
@@ -278,7 +308,7 @@ cdef class Job:
278308
if not slurm.IS_JOB_PENDING(wrap.ptr):
279309
# Just ignore if the steps couldn't be loaded here.
280310
try:
281-
wrap.steps = JobSteps._load(wrap)
311+
wrap.steps = JobSteps._load_single(wrap)
282312
except RPCError:
283313
pass
284314
else:

pyslurm/core/job/step.pxd

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ from pyslurm.utils.ctime cimport time_t
4949
from pyslurm.core.job.task_dist cimport TaskDistribution
5050

5151

52-
cdef class JobSteps(dict):
52+
cdef class JobSteps(list):
5353
"""A collection of [pyslurm.JobStep][] objects for a given Job.
5454
5555
Args:
@@ -64,11 +64,12 @@ cdef class JobSteps(dict):
6464
cdef:
6565
job_step_info_response_msg_t *info
6666
job_step_info_t tmp_info
67+
_job_id
6768

6869
@staticmethod
69-
cdef JobSteps _load(Job job)
70+
cdef JobSteps _load_single(Job job)
7071

71-
cdef dict _get_info(self, uint32_t job_id, int flags)
72+
cdef _load_data(self, uint32_t job_id, int flags)
7273

7374

7475
cdef class JobStep:

0 commit comments

Comments
 (0)