hpcflow-new2 0.2.0a179__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.0a179.dist-info → hpcflow_new2-0.2.0a180.dist-info}/METADATA +1 -1
  67. {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a180.dist-info}/RECORD +70 -70
  68. {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a180.dist-info}/LICENSE +0 -0
  69. {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a180.dist-info}/WHEEL +0 -0
  70. {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a180.dist-info}/entry_points.txt +0 -0
@@ -1,3 +1,7 @@
1
+ """
2
+ Main workflow model.
3
+ """
4
+
1
5
  from __future__ import annotations
2
6
  from collections import defaultdict
3
7
  from contextlib import contextmanager
@@ -90,20 +94,32 @@ class WorkflowTemplate(JSONLike):
90
94
 
91
95
  Parameters
92
96
  ----------
93
- name
97
+ name:
94
98
  A string name for the workflow. By default this name will be used in combination
95
99
  with a date-time stamp when generating a persistent workflow from the template.
96
- tasks
100
+ tasks: list[~hpcflow.app.Task]
97
101
  A list of Task objects to include in the workflow.
98
- loops
102
+ loops: list[~hpcflow.app.Loop]
99
103
  A list of Loop objects to include in the workflow.
100
- resources
104
+ workflow:
105
+ The associated concrete workflow.
106
+ resources: dict[str, dict] | list[~hpcflow.app.ResourceSpec] | ~hpcflow.app.ResourceList
101
107
  Template-level resources to apply to all tasks as default values. This can be a
102
108
  dict that maps action scopes to resources (e.g. `{{"any": {{"num_cores": 2}}}}`)
103
109
  or a list of `ResourceSpec` objects, or a `ResourceList` object.
104
- merge_resources
110
+ environments:
111
+ The execution environments to use.
112
+ env_presets:
113
+ The environment presets to use.
114
+ source_file:
115
+ The file this was derived from.
116
+ store_kwargs:
117
+ Additional arguments to pass to the persistent data store constructor.
118
+ merge_resources:
105
119
  If True, merge template-level `resources` into element set resources. If False,
106
120
  template-level resources are ignored.
121
+ merge_envs:
122
+ Whether to merge the environemtns into task resources.
107
123
  """
108
124
 
109
125
  _app_attr = "app"
@@ -129,17 +145,29 @@ class WorkflowTemplate(JSONLike):
129
145
  ),
130
146
  )
131
147
 
148
+ #: A string name for the workflow.
132
149
  name: str
150
+ #: Documentation information.
133
151
  doc: Optional[Union[List[str], str]] = field(repr=False, default=None)
152
+ #: A list of Task objects to include in the workflow.
134
153
  tasks: Optional[List[app.Task]] = field(default_factory=lambda: [])
154
+ #: A list of Loop objects to include in the workflow.
135
155
  loops: Optional[List[app.Loop]] = field(default_factory=lambda: [])
156
+ #: The associated concrete workflow.
136
157
  workflow: Optional[app.Workflow] = None
158
+ #: Template-level resources to apply to all tasks as default values.
137
159
  resources: Optional[Dict[str, Dict]] = None
160
+ #: The execution environments to use.
138
161
  environments: Optional[Dict[str, Dict[str, Any]]] = None
162
+ #: The environment presets to use.
139
163
  env_presets: Optional[Union[str, List[str]]] = None
164
+ #: The file this was derived from.
140
165
  source_file: Optional[str] = field(default=None, compare=False)
166
+ #: Additional arguments to pass to the persistent data store constructor.
141
167
  store_kwargs: Optional[Dict] = field(default_factory=lambda: {})
168
+ #: Whether to merge template-level `resources` into element set resources.
142
169
  merge_resources: Optional[bool] = True
170
+ #: Whether to merge the environemtns into task resources.
143
171
  merge_envs: Optional[bool] = True
144
172
 
145
173
  def __post_init__(self):
@@ -455,6 +483,8 @@ class WorkflowTemplate(JSONLike):
455
483
 
456
484
  def resolve_fsspec(path: PathLike, **kwargs) -> Tuple[Any, str, str]:
457
485
  """
486
+ Decide how to handle a particular virtual path.
487
+
458
488
  Parameters
459
489
  ----------
460
490
  kwargs
@@ -486,6 +516,23 @@ def resolve_fsspec(path: PathLike, **kwargs) -> Tuple[Any, str, str]:
486
516
 
487
517
 
488
518
  class Workflow:
519
+ """
520
+ A concrete workflow.
521
+
522
+ Parameters
523
+ ----------
524
+ workflow_ref:
525
+ Either the path to a persistent workflow, or an integer that will interpreted
526
+ as the local ID of a workflow submission, as reported by the app `show`
527
+ command.
528
+ store_fmt:
529
+ The format of persistent store to use. Used to select the store manager class.
530
+ fs_kwargs:
531
+ Additional arguments to pass when resolving a virtual workflow reference.
532
+ kwargs:
533
+ For compatibility during pre-stable development phase.
534
+ """
535
+
489
536
  _app_attr = "app"
490
537
  _default_ts_fmt = r"%Y-%m-%d %H:%M:%S.%f"
491
538
  _default_ts_name_fmt = r"%Y-%m-%d_%H%M%S"
@@ -499,17 +546,6 @@ class Workflow:
499
546
  fs_kwargs: Optional[Dict] = None,
500
547
  **kwargs,
501
548
  ):
502
- """
503
- Parameters
504
- ----------
505
- workflow_ref
506
- Either the path to a persistent workflow, or an integer that will interpreted
507
- as the local ID of a workflow submission, as reported by the app `show`
508
- command.
509
- kwargs
510
- For compatibility during pre-stable development phase.
511
- """
512
-
513
549
  if isinstance(workflow_ref, int):
514
550
  path = self.app._get_workflow_path_from_local_ID(workflow_ref)
515
551
  else:
@@ -546,15 +582,19 @@ class Workflow:
546
582
 
547
583
  @property
548
584
  def name(self):
549
- """The workflow name may be different from the template name, as it includes the
550
- creation date-timestamp if generated."""
585
+ """
586
+ The name of the workflow.
587
+
588
+ The workflow name may be different from the template name, as it includes the
589
+ creation date-timestamp if generated.
590
+ """
551
591
  if not self._name:
552
592
  self._name = self._store.get_name()
553
593
  return self._name
554
594
 
555
595
  @property
556
596
  def url(self):
557
- """Get an fsspec URL for this workflow."""
597
+ """An fsspec URL for this workflow."""
558
598
  if self._store.fs.protocol == "zip":
559
599
  return self._store.fs.of.path
560
600
  elif self._store.fs.protocol == "file":
@@ -564,10 +604,16 @@ class Workflow:
564
604
 
565
605
  @property
566
606
  def store_format(self):
607
+ """
608
+ The format of the workflow's persistent store.
609
+ """
567
610
  return self._store._name
568
611
 
569
612
  @property
570
613
  def num_tasks(self) -> int:
614
+ """
615
+ The number of tasks in the workflow.
616
+ """
571
617
  return len(self.tasks)
572
618
 
573
619
  @classmethod
@@ -588,28 +634,28 @@ class Workflow:
588
634
 
589
635
  Parameters
590
636
  ----------
591
- template
637
+ template:
592
638
  The WorkflowTemplate object to make persistent.
593
- path
639
+ path:
594
640
  The directory in which the workflow will be generated. The current directory
595
641
  if not specified.
596
- name
642
+ name:
597
643
  The name of the workflow. If specified, the workflow directory will be `path`
598
644
  joined with `name`. If not specified the `WorkflowTemplate` name will be used,
599
645
  in combination with a date-timestamp.
600
- overwrite
646
+ overwrite:
601
647
  If True and the workflow directory (`path` + `name`) already exists, the
602
648
  existing directory will be overwritten.
603
- store
649
+ store:
604
650
  The persistent store to use for this workflow.
605
- ts_fmt
651
+ ts_fmt:
606
652
  The datetime format to use for storing datetimes. Datetimes are always stored
607
653
  in UTC (because Numpy does not store time zone info), so this should not
608
654
  include a time zone name.
609
- ts_name_fmt
655
+ ts_name_fmt:
610
656
  The datetime format to use when generating the workflow name, where it
611
657
  includes a timestamp.
612
- store_kwargs
658
+ store_kwargs:
613
659
  Keyword arguments to pass to the store's `write_empty_workflow` method.
614
660
  """
615
661
  if status:
@@ -673,30 +719,30 @@ class Workflow:
673
719
 
674
720
  Parameters
675
721
  ----------
676
- YAML_path
722
+ YAML_path:
677
723
  The path to a workflow template in the YAML file format.
678
- path
724
+ path:
679
725
  The directory in which the workflow will be generated. The current directory
680
726
  if not specified.
681
- name
727
+ name:
682
728
  The name of the workflow. If specified, the workflow directory will be `path`
683
729
  joined with `name`. If not specified the `WorkflowTemplate` name will be used,
684
730
  in combination with a date-timestamp.
685
- overwrite
731
+ overwrite:
686
732
  If True and the workflow directory (`path` + `name`) already exists, the
687
733
  existing directory will be overwritten.
688
- store
734
+ store:
689
735
  The persistent store to use for this workflow.
690
- ts_fmt
736
+ ts_fmt:
691
737
  The datetime format to use for storing datetimes. Datetimes are always stored
692
738
  in UTC (because Numpy does not store time zone info), so this should not
693
739
  include a time zone name.
694
- ts_name_fmt
740
+ ts_name_fmt:
695
741
  The datetime format to use when generating the workflow name, where it
696
742
  includes a timestamp.
697
- store_kwargs
743
+ store_kwargs:
698
744
  Keyword arguments to pass to the store's `write_empty_workflow` method.
699
- variables
745
+ variables:
700
746
  String variables to substitute in the file given by `YAML_path`.
701
747
  """
702
748
  template = cls.app.WorkflowTemplate.from_YAML_file(
@@ -731,30 +777,30 @@ class Workflow:
731
777
 
732
778
  Parameters
733
779
  ----------
734
- YAML_str
780
+ YAML_str:
735
781
  The YAML string containing a workflow template parametrisation.
736
- path
782
+ path:
737
783
  The directory in which the workflow will be generated. The current directory
738
784
  if not specified.
739
- name
785
+ name:
740
786
  The name of the workflow. If specified, the workflow directory will be `path`
741
787
  joined with `name`. If not specified the `WorkflowTemplate` name will be used,
742
788
  in combination with a date-timestamp.
743
- overwrite
789
+ overwrite:
744
790
  If True and the workflow directory (`path` + `name`) already exists, the
745
791
  existing directory will be overwritten.
746
- store
792
+ store:
747
793
  The persistent store to use for this workflow.
748
- ts_fmt
794
+ ts_fmt:
749
795
  The datetime format to use for storing datetimes. Datetimes are always stored
750
796
  in UTC (because Numpy does not store time zone info), so this should not
751
797
  include a time zone name.
752
- ts_name_fmt
798
+ ts_name_fmt:
753
799
  The datetime format to use when generating the workflow name, where it
754
800
  includes a timestamp.
755
- store_kwargs
801
+ store_kwargs:
756
802
  Keyword arguments to pass to the store's `write_empty_workflow` method.
757
- variables
803
+ variables:
758
804
  String variables to substitute in the string `YAML_str`.
759
805
  """
760
806
  template = cls.app.WorkflowTemplate.from_YAML_string(
@@ -790,30 +836,30 @@ class Workflow:
790
836
 
791
837
  Parameters
792
838
  ----------
793
- JSON_path
839
+ JSON_path:
794
840
  The path to a workflow template in the JSON file format.
795
- path
841
+ path:
796
842
  The directory in which the workflow will be generated. The current directory
797
843
  if not specified.
798
- name
844
+ name:
799
845
  The name of the workflow. If specified, the workflow directory will be `path`
800
846
  joined with `name`. If not specified the `WorkflowTemplate` name will be used,
801
847
  in combination with a date-timestamp.
802
- overwrite
848
+ overwrite:
803
849
  If True and the workflow directory (`path` + `name`) already exists, the
804
850
  existing directory will be overwritten.
805
- store
851
+ store:
806
852
  The persistent store to use for this workflow.
807
- ts_fmt
853
+ ts_fmt:
808
854
  The datetime format to use for storing datetimes. Datetimes are always stored
809
855
  in UTC (because Numpy does not store time zone info), so this should not
810
856
  include a time zone name.
811
- ts_name_fmt
857
+ ts_name_fmt:
812
858
  The datetime format to use when generating the workflow name, where it
813
859
  includes a timestamp.
814
- store_kwargs
860
+ store_kwargs:
815
861
  Keyword arguments to pass to the store's `write_empty_workflow` method.
816
- variables
862
+ variables:
817
863
  String variables to substitute in the file given by `JSON_path`.
818
864
  """
819
865
  template = cls.app.WorkflowTemplate.from_JSON_file(
@@ -850,30 +896,30 @@ class Workflow:
850
896
 
851
897
  Parameters
852
898
  ----------
853
- JSON_str
899
+ JSON_str:
854
900
  The JSON string containing a workflow template parametrisation.
855
- path
901
+ path:
856
902
  The directory in which the workflow will be generated. The current directory
857
903
  if not specified.
858
- name
904
+ name:
859
905
  The name of the workflow. If specified, the workflow directory will be `path`
860
906
  joined with `name`. If not specified the `WorkflowTemplate` name will be used,
861
907
  in combination with a date-timestamp.
862
- overwrite
908
+ overwrite:
863
909
  If True and the workflow directory (`path` + `name`) already exists, the
864
910
  existing directory will be overwritten.
865
- store
911
+ store:
866
912
  The persistent store to use for this workflow.
867
- ts_fmt
913
+ ts_fmt:
868
914
  The datetime format to use for storing datetimes. Datetimes are always stored
869
915
  in UTC (because Numpy does not store time zone info), so this should not
870
916
  include a time zone name.
871
- ts_name_fmt
917
+ ts_name_fmt:
872
918
  The datetime format to use when generating the workflow name, where it
873
919
  includes a timestamp.
874
- store_kwargs
920
+ store_kwargs:
875
921
  Keyword arguments to pass to the store's `write_empty_workflow` method.
876
- variables
922
+ variables:
877
923
  String variables to substitute in the string `JSON_str`.
878
924
  """
879
925
  template = cls.app.WorkflowTemplate.from_JSON_string(
@@ -912,34 +958,34 @@ class Workflow:
912
958
 
913
959
  Parameters
914
960
  ----------
915
- template_path
961
+ template_path:
916
962
  The path to a template file in YAML or JSON format, and with a ".yml",
917
963
  ".yaml", or ".json" extension.
918
- template_format
964
+ template_format:
919
965
  If specified, one of "json" or "yaml". This forces parsing from a particular
920
966
  format regardless of the file extension.
921
- path
967
+ path:
922
968
  The directory in which the workflow will be generated. The current directory
923
969
  if not specified.
924
- name
970
+ name:
925
971
  The name of the workflow. If specified, the workflow directory will be `path`
926
972
  joined with `name`. If not specified the `WorkflowTemplate` name will be used,
927
973
  in combination with a date-timestamp.
928
- overwrite
974
+ overwrite:
929
975
  If True and the workflow directory (`path` + `name`) already exists, the
930
976
  existing directory will be overwritten.
931
- store
977
+ store:
932
978
  The persistent store to use for this workflow.
933
- ts_fmt
979
+ ts_fmt:
934
980
  The datetime format to use for storing datetimes. Datetimes are always stored
935
981
  in UTC (because Numpy does not store time zone info), so this should not
936
982
  include a time zone name.
937
- ts_name_fmt
983
+ ts_name_fmt:
938
984
  The datetime format to use when generating the workflow name, where it
939
985
  includes a timestamp.
940
- store_kwargs
986
+ store_kwargs:
941
987
  Keyword arguments to pass to the store's `write_empty_workflow` method.
942
- variables
988
+ variables:
943
989
  String variables to substitute in the file given by `template_path`.
944
990
  """
945
991
  try:
@@ -984,37 +1030,37 @@ class Workflow:
984
1030
 
985
1031
  Parameters
986
1032
  ----------
987
- template_name
1033
+ template_name:
988
1034
  Name of the new workflow template, from which the new workflow will be
989
1035
  generated.
990
- tasks
1036
+ tasks:
991
1037
  List of Task objects to add to the new workflow.
992
- loops
1038
+ loops:
993
1039
  List of Loop objects to add to the new workflow.
994
- resources
1040
+ resources:
995
1041
  Mapping of action scopes to resource requirements, to be applied to all
996
1042
  element sets in the workflow. `resources` specified in an element set take
997
1043
  precedence of those defined here for the whole workflow.
998
- path
1044
+ path:
999
1045
  The directory in which the workflow will be generated. The current directory
1000
1046
  if not specified.
1001
- workflow_name
1047
+ workflow_name:
1002
1048
  The name of the workflow. If specified, the workflow directory will be `path`
1003
1049
  joined with `name`. If not specified `template_name` will be used, in
1004
1050
  combination with a date-timestamp.
1005
- overwrite
1051
+ overwrite:
1006
1052
  If True and the workflow directory (`path` + `name`) already exists, the
1007
1053
  existing directory will be overwritten.
1008
- store
1054
+ store:
1009
1055
  The persistent store to use for this workflow.
1010
- ts_fmt
1056
+ ts_fmt:
1011
1057
  The datetime format to use for storing datetimes. Datetimes are always stored
1012
1058
  in UTC (because Numpy does not store time zone info), so this should not
1013
1059
  include a time zone name.
1014
- ts_name_fmt
1060
+ ts_name_fmt:
1015
1061
  The datetime format to use when generating the workflow name, where it
1016
1062
  includes a timestamp.
1017
- store_kwargs
1063
+ store_kwargs:
1018
1064
  Keyword arguments to pass to the store's `write_empty_workflow` method.
1019
1065
  """
1020
1066
  template = cls.app.WorkflowTemplate(
@@ -1080,6 +1126,9 @@ class Workflow:
1080
1126
  new_wk_task._add_elements(element_sets=task.element_sets)
1081
1127
 
1082
1128
  def add_task(self, task: app.Task, new_index: Optional[int] = None) -> None:
1129
+ """
1130
+ Add a task to this workflow.
1131
+ """
1083
1132
  with self._store.cached_load():
1084
1133
  with self.batch_update():
1085
1134
  self._add_task(task, new_index=new_index)
@@ -1186,6 +1235,9 @@ class Workflow:
1186
1235
 
1187
1236
  @property
1188
1237
  def creation_info(self):
1238
+ """
1239
+ The creation descriptor for the workflow.
1240
+ """
1189
1241
  if not self._creation_info:
1190
1242
  info = self._store.get_creation_info()
1191
1243
  info["create_time"] = (
@@ -1198,22 +1250,34 @@ class Workflow:
1198
1250
 
1199
1251
  @property
1200
1252
  def id_(self):
1253
+ """
1254
+ The ID of this workflow.
1255
+ """
1201
1256
  return self.creation_info["id"]
1202
1257
 
1203
1258
  @property
1204
1259
  def ts_fmt(self):
1260
+ """
1261
+ The timestamp format.
1262
+ """
1205
1263
  if not self._ts_fmt:
1206
1264
  self._ts_fmt = self._store.get_ts_fmt()
1207
1265
  return self._ts_fmt
1208
1266
 
1209
1267
  @property
1210
1268
  def ts_name_fmt(self):
1269
+ """
1270
+ The timestamp format for names.
1271
+ """
1211
1272
  if not self._ts_name_fmt:
1212
1273
  self._ts_name_fmt = self._store.get_ts_name_fmt()
1213
1274
  return self._ts_name_fmt
1214
1275
 
1215
1276
  @property
1216
1277
  def template_components(self) -> Dict:
1278
+ """
1279
+ The template components used for this workflow.
1280
+ """
1217
1281
  if self._template_components is None:
1218
1282
  with self._store.cached_load():
1219
1283
  tc_js = self._store.get_template_components()
@@ -1222,6 +1286,9 @@ class Workflow:
1222
1286
 
1223
1287
  @property
1224
1288
  def template(self) -> app.WorkflowTemplate:
1289
+ """
1290
+ The template that this workflow was made from.
1291
+ """
1225
1292
  if self._template is None:
1226
1293
  with self._store.cached_load():
1227
1294
  temp_js = self._store.get_template()
@@ -1240,6 +1307,9 @@ class Workflow:
1240
1307
 
1241
1308
  @property
1242
1309
  def tasks(self) -> app.WorkflowTaskList:
1310
+ """
1311
+ The tasks in this workflow.
1312
+ """
1243
1313
  if self._tasks is None:
1244
1314
  with self._store.cached_load():
1245
1315
  all_tasks = self._store.get_tasks()
@@ -1258,6 +1328,9 @@ class Workflow:
1258
1328
 
1259
1329
  @property
1260
1330
  def loops(self) -> app.WorkflowLoopList:
1331
+ """
1332
+ The loops in this workflow.
1333
+ """
1261
1334
  if self._loops is None:
1262
1335
  with self._store.cached_load():
1263
1336
  wk_loops = []
@@ -1279,6 +1352,9 @@ class Workflow:
1279
1352
 
1280
1353
  @property
1281
1354
  def submissions(self) -> List[app.Submission]:
1355
+ """
1356
+ The job submissions done by this workflow.
1357
+ """
1282
1358
  if self._submissions is None:
1283
1359
  self.app.persistence_logger.debug("loading workflow submissions")
1284
1360
  with self._store.cached_load():
@@ -1293,42 +1369,66 @@ class Workflow:
1293
1369
 
1294
1370
  @property
1295
1371
  def num_added_tasks(self) -> int:
1372
+ """
1373
+ The total number of added tasks.
1374
+ """
1296
1375
  return self._store._get_num_total_added_tasks()
1297
1376
 
1298
1377
  @TimeIt.decorator
1299
1378
  def get_store_EARs(self, id_lst: Iterable[int]) -> List[AnySEAR]:
1379
+ """
1380
+ Get the persistent element action runs.
1381
+ """
1300
1382
  return self._store.get_EARs(id_lst)
1301
1383
 
1302
1384
  @TimeIt.decorator
1303
1385
  def get_store_element_iterations(
1304
1386
  self, id_lst: Iterable[int]
1305
1387
  ) -> List[AnySElementIter]:
1388
+ """
1389
+ Get the persistent element iterations.
1390
+ """
1306
1391
  return self._store.get_element_iterations(id_lst)
1307
1392
 
1308
1393
  @TimeIt.decorator
1309
1394
  def get_store_elements(self, id_lst: Iterable[int]) -> List[AnySElement]:
1395
+ """
1396
+ Get the persistent elements.
1397
+ """
1310
1398
  return self._store.get_elements(id_lst)
1311
1399
 
1312
1400
  @TimeIt.decorator
1313
1401
  def get_store_tasks(self, id_lst: Iterable[int]) -> List[AnySTask]:
1402
+ """
1403
+ Get the persistent tasks.
1404
+ """
1314
1405
  return self._store.get_tasks_by_IDs(id_lst)
1315
1406
 
1316
1407
  def get_element_iteration_IDs_from_EAR_IDs(self, id_lst: Iterable[int]) -> List[int]:
1408
+ """
1409
+ Get the element iteration IDs of EARs.
1410
+ """
1317
1411
  return [i.elem_iter_ID for i in self.get_store_EARs(id_lst)]
1318
1412
 
1319
1413
  def get_element_IDs_from_EAR_IDs(self, id_lst: Iterable[int]) -> List[int]:
1414
+ """
1415
+ Get the element IDs of EARs.
1416
+ """
1320
1417
  iter_IDs = self.get_element_iteration_IDs_from_EAR_IDs(id_lst)
1321
1418
  return [i.element_ID for i in self.get_store_element_iterations(iter_IDs)]
1322
1419
 
1323
1420
  def get_task_IDs_from_element_IDs(self, id_lst: Iterable[int]) -> List[int]:
1421
+ """
1422
+ Get the task IDs of elements.
1423
+ """
1324
1424
  return [i.task_ID for i in self.get_store_elements(id_lst)]
1325
1425
 
1326
1426
  def get_EAR_IDs_of_tasks(self, id_lst: int) -> List[int]:
1327
- """Get EAR IDs belonging to multiple tasks"""
1427
+ """Get EAR IDs belonging to multiple tasks."""
1328
1428
  return [i.id_ for i in self.get_EARs_of_tasks(id_lst)]
1329
1429
 
1330
1430
  def get_EARs_of_tasks(self, id_lst: Iterable[int]) -> List[app.ElementActionRun]:
1331
- """Get EARs belonging to multiple tasks"""
1431
+ """Get EARs belonging to multiple task.s"""
1332
1432
  EARs = []
1333
1433
  for i in id_lst:
1334
1434
  task = self.tasks.get(insert_ID=i)
@@ -1341,7 +1441,7 @@ class Workflow:
1341
1441
  def get_element_iterations_of_tasks(
1342
1442
  self, id_lst: Iterable[int]
1343
1443
  ) -> List[app.ElementIteration]:
1344
- """Get element iterations belonging to multiple tasks"""
1444
+ """Get element iterations belonging to multiple tasks."""
1345
1445
  iters = []
1346
1446
  for i in id_lst:
1347
1447
  task = self.tasks.get(insert_ID=i)
@@ -1431,7 +1531,7 @@ class Workflow:
1431
1531
 
1432
1532
  @TimeIt.decorator
1433
1533
  def get_EARs_from_IDs(self, id_lst: Iterable[int]) -> List[app.ElementActionRun]:
1434
- """Return element action run objects from a list of IDs."""
1534
+ """Get element action run objects from a list of IDs."""
1435
1535
  self.app.persistence_logger.debug(f"get_EARs_from_IDs: id_lst={id_lst!r}")
1436
1536
 
1437
1537
  store_EARs = self._store.get_EARs(id_lst)
@@ -1490,14 +1590,23 @@ class Workflow:
1490
1590
 
1491
1591
  @TimeIt.decorator
1492
1592
  def get_all_elements(self) -> List[app.Element]:
1593
+ """
1594
+ Get all elements in the workflow.
1595
+ """
1493
1596
  return self.get_elements_from_IDs(range(self.num_elements))
1494
1597
 
1495
1598
  @TimeIt.decorator
1496
1599
  def get_all_element_iterations(self) -> List[app.ElementIteration]:
1600
+ """
1601
+ Get all iterations in the workflow.
1602
+ """
1497
1603
  return self.get_element_iterations_from_IDs(range(self.num_element_iterations))
1498
1604
 
1499
1605
  @TimeIt.decorator
1500
1606
  def get_all_EARs(self) -> List[app.ElementActionRun]:
1607
+ """
1608
+ Get all runs in the workflow.
1609
+ """
1501
1610
  return self.get_EARs_from_IDs(range(self.num_EARs))
1502
1611
 
1503
1612
  @contextmanager
@@ -1705,6 +1814,8 @@ class Workflow:
1705
1814
  include_rechunk_backups=False,
1706
1815
  ) -> str:
1707
1816
  """
1817
+ Convert the workflow to a zipped form.
1818
+
1708
1819
  Parameters
1709
1820
  ----------
1710
1821
  path:
@@ -1722,6 +1833,8 @@ class Workflow:
1722
1833
 
1723
1834
  def unzip(self, path=".", log=None) -> str:
1724
1835
  """
1836
+ Convert the workflow to an unzipped form.
1837
+
1725
1838
  Parameters
1726
1839
  ----------
1727
1840
  path:
@@ -1736,6 +1849,9 @@ class Workflow:
1736
1849
  return self._store.copy(path)
1737
1850
 
1738
1851
  def delete(self):
1852
+ """
1853
+ Delete the persistent data.
1854
+ """
1739
1855
  self._store.delete()
1740
1856
 
1741
1857
  def _delete_no_confirm(self):
@@ -1744,22 +1860,37 @@ class Workflow:
1744
1860
  def get_parameters(
1745
1861
  self, id_lst: Iterable[int], **kwargs: Dict
1746
1862
  ) -> List[AnySParameter]:
1863
+ """
1864
+ Get parameters known to the workflow.
1865
+ """
1747
1866
  return self._store.get_parameters(id_lst, **kwargs)
1748
1867
 
1749
1868
  @TimeIt.decorator
1750
1869
  def get_parameter_sources(self, id_lst: Iterable[int]) -> List[Dict]:
1870
+ """
1871
+ Get parameter sources known to the workflow.
1872
+ """
1751
1873
  return self._store.get_parameter_sources(id_lst)
1752
1874
 
1753
1875
  @TimeIt.decorator
1754
1876
  def get_parameter_set_statuses(self, id_lst: Iterable[int]) -> List[bool]:
1877
+ """
1878
+ Get whether some parameters are set.
1879
+ """
1755
1880
  return self._store.get_parameter_set_statuses(id_lst)
1756
1881
 
1757
1882
  @TimeIt.decorator
1758
1883
  def get_parameter(self, index: int, **kwargs: Dict) -> AnySParameter:
1884
+ """
1885
+ Get a single parameter.
1886
+ """
1759
1887
  return self.get_parameters([index], **kwargs)[0]
1760
1888
 
1761
1889
  @TimeIt.decorator
1762
1890
  def get_parameter_data(self, index: int, **kwargs: Dict) -> Any:
1891
+ """
1892
+ Get the data relating to a parameter.
1893
+ """
1763
1894
  param = self.get_parameter(index, **kwargs)
1764
1895
  if param.data is not None:
1765
1896
  return param.data
@@ -1768,22 +1899,28 @@ class Workflow:
1768
1899
 
1769
1900
  @TimeIt.decorator
1770
1901
  def get_parameter_source(self, index: int) -> Dict:
1902
+ """
1903
+ Get the source of a particular parameter.
1904
+ """
1771
1905
  return self.get_parameter_sources([index])[0]
1772
1906
 
1773
1907
  @TimeIt.decorator
1774
1908
  def is_parameter_set(self, index: int) -> bool:
1909
+ """
1910
+ Test if a particular parameter is set.
1911
+ """
1775
1912
  return self.get_parameter_set_statuses([index])[0]
1776
1913
 
1777
1914
  @TimeIt.decorator
1778
1915
  def get_all_parameters(self, **kwargs: Dict) -> List[AnySParameter]:
1779
- """Retrieve all store parameters."""
1916
+ """Retrieve all persistent parameters."""
1780
1917
  num_params = self._store._get_num_total_parameters()
1781
1918
  id_lst = list(range(num_params))
1782
1919
  return self._store.get_parameters(id_lst, **kwargs)
1783
1920
 
1784
1921
  @TimeIt.decorator
1785
1922
  def get_all_parameter_sources(self, **kwargs: Dict) -> List[Dict]:
1786
- """Retrieve all store parameters."""
1923
+ """Retrieve all persistent parameters sources."""
1787
1924
  num_params = self._store._get_num_total_parameters()
1788
1925
  id_lst = list(range(num_params))
1789
1926
  return self._store.get_parameter_sources(id_lst, **kwargs)
@@ -1797,6 +1934,9 @@ class Workflow:
1797
1934
  def check_parameters_exist(
1798
1935
  self, id_lst: Union[int, List[int]]
1799
1936
  ) -> Union[bool, List[bool]]:
1937
+ """
1938
+ Check if parameters exist.
1939
+ """
1800
1940
  is_multi = True
1801
1941
  if isinstance(id_lst, int):
1802
1942
  is_multi = False
@@ -1858,7 +1998,7 @@ class Workflow:
1858
1998
 
1859
1999
  Parameters
1860
2000
  ----------
1861
- map_to_insert_ID : bool, optional
2001
+ map_to_insert_ID : bool
1862
2002
  If True, return a dict whose values are task insert IDs, otherwise return a
1863
2003
  list.
1864
2004
 
@@ -1922,48 +2062,81 @@ class Workflow:
1922
2062
 
1923
2063
  @property
1924
2064
  def num_tasks(self):
2065
+ """
2066
+ The total number of tasks.
2067
+ """
1925
2068
  return self._store._get_num_total_tasks()
1926
2069
 
1927
2070
  @property
1928
2071
  def num_submissions(self):
2072
+ """
2073
+ The total number of job submissions.
2074
+ """
1929
2075
  return self._store._get_num_total_submissions()
1930
2076
 
1931
2077
  @property
1932
2078
  def num_elements(self):
2079
+ """
2080
+ The total number of elements.
2081
+ """
1933
2082
  return self._store._get_num_total_elements()
1934
2083
 
1935
2084
  @property
1936
2085
  def num_element_iterations(self):
2086
+ """
2087
+ The total number of element iterations.
2088
+ """
1937
2089
  return self._store._get_num_total_elem_iters()
1938
2090
 
1939
2091
  @property
1940
2092
  @TimeIt.decorator
1941
2093
  def num_EARs(self):
2094
+ """
2095
+ The total number of element action runs.
2096
+ """
1942
2097
  return self._store._get_num_total_EARs()
1943
2098
 
1944
2099
  @property
1945
2100
  def num_loops(self) -> int:
2101
+ """
2102
+ The total number of loops.
2103
+ """
1946
2104
  return self._store._get_num_total_loops()
1947
2105
 
1948
2106
  @property
1949
2107
  def artifacts_path(self):
2108
+ """
2109
+ Path to artifacts of the workflow (temporary files, etc).
2110
+ """
1950
2111
  # TODO: allow customisation of artifacts path at submission and resources level
1951
2112
  return Path(self.path) / "artifacts"
1952
2113
 
1953
2114
  @property
1954
2115
  def input_files_path(self):
2116
+ """
2117
+ Path to input files for the workflow.
2118
+ """
1955
2119
  return self.artifacts_path / self._input_files_dir_name
1956
2120
 
1957
2121
  @property
1958
2122
  def submissions_path(self):
2123
+ """
2124
+ Path to submission data for ths workflow.
2125
+ """
1959
2126
  return self.artifacts_path / "submissions"
1960
2127
 
1961
2128
  @property
1962
2129
  def task_artifacts_path(self):
2130
+ """
2131
+ Path to artifacts of tasks.
2132
+ """
1963
2133
  return self.artifacts_path / "tasks"
1964
2134
 
1965
2135
  @property
1966
2136
  def execution_path(self):
2137
+ """
2138
+ Path to working directory path for executing.
2139
+ """
1967
2140
  return Path(self.path) / self._exec_dir_name
1968
2141
 
1969
2142
  @TimeIt.decorator
@@ -1972,6 +2145,9 @@ class Workflow:
1972
2145
  task: app.Task,
1973
2146
  idx_lst: Optional[List[int]] = None,
1974
2147
  ) -> List[app.Element]:
2148
+ """
2149
+ Get the elements of a task.
2150
+ """
1975
2151
  return [
1976
2152
  self.app.Element(task=task, **{k: v for k, v in i.items() if k != "task_ID"})
1977
2153
  for i in self._store.get_task_elements(task.insert_ID, idx_lst)
@@ -2107,8 +2283,10 @@ class Workflow:
2107
2283
  self._store.set_EAR_end(EAR_ID, exit_code, success)
2108
2284
 
2109
2285
  def set_EAR_skip(self, EAR_ID: int) -> None:
2110
- """Record that an EAR is to be skipped due to an upstream failure or loop
2111
- termination condition being met."""
2286
+ """
2287
+ Record that an EAR is to be skipped due to an upstream failure or loop
2288
+ termination condition being met.
2289
+ """
2112
2290
  with self._store.cached_load():
2113
2291
  with self.batch_update():
2114
2292
  self._store.set_EAR_skip(EAR_ID)
@@ -2122,6 +2300,9 @@ class Workflow:
2122
2300
  def set_parameter_value(
2123
2301
  self, param_id: int, value: Any, commit: bool = False
2124
2302
  ) -> None:
2303
+ """
2304
+ Set the value of a parameter.
2305
+ """
2125
2306
  with self._store.cached_load():
2126
2307
  with self.batch_update():
2127
2308
  self._store.set_parameter_value(param_id, value)
@@ -2131,18 +2312,28 @@ class Workflow:
2131
2312
  self._store._pending.commit_all()
2132
2313
 
2133
2314
  def set_EARs_initialised(self, iter_ID: int):
2134
- """Set `ElementIteration.EARs_initialised` to True for the specified iteration."""
2315
+ """
2316
+ Set :py:attr:`~hpcflow.app.ElementIteration.EARs_initialised` to True for the
2317
+ specified iteration.
2318
+ """
2135
2319
  with self._store.cached_load():
2136
2320
  with self.batch_update():
2137
2321
  self._store.set_EARs_initialised(iter_ID)
2138
2322
 
2139
2323
  def elements(self) -> Iterator[app.Element]:
2324
+ """
2325
+ Get the elements of the workflow's tasks.
2326
+ """
2140
2327
  for task in self.tasks:
2141
2328
  for element in task.elements[:]:
2142
2329
  yield element
2143
2330
 
2144
2331
  @TimeIt.decorator
2145
2332
  def get_iteration_task_pathway(self, ret_iter_IDs=False, ret_data_idx=False):
2333
+ """
2334
+ Get the iteration task pathway.
2335
+ """
2336
+ # FIXME: I don't understand this concept, alas.
2146
2337
  pathway = []
2147
2338
  for task in self.tasks:
2148
2339
  pathway.append((task.insert_ID, {}))
@@ -2574,6 +2765,9 @@ class Workflow:
2574
2765
  def add_submission(
2575
2766
  self, tasks: Optional[List[int]] = None, JS_parallelism: Optional[bool] = None
2576
2767
  ) -> app.Submission:
2768
+ """
2769
+ Add a job submission to this workflow.
2770
+ """
2577
2771
  with self._store.cached_load():
2578
2772
  with self.batch_update():
2579
2773
  return self._add_submission(tasks, JS_parallelism)
@@ -2617,6 +2811,9 @@ class Workflow:
2617
2811
  def resolve_jobscripts(
2618
2812
  self, tasks: Optional[List[int]] = None
2619
2813
  ) -> List[app.Jobscript]:
2814
+ """
2815
+ Resolve this workflow to a set of job scripts to run.
2816
+ """
2620
2817
  js, element_deps = self._resolve_singular_jobscripts(tasks)
2621
2818
  js_deps = resolve_jobscript_dependencies(js, element_deps)
2622
2819
 
@@ -2824,6 +3021,9 @@ class Workflow:
2824
3021
  value: Any,
2825
3022
  EAR_ID: int,
2826
3023
  ):
3024
+ """
3025
+ Save a parameter where an EAR can find it.
3026
+ """
2827
3027
  self.app.logger.info(f"save parameter {name!r} for EAR_ID {EAR_ID}.")
2828
3028
  self.app.logger.debug(f"save parameter {name!r} value is {value!r}.")
2829
3029
  with self._store.cached_load():
@@ -2833,6 +3033,10 @@ class Workflow:
2833
3033
  self.set_parameter_value(param_id, value)
2834
3034
 
2835
3035
  def show_all_EAR_statuses(self):
3036
+ """
3037
+ Print a description of the status of every element action run in
3038
+ the workflow.
3039
+ """
2836
3040
  print(
2837
3041
  f"{'task':8s} {'element':8s} {'iteration':8s} {'action':8s} "
2838
3042
  f"{'run':8s} {'sub.':8s} {'exitcode':8s} {'success':8s} {'skip':8s}"
@@ -2893,6 +3097,9 @@ class Workflow:
2893
3097
  )
2894
3098
 
2895
3099
  def get_all_submission_run_IDs(self) -> List[int]:
3100
+ """
3101
+ Get the run IDs of all submissions.
3102
+ """
2896
3103
  self.app.persistence_logger.debug("Workflow.get_all_submission_run_IDs")
2897
3104
  id_lst = []
2898
3105
  for sub in self.submissions:
@@ -2918,6 +3125,9 @@ class Workflow:
2918
3125
  self.set_EAR_skip(run_ID)
2919
3126
 
2920
3127
  def get_loop_map(self, id_lst: Optional[List[int]] = None):
3128
+ """
3129
+ Get a description of what is going on with looping.
3130
+ """
2921
3131
  # TODO: test this works across multiple jobscripts
2922
3132
  self.app.persistence_logger.debug("Workflow.get_loop_map")
2923
3133
  if id_lst is None:
@@ -2962,6 +3172,9 @@ class Workflow:
2962
3172
  backup: Optional[bool] = True,
2963
3173
  status: Optional[bool] = True,
2964
3174
  ):
3175
+ """
3176
+ Reorganise the stored data chunks for EARs to be more efficient.
3177
+ """
2965
3178
  self._store.rechunk_runs(chunk_size=chunk_size, backup=backup, status=status)
2966
3179
 
2967
3180
  def rechunk_parameter_base(
@@ -2970,6 +3183,9 @@ class Workflow:
2970
3183
  backup: Optional[bool] = True,
2971
3184
  status: Optional[bool] = True,
2972
3185
  ):
3186
+ """
3187
+ Reorganise the stored data chunks for parameterss to be more efficient.
3188
+ """
2973
3189
  self._store.rechunk_parameter_base(
2974
3190
  chunk_size=chunk_size, backup=backup, status=status
2975
3191
  )
@@ -2980,13 +3196,17 @@ class Workflow:
2980
3196
  backup: Optional[bool] = True,
2981
3197
  status: Optional[bool] = True,
2982
3198
  ):
2983
- """Rechunk metadata/runs and parameters/base arrays."""
3199
+ """
3200
+ Rechunk metadata/runs and parameters/base arrays, making them more efficient.
3201
+ """
2984
3202
  self.rechunk_runs(chunk_size=chunk_size, backup=backup, status=status)
2985
3203
  self.rechunk_parameter_base(chunk_size=chunk_size, backup=backup, status=status)
2986
3204
 
2987
3205
 
2988
3206
  @dataclass
2989
3207
  class WorkflowBlueprint:
2990
- """Pre-built workflow templates that are simpler to parametrise (e.g. fitting workflows)."""
3208
+ """Pre-built workflow templates that are simpler to parameterise.
3209
+ (For example, fitting workflows.)"""
2991
3210
 
3211
+ #: The template inside this blueprint.
2992
3212
  workflow_template: WorkflowTemplate