|
7 | 7 | },
|
8 | 8 | "language_info": {
|
9 | 9 | "name": "python",
|
10 |
| - "version": "3.12.9", |
| 10 | + "version": "3.12.10", |
11 | 11 | "mimetype": "text/x-python",
|
12 | 12 | "codemirror_mode": {
|
13 | 13 | "name": "ipython",
|
|
77 | 77 | "* `interfaces` - the different `Executor` classes are defined here, namely `SingleNodeExecutor`, `SlurmClusterExecutor`, `SlurmJobExecutor`, `FluxClusterExecutor` and `FluxJobExecutor`.\n",
|
78 | 78 | "* `standalone` - the standalone module contains a number of utility functions which only depend on external libraries and do not have any internal dependency to other parts of `executorlib`. This includes the functionality to generate executable commands, the [h5py](https://www.h5py.org) based interface for caching, a number of input checks, routines to plot the dependencies of a number of future objects, functionality to interact with the [queues defined in the Python standard library](https://docs.python.org/3/library/queue.html), the interface for serialization based on [cloudpickle](https://github.com/cloudpipe/cloudpickle) and finally an extension to the [threading](https://docs.python.org/3/library/threading.html) of the Python standard library.\n",
|
79 | 79 | "\n",
|
80 |
| - "Given the level of separation the integration of submodules from the standalone module in external software packages should be the easiest way to benefit from the developments in executorlib beyond just using the `Executor` class. " |
| 80 | + "Given the level of separation the integration of submodules from the standalone module in external software packages should be the easiest way to benefit from the developments in executorlib beyond just using the `Executor` class. \n", |
| 81 | + "\n", |
| 82 | + "## Interface Class Hierarchy\n", |
| 83 | + "executorlib provides five different interfaces, namely `SingleNodeExecutor`, `SlurmClusterExecutor`, `SlurmJobExecutor`, `FluxClusterExecutor` and `FluxJobExecutor`, internally these are mapped to four types of `Executor` classes, namely `BlockAllocationExecutor`, `DependencyExecutor`, `FileExecutor` and `OneTaskPerProcessExecutor` depending on which options are selected. The dependence is illustrated in the following table:\n", |
| 84 | + "\n", |
| 85 | + "| | `BlockAllocationExecutor` | `DependencyExecutor` | `FileExecutor` | `OneTaskPerProcessExecutor` |\n", |
| 86 | + "|-------------------------------------------------------------------------|---------------------------|--------------------------|----------------|-----------------------------|\n", |
| 87 | + "| `SingleNodeExecutor(disable_dependencies=False)` | | with `MpiExecSpawner` | | |\n", |
| 88 | + "| `SingleNodeExecutor(disable_dependencies=True, block_allocation=False)` | | | | with `MpiExecSpawner` |\n", |
| 89 | + "| `SingleNodeExecutor(disable_dependencies=True, block_allocation=True)` | with `MpiExecSpawner` | | | |\n", |
| 90 | + "| `SlurmClusterExecutor(plot_dependency_graph=False)` | | | with `pysqa` | |\n", |
| 91 | + "| `SlurmClusterExecutor(plot_dependency_graph=True)` | | with `SrunSpawner` | | |\n", |
| 92 | + "| `SlurmJobExecutor(disable_dependencies=False)` | | with `SrunSpawner` | | |\n", |
| 93 | + "| `SlurmJobExecutor(disable_dependencies=True, block_allocation=False)` | | | | with `SrunSpawner` |\n", |
| 94 | + "| `SlurmJobExecutor(disable_dependencies=True, block_allocation=True)` | with `SrunSpawner` | | | |\n", |
| 95 | + "| `FluxClusterExecutor(plot_dependency_graph=False)` | | | with `pysqa` | |\n", |
| 96 | + "| `FluxClusterExecutor(plot_dependency_graph=True)` | | with `FluxPythonSpawner` | | |\n", |
| 97 | + "| `FluxJobExecutor(disable_dependencies=False)` | | with `FluxPythonSpawner` | | |\n", |
| 98 | + "| `FluxJobExecutor(disable_dependencies=True, block_allocation=False)` | | | | with `FluxPythonSpawner` |\n", |
| 99 | + "| `FluxJobExecutor(disable_dependencies=True, block_allocation=True)` | with `FluxPythonSpawner` | | | |" |
81 | 100 | ],
|
82 | 101 | "metadata": {}
|
83 | 102 | },
|
|
110 | 129 | "cell_type": "code",
|
111 | 130 | "source": "from executorlib import SingleNodeExecutor",
|
112 | 131 | "metadata": {
|
113 |
| - "trusted": true |
| 132 | + "trusted": false |
114 | 133 | },
|
115 | 134 | "outputs": [],
|
116 | 135 | "execution_count": 1
|
|
120 | 139 | "cell_type": "code",
|
121 | 140 | "source": "def execute_shell_command(\n command: list, universal_newlines: bool = True, shell: bool = False\n):\n import subprocess\n\n return subprocess.check_output(\n command, universal_newlines=universal_newlines, shell=shell\n )",
|
122 | 141 | "metadata": {
|
123 |
| - "trusted": true |
| 142 | + "trusted": false |
124 | 143 | },
|
125 | 144 | "outputs": [],
|
126 | 145 | "execution_count": 2
|
|
130 | 149 | "cell_type": "code",
|
131 | 150 | "source": "with SingleNodeExecutor() as exe:\n future = exe.submit(\n execute_shell_command,\n [\"echo\", \"test\"],\n universal_newlines=True,\n shell=False,\n )\n print(future.result())",
|
132 | 151 | "metadata": {
|
133 |
| - "trusted": true |
| 152 | + "trusted": false |
134 | 153 | },
|
135 | 154 | "outputs": [
|
136 | 155 | {
|
|
152 | 171 | "cell_type": "code",
|
153 | 172 | "source": "count_script = \"\"\"\\\ndef count(iterations):\n for i in range(int(iterations)):\n print(i)\n print(\"done\")\n\n\nif __name__ == \"__main__\":\n while True:\n user_input = input()\n if \"shutdown\" in user_input:\n break\n else:\n count(iterations=int(user_input))\n\"\"\"\n\nwith open(\"count.py\", \"w\") as f:\n f.writelines(count_script)",
|
154 | 173 | "metadata": {
|
155 |
| - "trusted": true |
| 174 | + "trusted": false |
156 | 175 | },
|
157 | 176 | "outputs": [],
|
158 | 177 | "execution_count": 4
|
|
168 | 187 | "cell_type": "code",
|
169 | 188 | "source": "def init_process():\n import subprocess\n\n return {\n \"process\": subprocess.Popen(\n [\"python\", \"count.py\"],\n stdin=subprocess.PIPE,\n stdout=subprocess.PIPE,\n universal_newlines=True,\n shell=False,\n )\n }",
|
170 | 189 | "metadata": {
|
171 |
| - "trusted": true |
| 190 | + "trusted": false |
172 | 191 | },
|
173 | 192 | "outputs": [],
|
174 | 193 | "execution_count": 5
|
|
184 | 203 | "cell_type": "code",
|
185 | 204 | "source": "def interact(shell_input, process, lines_to_read=None, stop_read_pattern=None):\n process.stdin.write(shell_input)\n process.stdin.flush()\n lines_count = 0\n output = \"\"\n while True:\n output_current = process.stdout.readline()\n output += output_current\n lines_count += 1\n if stop_read_pattern is not None and stop_read_pattern in output_current:\n break\n elif lines_to_read is not None and lines_to_read == lines_count:\n break\n return output",
|
186 | 205 | "metadata": {
|
187 |
| - "trusted": true |
| 206 | + "trusted": false |
188 | 207 | },
|
189 | 208 | "outputs": [],
|
190 | 209 | "execution_count": 6
|
|
200 | 219 | "cell_type": "code",
|
201 | 220 | "source": "def shutdown(process):\n process.stdin.write(\"shutdown\\n\")\n process.stdin.flush()",
|
202 | 221 | "metadata": {
|
203 |
| - "trusted": true |
| 222 | + "trusted": false |
204 | 223 | },
|
205 | 224 | "outputs": [],
|
206 | 225 | "execution_count": 7
|
|
216 | 235 | "cell_type": "code",
|
217 | 236 | "source": "with SingleNodeExecutor(\n max_workers=1,\n init_function=init_process,\n block_allocation=True,\n) as exe:\n future = exe.submit(\n interact, shell_input=\"4\\n\", lines_to_read=5, stop_read_pattern=None\n )\n print(future.result())\n future_shutdown = exe.submit(shutdown)\n print(future_shutdown.result())",
|
218 | 237 | "metadata": {
|
219 |
| - "trusted": true |
| 238 | + "trusted": false |
220 | 239 | },
|
221 | 240 | "outputs": [
|
222 | 241 | {
|
|
0 commit comments