1818from reframe .core .exceptions import JobError , JobNotStartedError
1919from reframe .core .launchers import JobLauncher
2020from reframe .core .logging import getlogger , DEBUG2
21+ from reframe .core .meta import RegressionTestMeta
22+
23+
24+ class JobMeta (RegressionTestMeta , abc .ABCMeta ):
25+ '''Job metaclass.'''
2126
2227
2328class JobScheduler (abc .ABC ):
@@ -112,7 +117,7 @@ def log(self, message, level=DEBUG2):
112117 getlogger ().log (level , f'[S] { self .registered_name } : { message } ' )
113118
114119
115- class Job (jsonext .JSONSerializable ):
120+ class Job (jsonext .JSONSerializable , metaclass = JobMeta ):
116121 '''A job descriptor.
117122
118123 A job descriptor is created by the framework after the "setup" phase and
@@ -123,19 +128,120 @@ class Job(jsonext.JSONSerializable):
123128
124129 '''
125130
126- num_tasks = fields .TypedField (int )
127- num_tasks_per_node = fields .TypedField (int , type (None ))
128- num_tasks_per_core = fields .TypedField (int , type (None ))
129- num_tasks_per_socket = fields .TypedField (int , type (None ))
130- num_cpus_per_task = fields .TypedField (int , type (None ))
131- use_smt = fields .TypedField (bool , type (None ))
132- time_limit = fields .TimerField (type (None ))
131+ #: Number of tasks for this job.
132+ #:
133+ #: :type: integral
134+ #: :default: ``1``
135+ #:
136+ #: .. note::
137+ #: This attribute is set by the framework just before submitting the job
138+ #: based on the test information.
139+ #:
140+ #: .. versionadded:: 3.11.0
141+ num_tasks = variable (int , value = 1 )
142+
143+ #: Number of tasks per node for this job.
144+ #:
145+ #: :type: integral or :class:`NoneType`
146+ #: :default: ``None``
147+ #:
148+ #: .. note::
149+ #: This attribute is set by the framework just before submitting the job
150+ #: based on the test information.
151+ #:
152+ #: .. versionadded:: 3.11.0
153+ num_tasks_per_node = variable (int , type (None ), value = None )
154+
155+ #: Number of tasks per core for this job.
156+ #:
157+ #: :type: integral or :class:`NoneType`
158+ #: :default: ``None``
159+ #:
160+ #: .. note::
161+ #: This attribute is set by the framework just before submitting the job
162+ #: based on the test information.
163+ #:
164+ #: .. versionadded:: 3.11.0
165+ num_tasks_per_core = variable (int , type (None ), value = None )
166+
167+ #: Number of tasks per socket for this job.
168+ #:
169+ #: :type: integral or :class:`NoneType`
170+ #: :default: ``None``
171+ #:
172+ #: .. note::
173+ #: This attribute is set by the framework just before submitting the job
174+ #: based on the test information.
175+ #:
176+ #: .. versionadded:: 3.11.0
177+ num_tasks_per_socket = variable (int , type (None ), value = None )
178+
179+ #: Number of processing elements associated with each task for this job.
180+ #:
181+ #: :type: integral or :class:`NoneType`
182+ #: :default: ``None``
183+ #:
184+ #: .. note::
185+ #: This attribute is set by the framework just before submitting the job
186+ #: based on the test information.
187+ #:
188+ #: .. versionadded:: 3.11.0
189+ num_cpus_per_task = variable (int , type (None ), value = None )
190+
191+ #: Enable SMT for this job.
192+ #:
193+ #: :type: :class:`bool` or :class:`NoneType`
194+ #: :default: ``None``
195+ #:
196+ #: .. note::
197+ #: This attribute is set by the framework just before submitting the job
198+ #: based on the test information.
199+ #:
200+ #: .. versionadded:: 3.11.0
201+ use_smt = variable (bool , type (None ), value = None )
202+
203+ #: Request exclusive access on the nodes for this job.
204+ #:
205+ #: :type: :class:`bool`
206+ #: :default: ``false``
207+ #:
208+ #: .. note::
209+ #: This attribute is set by the framework just before submitting the job
210+ #: based on the test information.
211+ #:
212+ #: .. versionadded:: 3.11.0
213+ exclusive_access = variable (bool , value = False )
214+
215+ #: Time limit for this job.
216+ #:
217+ #: See :attr:`reframe.core.pipeline.RegressionTest.time_limit` for more
218+ #: details.
219+ #:
220+ #: .. note::
221+ #: This attribute is set by the framework just before submitting the job
222+ #: based on the test information.
223+ #:
224+ #: .. versionadded:: 3.11.0
225+ time_limit = variable (type (None ), field = fields .TimerField , value = None )
226+
227+ #: Maximum pending time for this job.
228+ #:
229+ #: See :attr:`reframe.core.pipeline.RegressionTest.max_pending_time` for
230+ #: more details.
231+ #:
232+ #: .. note::
233+ #: This attribute is set by the framework just before submitting the job
234+ #: based on the test information.
235+ #:
236+ #: .. versionadded:: 3.11.0
237+ max_pending_time = variable (type (None ),
238+ field = fields .TimerField , value = None )
133239
134- #: Options to be passed to the backend job scheduler.
240+ #: Arbitrary options to be passed to the backend job scheduler.
135241 #:
136242 #: :type: :class:`List[str]`
137243 #: :default: ``[]``
138- options = fields . TypedField (typ .List [str ])
244+ options = variable (typ .List [str ], value = [ ])
139245
140246 #: The (parallel) program launcher that will be used to launch the
141247 #: (parallel) executable of this job.
@@ -157,7 +263,7 @@ class Job(jsonext.JSONSerializable):
157263 #: self.job.launcher = getlauncher('local')()
158264 #:
159265 #: :type: :class:`reframe.core.launchers.JobLauncher`
160- launcher = fields . TypedField (JobLauncher )
266+ launcher = variable (JobLauncher )
161267
162268 # The sched_* arguments are exposed also to the frontend
163269 def __init__ (self ,
@@ -166,34 +272,20 @@ def __init__(self,
166272 script_filename = None ,
167273 stdout = None ,
168274 stderr = None ,
169- max_pending_time = None ,
170275 sched_flex_alloc_nodes = None ,
171276 sched_access = [],
172- sched_exclusive_access = None ,
173277 sched_options = None ):
174278
175- # Mutable fields
176- self .num_tasks = 1
177- self .num_tasks_per_node = None
178- self .num_tasks_per_core = None
179- self .num_tasks_per_socket = None
180- self .num_cpus_per_task = None
181- self .use_smt = None
182- self .time_limit = None
183- self .cli_options = list (sched_options ) if sched_options else []
184- self .options = []
185-
279+ self ._cli_options = list (sched_options ) if sched_options else []
186280 self ._name = name
187281 self ._workdir = workdir
188282 self ._script_filename = script_filename or '%s.sh' % name
189283 self ._stdout = stdout or '%s.out' % name
190284 self ._stderr = stderr or '%s.err' % name
191- self ._max_pending_time = max_pending_time
192285
193286 # Backend scheduler related information
194287 self ._sched_flex_alloc_nodes = sched_flex_alloc_nodes
195288 self ._sched_access = sched_access
196- self ._sched_exclusive_access = sched_exclusive_access
197289
198290 # Live job information; to be filled during job's lifetime by the
199291 # scheduler
@@ -224,8 +316,8 @@ def workdir(self):
224316 return self ._workdir
225317
226318 @property
227- def max_pending_time (self ):
228- return self ._max_pending_time
319+ def cli_options (self ):
320+ return self ._cli_options
229321
230322 @property
231323 def script_filename (self ):
@@ -247,10 +339,6 @@ def sched_flex_alloc_nodes(self):
247339 def sched_access (self ):
248340 return self ._sched_access
249341
250- @property
251- def sched_exclusive_access (self ):
252- return self ._sched_exclusive_access
253-
254342 @property
255343 def completion_time (self ):
256344 '''The completion time of this job as a floating point number
0 commit comments