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
hpcflow/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.2.0a179"
1
+ __version__ = "0.2.0a180"
@@ -0,0 +1,3 @@
1
+ """
2
+ Manifest for demonstration data.
3
+ """
hpcflow/sdk/__init__.py CHANGED
@@ -4,7 +4,8 @@ import logging
4
4
  import os
5
5
  import sys
6
6
 
7
- # classes used in the construction of a workflow:
7
+ #: Classes used in the construction of a workflow.
8
+ #: :meta hide-value:
8
9
  sdk_classes = {
9
10
  "Workflow": "hpcflow.sdk.core.workflow",
10
11
  "Task": "hpcflow.sdk.core.task",
@@ -95,6 +96,8 @@ sdk_classes = {
95
96
  }
96
97
 
97
98
  # these are defined as `BaseApp` methods with an underscore prefix:
99
+ #: Functions exported by the application.
100
+ #: :meta hide-value:
98
101
  sdk_funcs = (
99
102
  "make_workflow",
100
103
  "make_demo_workflow",
hpcflow/sdk/app.py CHANGED
@@ -65,7 +65,7 @@ DEMO_WK_FORMATS = {".yaml": "yaml", ".yml": "yaml", ".json": "json", ".jsonc": "
65
65
 
66
66
 
67
67
  def rate_limit_safe_url_to_fs(app, *args, logger=None, **kwargs):
68
- """Call fsspec's `url_to_fs` but retry on `requests.exceptions.HTTPError`s
68
+ R"""Call fsspec's ``url_to_fs`` but retry on ``requests.exceptions.HTTPError``\ s.
69
69
 
70
70
  References
71
71
  ----------
@@ -125,14 +125,24 @@ def get_app_attribute(name):
125
125
 
126
126
 
127
127
  def get_app_module_all():
128
+ """
129
+ The list of all symbols exported by this module.
130
+ """
128
131
  return ["app"] + list(sdk_classes.keys()) + list(sdk_funcs)
129
132
 
130
133
 
131
134
  def get_app_module_dir():
135
+ """
136
+ The sorted list of all symbols exported by this module.
137
+ """
132
138
  return lambda: sorted(get_app_module_all())
133
139
 
134
140
 
135
141
  class Singleton(type):
142
+ """
143
+ Metaclass that enforces that only one instance of a class can be made.
144
+ """
145
+
136
146
  _instances = {}
137
147
 
138
148
  def __call__(cls, *args, **kwargs):
@@ -158,15 +168,42 @@ class BaseApp(metaclass=Singleton):
158
168
 
159
169
  Parameters
160
170
  ----------
171
+ name:
172
+ The name of the application.
173
+ version:
174
+ The version of the application.
161
175
  module:
162
176
  The module name in which the app object is defined.
177
+ description:
178
+ Description of the application.
179
+ gh_org:
180
+ Name of Github organisation responsible for the application.
181
+ gh_repo:
182
+ Github repository containing the application source.
183
+ config_options:
184
+ Configuration options.
185
+ scripts_dir:
186
+ Directory for scripts.
187
+ workflows_dir:
188
+ Directory for workflows.
189
+ demo_data_dir:
190
+ Directory for demonstration data.
191
+ data_data_manifest_dir:
192
+ Directory for demonstration data manifests.
193
+ template_components:
194
+ Template components.
195
+ pytest_args:
196
+ Arguments for pytest.
197
+ package_name:
198
+ Name of package if not the application name.
163
199
  docs_import_conv:
164
200
  The convention for the app alias used in import statements in the documentation.
165
201
  E.g. for the `hpcflow` base app, this is `hf`. This is combined with `module` to
166
202
  form the complete import statement. E.g. for the `hpcflow` base app, the complete
167
203
  import statement is: `import hpcflow.app as hf`, where `hpcflow.app` is the
168
204
  `module` argument and `hf` is the `docs_import_conv` argument.
169
-
205
+ docs_url:
206
+ URL to documentation.
170
207
  """
171
208
 
172
209
  _known_subs_file_name = "known_submissions.txt"
@@ -194,22 +231,38 @@ class BaseApp(metaclass=Singleton):
194
231
  ):
195
232
  SDK_logger.info(f"Generating {self.__class__.__name__} {name!r}.")
196
233
 
234
+ #: The name of the application.
197
235
  self.name = name
236
+ #: Name of package.
198
237
  self.package_name = package_name or name.lower()
238
+ #: The version of the application.
199
239
  self.version = version
240
+ #: The module name in which the app object is defined.
200
241
  self.module = module
242
+ #: Description of the application.
201
243
  self.description = description
244
+ #: Name of Github organisation responsible for the application.
202
245
  self.gh_org = gh_org
246
+ #: Github repository containing the application source.
203
247
  self.gh_repo = gh_repo
248
+ #: Configuration options.
204
249
  self.config_options = config_options
250
+ #: Arguments for pytest.
205
251
  self.pytest_args = pytest_args
252
+ #: Directory for scripts.
206
253
  self.scripts_dir = scripts_dir
254
+ #: Directory for workflows.
207
255
  self.workflows_dir = workflows_dir
256
+ #: Directory for demonstration data.
208
257
  self.demo_data_dir = demo_data_dir
258
+ #: Directory for demonstration data manifests.
209
259
  self.demo_data_manifest_dir = demo_data_manifest_dir
260
+ #: The convention for the app alias used in import statements in the documentation.
210
261
  self.docs_import_conv = docs_import_conv
262
+ #: URL to documentation.
211
263
  self.docs_url = docs_url
212
264
 
265
+ #: Command line interface subsystem.
213
266
  self.cli = make_cli(self)
214
267
 
215
268
  self._log = AppLog(self)
@@ -293,14 +346,23 @@ class BaseApp(metaclass=Singleton):
293
346
 
294
347
  @property
295
348
  def run_time_info(self) -> RunTimeInfo:
349
+ """
350
+ Information about the runtime.
351
+ """
296
352
  return self._run_time_info
297
353
 
298
354
  @property
299
355
  def log(self) -> AppLog:
356
+ """
357
+ The application log.
358
+ """
300
359
  return self._log
301
360
 
302
361
  @property
303
362
  def timeit(self) -> bool:
363
+ """
364
+ Whether the timing analysis system is active.
365
+ """
304
366
  return TimeIt.active
305
367
 
306
368
  @timeit.setter
@@ -309,6 +371,9 @@ class BaseApp(metaclass=Singleton):
309
371
 
310
372
  @property
311
373
  def template_components(self) -> Dict[str, ObjectList]:
374
+ """
375
+ The template component data.
376
+ """
312
377
  if not self.is_template_components_loaded:
313
378
  self._load_template_components()
314
379
  return self._template_components
@@ -406,6 +471,10 @@ class BaseApp(metaclass=Singleton):
406
471
  def load_builtin_template_component_data(
407
472
  cls, package
408
473
  ) -> Dict[str, Union[List, Dict]]:
474
+ """
475
+ Load the template component data built into the package.
476
+ This is as opposed to the template components defined by users.
477
+ """
409
478
  SDK_logger.info(
410
479
  f"Loading built-in template component data for package: {package!r}."
411
480
  )
@@ -426,74 +495,119 @@ class BaseApp(metaclass=Singleton):
426
495
 
427
496
  @property
428
497
  def parameters(self) -> get_app_attribute("ParametersList"):
498
+ """
499
+ The known template parameters.
500
+ """
429
501
  self._ensure_template_component("parameters")
430
502
  return self._parameters
431
503
 
432
504
  @property
433
505
  def command_files(self) -> get_app_attribute("CommandFilesList"):
506
+ """
507
+ The known template command files.
508
+ """
434
509
  self._ensure_template_component("command_files")
435
510
  return self._command_files
436
511
 
437
512
  @property
438
513
  def envs(self) -> get_app_attribute("EnvironmentsList"):
514
+ """
515
+ The known template execution environments.
516
+ """
439
517
  self._ensure_template_component("environments")
440
518
  return self._environments
441
519
 
442
520
  @property
443
521
  def scripts(self):
522
+ """
523
+ The known template scripts.
524
+ """
444
525
  self._ensure_template_component("scripts")
445
526
  return self._scripts
446
527
 
447
528
  @property
448
529
  def task_schemas(self) -> get_app_attribute("TaskSchemasList"):
530
+ """
531
+ The known template task schemas.
532
+ """
449
533
  self._ensure_template_component("task_schemas")
450
534
  return self._task_schemas
451
535
 
452
536
  @property
453
537
  def logger(self) -> Logger:
538
+ """
539
+ The main underlying logger.
540
+ """
454
541
  return self.log.logger
455
542
 
456
543
  @property
457
544
  def API_logger(self) -> Logger:
545
+ """
546
+ The logger for API messages.
547
+ """
458
548
  return self.logger.getChild("api")
459
549
 
460
550
  @property
461
551
  def CLI_logger(self) -> Logger:
552
+ """
553
+ The logger for CLI messages.
554
+ """
462
555
  return self.logger.getChild("cli")
463
556
 
464
557
  @property
465
558
  def config_logger(self) -> Logger:
559
+ """
560
+ The logger for configuration messages.
561
+ """
466
562
  return self.logger.getChild("config")
467
563
 
468
564
  @property
469
565
  def persistence_logger(self) -> Logger:
566
+ """
567
+ The logger for persistence engine messages.
568
+ """
470
569
  return self.logger.getChild("persistence")
471
570
 
472
571
  @property
473
572
  def submission_logger(self) -> Logger:
573
+ """
574
+ The logger for job submission messages.
575
+ """
474
576
  return self.logger.getChild("submission")
475
577
 
476
578
  @property
477
579
  def runtime_info_logger(self) -> Logger:
580
+ """
581
+ The logger for runtime messages.
582
+ """
478
583
  return self.logger.getChild("runtime")
479
584
 
480
585
  @property
481
586
  def is_config_loaded(self) -> bool:
587
+ """
588
+ Whether the configuration is loaded.
589
+ """
482
590
  return bool(self._config)
483
591
 
484
592
  @property
485
593
  def is_template_components_loaded(self) -> bool:
486
- """Return True if any template component (e.g. parameters) has been loaded."""
594
+ """Whether any template component (e.g. parameters) has been loaded."""
487
595
  return bool(self._template_components)
488
596
 
489
597
  @property
490
598
  def config(self) -> Config:
599
+ """
600
+ The configuration.
601
+ """
491
602
  if not self.is_config_loaded:
492
603
  self.load_config()
493
604
  return self._config
494
605
 
495
606
  @property
496
607
  def scheduler_lookup(self):
608
+ """
609
+ The scheduler mapping.
610
+ """
497
611
  return {
498
612
  ("direct", "posix"): self.DirectPosix,
499
613
  ("direct", "nt"): self.DirectWindows,
@@ -550,35 +664,42 @@ class BaseApp(metaclass=Singleton):
550
664
 
551
665
  @property
552
666
  def user_data_dir(self) -> Path:
667
+ """
668
+ The user's data directory.
669
+ """
553
670
  if self._user_data_dir is None:
554
671
  self._user_data_dir = Path(user_data_dir(appname=self.package_name))
555
672
  return self._user_data_dir
556
673
 
557
674
  @property
558
675
  def user_cache_dir(self) -> Path:
559
- """Retrieve the app cache directory."""
676
+ """The user's cache directory."""
560
677
  if self._user_cache_dir is None:
561
678
  self._user_cache_dir = Path(user_cache_path(appname=self.package_name))
562
679
  return self._user_cache_dir
563
680
 
564
681
  @property
565
682
  def user_runtime_dir(self) -> Path:
566
- """Retrieve a temporary directory."""
683
+ """The user's temporary runtime directory."""
567
684
  if self._user_runtime_dir is None:
568
685
  self._user_runtime_dir = self.user_data_dir.joinpath("temp")
569
686
  return self._user_runtime_dir
570
687
 
571
688
  @property
572
689
  def demo_data_cache_dir(self) -> Path:
573
- """Retrieve a directory for example data caching."""
690
+ """A directory for example data caching."""
574
691
  if self._demo_data_cache_dir is None:
575
692
  self._demo_data_cache_dir = self.user_cache_dir.joinpath("demo_data")
576
693
  return self._demo_data_cache_dir
577
694
 
578
695
  @property
579
696
  def user_data_hostname_dir(self) -> Path:
580
- """We segregate by hostname to account for the case where multiple machines might
581
- use the same shared file system"""
697
+ """
698
+ The directory for holding user data.
699
+
700
+ We segregate by hostname to account for the case where multiple machines might
701
+ use the same shared file system.
702
+ """
582
703
 
583
704
  # This might need to cover e.g. multiple login nodes, as described in the
584
705
  # config file:
@@ -589,7 +710,7 @@ class BaseApp(metaclass=Singleton):
589
710
 
590
711
  @property
591
712
  def user_cache_hostname_dir(self) -> Path:
592
- """Retrieve the hostname-scoped app cache directory."""
713
+ """The hostname-scoped app cache directory."""
593
714
  if self._user_cache_hostname_dir is None:
594
715
  machine_name = self.config.get("machine")
595
716
  self._user_cache_hostname_dir = self.user_cache_dir.joinpath(machine_name)
@@ -718,11 +839,17 @@ class BaseApp(metaclass=Singleton):
718
839
  warn=True,
719
840
  **overrides,
720
841
  ) -> None:
842
+ """
843
+ Load the user's configuration.
844
+ """
721
845
  if warn and self.is_config_loaded:
722
846
  warnings.warn("Configuration is already loaded; reloading.")
723
847
  self._load_config(config_dir, config_key, **overrides)
724
848
 
725
849
  def unload_config(self):
850
+ """
851
+ Discard any loaded configuration.
852
+ """
726
853
  self._config_files = {}
727
854
  self._config = None
728
855
 
@@ -762,6 +889,10 @@ class BaseApp(metaclass=Singleton):
762
889
  warn=True,
763
890
  **overrides,
764
891
  ) -> None:
892
+ """
893
+ Reload the configuration. Use if a user has updated the configuration file
894
+ outside the scope of this application.
895
+ """
765
896
  if warn and not self.is_config_loaded:
766
897
  warnings.warn("Configuration is not loaded; loading.")
767
898
  self.log.remove_file_handlers()
@@ -909,7 +1040,10 @@ class BaseApp(metaclass=Singleton):
909
1040
  with self.get_demo_workflow_template_file(name) as path:
910
1041
  return self.WorkflowTemplate.from_file(path)
911
1042
 
912
- def template_components_from_json_like(self, json_like) -> None:
1043
+ def template_components_from_json_like(self, json_like):
1044
+ """
1045
+ Get template components from a (simply parsed) JSOM document.
1046
+ """
913
1047
  cls_lookup = {
914
1048
  "parameters": self.ParametersList,
915
1049
  "command_files": self.CommandFilesList,
@@ -944,6 +1078,9 @@ class BaseApp(metaclass=Singleton):
944
1078
  return param_map
945
1079
 
946
1080
  def get_info(self) -> Dict[str, Any]:
1081
+ """
1082
+ Get miscellaneous runtime system information.
1083
+ """
947
1084
  return {
948
1085
  "name": self.name,
949
1086
  "version": self.version,
@@ -951,13 +1088,12 @@ class BaseApp(metaclass=Singleton):
951
1088
  "is_frozen": self.run_time_info.is_frozen,
952
1089
  }
953
1090
 
954
- @property
955
- def known_subs_file_name(self):
956
- return self._known_subs_file_name
957
-
958
1091
  @property
959
1092
  def known_subs_file_path(self):
960
- return self.user_data_hostname_dir / self.known_subs_file_name
1093
+ """
1094
+ The path to the file describing known submissions.
1095
+ """
1096
+ return self.user_data_hostname_dir / self._known_subs_file_name
961
1097
 
962
1098
  def _format_known_submissions_line(
963
1099
  self,
@@ -2242,6 +2378,9 @@ class BaseApp(metaclass=Singleton):
2242
2378
  use_current_env=False,
2243
2379
  env_source_file=None,
2244
2380
  ):
2381
+ """
2382
+ Configure an execution environment.
2383
+ """
2245
2384
  if not setup:
2246
2385
  setup = []
2247
2386
  if not executables:
@@ -2505,9 +2644,15 @@ class BaseApp(metaclass=Singleton):
2505
2644
  return cache_file_path
2506
2645
 
2507
2646
  def cache_demo_data_file(self, file_name) -> Path:
2647
+ """
2648
+ Get the name of a cached demo data file.
2649
+ """
2508
2650
  return self.get_demo_data_file_path(file_name)
2509
2651
 
2510
2652
  def cache_all_demo_data_files(self) -> List[Path]:
2653
+ """
2654
+ Get the name of all cached demo data file.
2655
+ """
2511
2656
  return [self.get_demo_data_file_path(i) for i in self.list_demo_data_files()]
2512
2657
 
2513
2658
  def copy_demo_data(
hpcflow/sdk/cli.py CHANGED
@@ -1,3 +1,7 @@
1
+ """
2
+ Command line interface implementation.
3
+ """
4
+
1
5
  import json
2
6
  import os
3
7
  from typing import Dict, List
@@ -38,26 +42,36 @@ from hpcflow.sdk.cli_common import (
38
42
  rechunk_backup_opt,
39
43
  rechunk_chunk_size_opt,
40
44
  rechunk_status_opt,
45
+ _add_doc_from_help,
41
46
  )
42
47
  from hpcflow.sdk.helper.cli import get_helper_CLI
43
48
  from hpcflow.sdk.log import TimeIt
44
49
  from hpcflow.sdk.submission.shells import ALL_SHELLS
45
50
 
51
+ #: Standard option
46
52
  string_option = click.option(
47
53
  "--string",
48
54
  is_flag=True,
49
55
  default=False,
50
56
  help="Determines if passing a file path or a string.",
51
57
  )
58
+ #: Standard option
52
59
  workflow_ref_type_opt = click.option(
53
60
  "--ref-type",
54
61
  "-r",
55
62
  type=click.Choice(["assume-id", "id", "path"]),
56
63
  default="assume-id",
64
+ help="How to interpret a reference, as an ID, a path, or to guess.",
57
65
  )
58
66
 
59
67
 
68
+ _add_doc_from_help(string_option, workflow_ref_type_opt)
69
+
70
+
60
71
  def parse_jobscript_wait_spec(jobscripts: str) -> Dict[int, List[int]]:
72
+ """
73
+ Parse a jobscript wait specification.
74
+ """
61
75
  sub_js_idx_dct = {}
62
76
  for sub_i in jobscripts.split(";"):
63
77
  sub_idx_str, js_idx_lst_str = sub_i.split(":")