hpcflow-new2 0.2.0a169__py3-none-any.whl → 0.2.0a174__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.
@@ -4,7 +4,7 @@ from valida.conditions import Value
4
4
 
5
5
 
6
6
  from hpcflow.app import app as hf
7
- from hpcflow.sdk.core.errors import LoopAlreadyExistsError
7
+ from hpcflow.sdk.core.errors import LoopAlreadyExistsError, LoopTaskSubsetError
8
8
  from hpcflow.sdk.core.test_utils import P1_parameter_cls, make_workflow
9
9
 
10
10
 
@@ -245,6 +245,209 @@ def test_get_iteration_task_pathway_single_task_single_element_three_iters(
245
245
  ]
246
246
 
247
247
 
248
+ def test_get_iteration_task_pathway_nested_loops_multi_iter(null_config, tmp_path):
249
+ ts1 = hf.TaskSchema(
250
+ objective="t1",
251
+ inputs=[hf.SchemaInput("p1")],
252
+ outputs=[hf.SchemaOutput("p1")],
253
+ actions=[
254
+ hf.Action(
255
+ commands=[
256
+ hf.Command(
257
+ "Write-Output (<<parameter:p1>> + 100)",
258
+ stdout="<<int(parameter:p1)>>",
259
+ )
260
+ ],
261
+ ),
262
+ ],
263
+ )
264
+ wk = hf.Workflow.from_template_data(
265
+ template_name="test_loop",
266
+ path=tmp_path,
267
+ tasks=[
268
+ hf.Task(schema=ts1, inputs={"p1": 101}),
269
+ hf.Task(schema=ts1),
270
+ hf.Task(schema=ts1),
271
+ ],
272
+ loops=[
273
+ hf.Loop(name="inner_loop", tasks=[2], num_iterations=2),
274
+ hf.Loop(name="outer_loop", tasks=[1, 2], num_iterations=2),
275
+ ],
276
+ )
277
+ assert wk.get_iteration_task_pathway() == [
278
+ (0, {}),
279
+ (1, {"outer_loop": 0}),
280
+ (2, {"outer_loop": 0, "inner_loop": 0}),
281
+ (2, {"outer_loop": 0, "inner_loop": 1}),
282
+ (1, {"outer_loop": 1}),
283
+ (2, {"outer_loop": 1, "inner_loop": 0}),
284
+ (2, {"outer_loop": 1, "inner_loop": 1}),
285
+ ]
286
+
287
+
288
+ @pytest.mark.skip(
289
+ reason="second set of asserts fail; need to re-source inputs on adding iterations."
290
+ )
291
+ def test_get_iteration_task_pathway_nested_loops_multi_iter_jagged(null_config, tmp_path):
292
+ ts1 = hf.TaskSchema(
293
+ objective="t1",
294
+ inputs=[hf.SchemaInput("p1")],
295
+ outputs=[hf.SchemaOutput("p1")],
296
+ actions=[
297
+ hf.Action(
298
+ commands=[
299
+ hf.Command(
300
+ "Write-Output (<<parameter:p1>> + 100)",
301
+ stdout="<<int(parameter:p1)>>",
302
+ )
303
+ ],
304
+ ),
305
+ ],
306
+ )
307
+ wk = hf.Workflow.from_template_data(
308
+ template_name="test_loop",
309
+ path=tmp_path,
310
+ tasks=[
311
+ hf.Task(schema=ts1, inputs={"p1": 101}),
312
+ hf.Task(schema=ts1),
313
+ hf.Task(schema=ts1),
314
+ hf.Task(schema=ts1),
315
+ ],
316
+ loops=[
317
+ hf.Loop(name="inner_loop", tasks=[2], num_iterations=2),
318
+ hf.Loop(name="outer_loop", tasks=[1, 2], num_iterations=2),
319
+ ],
320
+ )
321
+ wk.loops.inner_loop.add_iteration(parent_loop_indices={"outer_loop": 1})
322
+ wk.loops.inner_loop.add_iteration(parent_loop_indices={"outer_loop": 1})
323
+ assert wk.get_iteration_task_pathway() == [
324
+ (0, {}),
325
+ (1, {"outer_loop": 0}),
326
+ (2, {"outer_loop": 0, "inner_loop": 0}),
327
+ (2, {"outer_loop": 0, "inner_loop": 1}),
328
+ (1, {"outer_loop": 1}),
329
+ (2, {"outer_loop": 1, "inner_loop": 0}),
330
+ (2, {"outer_loop": 1, "inner_loop": 1}),
331
+ (2, {"outer_loop": 1, "inner_loop": 2}),
332
+ (2, {"outer_loop": 1, "inner_loop": 3}),
333
+ (3, {}),
334
+ ]
335
+ pathway = wk.get_iteration_task_pathway(ret_data_idx=True)
336
+ assert pathway[1][2][0]["inputs.p1"] == pathway[0][2][0]["outputs.p1"]
337
+ assert pathway[2][2][0]["inputs.p1"] == pathway[1][2][0]["outputs.p1"]
338
+ assert pathway[3][2][0]["inputs.p1"] == pathway[2][2][0]["outputs.p1"]
339
+ assert pathway[4][2][0]["inputs.p1"] == pathway[3][2][0]["outputs.p1"]
340
+ assert pathway[5][2][0]["inputs.p1"] == pathway[4][2][0]["outputs.p1"]
341
+ assert pathway[6][2][0]["inputs.p1"] == pathway[5][2][0]["outputs.p1"]
342
+ assert pathway[7][2][0]["inputs.p1"] == pathway[6][2][0]["outputs.p1"]
343
+ assert pathway[8][2][0]["inputs.p1"] == pathway[7][2][0]["outputs.p1"]
344
+
345
+ # FAILS currently:
346
+ assert pathway[9][2][0]["inputs.p1"] == pathway[8][2][0]["outputs.p1"]
347
+
348
+
349
+ def test_get_iteration_task_pathway_nested_loops_multi_iter_add_outer_iter(
350
+ null_config, tmp_path
351
+ ):
352
+ ts1 = hf.TaskSchema(
353
+ objective="t1",
354
+ inputs=[hf.SchemaInput("p1")],
355
+ outputs=[hf.SchemaOutput("p1")],
356
+ actions=[
357
+ hf.Action(
358
+ commands=[
359
+ hf.Command(
360
+ "Write-Output (<<parameter:p1>> + 100)",
361
+ stdout="<<int(parameter:p1)>>",
362
+ )
363
+ ],
364
+ ),
365
+ ],
366
+ )
367
+ wk = hf.Workflow.from_template_data(
368
+ template_name="test_loop",
369
+ path=tmp_path,
370
+ tasks=[
371
+ hf.Task(schema=ts1, inputs={"p1": 101}),
372
+ hf.Task(schema=ts1),
373
+ hf.Task(schema=ts1),
374
+ ],
375
+ loops=[
376
+ hf.Loop(name="inner_loop", tasks=[2], num_iterations=2),
377
+ hf.Loop(name="outer_loop", tasks=[1, 2], num_iterations=2),
378
+ ],
379
+ )
380
+ wk.loops.outer_loop.add_iteration()
381
+ assert wk.get_iteration_task_pathway() == [
382
+ (0, {}),
383
+ (1, {"outer_loop": 0}),
384
+ (2, {"outer_loop": 0, "inner_loop": 0}),
385
+ (2, {"outer_loop": 0, "inner_loop": 1}),
386
+ (1, {"outer_loop": 1}),
387
+ (2, {"outer_loop": 1, "inner_loop": 0}),
388
+ (2, {"outer_loop": 1, "inner_loop": 1}),
389
+ (1, {"outer_loop": 2}),
390
+ (2, {"outer_loop": 2, "inner_loop": 0}),
391
+ (2, {"outer_loop": 2, "inner_loop": 1}),
392
+ ]
393
+
394
+
395
+ @pytest.mark.skip(
396
+ reason="second set of asserts fail; need to re-source inputs on adding iterations."
397
+ )
398
+ def test_get_iteration_task_pathway_unconnected_loops(null_config, tmp_path):
399
+ ts1 = hf.TaskSchema(
400
+ objective="t1",
401
+ inputs=[hf.SchemaInput("p1")],
402
+ outputs=[hf.SchemaOutput("p1")],
403
+ actions=[
404
+ hf.Action(
405
+ commands=[
406
+ hf.Command(
407
+ "Write-Output (<<parameter:p1>> + 100)",
408
+ stdout="<<int(parameter:p1)>>",
409
+ )
410
+ ],
411
+ ),
412
+ ],
413
+ )
414
+ wk = hf.Workflow.from_template_data(
415
+ template_name="test_loop",
416
+ path=tmp_path,
417
+ tasks=[
418
+ hf.Task(schema=ts1, inputs={"p1": 101}),
419
+ hf.Task(schema=ts1),
420
+ hf.Task(schema=ts1),
421
+ hf.Task(schema=ts1),
422
+ ],
423
+ loops=[
424
+ hf.Loop(name="loop_A", tasks=[0, 1], num_iterations=2),
425
+ hf.Loop(name="loop_B", tasks=[2, 3], num_iterations=2),
426
+ ],
427
+ )
428
+ assert wk.get_iteration_task_pathway() == [
429
+ (0, {"loop_A": 0}),
430
+ (1, {"loop_A": 0}),
431
+ (0, {"loop_A": 1}),
432
+ (1, {"loop_A": 1}),
433
+ (2, {"loop_B": 0}),
434
+ (3, {"loop_B": 0}),
435
+ (2, {"loop_B": 1}),
436
+ (3, {"loop_B": 1}),
437
+ ]
438
+
439
+ pathway = wk.get_iteration_task_pathway(ret_data_idx=True)
440
+ assert pathway[1][2][0]["inputs.p1"] == pathway[0][2][0]["outputs.p1"]
441
+ assert pathway[2][2][0]["inputs.p1"] == pathway[1][2][0]["outputs.p1"]
442
+ assert pathway[3][2][0]["inputs.p1"] == pathway[2][2][0]["outputs.p1"]
443
+ assert pathway[5][2][0]["inputs.p1"] == pathway[4][2][0]["outputs.p1"]
444
+ assert pathway[6][2][0]["inputs.p1"] == pathway[5][2][0]["outputs.p1"]
445
+ assert pathway[7][2][0]["inputs.p1"] == pathway[6][2][0]["outputs.p1"]
446
+
447
+ # FAILS currently:
448
+ assert pathway[4][2][0]["inputs.p1"] == pathway[3][2][0]["outputs.p1"]
449
+
450
+
248
451
  def test_wk_loop_input_sources_including_non_iteration_task_source(null_config, tmp_path):
249
452
  act_env = hf.ActionEnvironment("null_env")
250
453
  ts1 = hf.TaskSchema(
@@ -591,3 +794,368 @@ def test_loop_local_sub_parameters(null_config, tmp_path):
591
794
  assert t1_iter_1["inputs.p1c"] == t2_iter_0["outputs.p1c"]
592
795
  assert t2_iter_1["inputs.p2"] == t1_iter_1["outputs.p2"]
593
796
  assert t1_iter_0["inputs.p1c.d"] == t1_iter_1["inputs.p1c.d"]
797
+
798
+
799
+ def test_nested_loop_iter_loop_idx(null_config, tmp_path):
800
+ ts1 = hf.TaskSchema(
801
+ objective="t1",
802
+ inputs=[hf.SchemaInput("p1")],
803
+ outputs=[hf.SchemaOutput("p1")],
804
+ actions=[
805
+ hf.Action(
806
+ commands=[
807
+ hf.Command(
808
+ "Write-Output (<<parameter:p1>> + 100)",
809
+ stdout="<<int(parameter:p1)>>",
810
+ )
811
+ ],
812
+ ),
813
+ ],
814
+ )
815
+
816
+ wk = hf.Workflow.from_template_data(
817
+ template_name="test_loop",
818
+ path=tmp_path,
819
+ tasks=[hf.Task(schema=ts1, inputs={"p1": 101})],
820
+ loops=[
821
+ hf.Loop(name="outer_loop", tasks=[0], num_iterations=1),
822
+ hf.Loop(name="inner_loop", tasks=[0], num_iterations=1),
823
+ ],
824
+ )
825
+ assert wk.tasks[0].elements[0].iterations[0].loop_idx == {
826
+ "inner_loop": 0,
827
+ "outer_loop": 0,
828
+ }
829
+
830
+
831
+ def test_schema_input_with_group_sourced_from_prev_iteration(null_config, tmp_path):
832
+ s1 = hf.TaskSchema(
833
+ objective="t1",
834
+ inputs=[hf.SchemaInput("p1")],
835
+ outputs=[hf.SchemaOutput("p2")],
836
+ actions=[
837
+ hf.Action(
838
+ commands=[
839
+ hf.Command(
840
+ "echo $(( <<parameter:p1>> + 1 ))", stdout="<<parameter:p2>>"
841
+ )
842
+ ]
843
+ )
844
+ ],
845
+ )
846
+ s2 = hf.TaskSchema(
847
+ objective="t2",
848
+ inputs=[hf.SchemaInput("p2", group="my_group")],
849
+ outputs=[hf.SchemaOutput("p3")],
850
+ actions=[
851
+ hf.Action(
852
+ commands=[
853
+ hf.Command(
854
+ "echo $(( <<parameter:p2>> + 2 ))", stdout="<<parameter:p3>>"
855
+ )
856
+ ]
857
+ )
858
+ ],
859
+ )
860
+ s3 = hf.TaskSchema(
861
+ objective="t3",
862
+ inputs=[hf.SchemaInput("p3")],
863
+ outputs=[hf.SchemaOutput("p2")],
864
+ actions=[
865
+ hf.Action(
866
+ commands=[
867
+ hf.Command(
868
+ "echo $(( <<parameter:p3>> + 3 ))", stdout="<<parameter:p2>>"
869
+ )
870
+ ]
871
+ )
872
+ ],
873
+ )
874
+
875
+ t1 = hf.Task(
876
+ schema=s1,
877
+ sequences=[hf.ValueSequence("inputs.p1", values=[1, 2, 3])],
878
+ groups=[hf.ElementGroup(name="my_group")],
879
+ )
880
+ t2 = hf.Task(schema=s2)
881
+ t3 = hf.Task(
882
+ schema=s3,
883
+ repeats=3,
884
+ groups=[hf.ElementGroup(name="my_group")],
885
+ )
886
+
887
+ l1 = hf.Loop(name="my_loop", tasks=[1, 2], num_iterations=2)
888
+
889
+ wk = hf.Workflow.from_template_data(
890
+ template_name="test_loops",
891
+ path=tmp_path,
892
+ tasks=[t1, t2, t3],
893
+ loops=[l1],
894
+ )
895
+
896
+ assert wk.tasks.t2.elements[0].iterations[0].get_data_idx()["inputs.p2"] == [
897
+ i.get_data_idx()["outputs.p2"] for i in wk.tasks.t1.elements
898
+ ]
899
+ assert [
900
+ i.iterations[0].get_data_idx()["inputs.p3"] for i in wk.tasks.t3.elements
901
+ ] == [wk.tasks.t2.elements[0].iterations[0].get_data_idx()["outputs.p3"]] * 3
902
+ assert wk.tasks.t2.elements[0].iterations[1].get_data_idx()["inputs.p2"] == [
903
+ i.iterations[0].get_data_idx()["outputs.p2"] for i in wk.tasks.t3.elements
904
+ ]
905
+ assert [
906
+ i.iterations[1].get_data_idx()["inputs.p3"] for i in wk.tasks.t3.elements
907
+ ] == [wk.tasks.t2.elements[0].iterations[1].get_data_idx()["outputs.p3"]] * 3
908
+
909
+
910
+ def test_loop_downstream_tasks(null_config, tmp_path):
911
+ ts1 = hf.TaskSchema(
912
+ objective="t1",
913
+ inputs=[hf.SchemaInput("p1")],
914
+ outputs=[hf.SchemaOutput("p1")],
915
+ actions=[
916
+ hf.Action(
917
+ commands=[
918
+ hf.Command(
919
+ "Write-Output (<<parameter:p1>> + 100)",
920
+ stdout="<<int(parameter:p1)>>",
921
+ )
922
+ ],
923
+ ),
924
+ ],
925
+ )
926
+ ts2 = hf.TaskSchema(
927
+ objective="t2",
928
+ inputs=[hf.SchemaInput("p2")],
929
+ outputs=[hf.SchemaOutput("p2")],
930
+ actions=[
931
+ hf.Action(
932
+ commands=[
933
+ hf.Command(
934
+ "Write-Output (<<parameter:p2>> + 100)",
935
+ stdout="<<int(parameter:p2)>>",
936
+ )
937
+ ],
938
+ ),
939
+ ],
940
+ )
941
+ wk = hf.Workflow.from_template_data(
942
+ template_name="test_loop",
943
+ path=tmp_path,
944
+ tasks=[
945
+ hf.Task(schema=ts1, inputs={"p1": 101}),
946
+ hf.Task(schema=ts1),
947
+ hf.Task(schema=ts1),
948
+ hf.Task(schema=ts2, inputs={"p2": 201}),
949
+ ],
950
+ loops=[
951
+ hf.Loop(name="my_loop", tasks=[1, 2], num_iterations=2),
952
+ ],
953
+ )
954
+ assert wk.loops.my_loop.downstream_tasks == [wk.tasks[3]]
955
+ assert wk.loops.my_loop.upstream_tasks == [wk.tasks[0]]
956
+
957
+
958
+ def test_raise_loop_task_subset_error(null_config, tmp_path):
959
+ ts1 = hf.TaskSchema(
960
+ objective="t1",
961
+ inputs=[hf.SchemaInput("p1")],
962
+ outputs=[hf.SchemaOutput("p1")],
963
+ actions=[
964
+ hf.Action(
965
+ commands=[
966
+ hf.Command(
967
+ "Write-Output (<<parameter:p1>> + 100)",
968
+ stdout="<<int(parameter:p1)>>",
969
+ )
970
+ ],
971
+ ),
972
+ ],
973
+ )
974
+ with pytest.raises(LoopTaskSubsetError):
975
+ hf.Workflow.from_template_data(
976
+ template_name="test_loop",
977
+ path=tmp_path,
978
+ tasks=[
979
+ hf.Task(schema=ts1, inputs={"p1": 101}),
980
+ hf.Task(schema=ts1),
981
+ hf.Task(schema=ts1),
982
+ ],
983
+ loops=[
984
+ hf.Loop(name="my_loop", tasks=[2, 1], num_iterations=2),
985
+ ],
986
+ )
987
+
988
+
989
+ def test_raise_downstream_task_with_iterable_parameter(null_config, tmp_path):
990
+ ts1 = hf.TaskSchema(
991
+ objective="t1",
992
+ inputs=[hf.SchemaInput("p1")],
993
+ outputs=[hf.SchemaOutput("p1")],
994
+ actions=[
995
+ hf.Action(
996
+ commands=[
997
+ hf.Command(
998
+ "Write-Output (<<parameter:p1>> + 100)",
999
+ stdout="<<int(parameter:p1)>>",
1000
+ )
1001
+ ],
1002
+ ),
1003
+ ],
1004
+ )
1005
+ with pytest.raises(NotImplementedError):
1006
+ hf.Workflow.from_template_data(
1007
+ template_name="test_loop",
1008
+ path=tmp_path,
1009
+ tasks=[
1010
+ hf.Task(schema=ts1, inputs={"p1": 101}),
1011
+ hf.Task(schema=ts1),
1012
+ hf.Task(schema=ts1),
1013
+ ],
1014
+ loops=[
1015
+ hf.Loop(name="my_loop", tasks=[1], num_iterations=2),
1016
+ ],
1017
+ )
1018
+
1019
+
1020
+ def test_adjacent_loops_iteration_pathway(null_config, tmp_path):
1021
+ ts1 = hf.TaskSchema(
1022
+ objective="t1",
1023
+ inputs=[hf.SchemaInput("p1")],
1024
+ outputs=[hf.SchemaOutput("p1")],
1025
+ actions=[
1026
+ hf.Action(
1027
+ commands=[
1028
+ hf.Command(
1029
+ "Write-Output (<<parameter:p1>> + 100)",
1030
+ stdout="<<int(parameter:p1)>>",
1031
+ )
1032
+ ],
1033
+ ),
1034
+ ],
1035
+ )
1036
+ ts2 = hf.TaskSchema(
1037
+ objective="t2",
1038
+ inputs=[hf.SchemaInput("p2")],
1039
+ outputs=[hf.SchemaOutput("p2")],
1040
+ actions=[
1041
+ hf.Action(
1042
+ commands=[
1043
+ hf.Command(
1044
+ "Write-Output (<<parameter:p2>> + 100)",
1045
+ stdout="<<int(parameter:p2)>>",
1046
+ )
1047
+ ],
1048
+ ),
1049
+ ],
1050
+ )
1051
+ wk = hf.Workflow.from_template_data(
1052
+ template_name="test_loop",
1053
+ path=tmp_path,
1054
+ tasks=[
1055
+ hf.Task(schema=ts1, inputs={"p1": 101}),
1056
+ hf.Task(schema=ts1),
1057
+ hf.Task(schema=ts2, inputs={"p2": 201}),
1058
+ ],
1059
+ loops=[
1060
+ hf.Loop(name="loop_A", tasks=[0, 1], num_iterations=2),
1061
+ hf.Loop(name="loop_B", tasks=[2], num_iterations=2),
1062
+ ],
1063
+ )
1064
+ assert wk.get_iteration_task_pathway() == [
1065
+ (0, {"loop_A": 0}),
1066
+ (1, {"loop_A": 0}),
1067
+ (0, {"loop_A": 1}),
1068
+ (1, {"loop_A": 1}),
1069
+ (2, {"loop_B": 0}),
1070
+ (2, {"loop_B": 1}),
1071
+ ]
1072
+
1073
+
1074
+ def test_get_child_loops_ordered_by_depth(null_config, tmp_path):
1075
+ ts1 = hf.TaskSchema(
1076
+ objective="t1",
1077
+ inputs=[hf.SchemaInput("p1")],
1078
+ outputs=[hf.SchemaOutput("p1")],
1079
+ actions=[
1080
+ hf.Action(
1081
+ commands=[
1082
+ hf.Command(
1083
+ "Write-Output (<<parameter:p1>> + 100)",
1084
+ stdout="<<int(parameter:p1)>>",
1085
+ )
1086
+ ],
1087
+ ),
1088
+ ],
1089
+ )
1090
+ wk = hf.Workflow.from_template_data(
1091
+ template_name="test_loop",
1092
+ path=tmp_path,
1093
+ tasks=[
1094
+ hf.Task(schema=ts1, inputs={"p1": 101}),
1095
+ ],
1096
+ loops=[
1097
+ hf.Loop(name="inner", tasks=[0], num_iterations=1),
1098
+ hf.Loop(name="middle", tasks=[0], num_iterations=1),
1099
+ hf.Loop(name="outer", tasks=[0], num_iterations=1),
1100
+ ],
1101
+ )
1102
+ assert wk.loops.inner.get_child_loops() == []
1103
+ assert wk.loops.middle.get_child_loops() == [wk.loops.inner]
1104
+ assert wk.loops.outer.get_child_loops() == [wk.loops.middle, wk.loops.inner]
1105
+
1106
+
1107
+ def test_multi_nested_loops(null_config, tmp_path):
1108
+ ts1 = hf.TaskSchema(
1109
+ objective="t1",
1110
+ inputs=[hf.SchemaInput("p1")],
1111
+ outputs=[hf.SchemaOutput("p1")],
1112
+ actions=[
1113
+ hf.Action(
1114
+ commands=[
1115
+ hf.Command(
1116
+ "Write-Output (<<parameter:p1>> + 100)",
1117
+ stdout="<<int(parameter:p1)>>",
1118
+ )
1119
+ ],
1120
+ ),
1121
+ ],
1122
+ )
1123
+ wk = hf.Workflow.from_template_data(
1124
+ template_name="test_loop",
1125
+ path=tmp_path,
1126
+ tasks=[hf.Task(schema=ts1, inputs={"p1": 101})],
1127
+ loops=[
1128
+ hf.Loop(name="inner", tasks=[0], num_iterations=2),
1129
+ hf.Loop(name="middle_1", tasks=[0], num_iterations=3),
1130
+ hf.Loop(name="middle_2", tasks=[0], num_iterations=2),
1131
+ hf.Loop(name="outer", tasks=[0], num_iterations=2),
1132
+ ],
1133
+ )
1134
+ pathway = wk.get_iteration_task_pathway(ret_iter_IDs=True)
1135
+ assert len(pathway) == 2 * 3 * 2 * 2
1136
+ assert wk.get_iteration_task_pathway(ret_iter_IDs=True) == [
1137
+ (0, {"inner": 0, "middle_1": 0, "middle_2": 0, "outer": 0}, (0,)),
1138
+ (0, {"inner": 1, "middle_1": 0, "middle_2": 0, "outer": 0}, (1,)),
1139
+ (0, {"inner": 0, "middle_1": 1, "middle_2": 0, "outer": 0}, (2,)),
1140
+ (0, {"inner": 1, "middle_1": 1, "middle_2": 0, "outer": 0}, (3,)),
1141
+ (0, {"inner": 0, "middle_1": 2, "middle_2": 0, "outer": 0}, (4,)),
1142
+ (0, {"inner": 1, "middle_1": 2, "middle_2": 0, "outer": 0}, (5,)),
1143
+ (0, {"inner": 0, "middle_1": 0, "middle_2": 1, "outer": 0}, (6,)),
1144
+ (0, {"inner": 1, "middle_1": 0, "middle_2": 1, "outer": 0}, (7,)),
1145
+ (0, {"inner": 0, "middle_1": 1, "middle_2": 1, "outer": 0}, (8,)),
1146
+ (0, {"inner": 1, "middle_1": 1, "middle_2": 1, "outer": 0}, (9,)),
1147
+ (0, {"inner": 0, "middle_1": 2, "middle_2": 1, "outer": 0}, (10,)),
1148
+ (0, {"inner": 1, "middle_1": 2, "middle_2": 1, "outer": 0}, (11,)),
1149
+ (0, {"inner": 0, "middle_1": 0, "middle_2": 0, "outer": 1}, (12,)),
1150
+ (0, {"inner": 1, "middle_1": 0, "middle_2": 0, "outer": 1}, (13,)),
1151
+ (0, {"inner": 0, "middle_1": 1, "middle_2": 0, "outer": 1}, (14,)),
1152
+ (0, {"inner": 1, "middle_1": 1, "middle_2": 0, "outer": 1}, (15,)),
1153
+ (0, {"inner": 0, "middle_1": 2, "middle_2": 0, "outer": 1}, (16,)),
1154
+ (0, {"inner": 1, "middle_1": 2, "middle_2": 0, "outer": 1}, (17,)),
1155
+ (0, {"inner": 0, "middle_1": 0, "middle_2": 1, "outer": 1}, (18,)),
1156
+ (0, {"inner": 1, "middle_1": 0, "middle_2": 1, "outer": 1}, (19,)),
1157
+ (0, {"inner": 0, "middle_1": 1, "middle_2": 1, "outer": 1}, (20,)),
1158
+ (0, {"inner": 1, "middle_1": 1, "middle_2": 1, "outer": 1}, (21,)),
1159
+ (0, {"inner": 0, "middle_1": 2, "middle_2": 1, "outer": 1}, (22,)),
1160
+ (0, {"inner": 1, "middle_1": 2, "middle_2": 1, "outer": 1}, (23,)),
1161
+ ]
@@ -1,9 +1,11 @@
1
1
  from dataclasses import dataclass
2
2
  import random
3
3
  import string
4
+ import sys
4
5
  from textwrap import dedent
5
6
 
6
7
  import pytest
8
+ import requests
7
9
 
8
10
  from hpcflow.app import app as hf
9
11
  from hpcflow.sdk.core.parameters import ParameterValue
@@ -148,6 +150,14 @@ def test_slice(new_null_config, tmp_path):
148
150
  assert p1_params[1].value == values[2]
149
151
 
150
152
 
153
+ @pytest.mark.xfail(
154
+ condition=sys.platform == "darwin",
155
+ raises=requests.exceptions.HTTPError,
156
+ reason=(
157
+ "GHA MacOS runners use the same IP address, so we get rate limited when "
158
+ "retrieving demo data from GitHub."
159
+ ),
160
+ )
151
161
  def test_demo_data_substitution_param_value_class_method(new_null_config, tmp_path):
152
162
  yaml_str = dedent(
153
163
  """\
@@ -171,6 +181,14 @@ def test_demo_data_substitution_param_value_class_method(new_null_config, tmp_pa
171
181
  }
172
182
 
173
183
 
184
+ @pytest.mark.xfail(
185
+ condition=sys.platform == "darwin",
186
+ raises=requests.exceptions.HTTPError,
187
+ reason=(
188
+ "GHA MacOS runners use the same IP address, so we get rate limited when "
189
+ "retrieving demo data from GitHub."
190
+ ),
191
+ )
174
192
  def test_demo_data_substitution_value_sequence_class_method(new_null_config, tmp_path):
175
193
  yaml_str = dedent(
176
194
  """\