hpcflow-new2 0.2.0a178__py3-none-any.whl → 0.2.0a180__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. hpcflow/_version.py +1 -1
  2. hpcflow/data/demo_data_manifest/__init__.py +3 -0
  3. hpcflow/sdk/__init__.py +4 -1
  4. hpcflow/sdk/app.py +160 -15
  5. hpcflow/sdk/cli.py +14 -0
  6. hpcflow/sdk/cli_common.py +83 -0
  7. hpcflow/sdk/config/__init__.py +4 -0
  8. hpcflow/sdk/config/callbacks.py +25 -2
  9. hpcflow/sdk/config/cli.py +4 -1
  10. hpcflow/sdk/config/config.py +188 -14
  11. hpcflow/sdk/config/config_file.py +91 -3
  12. hpcflow/sdk/config/errors.py +33 -0
  13. hpcflow/sdk/core/__init__.py +2 -0
  14. hpcflow/sdk/core/actions.py +492 -35
  15. hpcflow/sdk/core/cache.py +22 -0
  16. hpcflow/sdk/core/command_files.py +221 -5
  17. hpcflow/sdk/core/commands.py +57 -0
  18. hpcflow/sdk/core/element.py +407 -8
  19. hpcflow/sdk/core/environment.py +92 -0
  20. hpcflow/sdk/core/errors.py +245 -61
  21. hpcflow/sdk/core/json_like.py +72 -14
  22. hpcflow/sdk/core/loop.py +122 -21
  23. hpcflow/sdk/core/loop_cache.py +34 -9
  24. hpcflow/sdk/core/object_list.py +172 -26
  25. hpcflow/sdk/core/parallel.py +14 -0
  26. hpcflow/sdk/core/parameters.py +478 -25
  27. hpcflow/sdk/core/rule.py +31 -1
  28. hpcflow/sdk/core/run_dir_files.py +12 -2
  29. hpcflow/sdk/core/task.py +407 -80
  30. hpcflow/sdk/core/task_schema.py +70 -9
  31. hpcflow/sdk/core/test_utils.py +35 -0
  32. hpcflow/sdk/core/utils.py +101 -4
  33. hpcflow/sdk/core/validation.py +13 -1
  34. hpcflow/sdk/core/workflow.py +316 -96
  35. hpcflow/sdk/core/zarr_io.py +23 -0
  36. hpcflow/sdk/data/__init__.py +13 -0
  37. hpcflow/sdk/demo/__init__.py +3 -0
  38. hpcflow/sdk/helper/__init__.py +3 -0
  39. hpcflow/sdk/helper/cli.py +9 -0
  40. hpcflow/sdk/helper/helper.py +28 -0
  41. hpcflow/sdk/helper/watcher.py +33 -0
  42. hpcflow/sdk/log.py +40 -0
  43. hpcflow/sdk/persistence/__init__.py +14 -4
  44. hpcflow/sdk/persistence/base.py +289 -23
  45. hpcflow/sdk/persistence/json.py +29 -0
  46. hpcflow/sdk/persistence/pending.py +217 -107
  47. hpcflow/sdk/persistence/store_resource.py +58 -2
  48. hpcflow/sdk/persistence/utils.py +8 -0
  49. hpcflow/sdk/persistence/zarr.py +68 -1
  50. hpcflow/sdk/runtime.py +52 -10
  51. hpcflow/sdk/submission/__init__.py +3 -0
  52. hpcflow/sdk/submission/jobscript.py +198 -9
  53. hpcflow/sdk/submission/jobscript_info.py +13 -0
  54. hpcflow/sdk/submission/schedulers/__init__.py +60 -0
  55. hpcflow/sdk/submission/schedulers/direct.py +53 -0
  56. hpcflow/sdk/submission/schedulers/sge.py +45 -7
  57. hpcflow/sdk/submission/schedulers/slurm.py +45 -8
  58. hpcflow/sdk/submission/schedulers/utils.py +4 -0
  59. hpcflow/sdk/submission/shells/__init__.py +11 -1
  60. hpcflow/sdk/submission/shells/base.py +32 -1
  61. hpcflow/sdk/submission/shells/bash.py +36 -1
  62. hpcflow/sdk/submission/shells/os_version.py +18 -6
  63. hpcflow/sdk/submission/shells/powershell.py +22 -0
  64. hpcflow/sdk/submission/submission.py +88 -3
  65. hpcflow/sdk/typing.py +10 -1
  66. {hpcflow_new2-0.2.0a178.dist-info → hpcflow_new2-0.2.0a180.dist-info}/METADATA +1 -1
  67. {hpcflow_new2-0.2.0a178.dist-info → hpcflow_new2-0.2.0a180.dist-info}/RECORD +70 -70
  68. {hpcflow_new2-0.2.0a178.dist-info → hpcflow_new2-0.2.0a180.dist-info}/LICENSE +0 -0
  69. {hpcflow_new2-0.2.0a178.dist-info → hpcflow_new2-0.2.0a180.dist-info}/WHEEL +0 -0
  70. {hpcflow_new2-0.2.0a178.dist-info → hpcflow_new2-0.2.0a180.dist-info}/entry_points.txt +0 -0
@@ -1,8 +1,12 @@
1
+ """
2
+ Class to hold the state that is waiting to be committed to disk.
3
+ """
4
+
1
5
  from __future__ import annotations
2
6
 
3
7
  from collections import defaultdict
4
8
  import contextlib
5
- from dataclasses import dataclass, fields
9
+ from dataclasses import dataclass, field, fields
6
10
  from datetime import datetime
7
11
 
8
12
  from typing import Any, Dict, List, Optional, Tuple
@@ -15,26 +19,12 @@ class PendingChanges:
15
19
 
16
20
  Parameters
17
21
  ----------
18
- add_tasks
19
- Keys are new task IDs
20
- add_elem_iter_EAR_IDs
21
- Keys are element iteration IDs, then EAR action index, and values are EAR IDs.
22
- This is a list of EAR IDs to add to a given element iteration action.
23
- add_elem_iter_IDs
24
- Keys are element IDs, and values are iteration IDs to add to that element.
25
- add_elem_IDs
26
- Keys are task IDs, and values are element IDs to add to that task.
27
- add_parameters
28
- Keys are parameter indices and values are tuples whose first element is data to
29
- add and whose second element is the source dict for the new data.
30
- update_param_sources
31
- Keys are parameter indices and values are dict parameter sources to merge with
32
- existing source of that parameter.
33
- set_EAR_starts
34
- Keys are EAR IDs and values are tuples of start time, and start dir snapshot.
35
- set_EAR_ends
36
- Keys are EAR IDs and values are tuples of end time, end dir snapshot, exit
37
- code, and success boolean.
22
+ app: App
23
+ The main application context.
24
+ store: PersistentStore
25
+ The persistent store that owns this object
26
+ resource_map: CommitResourceMap
27
+ Map of resources, used when processing commits.
38
28
  """
39
29
 
40
30
  def __init__(self, app, store, resource_map):
@@ -42,35 +32,64 @@ class PendingChanges:
42
32
  self.store = store
43
33
  self.resource_map = resource_map
44
34
 
35
+ #: Keys are new task IDs.
45
36
  self.add_tasks: Dict[int, AnySTask] = None
37
+ #: Keys are loop IDs, values are loop descriptors.
46
38
  self.add_loops: Dict[int, Dict] = None
39
+ #: Keys are submission IDs, values are submission descriptors.
47
40
  self.add_submissions: Dict[int, Dict] = None
41
+ #: Keys are element IDs.
48
42
  self.add_elements: Dict[int, AnySElement] = None
43
+ #: Keys are element iteration IDs.
49
44
  self.add_elem_iters: Dict[int, AnySElementIter] = None
45
+ #: Keys are element action run IDs.
50
46
  self.add_EARs: Dict[int, AnySEAR] = None
47
+ #: Keys are parameter indices and values are tuples whose first element is data
48
+ #: to add and whose second element is the source dict for the new data.
51
49
  self.add_parameters: Dict[int, AnySParameter] = None
50
+ #: Workflow-related files (inputs, outputs) added to the persistent store.
52
51
  self.add_files: List[Dict] = None
52
+ #: Template components to add.
53
53
  self.add_template_components: Dict[str, Dict[str, Dict]] = None
54
+ #: Keys are element set IDs, values are descriptors.
54
55
  self.add_element_sets: Dict[int, Dict] = None
55
56
 
57
+ #: Keys are task IDs, and values are element IDs to add to that task.
56
58
  self.add_elem_IDs: Dict[int, List] = None
59
+ #: Keys are element IDs, and values are iteration IDs to add to that element.
57
60
  self.add_elem_iter_IDs: Dict[int, List] = None
61
+ #: Keys are element iteration IDs, then EAR action index, and values are EAR IDs.
62
+ #: This is a list of EAR IDs to add to a given element iteration action.
58
63
  self.add_elem_iter_EAR_IDs: Dict[int, Dict[int, List]] = None
64
+ #: Submission parts to add.
59
65
  self.add_submission_parts: Dict[int, Dict[str, List[int]]] = None
60
66
 
67
+ #: IDs of EARs to mark as initialised.
61
68
  self.set_EARs_initialised: List[int] = None
69
+ #: Submission IDs to attach to EARs.
62
70
  self.set_EAR_submission_indices: Dict[int, int] = None
71
+ #: IDs of EARs to mark as skipped.
63
72
  self.set_EAR_skips: List[int] = None
73
+ #: Keys are EAR IDs and values are tuples of start time, and start dir snapshot.
64
74
  self.set_EAR_starts: Dict[int, Tuple[datetime, Dict], str] = None
75
+ #: Keys are EAR IDs and values are tuples of end time, end dir snapshot, exit
76
+ #: code, and success boolean.
65
77
  self.set_EAR_ends: Dict[int, Tuple[datetime, Dict, int, bool]] = None
66
78
 
79
+ #: Keys are IDs of jobscripts.
67
80
  self.set_js_metadata: Dict[int, Dict[int, Any]] = None
68
81
 
82
+ #: Keys are IDs of parameters to add or modify.
69
83
  self.set_parameters: Dict[int, AnySParameter] = None
70
84
 
85
+ #: Keys are parameter indices and values are dict parameter sources to merge
86
+ #: with existing source of that parameter.
71
87
  self.update_param_sources: Dict[int, Dict] = None
88
+ #: Keys are indices of loops, values are descriptions of what to update.
72
89
  self.update_loop_indices: Dict[int, Dict[str, int]] = None
90
+ #: Keys are indices of loops, values are number of iterations.
73
91
  self.update_loop_num_iters: Dict[int, int] = None
92
+ #: Keys are indices of loops, values are list of parent names.
74
93
  self.update_loop_parents: Dict[int, List[str]] = None
75
94
 
76
95
  self.reset(is_init=True) # set up initial data structures
@@ -106,6 +125,9 @@ class PendingChanges:
106
125
  )
107
126
 
108
127
  def where_pending(self) -> List[str]:
128
+ """
129
+ Get the list of items for which there is some outstanding pending items.
130
+ """
109
131
  return [
110
132
  k
111
133
  for k, v in self.__dict__.items()
@@ -114,6 +136,9 @@ class PendingChanges:
114
136
 
115
137
  @property
116
138
  def logger(self):
139
+ """
140
+ The logger.
141
+ """
117
142
  return self.app.persistence_logger
118
143
 
119
144
  @TimeIt.decorator
@@ -149,7 +174,7 @@ class PendingChanges:
149
174
  self.add_elem_IDs = {
150
175
  k: v for k, v in self.add_elem_IDs.items() if k not in task_ids
151
176
  }
152
- self.clear_add_tasks()
177
+ self._clear_add_tasks()
153
178
 
154
179
  @TimeIt.decorator
155
180
  def commit_loops(self) -> None:
@@ -170,7 +195,7 @@ class PendingChanges:
170
195
  k: v for k, v in self.update_loop_parents.items() if k not in loop_ids
171
196
  }
172
197
 
173
- self.clear_add_loops()
198
+ self._clear_add_loops()
174
199
 
175
200
  @TimeIt.decorator
176
201
  def commit_submissions(self) -> None:
@@ -183,17 +208,23 @@ class PendingChanges:
183
208
  f"commit: adding pending submissions with indices {sub_ids!r}"
184
209
  )
185
210
  self.store._append_submissions(subs)
186
- self.clear_add_submissions()
211
+ self._clear_add_submissions()
187
212
 
188
213
  @TimeIt.decorator
189
214
  def commit_submission_parts(self) -> None:
215
+ """
216
+ Commit pending submission parts to disk.
217
+ """
190
218
  if self.add_submission_parts:
191
219
  self.logger.debug(f"commit: adding pending submission parts")
192
220
  self.store._append_submission_parts(self.add_submission_parts)
193
- self.clear_add_submission_parts()
221
+ self._clear_add_submission_parts()
194
222
 
195
223
  @TimeIt.decorator
196
224
  def commit_elem_IDs(self) -> None:
225
+ """
226
+ Commit pending element ID updates to disk.
227
+ """
197
228
  # TODO: could be batched up?
198
229
  for task_ID, elem_IDs in self.add_elem_IDs.items():
199
230
  self.logger.debug(
@@ -201,10 +232,13 @@ class PendingChanges:
201
232
  )
202
233
  self.store._append_task_element_IDs(task_ID, elem_IDs)
203
234
  self.store.task_cache.pop(task_ID, None) # invalidate cache
204
- self.clear_add_elem_IDs()
235
+ self._clear_add_elem_IDs()
205
236
 
206
237
  @TimeIt.decorator
207
238
  def commit_elements(self) -> None:
239
+ """
240
+ Commit pending elements to disk.
241
+ """
208
242
  if self.add_elements:
209
243
  elems = self.store.get_elements(self.add_elements)
210
244
  elem_ids = list(self.add_elements.keys())
@@ -214,18 +248,24 @@ class PendingChanges:
214
248
  self.add_elem_iter_IDs = {
215
249
  k: v for k, v in self.add_elem_iter_IDs.items() if k not in elem_ids
216
250
  }
217
- self.clear_add_elements()
251
+ self._clear_add_elements()
218
252
 
219
253
  @TimeIt.decorator
220
254
  def commit_element_sets(self) -> None:
255
+ """
256
+ Commit pending element sets to disk.
257
+ """
221
258
  # TODO: could be batched up?
222
259
  for task_id, es_js in self.add_element_sets.items():
223
260
  self.logger.debug(f"commit: adding pending element sets.")
224
261
  self.store._append_element_sets(task_id, es_js)
225
- self.clear_add_element_sets()
262
+ self._clear_add_element_sets()
226
263
 
227
264
  @TimeIt.decorator
228
265
  def commit_elem_iter_IDs(self) -> None:
266
+ """
267
+ Commit pending element iteration ID updates to disk.
268
+ """
229
269
  # TODO: could be batched up?
230
270
  for elem_ID, iter_IDs in self.add_elem_iter_IDs.items():
231
271
  self.logger.debug(
@@ -234,10 +274,13 @@ class PendingChanges:
234
274
  )
235
275
  self.store._append_elem_iter_IDs(elem_ID, iter_IDs)
236
276
  self.store.element_cache.pop(elem_ID, None) # invalidate cache
237
- self.clear_add_elem_iter_IDs()
277
+ self._clear_add_elem_iter_IDs()
238
278
 
239
279
  @TimeIt.decorator
240
280
  def commit_elem_iters(self) -> None:
281
+ """
282
+ Commit pending element iterations to disk.
283
+ """
241
284
  if self.add_elem_iters:
242
285
  iters = self.store.get_element_iterations(self.add_elem_iters.keys())
243
286
  iter_ids = list(self.add_elem_iters.keys())
@@ -253,10 +296,13 @@ class PendingChanges:
253
296
  self.set_EARs_initialised = [
254
297
  i for i in self.set_EARs_initialised if i not in iter_ids
255
298
  ]
256
- self.clear_add_elem_iters()
299
+ self._clear_add_elem_iters()
257
300
 
258
301
  @TimeIt.decorator
259
302
  def commit_elem_iter_EAR_IDs(self) -> None:
303
+ """
304
+ Commit pending element action run ID updates to disk.
305
+ """
260
306
  # TODO: could be batched up?
261
307
  for iter_ID, act_EAR_IDs in self.add_elem_iter_EAR_IDs.items():
262
308
  self.logger.debug(
@@ -266,10 +312,13 @@ class PendingChanges:
266
312
  for act_idx, EAR_IDs in act_EAR_IDs.items():
267
313
  self.store._append_elem_iter_EAR_IDs(iter_ID, act_idx, EAR_IDs)
268
314
  self.store.element_iter_cache.pop(iter_ID, None) # invalidate cache
269
- self.clear_add_elem_iter_EAR_IDs()
315
+ self._clear_add_elem_iter_EAR_IDs()
270
316
 
271
317
  @TimeIt.decorator
272
318
  def commit_EARs(self) -> None:
319
+ """
320
+ Commit pending element action runs to disk.
321
+ """
273
322
  if self.add_EARs:
274
323
  EARs = self.store.get_EARs(self.add_EARs)
275
324
  EAR_ids = list(self.add_EARs.keys())
@@ -291,10 +340,13 @@ class PendingChanges:
291
340
  k: v for k, v in self.set_EAR_ends.items() if k not in EAR_ids
292
341
  }
293
342
 
294
- self.clear_add_EARs()
343
+ self._clear_add_EARs()
295
344
 
296
345
  @TimeIt.decorator
297
346
  def commit_EARs_initialised(self) -> None:
347
+ """
348
+ Commit pending element action run init state updates to disk.
349
+ """
298
350
  if self.set_EARs_initialised:
299
351
  iter_ids = self.set_EARs_initialised
300
352
  self.logger.debug(
@@ -305,10 +357,13 @@ class PendingChanges:
305
357
  for i in iter_ids:
306
358
  self.store._update_elem_iter_EARs_initialised(i)
307
359
  self.store.element_iter_cache.pop(i, None) # invalidate cache
308
- self.clear_set_EARs_initialised()
360
+ self._clear_set_EARs_initialised()
309
361
 
310
362
  @TimeIt.decorator
311
363
  def commit_EAR_submission_indices(self) -> None:
364
+ """
365
+ Commit pending element action run submission index updates to disk.
366
+ """
312
367
  if self.set_EAR_submission_indices:
313
368
  self.logger.debug(
314
369
  f"commit: updating submission indices: "
@@ -317,10 +372,13 @@ class PendingChanges:
317
372
  self.store._update_EAR_submission_indices(self.set_EAR_submission_indices)
318
373
  for EAR_ID_i in self.set_EAR_submission_indices.keys():
319
374
  self.store.EAR_cache.pop(EAR_ID_i, None) # invalidate cache
320
- self.clear_set_EAR_submission_indices()
375
+ self._clear_set_EAR_submission_indices()
321
376
 
322
377
  @TimeIt.decorator
323
378
  def commit_EAR_starts(self) -> None:
379
+ """
380
+ Commit pending element action run start information to disk.
381
+ """
324
382
  # TODO: could be batched up?
325
383
  for EAR_id, (time, snap, hostname) in self.set_EAR_starts.items():
326
384
  self.logger.debug(
@@ -329,10 +387,13 @@ class PendingChanges:
329
387
  )
330
388
  self.store._update_EAR_start(EAR_id, time, snap, hostname)
331
389
  self.store.EAR_cache.pop(EAR_id, None) # invalidate cache
332
- self.clear_set_EAR_starts()
390
+ self._clear_set_EAR_starts()
333
391
 
334
392
  @TimeIt.decorator
335
393
  def commit_EAR_ends(self) -> None:
394
+ """
395
+ Commit pending element action run finish information to disk.
396
+ """
336
397
  # TODO: could be batched up?
337
398
  for EAR_id, (time, snap, ext, suc) in self.set_EAR_ends.items():
338
399
  self.logger.debug(
@@ -341,25 +402,31 @@ class PendingChanges:
341
402
  )
342
403
  self.store._update_EAR_end(EAR_id, time, snap, ext, suc)
343
404
  self.store.EAR_cache.pop(EAR_id, None) # invalidate cache
344
- self.clear_set_EAR_ends()
405
+ self._clear_set_EAR_ends()
345
406
 
346
407
  @TimeIt.decorator
347
408
  def commit_EAR_skips(self) -> None:
409
+ """
410
+ Commit pending element action skip flags to disk.
411
+ """
348
412
  # TODO: could be batched up?
349
413
  for EAR_id in self.set_EAR_skips:
350
414
  self.logger.debug(f"commit: setting EAR ID {EAR_id!r} as skipped.")
351
415
  self.store._update_EAR_skip(EAR_id)
352
416
  self.store.EAR_cache.pop(EAR_id, None) # invalidate cache
353
- self.clear_set_EAR_skips()
417
+ self._clear_set_EAR_skips()
354
418
 
355
419
  @TimeIt.decorator
356
420
  def commit_js_metadata(self) -> None:
421
+ """
422
+ Commit pending jobscript metadata changes to disk.
423
+ """
357
424
  if self.set_js_metadata:
358
425
  self.logger.debug(
359
426
  f"commit: setting jobscript metadata: {self.set_js_metadata!r}"
360
427
  )
361
428
  self.store._update_js_metadata(self.set_js_metadata)
362
- self.clear_set_js_metadata()
429
+ self._clear_set_js_metadata()
363
430
 
364
431
  @TimeIt.decorator
365
432
  def commit_parameters(self) -> None:
@@ -369,7 +436,7 @@ class PendingChanges:
369
436
  param_ids = list(self.add_parameters.keys())
370
437
  self.logger.debug(f"commit: adding pending parameters IDs: {param_ids!r}")
371
438
  self.store._append_parameters(params)
372
- self.clear_add_parameters()
439
+ self._clear_add_parameters()
373
440
 
374
441
  if self.set_parameters:
375
442
  param_ids = list(self.set_parameters.keys())
@@ -378,7 +445,7 @@ class PendingChanges:
378
445
  for id_i in param_ids:
379
446
  self.store.parameter_cache.pop(id_i, None)
380
447
 
381
- self.clear_set_parameters()
448
+ self._clear_set_parameters()
382
449
 
383
450
  @TimeIt.decorator
384
451
  def commit_files(self) -> None:
@@ -386,14 +453,17 @@ class PendingChanges:
386
453
  if self.add_files:
387
454
  self.logger.debug(f"commit: adding pending files to the files directory.")
388
455
  self.store._append_files(self.add_files)
389
- self.clear_add_files()
456
+ self._clear_add_files()
390
457
 
391
458
  @TimeIt.decorator
392
459
  def commit_template_components(self) -> None:
460
+ """
461
+ Commit pending template components to disk.
462
+ """
393
463
  if self.add_template_components:
394
464
  self.logger.debug(f"commit: adding template components.")
395
465
  self.store._update_template_components(self.store.get_template_components())
396
- self.clear_add_template_components()
466
+ self._clear_add_template_components()
397
467
 
398
468
  @TimeIt.decorator
399
469
  def commit_param_sources(self) -> None:
@@ -404,7 +474,7 @@ class PendingChanges:
404
474
  self.store._update_parameter_sources(self.update_param_sources)
405
475
  for id_i in param_ids:
406
476
  self.store.param_sources_cache.pop(id_i, None) # invalidate cache
407
- self.clear_update_param_sources()
477
+ self._clear_update_param_sources()
408
478
 
409
479
  @TimeIt.decorator
410
480
  def commit_loop_indices(self) -> None:
@@ -417,7 +487,7 @@ class PendingChanges:
417
487
  )
418
488
  self.store._update_loop_index(iter_ID, loop_idx)
419
489
  self.store.element_iter_cache.pop(iter_ID, None) # invalidate cache
420
- self.clear_update_loop_indices()
490
+ self._clear_update_loop_indices()
421
491
 
422
492
  @TimeIt.decorator
423
493
  def commit_loop_num_iters(self) -> None:
@@ -427,7 +497,7 @@ class PendingChanges:
427
497
  f"commit: updating loop {index!r} number of iterations to {num_iters!r}."
428
498
  )
429
499
  self.store._update_loop_num_iters(index, num_iters)
430
- self.clear_update_loop_num_iters()
500
+ self._clear_update_loop_num_iters()
431
501
 
432
502
  @TimeIt.decorator
433
503
  def commit_loop_parents(self) -> None:
@@ -435,81 +505,81 @@ class PendingChanges:
435
505
  for index, parents in self.update_loop_parents.items():
436
506
  self.logger.debug(f"commit: updating loop {index!r} parents to {parents!r}.")
437
507
  self.store._update_loop_parents(index, parents)
438
- self.clear_update_loop_parents()
508
+ self._clear_update_loop_parents()
439
509
 
440
- def clear_add_tasks(self):
510
+ def _clear_add_tasks(self):
441
511
  self.add_tasks = {}
442
512
 
443
- def clear_add_loops(self):
513
+ def _clear_add_loops(self):
444
514
  self.add_loops = {}
445
515
 
446
- def clear_add_submissions(self):
516
+ def _clear_add_submissions(self):
447
517
  self.add_submissions = {}
448
518
 
449
- def clear_add_submission_parts(self):
519
+ def _clear_add_submission_parts(self):
450
520
  self.add_submission_parts = defaultdict(dict)
451
521
 
452
- def clear_add_elements(self):
522
+ def _clear_add_elements(self):
453
523
  self.add_elements = {}
454
524
 
455
- def clear_add_element_sets(self):
525
+ def _clear_add_element_sets(self):
456
526
  self.add_element_sets = defaultdict(list)
457
527
 
458
- def clear_add_elem_iters(self):
528
+ def _clear_add_elem_iters(self):
459
529
  self.add_elem_iters = {}
460
530
 
461
- def clear_add_EARs(self):
531
+ def _clear_add_EARs(self):
462
532
  self.add_EARs = {}
463
533
 
464
- def clear_add_elem_IDs(self):
534
+ def _clear_add_elem_IDs(self):
465
535
  self.add_elem_IDs = defaultdict(list)
466
536
 
467
- def clear_add_elem_iter_IDs(self):
537
+ def _clear_add_elem_iter_IDs(self):
468
538
  self.add_elem_iter_IDs = defaultdict(list)
469
539
 
470
- def clear_add_elem_iter_EAR_IDs(self):
540
+ def _clear_add_elem_iter_EAR_IDs(self):
471
541
  self.add_elem_iter_EAR_IDs = defaultdict(lambda: defaultdict(list))
472
542
 
473
- def clear_set_EARs_initialised(self):
543
+ def _clear_set_EARs_initialised(self):
474
544
  self.set_EARs_initialised = []
475
545
 
476
- def clear_set_EAR_submission_indices(self):
546
+ def _clear_set_EAR_submission_indices(self):
477
547
  self.set_EAR_submission_indices = {}
478
548
 
479
- def clear_set_EAR_starts(self):
549
+ def _clear_set_EAR_starts(self):
480
550
  self.set_EAR_starts = {}
481
551
 
482
- def clear_set_EAR_ends(self):
552
+ def _clear_set_EAR_ends(self):
483
553
  self.set_EAR_ends = {}
484
554
 
485
- def clear_set_EAR_skips(self):
555
+ def _clear_set_EAR_skips(self):
486
556
  self.set_EAR_skips = []
487
557
 
488
- def clear_set_js_metadata(self):
558
+ def _clear_set_js_metadata(self):
489
559
  self.set_js_metadata = defaultdict(lambda: defaultdict(dict))
490
560
 
491
- def clear_add_parameters(self):
561
+ def _clear_add_parameters(self):
492
562
  self.add_parameters = {}
493
563
 
494
- def clear_add_files(self):
564
+ def _clear_add_files(self):
495
565
  self.add_files = []
496
566
 
497
- def clear_add_template_components(self):
567
+ def _clear_add_template_components(self):
498
568
  self.add_template_components = defaultdict(dict)
499
569
 
500
- def clear_set_parameters(self):
570
+ def _clear_set_parameters(self):
501
571
  self.set_parameters = {}
502
572
 
503
- def clear_update_param_sources(self):
573
+ def _clear_update_param_sources(self):
504
574
  self.update_param_sources = {}
505
575
 
506
- def clear_update_loop_indices(self):
576
+ def _clear_update_loop_indices(self):
507
577
  self.update_loop_indices = defaultdict(dict)
508
578
 
509
- def clear_update_loop_num_iters(self):
579
+ def _clear_update_loop_num_iters(self):
510
580
  self.update_loop_num_iters = {}
511
581
 
512
- def clear_update_loop_parents(self):
582
+ def _clear_update_loop_parents(self):
513
583
  self.update_loop_parents = {}
514
584
 
515
585
  def reset(self, is_init=False) -> None:
@@ -522,87 +592,127 @@ class PendingChanges:
522
592
  if not is_init:
523
593
  self.logger.info("resetting pending changes.")
524
594
 
525
- self.clear_add_tasks()
526
- self.clear_add_loops()
527
- self.clear_add_submissions()
528
- self.clear_add_submission_parts()
529
- self.clear_add_elements()
530
- self.clear_add_element_sets()
531
- self.clear_add_elem_iters()
532
- self.clear_add_EARs()
595
+ self._clear_add_tasks()
596
+ self._clear_add_loops()
597
+ self._clear_add_submissions()
598
+ self._clear_add_submission_parts()
599
+ self._clear_add_elements()
600
+ self._clear_add_element_sets()
601
+ self._clear_add_elem_iters()
602
+ self._clear_add_EARs()
533
603
 
534
- self.clear_set_EARs_initialised()
535
- self.clear_add_elem_IDs()
536
- self.clear_add_elem_iter_IDs()
537
- self.clear_add_elem_iter_EAR_IDs()
604
+ self._clear_set_EARs_initialised()
605
+ self._clear_add_elem_IDs()
606
+ self._clear_add_elem_iter_IDs()
607
+ self._clear_add_elem_iter_EAR_IDs()
538
608
 
539
- self.clear_add_parameters()
540
- self.clear_add_files()
541
- self.clear_add_template_components()
609
+ self._clear_add_parameters()
610
+ self._clear_add_files()
611
+ self._clear_add_template_components()
542
612
 
543
- self.clear_set_EAR_submission_indices()
544
- self.clear_set_EAR_starts()
545
- self.clear_set_EAR_ends()
546
- self.clear_set_EAR_skips()
613
+ self._clear_set_EAR_submission_indices()
614
+ self._clear_set_EAR_starts()
615
+ self._clear_set_EAR_ends()
616
+ self._clear_set_EAR_skips()
547
617
 
548
- self.clear_set_js_metadata()
549
- self.clear_set_parameters()
618
+ self._clear_set_js_metadata()
619
+ self._clear_set_parameters()
550
620
 
551
- self.clear_update_param_sources()
552
- self.clear_update_loop_indices()
553
- self.clear_update_loop_num_iters()
554
- self.clear_update_loop_parents()
621
+ self._clear_update_param_sources()
622
+ self._clear_update_loop_indices()
623
+ self._clear_update_loop_num_iters()
624
+ self._clear_update_loop_parents()
555
625
 
556
626
 
557
627
  @dataclass
558
628
  class CommitResourceMap:
559
- """Map of `PendingChanges` commit method names to store resource labels, representing
560
- the store resources required by each commit method, for a given `PersistentStore`
629
+ """
630
+ Map of :py:class:`PendingChanges` commit method names to store resource labels,
631
+ representing the store resources required by each ``commit_*`` method, for a given
632
+ :py:class:`~.PersistentStore`.
561
633
 
562
- When `PendingChanges.commit_all` is called, the resources specified will be opened in
563
- "update" mode, for each `commit_` method.
634
+ When :py:meth:`PendingChanges.commit_all` is called, the resources specified will be
635
+ opened in "update" mode, for each ``commit_*`` method.
564
636
 
637
+ Notes
638
+ -----
639
+ Normally only of interest to implementations of persistent stores.
565
640
  """
566
641
 
642
+ #: Resources for :py:meth:`~.PendingChanges.commit_tasks`.
567
643
  commit_tasks: Optional[Tuple[str]] = tuple()
644
+ #: Resources for :py:meth:`~.PendingChanges.commit_loops`.
568
645
  commit_loops: Optional[Tuple[str]] = tuple()
646
+ #: Resources for :py:meth:`~.PendingChanges.commit_submissions`.
569
647
  commit_submissions: Optional[Tuple[str]] = tuple()
648
+ #: Resources for :py:meth:`~.PendingChanges.commit_submission_parts`.
570
649
  commit_submission_parts: Optional[Tuple[str]] = tuple()
650
+ #: Resources for :py:meth:`~.PendingChanges.commit_elem_IDs`.
571
651
  commit_elem_IDs: Optional[Tuple[str]] = tuple()
652
+ #: Resources for :py:meth:`~.PendingChanges.commit_elements`.
572
653
  commit_elements: Optional[Tuple[str]] = tuple()
654
+ #: Resources for :py:meth:`~.PendingChanges.commit_element_sets`.
573
655
  commit_element_sets: Optional[Tuple[str]] = tuple()
656
+ #: Resources for :py:meth:`~.PendingChanges.commit_elem_iter_IDs`.
574
657
  commit_elem_iter_IDs: Optional[Tuple[str]] = tuple()
658
+ #: Resources for :py:meth:`~.PendingChanges.commit_elem_iters`.
575
659
  commit_elem_iters: Optional[Tuple[str]] = tuple()
660
+ #: Resources for :py:meth:`~.PendingChanges.commit_elem_iter_EAR_IDs`.
576
661
  commit_elem_iter_EAR_IDs: Optional[Tuple[str]] = tuple()
662
+ #: Resources for :py:meth:`~.PendingChanges.commit_EARs_initialised`.
577
663
  commit_EARs_initialised: Optional[Tuple[str]] = tuple()
664
+ #: Resources for :py:meth:`~.PendingChanges.commit_EARs`.
578
665
  commit_EARs: Optional[Tuple[str]] = tuple()
666
+ #: Resources for :py:meth:`~.PendingChanges.commit_EAR_submission_indices`.
579
667
  commit_EAR_submission_indices: Optional[Tuple[str]] = tuple()
668
+ #: Resources for :py:meth:`~.PendingChanges.commit_EAR_skips`.
580
669
  commit_EAR_skips: Optional[Tuple[str]] = tuple()
670
+ #: Resources for :py:meth:`~.PendingChanges.commit_EAR_starts`.
581
671
  commit_EAR_starts: Optional[Tuple[str]] = tuple()
672
+ #: Resources for :py:meth:`~.PendingChanges.commit_EAR_ends`.
582
673
  commit_EAR_ends: Optional[Tuple[str]] = tuple()
674
+ #: Resources for :py:meth:`~.PendingChanges.commit_js_metadata`.
583
675
  commit_js_metadata: Optional[Tuple[str]] = tuple()
676
+ #: Resources for :py:meth:`~.PendingChanges.commit_parameters`.
584
677
  commit_parameters: Optional[Tuple[str]] = tuple()
678
+ #: Resources for :py:meth:`~.PendingChanges.commit_files`.
585
679
  commit_files: Optional[Tuple[str]] = tuple()
680
+ #: Resources for :py:meth:`~.PendingChanges.commit_template_components`.
586
681
  commit_template_components: Optional[Tuple[str]] = tuple()
682
+ #: Resources for :py:meth:`~.PendingChanges.commit_param_sources`.
587
683
  commit_param_sources: Optional[Tuple[str]] = tuple()
684
+ #: Resources for :py:meth:`~.PendingChanges.commit_loop_indices`.
588
685
  commit_loop_indices: Optional[Tuple[str]] = tuple()
686
+ #: Resources for :py:meth:`~.PendingChanges.commit_loop_num_iters`.
589
687
  commit_loop_num_iters: Optional[Tuple[str]] = tuple()
688
+ #: Resources for :py:meth:`~.PendingChanges.commit_loop_parents`.
590
689
  commit_loop_parents: Optional[Tuple[str]] = tuple()
690
+ #: A dict whose keys are tuples of resource labels and whose values are lists
691
+ #: of :py:class:`PendingChanges` commit method names that require those resources.
692
+ #:
693
+ #: This grouping allows us to batch up commit methods by resource requirements,
694
+ #: which in turn means we can potentially minimise, e.g., the number of network
695
+ #: requests.
696
+ groups: Dict[Tuple[str], List[str]] = field(init=False, repr=False, compare=False)
591
697
 
592
698
  def __post_init__(self):
593
- self.groups = self.group_by_resource()
699
+ self.groups = self._group_by_resource()
594
700
 
595
- def group_by_resource(self) -> Dict[Tuple[str], List[str]]:
596
- """Return a dict whose keys are tuples of resource labels and whose values are
597
- lists of `PendingChanges` commit method names that require those resource.
598
-
599
- This grouping allows us to batch up commit methods by resource requirements, which
600
- in turn means we can potentially minimise e.g. the number of network requests.
701
+ def _group_by_resource(self) -> Dict[Tuple[str], List[str]]:
702
+ """
703
+ Get a dict whose keys are tuples of resource labels and whose values are
704
+ lists of :py:class:`PendingChanges` commit method names that require those
705
+ resource.
601
706
 
707
+ This grouping allows us to batch up commit methods by resource requirements,
708
+ which in turn means we can potentially minimise e.g. the number of network
709
+ requests.
602
710
  """
603
711
  groups = {}
604
712
  cur_res_group = None
605
713
  for fld in fields(self):
714
+ if not fld.name.startswith("commit_"):
715
+ continue
606
716
  res_labels = getattr(self, fld.name)
607
717
 
608
718
  if not cur_res_group: