hpcflow-new2 0.2.0a218__py3-none-any.whl → 0.2.0a219__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.
hpcflow/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.2.0a218"
1
+ __version__ = "0.2.0a219"
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ def import_future_script():
5
+ """Used to test the `__future__` import is moved to the top of the file in the
6
+ generated script in the case of `resources.combine_scripts == True`"""
7
+ pass
hpcflow/sdk/app.py CHANGED
@@ -1794,7 +1794,8 @@ class BaseApp(metaclass=Singleton):
1794
1794
  for comp_type in TEMPLATE_COMP_TYPES:
1795
1795
  with open_text_resource(package, f"{comp_type}.yaml") as fh:
1796
1796
  SDK_logger.info(f"Parsing file as YAML: {fh.name!r}")
1797
- components[comp_type] = read_YAML_str(fh.read())
1797
+ source = f"from {Path(fh.name)!r}"
1798
+ components[comp_type] = read_YAML_str(fh.read(), source=source)
1798
1799
 
1799
1800
  return components
1800
1801
 
@@ -1020,3 +1020,9 @@ class MissingElementGroup(ValueError):
1020
1020
  f"Adding elements to task {task_name!r}: "
1021
1021
  f"no element group named {group_name!r} found for input {input_path!r}."
1022
1022
  )
1023
+
1024
+
1025
+ class YAMLError(ValueError):
1026
+ """
1027
+ A problem with parsing a YAML file.
1028
+ """
@@ -2788,31 +2788,31 @@ class InputSource(JSONLike):
2788
2788
 
2789
2789
  Examples
2790
2790
  --------
2791
- task.[task_ref].input
2792
- task.[task_ref].output
2793
- local
2794
- default
2795
- import.[import_ref]
2791
+ For a local task input source, use:
2792
+
2793
+ >>> InputSource.from_string("local")
2794
+
2795
+ For a schema input default source, use:
2796
+
2797
+ >>> InputSource.from_string("default")
2798
+
2799
+ For task input sources, specify either the task insert ID (typically this is just
2800
+ the task index within the workflow), or the task's unique name, which is usually
2801
+ just the associated task schema's objective, but if multiple tasks use the same
2802
+ schema, it will be suffixed by an index, starting from one.
2803
+
2804
+ >>> InputSource.from_string("task.0.input")
2805
+ >>> InputSource.from_string("task.my_task.input")
2796
2806
  """
2797
2807
  return cls(**cls._parse_from_string(str_defn))
2798
2808
 
2799
2809
  @staticmethod
2800
2810
  def _parse_from_string(str_defn: str) -> dict[str, Any]:
2801
- """Parse a dot-delimited string definition of an InputSource.
2802
-
2803
- Examples
2804
- --------
2805
- task.[task_ref].input
2806
- task.[task_ref].output
2807
- local
2808
- default
2809
- import.[import_ref]
2810
- """
2811
+ """Parse a dot-delimited string definition of an InputSource."""
2811
2812
  parts = str_defn.split(".")
2812
2813
  source_type = get_enum_by_name_or_val(InputSourceType, parts[0])
2813
- task_ref: int | None = None
2814
+ task_ref: int | str | None = None
2814
2815
  task_source_type: TaskSourceType | None = None
2815
- import_ref: int | None = None
2816
2816
  if (
2817
2817
  (
2818
2818
  source_type in (InputSourceType.LOCAL, InputSourceType.DEFAULT)
@@ -2826,22 +2826,20 @@ class InputSource(JSONLike):
2826
2826
  if source_type is InputSourceType.TASK:
2827
2827
  # TODO: does this include element_iters?
2828
2828
  try:
2829
+ # assume specified by task insert ID
2829
2830
  task_ref = int(parts[1])
2830
2831
  except ValueError:
2831
- pass
2832
+ # assume specified by task unique name
2833
+ task_ref = parts[1]
2832
2834
  try:
2833
2835
  task_source_type = get_enum_by_name_or_val(TaskSourceType, parts[2])
2834
2836
  except IndexError:
2835
2837
  task_source_type = TaskSourceType.OUTPUT
2836
2838
  elif source_type is InputSourceType.IMPORT:
2837
- try:
2838
- import_ref = int(parts[1])
2839
- except ValueError:
2840
- pass
2839
+ raise NotImplementedError("Import input sources are not yet supported.")
2841
2840
 
2842
2841
  return {
2843
2842
  "source_type": source_type,
2844
- "import_ref": import_ref,
2845
2843
  "task_ref": task_ref,
2846
2844
  "task_source_type": task_source_type,
2847
2845
  }
hpcflow/sdk/core/utils.py CHANGED
@@ -30,12 +30,14 @@ import fsspec # type: ignore
30
30
  import numpy as np
31
31
 
32
32
  from ruamel.yaml import YAML
33
+ from ruamel.yaml.error import MarkedYAMLError
33
34
  from watchdog.utils.dirsnapshot import DirectorySnapshot
34
35
 
35
36
  from hpcflow.sdk.core.errors import (
36
37
  ContainerKeyError,
37
38
  InvalidIdentifier,
38
39
  MissingVariableSubstitutionError,
40
+ YAMLError,
39
41
  )
40
42
  from hpcflow.sdk.log import TimeIt
41
43
  from hpcflow.sdk.utils.deferred_file import DeferredFileWriter
@@ -412,7 +414,10 @@ def substitute_string_vars(string: str, variables: dict[str, str]):
412
414
 
413
415
  @TimeIt.decorator
414
416
  def read_YAML_str(
415
- yaml_str: str, typ="safe", variables: dict[str, str] | Literal[False] | None = None
417
+ yaml_str: str,
418
+ typ="safe",
419
+ variables: dict[str, str] | Literal[False] | None = None,
420
+ source: str | None = None,
416
421
  ) -> Any:
417
422
  """Load a YAML string. This will produce basic objects.
418
423
 
@@ -426,11 +431,21 @@ def read_YAML_str(
426
431
  String variables to substitute in `yaml_str`. Substitutions will be attempted if
427
432
  the file looks to contain variable references (like "<<var:name>>"). If set to
428
433
  `False`, no substitutions will occur.
434
+ source:
435
+ Used to document the source of the YAML string if raising a parsing error.
436
+ Typically, this should be a string that starts with "from ...", e.g.
437
+ "from the file path '/path/to/bad/file'".
429
438
  """
430
439
  if variables is not False and "<<var:" in yaml_str:
431
440
  yaml_str = substitute_string_vars(yaml_str, variables=variables or {})
432
441
  yaml = YAML(typ=typ)
433
- return yaml.load(yaml_str)
442
+ try:
443
+ return yaml.load(yaml_str)
444
+ except MarkedYAMLError as err: # includes `ScannerError` and `ParserError`
445
+ source_str = f"{source} " if source else ""
446
+ raise YAMLError(
447
+ f"The YAML string {source_str}is not formatted correctly."
448
+ ) from err
434
449
 
435
450
 
436
451
  @TimeIt.decorator
@@ -452,7 +467,7 @@ def read_YAML_file(
452
467
  """
453
468
  with fsspec.open(path, "rt") as f:
454
469
  yaml_str: str = f.read()
455
- return read_YAML_str(yaml_str, typ=typ, variables=variables)
470
+ return read_YAML_str(yaml_str, typ=typ, variables=variables, source=f"from {path!r}")
456
471
 
457
472
 
458
473
  def write_YAML_file(obj, path: str | Path, typ: str = "safe") -> None:
@@ -578,7 +578,13 @@ class WorkflowTemplate(JSONLike):
578
578
  set to `False`, no substitutions will occur, which may result in an invalid
579
579
  workflow template!
580
580
  """
581
- return cls._from_data(read_YAML_str(string, variables=variables))
581
+ return cls._from_data(
582
+ read_YAML_str(
583
+ string,
584
+ variables=variables,
585
+ source="(from the inline workflow template definition)",
586
+ )
587
+ )
582
588
 
583
589
  @classmethod
584
590
  def _check_name(cls, data: dict[str, Any], path: PathLike) -> None:
@@ -962,9 +968,12 @@ class Workflow(AppAware):
962
968
  f"({task.name!r})..."
963
969
  )
964
970
  wk._add_task(task)
965
- if status:
966
- status.update(f"Preparing to add {len(template.loops)} loops...")
967
971
  if template.loops:
972
+ if status:
973
+ status.update(
974
+ f"Preparing to add {len(template.loops)} loops; building "
975
+ f"cache..."
976
+ )
968
977
  # TODO: if loop with non-initialisable actions, will fail
969
978
  cache = LoopCache.build(workflow=wk, loops=template.loops)
970
979
  for idx, loop in enumerate(template.loops):
@@ -979,6 +988,8 @@ class Workflow(AppAware):
979
988
  f"Added {len(template.loops)} loops. "
980
989
  f"Committing to store..."
981
990
  )
991
+ elif status:
992
+ status.update("Committing to store...")
982
993
  except (Exception, NotImplementedError):
983
994
  if status:
984
995
  status.stop()
@@ -4235,8 +4246,7 @@ class Workflow(AppAware):
4235
4246
  input_source.task_ref = uniq_names_cur[input_source.task_ref]
4236
4247
  except KeyError:
4237
4248
  raise InvalidInputSourceTaskReference(
4238
- f"Input source {input_source.to_string()!r} refers to a missing "
4239
- f"or inaccessible task: {input_source.task_ref!r}."
4249
+ input_source, task_ref=input_source.task_ref
4240
4250
  )
4241
4251
 
4242
4252
  @TimeIt.decorator
@@ -124,7 +124,7 @@ def _encode_numpy_array(
124
124
  new_idx = (
125
125
  max((int(i.removeprefix("arr_")) for i in param_arr_group.keys()), default=-1) + 1
126
126
  )
127
- param_arr_group.create_dataset(name=f"arr_{new_idx}", data=obj)
127
+ param_arr_group.create_dataset(name=f"arr_{new_idx}", data=obj, chunks=obj.shape)
128
128
  type_lookup["arrays"].append([path, new_idx])
129
129
 
130
130
  return len(type_lookup["arrays"]) - 1
@@ -24,6 +24,7 @@ from hpcflow.sdk.core.errors import (
24
24
  from hpcflow.sdk.typing import hydrate
25
25
  from hpcflow.sdk.core.json_like import ChildObjectSpec, JSONLike
26
26
  from hpcflow.sdk.core.utils import nth_value, parse_timestamp, current_timestamp
27
+ from hpcflow.sdk.utils.strings import extract_py_from_future_imports
27
28
  from hpcflow.sdk.log import TimeIt
28
29
  from hpcflow.sdk.submission.schedulers import QueuedScheduler
29
30
  from hpcflow.sdk.submission.schedulers.direct import DirectScheduler
@@ -1942,10 +1943,13 @@ class Jobscript(JSONLike):
1942
1943
  tab_indent = " "
1943
1944
 
1944
1945
  script_funcs_lst: list[str] = []
1946
+ future_imports: set[str] = set()
1945
1947
  for act_name, (_, snip_path) in script_data.items():
1946
1948
  main_func_name = snip_path.stem
1947
1949
  with snip_path.open("rt") as fp:
1948
1950
  script_str = fp.read()
1951
+ script_str, future_imports_i = extract_py_from_future_imports(script_str)
1952
+ future_imports.update(future_imports_i)
1949
1953
  script_funcs_lst.append(
1950
1954
  dedent(
1951
1955
  """\
@@ -2325,13 +2329,22 @@ class Jobscript(JSONLike):
2325
2329
  func_invoc_lines=indent(func_invoc_lines, tab_indent * 4),
2326
2330
  )
2327
2331
 
2332
+ future_imports_str = (
2333
+ f"from __future__ import {', '.join(future_imports)}\n\n"
2334
+ if future_imports
2335
+ else ""
2336
+ )
2328
2337
  script = dedent(
2329
2338
  """\
2330
- {script_funcs}
2339
+ {future_imports_str}{script_funcs}
2331
2340
  if __name__ == "__main__":
2332
2341
  {main}
2333
2342
  """
2334
- ).format(script_funcs=script_funcs, main=indent(main, tab_indent))
2343
+ ).format(
2344
+ future_imports_str=future_imports_str,
2345
+ script_funcs=script_funcs,
2346
+ main=indent(main, tab_indent),
2347
+ )
2335
2348
 
2336
2349
  num_elems = [i.num_elements for i in self.blocks]
2337
2350
  num_acts = [len(i) for i in action_scripts]
@@ -1,4 +1,5 @@
1
1
  from typing import Iterable
2
+ import re
2
3
 
3
4
 
4
5
  def shorten_list_str(
@@ -31,3 +32,30 @@ def shorten_list_str(
31
32
  lst_short = lst[:start_num] + ["..."] + lst[-end_num:]
32
33
 
33
34
  return "[" + ", ".join(f"{i}" for i in lst_short) + "]"
35
+
36
+
37
+ def extract_py_from_future_imports(py_str: str) -> tuple[str, set[str]]:
38
+ """
39
+ Remove any `from __future__ import <feature>` lines from a string of Python code, and
40
+ return the modified string, and a list of `<feature>`s that were imported.
41
+
42
+ Notes
43
+ -----
44
+ This is required when generated a combined-scripts jobscript that concatenates
45
+ multiple Python scripts into one script. If `__future__` statements are included in
46
+ these individual scripts, they must be moved to the top of the file [1].
47
+
48
+ References
49
+ ----------
50
+ [1] https://docs.python.org/3/reference/simple_stmts.html#future-statements
51
+
52
+ """
53
+
54
+ pattern = r"^from __future__ import (.*)\n"
55
+ if future_imports := (set(re.findall(pattern, py_str, flags=re.MULTILINE) or ())):
56
+ future_imports = {
57
+ j.strip() for i in future_imports for j in i.split(",") if j.strip()
58
+ }
59
+ py_str = re.sub(pattern, "", py_str, flags=re.MULTILINE)
60
+
61
+ return (py_str, future_imports)
@@ -6,6 +6,7 @@ import time
6
6
  import pytest
7
7
 
8
8
  from hpcflow.app import app as hf
9
+ from hpcflow.sdk.core.enums import EARStatus
9
10
  from hpcflow.sdk.core.test_utils import P1_parameter_cls as P1
10
11
 
11
12
  # note: when testing the frozen app, we might not have MatFlow installed in the built in
@@ -1332,3 +1333,29 @@ def test_combine_scripts_script_data_multiple_input_file_formats(
1332
1333
  assert isinstance(t1_p3, hf.ElementParameter)
1333
1334
  assert t0_p2.value == p1_val + 100
1334
1335
  assert t1_p3.value == p1_val + 100
1336
+
1337
+
1338
+ @pytest.mark.integration
1339
+ @pytest.mark.skipif("hf.run_time_info.is_frozen")
1340
+ def test_combine_scripts_from_future_import(null_config, tmp_path: Path):
1341
+ s1 = hf.TaskSchema(
1342
+ objective="t1",
1343
+ actions=[
1344
+ hf.Action(
1345
+ script="<<script:import_future_script.py>>",
1346
+ script_exe="python_script",
1347
+ environments=[hf.ActionEnvironment(environment="python_env")],
1348
+ ),
1349
+ ],
1350
+ )
1351
+
1352
+ wk = hf.Workflow.from_template_data(
1353
+ template_name="test_future_import",
1354
+ tasks=[hf.Task(schema=s1)],
1355
+ resources={"any": {"combine_scripts": True}},
1356
+ path=tmp_path,
1357
+ )
1358
+ wk.submit(status=False, add_to_known=False, wait=True)
1359
+
1360
+ run = wk.get_EARs_from_IDs([0])[0]
1361
+ assert run.status is EARStatus.success
@@ -121,3 +121,28 @@ def test_nesting_order_paths_raise(null_config) -> None:
121
121
 
122
122
  def test_nesting_order_paths_no_raise(null_config) -> None:
123
123
  hf.ElementSet(nesting_order={"inputs.p1": 1, "resources.any": 2, "repeats": 3})
124
+
125
+
126
+ def test_input_source_str_dict_list_str_list_dict_equivalence(null_config) -> None:
127
+ inp_source_dict: dict[str, str | int] = {
128
+ "source_type": "task",
129
+ "task_source_type": "output",
130
+ "task_ref": 0,
131
+ }
132
+ inp_source_str = "task.0.output"
133
+ inp_source_list_dict = [inp_source_dict]
134
+ inp_source_list_str = [inp_source_str]
135
+ assert (
136
+ hf.ElementSet.from_json_like(
137
+ {"input_sources": {"p1": inp_source_dict}}
138
+ ).input_sources
139
+ == hf.ElementSet.from_json_like(
140
+ {"input_sources": {"p1": inp_source_list_dict}}
141
+ ).input_sources
142
+ == hf.ElementSet.from_json_like(
143
+ {"input_sources": {"p1": inp_source_str}}
144
+ ).input_sources
145
+ == hf.ElementSet.from_json_like(
146
+ {"input_sources": {"p1": inp_source_list_str}}
147
+ ).input_sources
148
+ )
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+ from textwrap import dedent
2
3
  from typing import TYPE_CHECKING
3
4
  import numpy as np
4
5
  import pytest
@@ -183,6 +184,7 @@ def test_input_source_from_string_task_same_default_task_source() -> None:
183
184
  )
184
185
 
185
186
 
187
+ @pytest.mark.skip(reason="Import not yet implemented.")
186
188
  def test_input_source_from_string_import() -> None:
187
189
  import_ref = 0
188
190
  assert hf.InputSource.from_string(f"import.{import_ref}") == hf.InputSource(
@@ -1283,3 +1285,72 @@ def test_input_source_inputs_from_multiple_element_sets_with_sub_parameter_seque
1283
1285
  {"a": 5, "b": 22},
1284
1286
  {"a": 5, "b": 22},
1285
1287
  ]
1288
+
1289
+
1290
+ def test_input_source_task_ref_equivalence(null_config, tmp_path):
1291
+ yml = dedent(
1292
+ """\
1293
+ name: test
1294
+ template_components:
1295
+ task_schemas:
1296
+ - objective: t1
1297
+ inputs:
1298
+ - parameter: p1
1299
+ tasks:
1300
+ - schema: t1
1301
+ inputs:
1302
+ p1: 100 # all subsequent tasks will source from this input
1303
+
1304
+ - schema: t1 # t1_2
1305
+ input_sources: # single source dict; by task insert ID
1306
+ p1:
1307
+ source_type: task
1308
+ task_source_type: input
1309
+ task_ref: 0
1310
+
1311
+ - schema: t1 # t1_3
1312
+ input_sources: # as a list of dicts; by task insert ID
1313
+ p1:
1314
+ - source_type: task
1315
+ task_source_type: input
1316
+ task_ref: 0
1317
+
1318
+ - schema: t1 # t1_4
1319
+ input_sources: # as a single source dict; by task unique name
1320
+ p1:
1321
+ source_type: task
1322
+ task_source_type: input
1323
+ task_ref: t1_1
1324
+
1325
+ - schema: t1 # t1_5
1326
+ input_sources: # as a list of dicts; by task unique name
1327
+ p1:
1328
+ - source_type: task
1329
+ task_source_type: input
1330
+ task_ref: t1_1
1331
+
1332
+ - schema: t1 # t1_6
1333
+ input_sources: # single source string; by task insert ID
1334
+ p1: task.0.input
1335
+
1336
+ - schema: t1 # t1_7
1337
+ input_sources: # as a list of strings; by task insert ID
1338
+ p1:
1339
+ - task.0.input
1340
+
1341
+ - schema: t1 # t1_8
1342
+ input_sources: # single source string; by task unique name
1343
+ p1: task.t1_1.input
1344
+
1345
+ - schema: t1 # t1_9
1346
+ input_sources: # as a list of strings; by task unique name
1347
+ p1:
1348
+ - task.t1_1.input
1349
+
1350
+ """
1351
+ )
1352
+ wk = hf.Workflow.from_YAML_string(YAML_str=yml, path=tmp_path)
1353
+
1354
+ all_sources = (task.elements[0].input_sources["inputs.p1"] for task in wk.tasks[1:])
1355
+ all_task_refs = (src.task_ref for src in all_sources)
1356
+ assert all(task_ref == 0 for task_ref in all_task_refs)
@@ -1,9 +1,14 @@
1
1
  from pathlib import Path
2
+ from textwrap import dedent
2
3
  import pytest
3
4
  import zarr # type: ignore
4
5
  import numpy as np
5
6
  from numpy.typing import NDArray
6
- from hpcflow.sdk.core.errors import InvalidIdentifier, MissingVariableSubstitutionError
7
+ from hpcflow.sdk.core.errors import (
8
+ InvalidIdentifier,
9
+ MissingVariableSubstitutionError,
10
+ YAMLError,
11
+ )
7
12
 
8
13
  from hpcflow.sdk.core.utils import (
9
14
  JSONLikeDirSnapShot,
@@ -16,6 +21,7 @@ from hpcflow.sdk.core.utils import (
16
21
  nth_key,
17
22
  nth_value,
18
23
  process_string_nodes,
24
+ read_YAML_str,
19
25
  replace_items,
20
26
  check_valid_py_identifier,
21
27
  reshape,
@@ -575,3 +581,36 @@ def test_nth_key_raises():
575
581
 
576
582
  with pytest.raises(Exception):
577
583
  nth_key(dct, -1)
584
+
585
+
586
+ def test_read_YAML_str():
587
+ good_yaml = dedent(
588
+ """\
589
+ a: 1
590
+ b: 2
591
+ """
592
+ )
593
+ assert read_YAML_str(good_yaml) == {"a": 1, "b": 2}
594
+
595
+
596
+ def test_read_YAML_str_raise_on_bad_indent():
597
+ bad_yaml = dedent(
598
+ """\
599
+ a: 1
600
+ b: 2
601
+ """
602
+ )
603
+ with pytest.raises(YAMLError):
604
+ read_YAML_str(bad_yaml)
605
+
606
+
607
+ def test_read_YAML_str_raise_on_mixed_tabs_spaces():
608
+ bad_yaml = dedent(
609
+ """\
610
+ a:
611
+ a1: 2 # this has a space indent
612
+ a2: 3 # this has a tab indent
613
+ """
614
+ )
615
+ with pytest.raises(YAMLError):
616
+ read_YAML_str(bad_yaml)
@@ -0,0 +1,97 @@
1
+ from textwrap import dedent
2
+
3
+ from hpcflow.sdk.utils.strings import extract_py_from_future_imports
4
+
5
+
6
+ def test_extract_py_from_future_imports_none():
7
+ py_str = dedent(
8
+ """\
9
+
10
+ def my_function():
11
+ print("blah!")
12
+ """
13
+ )
14
+ new_str, imports = extract_py_from_future_imports(py_str)
15
+ assert imports == set()
16
+ assert new_str == py_str
17
+
18
+
19
+ def test_extract_py_from_future_imports_single():
20
+ py_str = dedent(
21
+ """\
22
+ from __future__ import annotations
23
+
24
+ def my_function():
25
+ print("blah!")
26
+ """
27
+ )
28
+ new_str, imports = extract_py_from_future_imports(py_str)
29
+ assert imports == {"annotations"}
30
+ assert new_str == dedent(
31
+ """\
32
+
33
+ def my_function():
34
+ print("blah!")
35
+ """
36
+ )
37
+
38
+
39
+ def test_extract_py_from_future_imports_multi():
40
+ py_str = dedent(
41
+ """\
42
+ from __future__ import annotations, feature_2
43
+
44
+ def my_function():
45
+ print("blah!")
46
+ """
47
+ )
48
+ new_str, imports = extract_py_from_future_imports(py_str)
49
+ assert imports == {"annotations", "feature_2"}
50
+ assert new_str == dedent(
51
+ """\
52
+
53
+ def my_function():
54
+ print("blah!")
55
+ """
56
+ )
57
+
58
+
59
+ def test_extract_py_from_future_imports_trailing_comma():
60
+ py_str = dedent(
61
+ """\
62
+ from __future__ import annotations,
63
+
64
+ def my_function():
65
+ print("blah!")
66
+ """
67
+ )
68
+ new_str, imports = extract_py_from_future_imports(py_str)
69
+ assert imports == {"annotations"}
70
+ assert new_str == dedent(
71
+ """\
72
+
73
+ def my_function():
74
+ print("blah!")
75
+ """
76
+ )
77
+
78
+
79
+ def test_extract_py_from_future_imports_multi_lines():
80
+ py_str = dedent(
81
+ """\
82
+ from __future__ import annotations, feature_2
83
+ from __future__ import feature_2, feature_3,
84
+
85
+ def my_function():
86
+ print("blah!")
87
+ """
88
+ )
89
+ new_str, imports = extract_py_from_future_imports(py_str)
90
+ assert imports == {"annotations", "feature_2", "feature_3"}
91
+ assert new_str == dedent(
92
+ """\
93
+
94
+ def my_function():
95
+ print("blah!")
96
+ """
97
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hpcflow-new2
3
- Version: 0.2.0a218
3
+ Version: 0.2.0a219
4
4
  Summary: Computational workflow management
5
5
  License: MPL-2.0
6
6
  Author: aplowman
@@ -1,7 +1,7 @@
1
1
  hpcflow/__init__.py,sha256=WIETuRHeOp2SqUqHUzpjQ-lk9acbYv-6aWOhZPRdlhs,64
2
2
  hpcflow/__pyinstaller/__init__.py,sha256=YOzBlPSck6slucv6lJM9K80JtsJWxXRL00cv6tRj3oc,98
3
3
  hpcflow/__pyinstaller/hook-hpcflow.py,sha256=P2b-8QdQqkSS7cJB6CB3CudUuJ9iZzTh2fQF4hNdCa4,1118
4
- hpcflow/_version.py,sha256=Mke3CFbH1LlDjsfIqc62QRCiwiI5qesSPGG_hYGm1WA,26
4
+ hpcflow/_version.py,sha256=UPqegR29afxElZxjtVwzLBfjeBP_T5TFhl_3XnX3PkM,26
5
5
  hpcflow/app.py,sha256=gl2viVS65PbpDhUp2DARaYHFDqDWQjuoyB3ikrCNRW4,1367
6
6
  hpcflow/cli.py,sha256=G2J3D9v6MnMWOWMMWK6UEKLn_6wnV9lT_qygEBBxg-I,66
7
7
  hpcflow/data/demo_data_manifest/__init__.py,sha256=Hsq0jT8EXM13wu1MpGy5FQgyuz56ygep4VWOnulFn50,41
@@ -20,6 +20,7 @@ hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out
20
20
  hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py,sha256=l50Pvl5bZSeD-BImdaQ4dZ6lTaNKjrxnLFbz-YvtJok,139
21
21
  hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py,sha256=CJe8th-rNseGhzYarIDGfIK2eJoLBU2tKRXqzPIPfDI,137
22
22
  hpcflow/data/scripts/generate_t1_file_01.py,sha256=QtrtIC-lDZynzhIUxjJPTSAjLffSug-HMWEpnmNpHlI,168
23
+ hpcflow/data/scripts/import_future_script.py,sha256=LRkxJla-Q42JFMzq52AfKIEnJZVnEpJ-QjuvBPGnnPY,232
23
24
  hpcflow/data/scripts/input_file_generator_basic.py,sha256=wekw1-XIyg1zPxgrvBUlVJRlSqMUC0JrfbK8xDQRdTE,101
24
25
  hpcflow/data/scripts/input_file_generator_basic_FAIL.py,sha256=uNho_hqvGlqeRFhReDZTvLUUL4qef3QB_3Q5luvHeqI,104
25
26
  hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py,sha256=yqOgBJlhRvXxBhRtT1VD_DEAzEjRrkwxLGen9TAFyVs,172
@@ -63,7 +64,7 @@ hpcflow/data/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
63
64
  hpcflow/data/workflows/workflow_1.yaml,sha256=lF7Re2SVc_5gQk5AwB0gXaq-n-T5ia4su3zNQ9oMRV0,220
64
65
  hpcflow/examples.ipynb,sha256=cLKp4QsxwwMXRanDnfWY9kqsV23q6G4raOpu6IZXnMw,28553
65
66
  hpcflow/sdk/__init__.py,sha256=BEbIz8adCAX06ZmvEcquEu29PmQDvVP1I1qn_q78dYc,6289
66
- hpcflow/sdk/app.py,sha256=Gn-b-tVcfWZJWtS6aANDj0RFXkoEcngr0pmpUV4suUk,146660
67
+ hpcflow/sdk/app.py,sha256=ZFMnEV43dlZlnTpJNzBsfRZ1ndiMGyeJ3hKxlhrm39I,146726
67
68
  hpcflow/sdk/cli.py,sha256=C_7Ts6TbspTZtUXlPwF37Vt-t71NABJ_2zNpZLYFPUE,45563
68
69
  hpcflow/sdk/cli_common.py,sha256=MO0TbC280JdZTLnxSV15wE7PDNx83sotpJsyAN1E2F4,10458
69
70
  hpcflow/sdk/config/__init__.py,sha256=pkufl_CXm_Kswk1x7LTavuplWjUbUCitK0AJ2E60gwM,137
@@ -82,13 +83,13 @@ hpcflow/sdk/core/commands.py,sha256=vFyZ9Cv_tvGqSGtVEnAHAeXA-HAxWoF0nmf1e0bFCvs,
82
83
  hpcflow/sdk/core/element.py,sha256=pPRezx0HATKH55qEstXp1xOG4S8oGLVnJ3sQcEmLsXo,68152
83
84
  hpcflow/sdk/core/enums.py,sha256=BV_cFqTWkME3zVLswmb21QXeIiCBb9_9avr6VIhL3HM,4387
84
85
  hpcflow/sdk/core/environment.py,sha256=m0Qp4Y1XcPJ1yPrdHp6RJv9_H61nA9Qo2ZJV_-wGne8,7959
85
- hpcflow/sdk/core/errors.py,sha256=5Bjk1w8FdVP4X7oFCNtzxJ_q4uaSZfQausZkWvYMkS4,28782
86
+ hpcflow/sdk/core/errors.py,sha256=8PxlFewrRZU2MSPoNUiQ2jAX9iQTr9-Qr-O5hDywU_A,28869
86
87
  hpcflow/sdk/core/execute.py,sha256=Gc2SauA4QNA1W4Gi2FYoKoqkoJtGg57cgn1p87WNCIs,7221
87
88
  hpcflow/sdk/core/json_like.py,sha256=tzCpaarGCHr_NXA-vLcX1jrDYlWbZuAbUPjR6jREhX8,29414
88
89
  hpcflow/sdk/core/loop.py,sha256=vs7pNVe3Io5zNO6Xl_Tqmra9mCJUw8IESLZC4p2QFvo,50084
89
90
  hpcflow/sdk/core/loop_cache.py,sha256=C0t9UpvHFdTmw3bViZ2wccjZWtkbSbN9APvcwzBSREg,10801
90
91
  hpcflow/sdk/core/object_list.py,sha256=WvUBb-2DA7-7_7oNoPykSsTihVbGB-1cgHh8W78IzKI,29882
91
- hpcflow/sdk/core/parameters.py,sha256=EHl6v_2yQdxlFnN2Vv2slEsSxJuLFJR7CV5IDL8tWJA,98259
92
+ hpcflow/sdk/core/parameters.py,sha256=5iuqTCz9ZxQnhgDElDIRBB32PDSHnqMJ1701v-M5GVI,98597
92
93
  hpcflow/sdk/core/rule.py,sha256=LiNyKp09a6TRGJvtiB1CcNqgyAIuPhuSW2RVfDWf5FE,6274
93
94
  hpcflow/sdk/core/run_dir_files.py,sha256=yh49BSlswqqyr97x5nDT87qu2MlOLMu7Arzl_B-fxy0,2061
94
95
  hpcflow/sdk/core/skip_reason.py,sha256=xPm8bd8sbPpiZ3sx9ZeW0jxgRQ0TBokSv0XIJKrEW2Y,113
@@ -96,9 +97,9 @@ hpcflow/sdk/core/task.py,sha256=7jXQUDU0mVS33b3tVimkHAnC6k1QMofwu45qt7xvuKc,1424
96
97
  hpcflow/sdk/core/task_schema.py,sha256=LjHnpS8ccEWGDQFzGhhAzpMvWGeoUlPDY2XTY2_gqMQ,38844
97
98
  hpcflow/sdk/core/test_utils.py,sha256=UXe8P-1b_6Pn6vdgJfMVsTGJdVI65TZ2mSUgWHlVnnc,13766
98
99
  hpcflow/sdk/core/types.py,sha256=kqVXpiH8qwOVVBAw6GFvUQVdNYLEv7qIbgEkgfK0DO8,13021
99
- hpcflow/sdk/core/utils.py,sha256=NTbXX2tkfb2Wp0hpGuisnfpBNAo9j-4EXh-KVqJygqA,36342
100
+ hpcflow/sdk/core/utils.py,sha256=EFflyTr6Ju8v0AfZYQuFnJm-hNjtx0XbnFuccM7cV-k,36950
100
101
  hpcflow/sdk/core/validation.py,sha256=4-0g5z3CgjFLQ2FCM0p0mjvNqHYuLhIqvQR_kpM0tsU,1838
101
- hpcflow/sdk/core/workflow.py,sha256=Id5CSPvjP_dJUCTWuCGU4bNFmRA-ESW05miVoB44yT4,183535
102
+ hpcflow/sdk/core/workflow.py,sha256=aM4JTfaIhtDA3dOeNC4ThTA1L7_EGOyBOPMcvf6KjC8,183779
102
103
  hpcflow/sdk/core/zarr_io.py,sha256=i6WqkFXe-q1sJGTCYAbsNRXRrdkxZy6NRahTcuZPuX4,5906
103
104
  hpcflow/sdk/data/__init__.py,sha256=-YzROirohSKU2UGYj5vkCe_J2KejbzhIjUXNaJwKHLk,568
104
105
  hpcflow/sdk/data/config_file_schema.yaml,sha256=7i3z_m3GBRtLyB4c7qPngnlQWqcIq1CyCcOysDyq4es,791
@@ -124,11 +125,11 @@ hpcflow/sdk/persistence/pending.py,sha256=JB42Emk2cN639bPlNtQtoFhe5WXGWDJeFm7aFU
124
125
  hpcflow/sdk/persistence/store_resource.py,sha256=P-VZF7gMIsVvgOsHJEplK1Se4jHsaAqbdoKoQo-7_LU,5903
125
126
  hpcflow/sdk/persistence/types.py,sha256=c4vd8RW8bg8Meoz1HNXjGT8Za9Hpfl1Sxp4pLAgdV0g,9125
126
127
  hpcflow/sdk/persistence/utils.py,sha256=TuQp0BKwiy1-5WTgiMaYXn00bZb6HWHAjs4BBFE9khw,1804
127
- hpcflow/sdk/persistence/zarr.py,sha256=--H7uWNJ-SlcElFPHvbFZLMqwKsz1qh8oUAogXb8HX0,88249
128
+ hpcflow/sdk/persistence/zarr.py,sha256=yCsiNteaok-eM9x5jogeDoCPdnIpx1Yv7LgYm8qoVeA,88267
128
129
  hpcflow/sdk/runtime.py,sha256=vPNu4_DXYMnRyJenkVLq8yt6_XG4tCuwX69Oew4OWmo,11269
129
130
  hpcflow/sdk/submission/__init__.py,sha256=79xJXVkddsuj3uJz3BV9iOnAV7vCeJvLrhKccOA_dnU,67
130
131
  hpcflow/sdk/submission/enums.py,sha256=DykNHISQDmfa0oDggj6iIKugeFXJd8iF9NwqV3MN2oA,2049
131
- hpcflow/sdk/submission/jobscript.py,sha256=Td4z2OKqnqAsXgjZOVciioJph7Gj3nghpHB4396_XHM,90331
132
+ hpcflow/sdk/submission/jobscript.py,sha256=QvvJAwOAYQ6gkNkRAU1Ke4zJnQd6OneMUkAJapmPMGw,90846
132
133
  hpcflow/sdk/submission/schedulers/__init__.py,sha256=E-XBhSMflInTrQ5ZBUy8Hhq-JtAuIcCx6IM_7fI1Axw,9291
133
134
  hpcflow/sdk/submission/schedulers/direct.py,sha256=b_LS_xB0vu2tRHs-Xd08-16PC-nojRB6aNXwT9qtv0A,7493
134
135
  hpcflow/sdk/submission/schedulers/sge.py,sha256=WRohm-1eBEcadcKGg5QPd1DHvGEUV759IVtLbbDx2qU,13927
@@ -146,7 +147,7 @@ hpcflow/sdk/utils/arrays.py,sha256=7znIaqfl_bGElZ6U_da0vumWPez6ITyNrSDu_AlACDI,2
146
147
  hpcflow/sdk/utils/deferred_file.py,sha256=LYIjTPjAmbOflwUaq_-o-O6DZJCQzRHX8dl_rIWTO5c,1381
147
148
  hpcflow/sdk/utils/hashing.py,sha256=40H7yO-oYgrt6RCdlTDDzJV9YIGiQeDx6zGpMJrRKZo,484
148
149
  hpcflow/sdk/utils/patches.py,sha256=jpbodcOjhE5ccrHPz5-L-r9yC7s2ymQccIdJi6Zd2Jo,398
149
- hpcflow/sdk/utils/strings.py,sha256=8zaZYCeaL5ShYFtlKhYIcmGfKsgWm4BFNBp0yD9wK0k,959
150
+ hpcflow/sdk/utils/strings.py,sha256=MGdVeQpEZjo_9_VSMNI2ArAesQpgAx4-RkHL7IAQInQ,1966
150
151
  hpcflow/tests/api/test_api.py,sha256=h0HT9W0Jd1pChrXYaBOVwGThaI3slGkloS0sbq2YX88,962
151
152
  hpcflow/tests/conftest.py,sha256=xtSqhOxjZYioiAPvrKwf7NFicZoA4BR9Si4J1A8mWHw,4083
152
153
  hpcflow/tests/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -162,7 +163,7 @@ hpcflow/tests/schedulers/direct_linux/test_direct_linux_submission.py,sha256=pgH
162
163
  hpcflow/tests/schedulers/sge/test_sge_submission.py,sha256=NKO2wBR9AOW8chEBW2nZ9OyDMa_8xZWDw16gtg5g-C8,1107
163
164
  hpcflow/tests/schedulers/slurm/test_slurm_submission.py,sha256=Hcok40_hWb68T0hFzODcmzXr10M6WavLD3qiyzjcNOI,493
164
165
  hpcflow/tests/scripts/test_input_file_generators.py,sha256=zyM_0AXFDCHMO75GC3we__z4KeqcF5s7MFw2MJfeDec,9960
165
- hpcflow/tests/scripts/test_main_scripts.py,sha256=qj4tg5s9Ii-n9b0bbWHAAGeXIoCPBZDhEMYH9LoAmbk,47911
166
+ hpcflow/tests/scripts/test_main_scripts.py,sha256=pM_aA_XZ1py78yVJ-Fu-UQ7zJciIq8Ywv4CwiUuJEo4,48759
166
167
  hpcflow/tests/scripts/test_non_snippet_script.py,sha256=XFn34lLxd8iL03a4OIY9pbT5NWbjswDcjp2jaQF2ICU,1304
167
168
  hpcflow/tests/scripts/test_ouput_file_parsers.py,sha256=gB6W1stp5TdoRjupbvt6_Ufs1TsOQwTUS5oHif4P3c8,12092
168
169
  hpcflow/tests/shells/wsl/test_wsl_submission.py,sha256=L9TChOSwLKGIcHrfNno843SNpNWopgHY7Ptc_HzMEtA,1069
@@ -176,9 +177,9 @@ hpcflow/tests/unit/test_config.py,sha256=__rJADdEIia1lA2eSoT40IVw-7ArZSA86PDCQiW
176
177
  hpcflow/tests/unit/test_config_file.py,sha256=H0jXqPDEz4KKJ-VcnhpY9Xf_iVxFZr9eOvKvGRzmHCU,4599
177
178
  hpcflow/tests/unit/test_element.py,sha256=UMSNbNDZa2Ok-GQ9JWtqAWXYnOwkJdbjAeR3hHrXntU,20918
178
179
  hpcflow/tests/unit/test_element_iteration.py,sha256=ykn0eyhAhiL1NJlRp5B6PxE68GoqFGpIllHNoEriV3c,3101
179
- hpcflow/tests/unit/test_element_set.py,sha256=acKKnShu-DUlSvKqzbukQ_09nFEf-SBtGF8S3Vh3fDI,4223
180
+ hpcflow/tests/unit/test_element_set.py,sha256=R1kqKkvYhSZeXfL_yzDhQgLc9TXXkewZaayM7cbWLPw,5082
180
181
  hpcflow/tests/unit/test_group.py,sha256=izOxElHzBtj7YafdTOZv4z_AyHXLhcfRvdNA4Bll9Ww,2660
181
- hpcflow/tests/unit/test_input_source.py,sha256=2xFXCjO3rcPxcRyYW2Mfg-qL5XfMX3QWxYfNQIopE44,40065
182
+ hpcflow/tests/unit/test_input_source.py,sha256=0qRuKMDP8GkIN30PiLOUoGAkd9kfCFOFJ9LNEnw1mjc,42127
182
183
  hpcflow/tests/unit/test_input_value.py,sha256=UwW8bdSLkMXOEmhRw-n7fyMeVPEBHTxf5-WUNnVQVFg,6031
183
184
  hpcflow/tests/unit/test_jobscript_unit.py,sha256=E_uszw3-3Lf2nBcIUZUngMMIgd0S1X6nK40KYEBjGrg,26077
184
185
  hpcflow/tests/unit/test_json_like.py,sha256=b0_BHW0zfBmc_J848s0ZcYIl4NJkTxNciiAMMRvUCtA,30434
@@ -198,7 +199,7 @@ hpcflow/tests/unit/test_slurm.py,sha256=TGYLsEWtzmZg5hNa0LzWPQTWIQ4ZfINX3_XuxbTV
198
199
  hpcflow/tests/unit/test_submission.py,sha256=N5g5lR5_g4vtGZ7501q74UtB7QP6uEdPY-FAH2HAQJM,15411
199
200
  hpcflow/tests/unit/test_task.py,sha256=vI38Wx-EOCUkEipYoB1LL21Cxr5t073-F8eUluBsP28,83958
200
201
  hpcflow/tests/unit/test_task_schema.py,sha256=8qLioZQxdthDRU_-wFRFqfnuC8ygjyXt9iQGSw-0GGE,5065
201
- hpcflow/tests/unit/test_utils.py,sha256=Owr-7BP0_ryAhgnf14CMYFlDkY8_fqfnSwHMJTv8BAc,14645
202
+ hpcflow/tests/unit/test_utils.py,sha256=m4wshifm6g19xnEXG8-sj-gldTKZ-NPx-xPE9BwcQiM,15350
202
203
  hpcflow/tests/unit/test_value_sequence.py,sha256=wQl-PVB72159OSVrpLPrHd2DyVjc2jNi79nDKhFbWMU,15858
203
204
  hpcflow/tests/unit/test_workflow.py,sha256=SKH1GwFufZTEkgKCyOiJhvajxJwQdX2dpVoFGLrQD2s,24409
204
205
  hpcflow/tests/unit/test_workflow_template.py,sha256=M99VxA7hHRsrN7IjrcwHzA_yShH3O6BhoPU0rrl5jJc,6709
@@ -207,6 +208,7 @@ hpcflow/tests/unit/utils/test_deferred_file_writer.py,sha256=1UvMGRT2pes1iDZdgL1
207
208
  hpcflow/tests/unit/utils/test_hashing.py,sha256=Q1BZRdrbF5BKb5jkmOZE0QoaoOg-zHwymmamekxO5Mo,2334
208
209
  hpcflow/tests/unit/utils/test_patches.py,sha256=31P3OZ5cMHo7FL3svOWNiiGqVxdlTpgKAqWgI2lQL1s,133
209
210
  hpcflow/tests/unit/utils/test_redirect_std.py,sha256=Aw56qOWL42HUL2kmhmgsjBdjutErhyOt0SHajl5CdxU,1463
211
+ hpcflow/tests/unit/utils/test_strings.py,sha256=TUB87sSjSPLsq9GPu8ebXVbL74MpuLbsgySCN5gASQU,2045
210
212
  hpcflow/tests/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
211
213
  hpcflow/tests/workflows/test_directory_structure.py,sha256=Fp2kHMi6iydCJjRmVXPWj5gnZ-QzGj9lL-T_jb4kG2Y,1143
212
214
  hpcflow/tests/workflows/test_jobscript.py,sha256=jbInUDTxvN3qRVzLrF81OXrlP1RdgSfQZ5Zy3wUN9GI,11539
@@ -216,8 +218,8 @@ hpcflow/tests/workflows/test_submission.py,sha256=SUbBUbD8C8LSulrI7aETkzP9RqED48
216
218
  hpcflow/tests/workflows/test_workflows.py,sha256=9z3rtXjA5iMOp4C0q4TkD_9kLzwourCY-obpeOtnNt0,18927
217
219
  hpcflow/tests/workflows/test_zip.py,sha256=MzEwsIAYV_1A3bD0XRo23zUwUKVzkkmNd8_cil6YdWQ,578
218
220
  hpcflow/viz_demo.ipynb,sha256=6D9uBbWK3oMfbaf93Tnv5riFPtW-2miUTWNr9kGcnd4,228913
219
- hpcflow_new2-0.2.0a218.dist-info/LICENSE,sha256=Xhxf_KsrJNJFGMogumZhXSTPhUOVHCWf7nU-TDzqg0E,16763
220
- hpcflow_new2-0.2.0a218.dist-info/METADATA,sha256=v8HY5I7xM675Mu7q95T0DSHk2SEd_xpgyZWDVLBRnTo,2663
221
- hpcflow_new2-0.2.0a218.dist-info/WHEEL,sha256=kLuE8m1WYU0Ig0_YEGrXyTtiJvKPpLpDEiChiNyei5Y,88
222
- hpcflow_new2-0.2.0a218.dist-info/entry_points.txt,sha256=aoGtCnFdfPcXfBdu2zZyMOJoz6fPgdR0elqsgrE-USU,106
223
- hpcflow_new2-0.2.0a218.dist-info/RECORD,,
221
+ hpcflow_new2-0.2.0a219.dist-info/LICENSE,sha256=Xhxf_KsrJNJFGMogumZhXSTPhUOVHCWf7nU-TDzqg0E,16763
222
+ hpcflow_new2-0.2.0a219.dist-info/METADATA,sha256=5YbT79dL7KTRczewYTZSD5cOxYJm7cKlbGdzFfaGcOw,2663
223
+ hpcflow_new2-0.2.0a219.dist-info/WHEEL,sha256=kLuE8m1WYU0Ig0_YEGrXyTtiJvKPpLpDEiChiNyei5Y,88
224
+ hpcflow_new2-0.2.0a219.dist-info/entry_points.txt,sha256=aoGtCnFdfPcXfBdu2zZyMOJoz6fPgdR0elqsgrE-USU,106
225
+ hpcflow_new2-0.2.0a219.dist-info/RECORD,,