executorlib 0.0.1__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.
- executorlib/__init__.py +225 -0
- executorlib/_version.py +716 -0
- executorlib/backend/__init__.py +0 -0
- executorlib/backend/cache_parallel.py +40 -0
- executorlib/backend/cache_serial.py +6 -0
- executorlib/backend/interactive_parallel.py +91 -0
- executorlib/backend/interactive_serial.py +65 -0
- executorlib/cache/__init__.py +0 -0
- executorlib/cache/executor.py +28 -0
- executorlib/cache/hdf.py +83 -0
- executorlib/cache/shared.py +217 -0
- executorlib/interactive/__init__.py +168 -0
- executorlib/interactive/backend.py +76 -0
- executorlib/interactive/dependencies.py +67 -0
- executorlib/interactive/executor.py +112 -0
- executorlib/interactive/flux.py +79 -0
- executorlib/shared/__init__.py +24 -0
- executorlib/shared/communication.py +195 -0
- executorlib/shared/executor.py +635 -0
- executorlib/shared/inputcheck.py +121 -0
- executorlib/shared/interface.py +154 -0
- executorlib/shared/plot.py +82 -0
- executorlib/shared/thread.py +31 -0
- executorlib/shell/__init__.py +7 -0
- executorlib/shell/executor.py +114 -0
- executorlib/shell/interactive.py +181 -0
- executorlib-0.0.1.dist-info/LICENSE +29 -0
- executorlib-0.0.1.dist-info/METADATA +193 -0
- executorlib-0.0.1.dist-info/RECORD +31 -0
- executorlib-0.0.1.dist-info/WHEEL +5 -0
- executorlib-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
from socket import gethostname
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import cloudpickle
|
|
5
|
+
import zmq
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SocketInterface(object):
|
|
9
|
+
"""
|
|
10
|
+
The SocketInterface is an abstraction layer on top of the zero message queue.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
interface (executorlib.shared.interface.BaseInterface): Interface for starting the parallel process
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, interface=None):
|
|
17
|
+
self._context = zmq.Context()
|
|
18
|
+
self._socket = self._context.socket(zmq.PAIR)
|
|
19
|
+
self._process = None
|
|
20
|
+
self._interface = interface
|
|
21
|
+
|
|
22
|
+
def send_dict(self, input_dict: dict):
|
|
23
|
+
"""
|
|
24
|
+
Send a dictionary with instructions to a connected client process.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
input_dict (dict): dictionary of commands to be communicated. The key "shutdown" is reserved to stop the
|
|
28
|
+
connected client from listening.
|
|
29
|
+
"""
|
|
30
|
+
self._socket.send(cloudpickle.dumps(input_dict))
|
|
31
|
+
|
|
32
|
+
def receive_dict(self):
|
|
33
|
+
"""
|
|
34
|
+
Receive a dictionary from a connected client process.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
dict: dictionary with response received from the connected client
|
|
38
|
+
"""
|
|
39
|
+
output = cloudpickle.loads(self._socket.recv())
|
|
40
|
+
if "result" in output.keys():
|
|
41
|
+
return output["result"]
|
|
42
|
+
else:
|
|
43
|
+
error_type = output["error_type"].split("'")[1]
|
|
44
|
+
raise eval(error_type)(output["error"])
|
|
45
|
+
|
|
46
|
+
def send_and_receive_dict(self, input_dict: dict) -> dict:
|
|
47
|
+
"""
|
|
48
|
+
Combine both the send_dict() and receive_dict() function in a single call.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
input_dict (dict): dictionary of commands to be communicated. The key "shutdown" is reserved to stop the
|
|
52
|
+
connected client from listening.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
dict: dictionary with response received from the connected client
|
|
56
|
+
"""
|
|
57
|
+
self.send_dict(input_dict=input_dict)
|
|
58
|
+
return self.receive_dict()
|
|
59
|
+
|
|
60
|
+
def bind_to_random_port(self):
|
|
61
|
+
"""
|
|
62
|
+
Identify a random port typically in the range from 49152 to 65536 to bind the SocketInterface instance to. Other
|
|
63
|
+
processes can then connect to this port to receive instructions and send results.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
int: port the SocketInterface instance is bound to.
|
|
67
|
+
"""
|
|
68
|
+
return self._socket.bind_to_random_port("tcp://*")
|
|
69
|
+
|
|
70
|
+
def bootup(
|
|
71
|
+
self,
|
|
72
|
+
command_lst: list[str],
|
|
73
|
+
prefix_name: Optional[str] = None,
|
|
74
|
+
prefix_path: Optional[str] = None,
|
|
75
|
+
):
|
|
76
|
+
"""
|
|
77
|
+
Boot up the client process to connect to the SocketInterface.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
command_lst (list): list of strings to start the client process
|
|
81
|
+
prefix_name (str): name of the conda environment to initialize
|
|
82
|
+
prefix_path (str): path of the conda environment to initialize
|
|
83
|
+
"""
|
|
84
|
+
self._interface.bootup(
|
|
85
|
+
command_lst=command_lst, prefix_name=prefix_name, prefix_path=prefix_path
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def shutdown(self, wait: bool = True):
|
|
89
|
+
result = None
|
|
90
|
+
if self._interface.poll():
|
|
91
|
+
result = self.send_and_receive_dict(
|
|
92
|
+
input_dict={"shutdown": True, "wait": wait}
|
|
93
|
+
)
|
|
94
|
+
self._interface.shutdown(wait=wait)
|
|
95
|
+
if self._socket is not None:
|
|
96
|
+
self._socket.close()
|
|
97
|
+
if self._context is not None:
|
|
98
|
+
self._context.term()
|
|
99
|
+
self._process = None
|
|
100
|
+
self._socket = None
|
|
101
|
+
self._context = None
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
def __del__(self):
|
|
105
|
+
self.shutdown(wait=True)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def interface_bootup(
|
|
109
|
+
command_lst: list[str],
|
|
110
|
+
connections,
|
|
111
|
+
hostname_localhost: bool = False,
|
|
112
|
+
prefix_name: Optional[str] = None,
|
|
113
|
+
prefix_path: Optional[str] = None,
|
|
114
|
+
):
|
|
115
|
+
"""
|
|
116
|
+
Start interface for ZMQ communication
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
command_lst (list): List of commands as strings
|
|
120
|
+
connections (executorlib.shared.interface.BaseInterface): Interface to start parallel process, like MPI, SLURM
|
|
121
|
+
or Flux
|
|
122
|
+
hostname_localhost (boolean): use localhost instead of the hostname to establish the zmq connection. In the
|
|
123
|
+
context of an HPC cluster this essential to be able to communicate to an
|
|
124
|
+
Executor running on a different compute node within the same allocation. And
|
|
125
|
+
in principle any computer should be able to resolve that their own hostname
|
|
126
|
+
points to the same address as localhost. Still MacOS >= 12 seems to disable
|
|
127
|
+
this look up for security reasons. So on MacOS it is required to set this
|
|
128
|
+
option to true
|
|
129
|
+
prefix_name (str): name of the conda environment to initialize
|
|
130
|
+
prefix_path (str): path of the conda environment to initialize
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
executorlib.shared.communication.SocketInterface: socket interface for zmq communication
|
|
134
|
+
"""
|
|
135
|
+
if not hostname_localhost:
|
|
136
|
+
command_lst += [
|
|
137
|
+
"--host",
|
|
138
|
+
gethostname(),
|
|
139
|
+
]
|
|
140
|
+
interface = SocketInterface(interface=connections)
|
|
141
|
+
command_lst += [
|
|
142
|
+
"--zmqport",
|
|
143
|
+
str(interface.bind_to_random_port()),
|
|
144
|
+
]
|
|
145
|
+
interface.bootup(
|
|
146
|
+
command_lst=command_lst, prefix_name=prefix_name, prefix_path=prefix_path
|
|
147
|
+
)
|
|
148
|
+
return interface
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def interface_connect(host: str, port: str):
|
|
152
|
+
"""
|
|
153
|
+
Connect to an existing SocketInterface instance by providing the hostname and the port as strings.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
host (str): hostname of the host running the SocketInterface instance to connect to.
|
|
157
|
+
port (str): port on the host the SocketInterface instance is running on.
|
|
158
|
+
"""
|
|
159
|
+
context = zmq.Context()
|
|
160
|
+
socket = context.socket(zmq.PAIR)
|
|
161
|
+
socket.connect("tcp://" + host + ":" + port)
|
|
162
|
+
return context, socket
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def interface_send(socket: zmq.Socket, result_dict: dict):
|
|
166
|
+
"""
|
|
167
|
+
Send results to a SocketInterface instance.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
socket (zmq.Socket): socket for the connection
|
|
171
|
+
result_dict (dict): dictionary to be sent, supported keys are result, error and error_type.
|
|
172
|
+
"""
|
|
173
|
+
socket.send(cloudpickle.dumps(result_dict))
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def interface_receive(socket: zmq.Socket):
|
|
177
|
+
"""
|
|
178
|
+
Receive instructions from a SocketInterface instance.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
socket (zmq.Socket): socket for the connection
|
|
182
|
+
"""
|
|
183
|
+
return cloudpickle.loads(socket.recv())
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def interface_shutdown(socket: zmq.Socket, context: zmq.Context):
|
|
187
|
+
"""
|
|
188
|
+
Close the connection to a SocketInterface instance.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
socket (zmq.Socket): socket for the connection
|
|
192
|
+
context (zmq.sugar.context.Context): context for the connection
|
|
193
|
+
"""
|
|
194
|
+
socket.close()
|
|
195
|
+
context.term()
|