executorlib 0.0.10__tar.gz → 0.0.11__tar.gz
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.
- {executorlib-0.0.10/executorlib.egg-info → executorlib-0.0.11}/PKG-INFO +3 -3
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/_version.py +3 -3
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/backend/cache_parallel.py +1 -5
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/backend/interactive_parallel.py +8 -8
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/backend/interactive_serial.py +10 -10
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/base/executor.py +16 -9
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/cache/queue_spawner.py +5 -3
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/cache/shared.py +6 -11
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/cache/subprocess_spawner.py +4 -5
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/interactive/create.py +17 -9
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/interactive/executor.py +4 -2
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/interactive/shared.py +30 -26
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/interactive/slurm.py +3 -3
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/hdf.py +4 -4
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/inputcheck.py +8 -9
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/interactive/backend.py +1 -1
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/interactive/communication.py +3 -3
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/interactive/spawner.py +4 -1
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/plot.py +3 -3
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/queue.py +1 -1
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/serialize.py +12 -3
- {executorlib-0.0.10 → executorlib-0.0.11/executorlib.egg-info}/PKG-INFO +3 -3
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib.egg-info/requires.txt +2 -2
- {executorlib-0.0.10 → executorlib-0.0.11}/pyproject.toml +37 -2
- {executorlib-0.0.10 → executorlib-0.0.11}/LICENSE +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/MANIFEST.in +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/README.md +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/__init__.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/backend/__init__.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/backend/cache_serial.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/base/__init__.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/cache/__init__.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/cache/backend.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/cache/executor.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/interactive/__init__.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/interactive/flux.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/__init__.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/command.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/interactive/__init__.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/thread.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib.egg-info/SOURCES.txt +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib.egg-info/dependency_links.txt +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/executorlib.egg-info/top_level.txt +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/setup.cfg +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/setup.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_backend_serial.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_cache_executor_interactive.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_cache_executor_mpi.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_cache_executor_pysqa_flux.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_cache_executor_serial.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_cache_hdf.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_cache_shared.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_dependencies_executor.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_executor_backend_flux.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_executor_backend_mpi.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_executor_backend_mpi_noblock.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_flux_executor.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_integration_pyiron_workflow.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_local_executor.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_local_executor_future.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_pysqa_subprocess.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_shared_backend.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_shared_communication.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_shared_executorbase.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_shared_input_check.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_shared_thread.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_shell_executor.py +0 -0
- {executorlib-0.0.10 → executorlib-0.0.11}/tests/test_shell_interactive.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: executorlib
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.11
|
|
4
4
|
Summary: Scale serial and MPI-parallel python functions over hundreds of compute nodes all from within a jupyter notebook or serial python process.
|
|
5
5
|
Author-email: Jan Janssen <janssen@lanl.gov>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -60,7 +60,7 @@ Requires-Dist: networkx<=3.4.2,>=2.8.8; extra == "graph"
|
|
|
60
60
|
Provides-Extra: graphnotebook
|
|
61
61
|
Requires-Dist: pygraphviz<=1.14,>=1.10; extra == "graphnotebook"
|
|
62
62
|
Requires-Dist: networkx<=3.4.2,>=2.8.8; extra == "graphnotebook"
|
|
63
|
-
Requires-Dist: ipython<=8.
|
|
63
|
+
Requires-Dist: ipython<=8.32.0,>=7.33.0; extra == "graphnotebook"
|
|
64
64
|
Provides-Extra: mpi
|
|
65
65
|
Requires-Dist: mpi4py<=4.0.1,>=3.1.4; extra == "mpi"
|
|
66
66
|
Provides-Extra: submission
|
|
@@ -72,7 +72,7 @@ Requires-Dist: pysqa==0.2.3; extra == "all"
|
|
|
72
72
|
Requires-Dist: h5py<=3.12.1,>=3.6.0; extra == "all"
|
|
73
73
|
Requires-Dist: pygraphviz<=1.14,>=1.10; extra == "all"
|
|
74
74
|
Requires-Dist: networkx<=3.4.2,>=2.8.8; extra == "all"
|
|
75
|
-
Requires-Dist: ipython<=8.
|
|
75
|
+
Requires-Dist: ipython<=8.32.0,>=7.33.0; extra == "all"
|
|
76
76
|
|
|
77
77
|
# executorlib
|
|
78
78
|
[](https://github.com/pyiron/executorlib/actions/workflows/unittest-openmpi.yml)
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-02-
|
|
11
|
+
"date": "2025-02-03T20:15:36+0100",
|
|
12
12
|
"dirty": true,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "0.0.
|
|
14
|
+
"full-revisionid": "11d44cc6f9a0f096fb8b95e72c6b28f3b1994146",
|
|
15
|
+
"version": "0.0.11"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import pickle
|
|
2
2
|
import sys
|
|
3
3
|
import time
|
|
4
|
-
from typing import Any
|
|
5
4
|
|
|
6
5
|
import cloudpickle
|
|
7
6
|
|
|
@@ -40,10 +39,7 @@ def main() -> None:
|
|
|
40
39
|
apply_dict = backend_load_file(file_name=file_name)
|
|
41
40
|
apply_dict = MPI.COMM_WORLD.bcast(apply_dict, root=0)
|
|
42
41
|
output = apply_dict["fn"].__call__(*apply_dict["args"], **apply_dict["kwargs"])
|
|
43
|
-
if mpi_size_larger_one
|
|
44
|
-
result = MPI.COMM_WORLD.gather(output, root=0)
|
|
45
|
-
else:
|
|
46
|
-
result = output
|
|
42
|
+
result = MPI.COMM_WORLD.gather(output, root=0) if mpi_size_larger_one else output
|
|
47
43
|
if mpi_rank_zero:
|
|
48
44
|
backend_write_file(
|
|
49
45
|
file_name=file_name,
|
|
@@ -57,17 +57,17 @@ def main() -> None:
|
|
|
57
57
|
input_dict = MPI.COMM_WORLD.bcast(input_dict, root=0)
|
|
58
58
|
|
|
59
59
|
# Parse input
|
|
60
|
-
if "shutdown" in input_dict
|
|
60
|
+
if "shutdown" in input_dict and input_dict["shutdown"]:
|
|
61
61
|
if mpi_rank_zero:
|
|
62
62
|
interface_send(socket=socket, result_dict={"result": True})
|
|
63
63
|
interface_shutdown(socket=socket, context=context)
|
|
64
64
|
MPI.COMM_WORLD.Barrier()
|
|
65
65
|
break
|
|
66
66
|
elif (
|
|
67
|
-
"fn" in input_dict
|
|
68
|
-
and "init" not in input_dict
|
|
69
|
-
and "args" in input_dict
|
|
70
|
-
and "kwargs" in input_dict
|
|
67
|
+
"fn" in input_dict
|
|
68
|
+
and "init" not in input_dict
|
|
69
|
+
and "args" in input_dict
|
|
70
|
+
and "kwargs" in input_dict
|
|
71
71
|
):
|
|
72
72
|
# Execute function
|
|
73
73
|
try:
|
|
@@ -87,10 +87,10 @@ def main() -> None:
|
|
|
87
87
|
if mpi_rank_zero:
|
|
88
88
|
interface_send(socket=socket, result_dict={"result": output_reply})
|
|
89
89
|
elif (
|
|
90
|
-
"init" in input_dict
|
|
90
|
+
"init" in input_dict
|
|
91
91
|
and input_dict["init"]
|
|
92
|
-
and "args" in input_dict
|
|
93
|
-
and "kwargs" in input_dict
|
|
92
|
+
and "args" in input_dict
|
|
93
|
+
and "kwargs" in input_dict
|
|
94
94
|
):
|
|
95
95
|
memory = call_funct(input_dict=input_dict, funct=None)
|
|
96
96
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from os.path import abspath
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
5
|
from executorlib.standalone.interactive.backend import call_funct, parse_arguments
|
|
6
6
|
from executorlib.standalone.interactive.communication import (
|
|
@@ -11,7 +11,7 @@ from executorlib.standalone.interactive.communication import (
|
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def main(argument_lst: Optional[
|
|
14
|
+
def main(argument_lst: Optional[list[str]] = None):
|
|
15
15
|
"""
|
|
16
16
|
The main function of the program.
|
|
17
17
|
|
|
@@ -40,15 +40,15 @@ def main(argument_lst: Optional[List[str]] = None):
|
|
|
40
40
|
input_dict = interface_receive(socket=socket)
|
|
41
41
|
|
|
42
42
|
# Parse input
|
|
43
|
-
if "shutdown" in input_dict
|
|
43
|
+
if "shutdown" in input_dict and input_dict["shutdown"]:
|
|
44
44
|
interface_send(socket=socket, result_dict={"result": True})
|
|
45
45
|
interface_shutdown(socket=socket, context=context)
|
|
46
46
|
break
|
|
47
47
|
elif (
|
|
48
|
-
"fn" in input_dict
|
|
49
|
-
and "init" not in input_dict
|
|
50
|
-
and "args" in input_dict
|
|
51
|
-
and "kwargs" in input_dict
|
|
48
|
+
"fn" in input_dict
|
|
49
|
+
and "init" not in input_dict
|
|
50
|
+
and "args" in input_dict
|
|
51
|
+
and "kwargs" in input_dict
|
|
52
52
|
):
|
|
53
53
|
# Execute function
|
|
54
54
|
try:
|
|
@@ -62,10 +62,10 @@ def main(argument_lst: Optional[List[str]] = None):
|
|
|
62
62
|
# Send output
|
|
63
63
|
interface_send(socket=socket, result_dict={"result": output})
|
|
64
64
|
elif (
|
|
65
|
-
"init" in input_dict
|
|
65
|
+
"init" in input_dict
|
|
66
66
|
and input_dict["init"]
|
|
67
|
-
and "args" in input_dict
|
|
68
|
-
and "kwargs" in input_dict
|
|
67
|
+
and "args" in input_dict
|
|
68
|
+
and "kwargs" in input_dict
|
|
69
69
|
):
|
|
70
70
|
memory = call_funct(input_dict=input_dict, funct=None)
|
|
71
71
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import queue
|
|
2
3
|
from concurrent.futures import (
|
|
3
4
|
Executor as FutureExecutor,
|
|
@@ -5,7 +6,7 @@ from concurrent.futures import (
|
|
|
5
6
|
from concurrent.futures import (
|
|
6
7
|
Future,
|
|
7
8
|
)
|
|
8
|
-
from typing import Callable,
|
|
9
|
+
from typing import Callable, Optional, Union
|
|
9
10
|
|
|
10
11
|
from executorlib.standalone.inputcheck import check_resource_dict
|
|
11
12
|
from executorlib.standalone.queue import cancel_items_in_queue
|
|
@@ -28,7 +29,7 @@ class ExecutorBase(FutureExecutor):
|
|
|
28
29
|
cloudpickle_register(ind=3)
|
|
29
30
|
self._max_cores = max_cores
|
|
30
31
|
self._future_queue: Optional[queue.Queue] = queue.Queue()
|
|
31
|
-
self._process: Optional[Union[RaisingThread,
|
|
32
|
+
self._process: Optional[Union[RaisingThread, list[RaisingThread]]] = None
|
|
32
33
|
|
|
33
34
|
@property
|
|
34
35
|
def info(self) -> Optional[dict]:
|
|
@@ -40,13 +41,13 @@ class ExecutorBase(FutureExecutor):
|
|
|
40
41
|
"""
|
|
41
42
|
if self._process is not None and isinstance(self._process, list):
|
|
42
43
|
meta_data_dict = self._process[0].get_kwargs().copy()
|
|
43
|
-
if "future_queue" in meta_data_dict
|
|
44
|
+
if "future_queue" in meta_data_dict:
|
|
44
45
|
del meta_data_dict["future_queue"]
|
|
45
46
|
meta_data_dict["max_workers"] = len(self._process)
|
|
46
47
|
return meta_data_dict
|
|
47
48
|
elif self._process is not None:
|
|
48
49
|
meta_data_dict = self._process.get_kwargs().copy()
|
|
49
|
-
if "future_queue" in meta_data_dict
|
|
50
|
+
if "future_queue" in meta_data_dict:
|
|
50
51
|
del meta_data_dict["future_queue"]
|
|
51
52
|
return meta_data_dict
|
|
52
53
|
else:
|
|
@@ -62,7 +63,13 @@ class ExecutorBase(FutureExecutor):
|
|
|
62
63
|
"""
|
|
63
64
|
return self._future_queue
|
|
64
65
|
|
|
65
|
-
def submit(
|
|
66
|
+
def submit( # type: ignore
|
|
67
|
+
self,
|
|
68
|
+
fn: Callable,
|
|
69
|
+
*args,
|
|
70
|
+
resource_dict: Optional[dict] = None,
|
|
71
|
+
**kwargs,
|
|
72
|
+
) -> Future:
|
|
66
73
|
"""
|
|
67
74
|
Submits a callable to be executed with the given arguments.
|
|
68
75
|
|
|
@@ -87,7 +94,9 @@ class ExecutorBase(FutureExecutor):
|
|
|
87
94
|
Returns:
|
|
88
95
|
Future: A Future representing the given call.
|
|
89
96
|
"""
|
|
90
|
-
|
|
97
|
+
if resource_dict is None:
|
|
98
|
+
resource_dict = {}
|
|
99
|
+
cores = resource_dict.get("cores")
|
|
91
100
|
if (
|
|
92
101
|
cores is not None
|
|
93
102
|
and self._max_cores is not None
|
|
@@ -161,7 +170,5 @@ class ExecutorBase(FutureExecutor):
|
|
|
161
170
|
"""
|
|
162
171
|
Clean-up the resources associated with the Executor.
|
|
163
172
|
"""
|
|
164
|
-
|
|
173
|
+
with contextlib.suppress(AttributeError, RuntimeError):
|
|
165
174
|
self.shutdown(wait=False)
|
|
166
|
-
except (AttributeError, RuntimeError):
|
|
167
|
-
pass
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import subprocess
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional, Union
|
|
4
4
|
|
|
5
5
|
from pysqa import QueueAdapter
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ from executorlib.standalone.inputcheck import check_file_exists
|
|
|
10
10
|
|
|
11
11
|
def execute_with_pysqa(
|
|
12
12
|
command: list,
|
|
13
|
-
task_dependent_lst: list[int] =
|
|
13
|
+
task_dependent_lst: Optional[list[int]] = None,
|
|
14
14
|
file_name: Optional[str] = None,
|
|
15
15
|
resource_dict: Optional[dict] = None,
|
|
16
16
|
config_directory: Optional[str] = None,
|
|
@@ -35,6 +35,8 @@ def execute_with_pysqa(
|
|
|
35
35
|
Returns:
|
|
36
36
|
int: queuing system ID
|
|
37
37
|
"""
|
|
38
|
+
if task_dependent_lst is None:
|
|
39
|
+
task_dependent_lst = []
|
|
38
40
|
check_file_exists(file_name=file_name)
|
|
39
41
|
queue_id = get_queue_id(file_name=file_name)
|
|
40
42
|
qa = QueueAdapter(
|
|
@@ -79,7 +81,7 @@ def _pysqa_execute_command(
|
|
|
79
81
|
split_output: bool = True,
|
|
80
82
|
shell: bool = False,
|
|
81
83
|
error_filename: str = "pysqa.err",
|
|
82
|
-
) -> Union[str,
|
|
84
|
+
) -> Union[str, list[str]]:
|
|
83
85
|
"""
|
|
84
86
|
A wrapper around the subprocess.check_output function. Modified from pysqa to raise an exception if the subprocess
|
|
85
87
|
fails to submit the job to the queue.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import importlib.util
|
|
2
3
|
import os
|
|
3
4
|
import queue
|
|
4
5
|
import sys
|
|
5
6
|
from concurrent.futures import Future
|
|
6
|
-
from typing import Any, Callable, Optional
|
|
7
|
+
from typing import Any, Callable, Optional
|
|
7
8
|
|
|
8
9
|
from executorlib.standalone.command import get_command_path
|
|
9
10
|
from executorlib.standalone.hdf import dump, get_output
|
|
@@ -79,15 +80,9 @@ def execute_tasks_h5(
|
|
|
79
80
|
file_name_dict: dict = {}
|
|
80
81
|
while True:
|
|
81
82
|
task_dict = None
|
|
82
|
-
|
|
83
|
+
with contextlib.suppress(queue.Empty):
|
|
83
84
|
task_dict = future_queue.get_nowait()
|
|
84
|
-
|
|
85
|
-
pass
|
|
86
|
-
if (
|
|
87
|
-
task_dict is not None
|
|
88
|
-
and "shutdown" in task_dict.keys()
|
|
89
|
-
and task_dict["shutdown"]
|
|
90
|
-
):
|
|
85
|
+
if task_dict is not None and "shutdown" in task_dict and task_dict["shutdown"]:
|
|
91
86
|
if terminate_function is not None:
|
|
92
87
|
for task in process_dict.values():
|
|
93
88
|
terminate_function(task=task)
|
|
@@ -110,7 +105,7 @@ def execute_tasks_h5(
|
|
|
110
105
|
fn_kwargs=task_kwargs,
|
|
111
106
|
resource_dict=task_resource_dict,
|
|
112
107
|
)
|
|
113
|
-
if task_key not in memory_dict
|
|
108
|
+
if task_key not in memory_dict:
|
|
114
109
|
if task_key + ".h5out" not in os.listdir(cache_directory):
|
|
115
110
|
file_name = os.path.join(cache_directory, task_key + ".h5in")
|
|
116
111
|
dump(file_name=file_name, data_dict=data_dict)
|
|
@@ -204,7 +199,7 @@ def _check_task_output(
|
|
|
204
199
|
|
|
205
200
|
def _convert_args_and_kwargs(
|
|
206
201
|
task_dict: dict, memory_dict: dict, file_name_dict: dict
|
|
207
|
-
) ->
|
|
202
|
+
) -> tuple[list, dict, list]:
|
|
208
203
|
"""
|
|
209
204
|
Convert the arguments and keyword arguments in a task dictionary to the appropriate types.
|
|
210
205
|
|
|
@@ -7,7 +7,7 @@ from executorlib.standalone.inputcheck import check_file_exists
|
|
|
7
7
|
|
|
8
8
|
def execute_in_subprocess(
|
|
9
9
|
command: list,
|
|
10
|
-
task_dependent_lst: list =
|
|
10
|
+
task_dependent_lst: Optional[list] = None,
|
|
11
11
|
file_name: Optional[str] = None,
|
|
12
12
|
resource_dict: Optional[dict] = None,
|
|
13
13
|
config_directory: Optional[str] = None,
|
|
@@ -33,6 +33,8 @@ def execute_in_subprocess(
|
|
|
33
33
|
subprocess.Popen: The subprocess object.
|
|
34
34
|
|
|
35
35
|
"""
|
|
36
|
+
if task_dependent_lst is None:
|
|
37
|
+
task_dependent_lst = []
|
|
36
38
|
check_file_exists(file_name=file_name)
|
|
37
39
|
while len(task_dependent_lst) > 0:
|
|
38
40
|
task_dependent_lst = [
|
|
@@ -46,10 +48,7 @@ def execute_in_subprocess(
|
|
|
46
48
|
raise ValueError("backend parameter is not supported for subprocess spawner.")
|
|
47
49
|
if resource_dict is None:
|
|
48
50
|
resource_dict = {}
|
|
49
|
-
|
|
50
|
-
cwd = resource_dict["cwd"]
|
|
51
|
-
else:
|
|
52
|
-
cwd = cache_directory
|
|
51
|
+
cwd = resource_dict.get("cwd", cache_directory)
|
|
53
52
|
return subprocess.Popen(command, universal_newlines=True, cwd=cwd)
|
|
54
53
|
|
|
55
54
|
|
|
@@ -35,7 +35,7 @@ def create_executor(
|
|
|
35
35
|
backend: str = "local",
|
|
36
36
|
max_cores: Optional[int] = None,
|
|
37
37
|
cache_directory: Optional[str] = None,
|
|
38
|
-
resource_dict: dict =
|
|
38
|
+
resource_dict: Optional[dict] = None,
|
|
39
39
|
flux_executor=None,
|
|
40
40
|
flux_executor_pmi_mode: Optional[str] = None,
|
|
41
41
|
flux_executor_nesting: bool = False,
|
|
@@ -83,6 +83,8 @@ def create_executor(
|
|
|
83
83
|
of the individual function.
|
|
84
84
|
init_function (None): optional function to preset arguments for functions which are submitted later
|
|
85
85
|
"""
|
|
86
|
+
if resource_dict is None:
|
|
87
|
+
resource_dict = {}
|
|
86
88
|
if flux_executor is not None and backend != "flux_allocation":
|
|
87
89
|
backend = "flux_allocation"
|
|
88
90
|
if backend == "flux_allocation":
|
|
@@ -149,7 +151,7 @@ def create_flux_allocation_executor(
|
|
|
149
151
|
max_workers: Optional[int] = None,
|
|
150
152
|
max_cores: Optional[int] = None,
|
|
151
153
|
cache_directory: Optional[str] = None,
|
|
152
|
-
resource_dict: dict =
|
|
154
|
+
resource_dict: Optional[dict] = None,
|
|
153
155
|
flux_executor=None,
|
|
154
156
|
flux_executor_pmi_mode: Optional[str] = None,
|
|
155
157
|
flux_executor_nesting: bool = False,
|
|
@@ -160,6 +162,8 @@ def create_flux_allocation_executor(
|
|
|
160
162
|
) -> Union[InteractiveStepExecutor, InteractiveExecutor]:
|
|
161
163
|
check_init_function(block_allocation=block_allocation, init_function=init_function)
|
|
162
164
|
check_pmi(backend="flux_allocation", pmi=flux_executor_pmi_mode)
|
|
165
|
+
if resource_dict is None:
|
|
166
|
+
resource_dict = {}
|
|
163
167
|
cores_per_worker = resource_dict.get("cores", 1)
|
|
164
168
|
resource_dict["cache_directory"] = cache_directory
|
|
165
169
|
resource_dict["hostname_localhost"] = hostname_localhost
|
|
@@ -167,9 +171,9 @@ def create_flux_allocation_executor(
|
|
|
167
171
|
check_command_line_argument_lst(
|
|
168
172
|
command_line_argument_lst=resource_dict.get("slurm_cmd_args", [])
|
|
169
173
|
)
|
|
170
|
-
if "openmpi_oversubscribe" in resource_dict
|
|
174
|
+
if "openmpi_oversubscribe" in resource_dict:
|
|
171
175
|
del resource_dict["openmpi_oversubscribe"]
|
|
172
|
-
if "slurm_cmd_args" in resource_dict
|
|
176
|
+
if "slurm_cmd_args" in resource_dict:
|
|
173
177
|
del resource_dict["slurm_cmd_args"]
|
|
174
178
|
resource_dict["flux_executor"] = flux_executor
|
|
175
179
|
resource_dict["flux_executor_pmi_mode"] = flux_executor_pmi_mode
|
|
@@ -206,12 +210,14 @@ def create_slurm_allocation_executor(
|
|
|
206
210
|
max_workers: Optional[int] = None,
|
|
207
211
|
max_cores: Optional[int] = None,
|
|
208
212
|
cache_directory: Optional[str] = None,
|
|
209
|
-
resource_dict: dict =
|
|
213
|
+
resource_dict: Optional[dict] = None,
|
|
210
214
|
hostname_localhost: Optional[bool] = None,
|
|
211
215
|
block_allocation: bool = False,
|
|
212
216
|
init_function: Optional[Callable] = None,
|
|
213
217
|
) -> Union[InteractiveStepExecutor, InteractiveExecutor]:
|
|
214
218
|
check_init_function(block_allocation=block_allocation, init_function=init_function)
|
|
219
|
+
if resource_dict is None:
|
|
220
|
+
resource_dict = {}
|
|
215
221
|
cores_per_worker = resource_dict.get("cores", 1)
|
|
216
222
|
resource_dict["cache_directory"] = cache_directory
|
|
217
223
|
resource_dict["hostname_localhost"] = hostname_localhost
|
|
@@ -246,12 +252,14 @@ def create_local_executor(
|
|
|
246
252
|
max_workers: Optional[int] = None,
|
|
247
253
|
max_cores: Optional[int] = None,
|
|
248
254
|
cache_directory: Optional[str] = None,
|
|
249
|
-
resource_dict: dict =
|
|
255
|
+
resource_dict: Optional[dict] = None,
|
|
250
256
|
hostname_localhost: Optional[bool] = None,
|
|
251
257
|
block_allocation: bool = False,
|
|
252
258
|
init_function: Optional[Callable] = None,
|
|
253
259
|
) -> Union[InteractiveStepExecutor, InteractiveExecutor]:
|
|
254
260
|
check_init_function(block_allocation=block_allocation, init_function=init_function)
|
|
261
|
+
if resource_dict is None:
|
|
262
|
+
resource_dict = {}
|
|
255
263
|
cores_per_worker = resource_dict.get("cores", 1)
|
|
256
264
|
resource_dict["cache_directory"] = cache_directory
|
|
257
265
|
resource_dict["hostname_localhost"] = hostname_localhost
|
|
@@ -260,11 +268,11 @@ def create_local_executor(
|
|
|
260
268
|
check_command_line_argument_lst(
|
|
261
269
|
command_line_argument_lst=resource_dict.get("slurm_cmd_args", [])
|
|
262
270
|
)
|
|
263
|
-
if "threads_per_core" in resource_dict
|
|
271
|
+
if "threads_per_core" in resource_dict:
|
|
264
272
|
del resource_dict["threads_per_core"]
|
|
265
|
-
if "gpus_per_core" in resource_dict
|
|
273
|
+
if "gpus_per_core" in resource_dict:
|
|
266
274
|
del resource_dict["gpus_per_core"]
|
|
267
|
-
if "slurm_cmd_args" in resource_dict
|
|
275
|
+
if "slurm_cmd_args" in resource_dict:
|
|
268
276
|
del resource_dict["slurm_cmd_args"]
|
|
269
277
|
if block_allocation:
|
|
270
278
|
resource_dict["init_function"] = init_function
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from concurrent.futures import Future
|
|
2
|
-
from typing import Any, Callable,
|
|
2
|
+
from typing import Any, Callable, Optional
|
|
3
3
|
|
|
4
4
|
from executorlib.base.executor import ExecutorBase
|
|
5
5
|
from executorlib.interactive.shared import execute_tasks_with_dependencies
|
|
@@ -64,7 +64,7 @@ class ExecutorWithDependencies(ExecutorBase):
|
|
|
64
64
|
self,
|
|
65
65
|
fn: Callable[..., Any],
|
|
66
66
|
*args: Any,
|
|
67
|
-
resource_dict:
|
|
67
|
+
resource_dict: Optional[dict[str, Any]] = None,
|
|
68
68
|
**kwargs: Any,
|
|
69
69
|
) -> Future:
|
|
70
70
|
"""
|
|
@@ -80,6 +80,8 @@ class ExecutorWithDependencies(ExecutorBase):
|
|
|
80
80
|
Future: A future object representing the result of the task.
|
|
81
81
|
|
|
82
82
|
"""
|
|
83
|
+
if resource_dict is None:
|
|
84
|
+
resource_dict = {}
|
|
83
85
|
if not self._generate_dependency_graph:
|
|
84
86
|
f = super().submit(fn, *args, resource_dict=resource_dict, **kwargs)
|
|
85
87
|
else:
|
|
@@ -5,7 +5,7 @@ import sys
|
|
|
5
5
|
import time
|
|
6
6
|
from concurrent.futures import Future
|
|
7
7
|
from time import sleep
|
|
8
|
-
from typing import Any, Callable,
|
|
8
|
+
from typing import Any, Callable, Optional, Union
|
|
9
9
|
|
|
10
10
|
from executorlib.base.executor import ExecutorBase, cancel_items_in_queue
|
|
11
11
|
from executorlib.standalone.command import get_command_path
|
|
@@ -23,7 +23,9 @@ from executorlib.standalone.thread import RaisingThread
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class ExecutorBroker(ExecutorBase):
|
|
26
|
-
def submit(
|
|
26
|
+
def submit( # type: ignore
|
|
27
|
+
self, fn: Callable, *args, resource_dict: Optional[dict] = None, **kwargs
|
|
28
|
+
) -> Future:
|
|
27
29
|
"""
|
|
28
30
|
Submits a callable to be executed with the given arguments.
|
|
29
31
|
|
|
@@ -48,6 +50,8 @@ class ExecutorBroker(ExecutorBase):
|
|
|
48
50
|
Returns:
|
|
49
51
|
Future: A Future representing the given call.
|
|
50
52
|
"""
|
|
53
|
+
if resource_dict is None:
|
|
54
|
+
resource_dict = {}
|
|
51
55
|
check_resource_dict_is_empty(resource_dict=resource_dict)
|
|
52
56
|
check_resource_dict(function=fn)
|
|
53
57
|
f: Future = Future()
|
|
@@ -84,7 +88,7 @@ class ExecutorBroker(ExecutorBase):
|
|
|
84
88
|
self._process = None
|
|
85
89
|
self._future_queue = None
|
|
86
90
|
|
|
87
|
-
def _set_process(self, process:
|
|
91
|
+
def _set_process(self, process: list[RaisingThread]): # type: ignore
|
|
88
92
|
"""
|
|
89
93
|
Set the process for the executor.
|
|
90
94
|
|
|
@@ -112,7 +116,7 @@ class InteractiveExecutor(ExecutorBroker):
|
|
|
112
116
|
Examples:
|
|
113
117
|
|
|
114
118
|
>>> import numpy as np
|
|
115
|
-
>>> from executorlib.interactive.
|
|
119
|
+
>>> from executorlib.interactive.shared import InteractiveExecutor
|
|
116
120
|
>>>
|
|
117
121
|
>>> def calc(i, j, k):
|
|
118
122
|
>>> from mpi4py import MPI
|
|
@@ -133,10 +137,12 @@ class InteractiveExecutor(ExecutorBroker):
|
|
|
133
137
|
def __init__(
|
|
134
138
|
self,
|
|
135
139
|
max_workers: int = 1,
|
|
136
|
-
executor_kwargs: dict =
|
|
140
|
+
executor_kwargs: Optional[dict] = None,
|
|
137
141
|
spawner: type[BaseSpawner] = MpiExecSpawner,
|
|
138
142
|
):
|
|
139
|
-
|
|
143
|
+
if executor_kwargs is None:
|
|
144
|
+
executor_kwargs = {}
|
|
145
|
+
super().__init__(max_cores=executor_kwargs.get("max_cores"))
|
|
140
146
|
executor_kwargs["future_queue"] = self._future_queue
|
|
141
147
|
executor_kwargs["spawner"] = spawner
|
|
142
148
|
executor_kwargs["queue_join_on_shutdown"] = False
|
|
@@ -167,7 +173,7 @@ class InteractiveStepExecutor(ExecutorBase):
|
|
|
167
173
|
Examples:
|
|
168
174
|
|
|
169
175
|
>>> import numpy as np
|
|
170
|
-
>>> from executorlib.interactive.
|
|
176
|
+
>>> from executorlib.interactive.shared import InteractiveStepExecutor
|
|
171
177
|
>>>
|
|
172
178
|
>>> def calc(i, j, k):
|
|
173
179
|
>>> from mpi4py import MPI
|
|
@@ -175,7 +181,7 @@ class InteractiveStepExecutor(ExecutorBase):
|
|
|
175
181
|
>>> rank = MPI.COMM_WORLD.Get_rank()
|
|
176
182
|
>>> return np.array([i, j, k]), size, rank
|
|
177
183
|
>>>
|
|
178
|
-
>>> with
|
|
184
|
+
>>> with InteractiveStepExecutor(max_cores=2) as p:
|
|
179
185
|
>>> fs = p.submit(calc, 2, j=4, k=3, resource_dict={"cores": 2})
|
|
180
186
|
>>> print(fs.result())
|
|
181
187
|
|
|
@@ -187,10 +193,12 @@ class InteractiveStepExecutor(ExecutorBase):
|
|
|
187
193
|
self,
|
|
188
194
|
max_cores: Optional[int] = None,
|
|
189
195
|
max_workers: Optional[int] = None,
|
|
190
|
-
executor_kwargs: dict =
|
|
196
|
+
executor_kwargs: Optional[dict] = None,
|
|
191
197
|
spawner: type[BaseSpawner] = MpiExecSpawner,
|
|
192
198
|
):
|
|
193
|
-
|
|
199
|
+
if executor_kwargs is None:
|
|
200
|
+
executor_kwargs = {}
|
|
201
|
+
super().__init__(max_cores=executor_kwargs.get("max_cores"))
|
|
194
202
|
executor_kwargs["future_queue"] = self._future_queue
|
|
195
203
|
executor_kwargs["spawner"] = spawner
|
|
196
204
|
executor_kwargs["max_cores"] = max_cores
|
|
@@ -244,13 +252,13 @@ def execute_parallel_tasks(
|
|
|
244
252
|
)
|
|
245
253
|
while True:
|
|
246
254
|
task_dict = future_queue.get()
|
|
247
|
-
if "shutdown" in task_dict
|
|
255
|
+
if "shutdown" in task_dict and task_dict["shutdown"]:
|
|
248
256
|
interface.shutdown(wait=task_dict["wait"])
|
|
249
257
|
future_queue.task_done()
|
|
250
258
|
if queue_join_on_shutdown:
|
|
251
259
|
future_queue.join()
|
|
252
260
|
break
|
|
253
|
-
elif "fn" in task_dict
|
|
261
|
+
elif "fn" in task_dict and "future" in task_dict:
|
|
254
262
|
if cache_directory is None:
|
|
255
263
|
_execute_task(
|
|
256
264
|
interface=interface, task_dict=task_dict, future_queue=future_queue
|
|
@@ -293,17 +301,17 @@ def execute_separate_tasks(
|
|
|
293
301
|
active_task_dict: dict = {}
|
|
294
302
|
process_lst: list = []
|
|
295
303
|
qtask_lst: list = []
|
|
296
|
-
if "cores" not in kwargs
|
|
304
|
+
if "cores" not in kwargs:
|
|
297
305
|
kwargs["cores"] = 1
|
|
298
306
|
while True:
|
|
299
307
|
task_dict = future_queue.get()
|
|
300
|
-
if "shutdown" in task_dict
|
|
308
|
+
if "shutdown" in task_dict and task_dict["shutdown"]:
|
|
301
309
|
if task_dict["wait"]:
|
|
302
310
|
_ = [process.join() for process in process_lst]
|
|
303
311
|
future_queue.task_done()
|
|
304
312
|
future_queue.join()
|
|
305
313
|
break
|
|
306
|
-
elif "fn" in task_dict
|
|
314
|
+
elif "fn" in task_dict and "future" in task_dict:
|
|
307
315
|
qtask: queue.Queue = queue.Queue()
|
|
308
316
|
process, active_task_dict = _submit_function_to_separate_process(
|
|
309
317
|
task_dict=task_dict,
|
|
@@ -343,18 +351,14 @@ def execute_tasks_with_dependencies(
|
|
|
343
351
|
except queue.Empty:
|
|
344
352
|
task_dict = None
|
|
345
353
|
if ( # shutdown the executor
|
|
346
|
-
task_dict is not None
|
|
347
|
-
and "shutdown" in task_dict.keys()
|
|
348
|
-
and task_dict["shutdown"]
|
|
354
|
+
task_dict is not None and "shutdown" in task_dict and task_dict["shutdown"]
|
|
349
355
|
):
|
|
350
356
|
executor.shutdown(wait=task_dict["wait"])
|
|
351
357
|
future_queue.task_done()
|
|
352
358
|
future_queue.join()
|
|
353
359
|
break
|
|
354
360
|
elif ( # handle function submitted to the executor
|
|
355
|
-
task_dict is not None
|
|
356
|
-
and "fn" in task_dict.keys()
|
|
357
|
-
and "future" in task_dict.keys()
|
|
361
|
+
task_dict is not None and "fn" in task_dict and "future" in task_dict
|
|
358
362
|
):
|
|
359
363
|
future_lst, ready_flag = _get_future_objects_from_input(task_dict=task_dict)
|
|
360
364
|
if len(future_lst) == 0 or ready_flag:
|
|
@@ -438,7 +442,7 @@ def _wait_for_free_slots(
|
|
|
438
442
|
return active_task_dict
|
|
439
443
|
|
|
440
444
|
|
|
441
|
-
def _submit_waiting_task(wait_lst:
|
|
445
|
+
def _submit_waiting_task(wait_lst: list[dict], executor_queue: queue.Queue) -> list:
|
|
442
446
|
"""
|
|
443
447
|
Submit the waiting tasks, which future inputs have been completed, to the executor
|
|
444
448
|
|
|
@@ -451,7 +455,7 @@ def _submit_waiting_task(wait_lst: List[dict], executor_queue: queue.Queue) -> l
|
|
|
451
455
|
"""
|
|
452
456
|
wait_tmp_lst = []
|
|
453
457
|
for task_wait_dict in wait_lst:
|
|
454
|
-
if all(
|
|
458
|
+
if all(future.done() for future in task_wait_dict["future_lst"]):
|
|
455
459
|
del task_wait_dict["future_lst"]
|
|
456
460
|
task_wait_dict["args"], task_wait_dict["kwargs"] = _update_futures_in_input(
|
|
457
461
|
args=task_wait_dict["args"], kwargs=task_wait_dict["kwargs"]
|
|
@@ -462,7 +466,7 @@ def _submit_waiting_task(wait_lst: List[dict], executor_queue: queue.Queue) -> l
|
|
|
462
466
|
return wait_tmp_lst
|
|
463
467
|
|
|
464
468
|
|
|
465
|
-
def _update_futures_in_input(args: tuple, kwargs: dict) ->
|
|
469
|
+
def _update_futures_in_input(args: tuple, kwargs: dict) -> tuple[tuple, dict]:
|
|
466
470
|
"""
|
|
467
471
|
Evaluate future objects in the arguments and keyword arguments by calling future.result()
|
|
468
472
|
|
|
@@ -474,7 +478,7 @@ def _update_futures_in_input(args: tuple, kwargs: dict) -> Tuple[tuple, dict]:
|
|
|
474
478
|
tuple, dict: arguments and keyword arguments with each future object in them being evaluated
|
|
475
479
|
"""
|
|
476
480
|
|
|
477
|
-
def get_result(arg: Union[
|
|
481
|
+
def get_result(arg: Union[list[Future], Future]) -> Any:
|
|
478
482
|
if isinstance(arg, Future):
|
|
479
483
|
return arg.result()
|
|
480
484
|
elif isinstance(arg, list):
|
|
@@ -552,7 +556,7 @@ def _submit_function_to_separate_process(
|
|
|
552
556
|
resource_dict = task_dict.pop("resource_dict").copy()
|
|
553
557
|
qtask.put(task_dict)
|
|
554
558
|
qtask.put({"shutdown": True, "wait": True})
|
|
555
|
-
if "cores" not in resource_dict
|
|
559
|
+
if "cores" not in resource_dict or (
|
|
556
560
|
resource_dict["cores"] == 1 and executor_kwargs["cores"] >= 1
|
|
557
561
|
):
|
|
558
562
|
resource_dict["cores"] = executor_kwargs["cores"]
|
|
@@ -28,7 +28,7 @@ class SrunSpawner(SubprocessSpawner):
|
|
|
28
28
|
threads_per_core: int = 1,
|
|
29
29
|
gpus_per_core: int = 0,
|
|
30
30
|
openmpi_oversubscribe: bool = False,
|
|
31
|
-
slurm_cmd_args: list[str] =
|
|
31
|
+
slurm_cmd_args: Optional[list[str]] = None,
|
|
32
32
|
):
|
|
33
33
|
"""
|
|
34
34
|
Srun interface implementation.
|
|
@@ -79,7 +79,7 @@ def generate_slurm_command(
|
|
|
79
79
|
threads_per_core: int = 1,
|
|
80
80
|
gpus_per_core: int = 0,
|
|
81
81
|
openmpi_oversubscribe: bool = False,
|
|
82
|
-
slurm_cmd_args: list[str] =
|
|
82
|
+
slurm_cmd_args: Optional[list[str]] = None,
|
|
83
83
|
) -> list[str]:
|
|
84
84
|
"""
|
|
85
85
|
Generate the command list for the SLURM interface.
|
|
@@ -104,6 +104,6 @@ def generate_slurm_command(
|
|
|
104
104
|
command_prepend_lst += ["--gpus-per-task=" + str(gpus_per_core)]
|
|
105
105
|
if openmpi_oversubscribe:
|
|
106
106
|
command_prepend_lst += ["--oversubscribe"]
|
|
107
|
-
if len(slurm_cmd_args) > 0:
|
|
107
|
+
if slurm_cmd_args is not None and len(slurm_cmd_args) > 0:
|
|
108
108
|
command_prepend_lst += slurm_cmd_args
|
|
109
109
|
return command_prepend_lst
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from typing import Any,
|
|
2
|
+
from typing import Any, Optional
|
|
3
3
|
|
|
4
4
|
import cloudpickle
|
|
5
5
|
import h5py
|
|
@@ -26,7 +26,7 @@ def dump(file_name: Optional[str], data_dict: dict) -> None:
|
|
|
26
26
|
if file_name is not None:
|
|
27
27
|
with h5py.File(file_name, "a") as fname:
|
|
28
28
|
for data_key, data_value in data_dict.items():
|
|
29
|
-
if data_key in group_dict
|
|
29
|
+
if data_key in group_dict:
|
|
30
30
|
fname.create_dataset(
|
|
31
31
|
name="/" + group_dict[data_key],
|
|
32
32
|
data=np.void(cloudpickle.dumps(data_value)),
|
|
@@ -60,7 +60,7 @@ def load(file_name: str) -> dict:
|
|
|
60
60
|
return data_dict
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def get_output(file_name: str) ->
|
|
63
|
+
def get_output(file_name: str) -> tuple[bool, Any]:
|
|
64
64
|
"""
|
|
65
65
|
Check if output is available in the HDF5 file
|
|
66
66
|
|
|
@@ -102,7 +102,7 @@ def get_queue_id(file_name: Optional[str]) -> Optional[int]:
|
|
|
102
102
|
return None
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
def get_cache_data(cache_directory: str) ->
|
|
105
|
+
def get_cache_data(cache_directory: str) -> list[dict]:
|
|
106
106
|
file_lst = []
|
|
107
107
|
for file_name in os.listdir(cache_directory):
|
|
108
108
|
with h5py.File(os.path.join(cache_directory, file_name), "r") as hdf:
|
|
@@ -2,7 +2,7 @@ import inspect
|
|
|
2
2
|
import multiprocessing
|
|
3
3
|
import os.path
|
|
4
4
|
from concurrent.futures import Executor
|
|
5
|
-
from typing import Callable,
|
|
5
|
+
from typing import Callable, Optional
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def check_oversubscribe(oversubscribe: bool) -> None:
|
|
@@ -16,7 +16,7 @@ def check_oversubscribe(oversubscribe: bool) -> None:
|
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def check_command_line_argument_lst(command_line_argument_lst:
|
|
19
|
+
def check_command_line_argument_lst(command_line_argument_lst: list[str]) -> None:
|
|
20
20
|
"""
|
|
21
21
|
Check if command_line_argument_lst is not empty and raise a ValueError if it is.
|
|
22
22
|
"""
|
|
@@ -63,7 +63,7 @@ def check_resource_dict(function: Callable) -> None:
|
|
|
63
63
|
"""
|
|
64
64
|
Check if the function has a parameter named 'resource_dict' and raise a ValueError if it does.
|
|
65
65
|
"""
|
|
66
|
-
if "resource_dict" in inspect.signature(function).parameters
|
|
66
|
+
if "resource_dict" in inspect.signature(function).parameters:
|
|
67
67
|
raise ValueError(
|
|
68
68
|
"The parameter resource_dict is used internally in executorlib, "
|
|
69
69
|
"so it cannot be used as a parameter in the submitted functions."
|
|
@@ -185,13 +185,12 @@ def validate_number_of_cores(
|
|
|
185
185
|
return int(max_cores / cores_per_worker)
|
|
186
186
|
elif max_workers is not None:
|
|
187
187
|
return int(max_workers)
|
|
188
|
+
elif max_cores is None and max_workers is None and not set_local_cores:
|
|
189
|
+
raise ValueError(
|
|
190
|
+
"Block allocation requires a fixed set of computational resources. Neither max_cores nor max_workers are defined."
|
|
191
|
+
)
|
|
188
192
|
else:
|
|
189
|
-
|
|
190
|
-
raise ValueError(
|
|
191
|
-
"Block allocation requires a fixed set of computational resources. Neither max_cores nor max_workers are defined."
|
|
192
|
-
)
|
|
193
|
-
else:
|
|
194
|
-
return multiprocessing.cpu_count()
|
|
193
|
+
return multiprocessing.cpu_count()
|
|
195
194
|
|
|
196
195
|
|
|
197
196
|
def check_file_exists(file_name: Optional[str]):
|
{executorlib-0.0.10 → executorlib-0.0.11}/executorlib/standalone/interactive/communication.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from socket import gethostname
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
5
|
import cloudpickle
|
|
6
6
|
import zmq
|
|
@@ -44,7 +44,7 @@ class SocketInterface:
|
|
|
44
44
|
dict: dictionary with response received from the connected client
|
|
45
45
|
"""
|
|
46
46
|
output = cloudpickle.loads(self._socket.recv())
|
|
47
|
-
if "result" in output
|
|
47
|
+
if "result" in output:
|
|
48
48
|
return output["result"]
|
|
49
49
|
else:
|
|
50
50
|
error_type = output["error_type"].split("'")[1]
|
|
@@ -161,7 +161,7 @@ def interface_bootup(
|
|
|
161
161
|
return interface
|
|
162
162
|
|
|
163
163
|
|
|
164
|
-
def interface_connect(host: str, port: str) ->
|
|
164
|
+
def interface_connect(host: str, port: str) -> tuple[zmq.Context, zmq.Socket]:
|
|
165
165
|
"""
|
|
166
166
|
Connect to an existing SocketInterface instance by providing the hostname and the port as strings.
|
|
167
167
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import subprocess
|
|
2
|
-
from abc import ABC
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
5
|
MPI_COMMAND = "mpiexec"
|
|
@@ -24,6 +24,7 @@ class BaseSpawner(ABC):
|
|
|
24
24
|
self._cores = cores
|
|
25
25
|
self._openmpi_oversubscribe = openmpi_oversubscribe
|
|
26
26
|
|
|
27
|
+
@abstractmethod
|
|
27
28
|
def bootup(
|
|
28
29
|
self,
|
|
29
30
|
command_lst: list[str],
|
|
@@ -36,6 +37,7 @@ class BaseSpawner(ABC):
|
|
|
36
37
|
"""
|
|
37
38
|
raise NotImplementedError
|
|
38
39
|
|
|
40
|
+
@abstractmethod
|
|
39
41
|
def shutdown(self, wait: bool = True):
|
|
40
42
|
"""
|
|
41
43
|
Method to shutdown the interface.
|
|
@@ -45,6 +47,7 @@ class BaseSpawner(ABC):
|
|
|
45
47
|
"""
|
|
46
48
|
raise NotImplementedError
|
|
47
49
|
|
|
50
|
+
@abstractmethod
|
|
48
51
|
def poll(self):
|
|
49
52
|
"""
|
|
50
53
|
Method to check if the interface is running.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import os.path
|
|
2
2
|
from concurrent.futures import Future
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
5
|
import cloudpickle
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def generate_nodes_and_edges(
|
|
9
9
|
task_hash_dict: dict, future_hash_inverse_dict: dict
|
|
10
|
-
) ->
|
|
10
|
+
) -> tuple[list, list]:
|
|
11
11
|
"""
|
|
12
12
|
Generate nodes and edges for visualization.
|
|
13
13
|
|
|
@@ -39,7 +39,7 @@ def generate_nodes_and_edges(
|
|
|
39
39
|
"label": label,
|
|
40
40
|
}
|
|
41
41
|
)
|
|
42
|
-
elif isinstance(arg, list) and all(
|
|
42
|
+
elif isinstance(arg, list) and all(isinstance(a, Future) for a in arg):
|
|
43
43
|
for a in arg:
|
|
44
44
|
add_element(arg=a, link_to=link_to, label=label)
|
|
45
45
|
else:
|
|
@@ -12,7 +12,7 @@ def cancel_items_in_queue(que: queue.Queue):
|
|
|
12
12
|
while True:
|
|
13
13
|
try:
|
|
14
14
|
item = que.get_nowait()
|
|
15
|
-
if isinstance(item, dict) and "future" in item
|
|
15
|
+
if isinstance(item, dict) and "future" in item:
|
|
16
16
|
item["future"].cancel()
|
|
17
17
|
que.task_done()
|
|
18
18
|
except queue.Empty:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import inspect
|
|
3
3
|
import re
|
|
4
|
-
from typing import Callable,
|
|
4
|
+
from typing import Callable, Optional
|
|
5
5
|
|
|
6
6
|
import cloudpickle
|
|
7
7
|
|
|
@@ -29,8 +29,11 @@ def cloudpickle_register(ind: int = 2):
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def serialize_funct_h5(
|
|
32
|
-
fn: Callable,
|
|
33
|
-
|
|
32
|
+
fn: Callable,
|
|
33
|
+
fn_args: Optional[list] = None,
|
|
34
|
+
fn_kwargs: Optional[dict] = None,
|
|
35
|
+
resource_dict: Optional[dict] = None,
|
|
36
|
+
) -> tuple[str, dict]:
|
|
34
37
|
"""
|
|
35
38
|
Serialize a function and its arguments and keyword arguments into an HDF5 file.
|
|
36
39
|
|
|
@@ -53,6 +56,12 @@ def serialize_funct_h5(
|
|
|
53
56
|
Tuple[str, dict]: A tuple containing the task key and the serialized data.
|
|
54
57
|
|
|
55
58
|
"""
|
|
59
|
+
if fn_args is None:
|
|
60
|
+
fn_args = []
|
|
61
|
+
if fn_kwargs is None:
|
|
62
|
+
fn_kwargs = {}
|
|
63
|
+
if resource_dict is None:
|
|
64
|
+
resource_dict = {}
|
|
56
65
|
binary_all = cloudpickle.dumps(
|
|
57
66
|
{"fn": fn, "args": fn_args, "kwargs": fn_kwargs, "resource_dict": resource_dict}
|
|
58
67
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: executorlib
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.11
|
|
4
4
|
Summary: Scale serial and MPI-parallel python functions over hundreds of compute nodes all from within a jupyter notebook or serial python process.
|
|
5
5
|
Author-email: Jan Janssen <janssen@lanl.gov>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -60,7 +60,7 @@ Requires-Dist: networkx<=3.4.2,>=2.8.8; extra == "graph"
|
|
|
60
60
|
Provides-Extra: graphnotebook
|
|
61
61
|
Requires-Dist: pygraphviz<=1.14,>=1.10; extra == "graphnotebook"
|
|
62
62
|
Requires-Dist: networkx<=3.4.2,>=2.8.8; extra == "graphnotebook"
|
|
63
|
-
Requires-Dist: ipython<=8.
|
|
63
|
+
Requires-Dist: ipython<=8.32.0,>=7.33.0; extra == "graphnotebook"
|
|
64
64
|
Provides-Extra: mpi
|
|
65
65
|
Requires-Dist: mpi4py<=4.0.1,>=3.1.4; extra == "mpi"
|
|
66
66
|
Provides-Extra: submission
|
|
@@ -72,7 +72,7 @@ Requires-Dist: pysqa==0.2.3; extra == "all"
|
|
|
72
72
|
Requires-Dist: h5py<=3.12.1,>=3.6.0; extra == "all"
|
|
73
73
|
Requires-Dist: pygraphviz<=1.14,>=1.10; extra == "all"
|
|
74
74
|
Requires-Dist: networkx<=3.4.2,>=2.8.8; extra == "all"
|
|
75
|
-
Requires-Dist: ipython<=8.
|
|
75
|
+
Requires-Dist: ipython<=8.32.0,>=7.33.0; extra == "all"
|
|
76
76
|
|
|
77
77
|
# executorlib
|
|
78
78
|
[](https://github.com/pyiron/executorlib/actions/workflows/unittest-openmpi.yml)
|
|
@@ -7,7 +7,7 @@ pysqa==0.2.3
|
|
|
7
7
|
h5py<=3.12.1,>=3.6.0
|
|
8
8
|
pygraphviz<=1.14,>=1.10
|
|
9
9
|
networkx<=3.4.2,>=2.8.8
|
|
10
|
-
ipython<=8.
|
|
10
|
+
ipython<=8.32.0,>=7.33.0
|
|
11
11
|
|
|
12
12
|
[cache]
|
|
13
13
|
h5py<=3.12.1,>=3.6.0
|
|
@@ -19,7 +19,7 @@ networkx<=3.4.2,>=2.8.8
|
|
|
19
19
|
[graphnotebook]
|
|
20
20
|
pygraphviz<=1.14,>=1.10
|
|
21
21
|
networkx<=3.4.2,>=2.8.8
|
|
22
|
-
ipython<=8.
|
|
22
|
+
ipython<=8.32.0,>=7.33.0
|
|
23
23
|
|
|
24
24
|
[mpi]
|
|
25
25
|
mpi4py<=4.0.1,>=3.1.4
|
|
@@ -44,7 +44,7 @@ graph = [
|
|
|
44
44
|
graphnotebook = [
|
|
45
45
|
"pygraphviz>=1.10,<=1.14",
|
|
46
46
|
"networkx>=2.8.8,<=3.4.2",
|
|
47
|
-
"ipython>=7.33.0,<=8.
|
|
47
|
+
"ipython>=7.33.0,<=8.32.0",
|
|
48
48
|
]
|
|
49
49
|
mpi = ["mpi4py>=3.1.4,<=4.0.1"]
|
|
50
50
|
submission = [
|
|
@@ -57,7 +57,42 @@ all = [
|
|
|
57
57
|
"h5py>=3.6.0,<=3.12.1",
|
|
58
58
|
"pygraphviz>=1.10,<=1.14",
|
|
59
59
|
"networkx>=2.8.8,<=3.4.2",
|
|
60
|
-
"ipython>=7.33.0,<=8.
|
|
60
|
+
"ipython>=7.33.0,<=8.32.0",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.ruff]
|
|
64
|
+
exclude = [".ci_support", "docs", "notebooks", "tests", "setup.py", "_version.py"]
|
|
65
|
+
|
|
66
|
+
[tool.ruff.lint]
|
|
67
|
+
select = [
|
|
68
|
+
# pycodestyle
|
|
69
|
+
"E",
|
|
70
|
+
# Pyflakes
|
|
71
|
+
"F",
|
|
72
|
+
# pyupgrade
|
|
73
|
+
"UP",
|
|
74
|
+
# flake8-bugbear
|
|
75
|
+
"B",
|
|
76
|
+
# flake8-simplify
|
|
77
|
+
"SIM",
|
|
78
|
+
# isort
|
|
79
|
+
"I",
|
|
80
|
+
# flake8-comprehensions
|
|
81
|
+
"C4",
|
|
82
|
+
# eradicate
|
|
83
|
+
"ERA",
|
|
84
|
+
# pylint
|
|
85
|
+
"PL",
|
|
86
|
+
]
|
|
87
|
+
ignore = [
|
|
88
|
+
# ignore line-length violations
|
|
89
|
+
"E501",
|
|
90
|
+
# Too many arguments in function definition
|
|
91
|
+
"PLR0913",
|
|
92
|
+
# Magic value used in comparison
|
|
93
|
+
"PLR2004",
|
|
94
|
+
# Too many branches
|
|
95
|
+
"PLR0912",
|
|
61
96
|
]
|
|
62
97
|
|
|
63
98
|
[tool.setuptools.packages.find]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|