westpa 2022.10__cp312-cp312-macosx_11_0_arm64.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.
Potentially problematic release.
This version of westpa might be problematic. Click here for more details.
- westpa/__init__.py +14 -0
- westpa/_version.py +21 -0
- westpa/analysis/__init__.py +5 -0
- westpa/analysis/core.py +746 -0
- westpa/analysis/statistics.py +27 -0
- westpa/analysis/trajectories.py +360 -0
- westpa/cli/__init__.py +0 -0
- westpa/cli/core/__init__.py +0 -0
- westpa/cli/core/w_fork.py +152 -0
- westpa/cli/core/w_init.py +230 -0
- westpa/cli/core/w_run.py +77 -0
- westpa/cli/core/w_states.py +212 -0
- westpa/cli/core/w_succ.py +99 -0
- westpa/cli/core/w_truncate.py +59 -0
- westpa/cli/tools/__init__.py +0 -0
- westpa/cli/tools/ploterr.py +506 -0
- westpa/cli/tools/plothist.py +706 -0
- westpa/cli/tools/w_assign.py +596 -0
- westpa/cli/tools/w_bins.py +166 -0
- westpa/cli/tools/w_crawl.py +119 -0
- westpa/cli/tools/w_direct.py +547 -0
- westpa/cli/tools/w_dumpsegs.py +94 -0
- westpa/cli/tools/w_eddist.py +506 -0
- westpa/cli/tools/w_fluxanl.py +378 -0
- westpa/cli/tools/w_ipa.py +833 -0
- westpa/cli/tools/w_kinavg.py +127 -0
- westpa/cli/tools/w_kinetics.py +96 -0
- westpa/cli/tools/w_multi_west.py +414 -0
- westpa/cli/tools/w_ntop.py +213 -0
- westpa/cli/tools/w_pdist.py +515 -0
- westpa/cli/tools/w_postanalysis_matrix.py +82 -0
- westpa/cli/tools/w_postanalysis_reweight.py +53 -0
- westpa/cli/tools/w_red.py +486 -0
- westpa/cli/tools/w_reweight.py +780 -0
- westpa/cli/tools/w_select.py +226 -0
- westpa/cli/tools/w_stateprobs.py +111 -0
- westpa/cli/tools/w_trace.py +599 -0
- westpa/core/__init__.py +0 -0
- westpa/core/_rc.py +673 -0
- westpa/core/binning/__init__.py +55 -0
- westpa/core/binning/_assign.cpython-312-darwin.so +0 -0
- westpa/core/binning/assign.py +449 -0
- westpa/core/binning/binless.py +96 -0
- westpa/core/binning/binless_driver.py +54 -0
- westpa/core/binning/binless_manager.py +190 -0
- westpa/core/binning/bins.py +47 -0
- westpa/core/binning/mab.py +427 -0
- westpa/core/binning/mab_driver.py +54 -0
- westpa/core/binning/mab_manager.py +198 -0
- westpa/core/data_manager.py +1694 -0
- westpa/core/extloader.py +74 -0
- westpa/core/h5io.py +995 -0
- westpa/core/kinetics/__init__.py +24 -0
- westpa/core/kinetics/_kinetics.cpython-312-darwin.so +0 -0
- westpa/core/kinetics/events.py +147 -0
- westpa/core/kinetics/matrates.py +156 -0
- westpa/core/kinetics/rate_averaging.py +266 -0
- westpa/core/progress.py +218 -0
- westpa/core/propagators/__init__.py +54 -0
- westpa/core/propagators/executable.py +715 -0
- westpa/core/reweight/__init__.py +14 -0
- westpa/core/reweight/_reweight.cpython-312-darwin.so +0 -0
- westpa/core/reweight/matrix.py +126 -0
- westpa/core/segment.py +119 -0
- westpa/core/sim_manager.py +830 -0
- westpa/core/states.py +359 -0
- westpa/core/systems.py +93 -0
- westpa/core/textio.py +74 -0
- westpa/core/trajectory.py +330 -0
- westpa/core/we_driver.py +908 -0
- westpa/core/wm_ops.py +43 -0
- westpa/core/yamlcfg.py +391 -0
- westpa/fasthist/__init__.py +34 -0
- westpa/fasthist/__main__.py +110 -0
- westpa/fasthist/_fasthist.cpython-312-darwin.so +0 -0
- westpa/mclib/__init__.py +264 -0
- westpa/mclib/__main__.py +28 -0
- westpa/mclib/_mclib.cpython-312-darwin.so +0 -0
- westpa/oldtools/__init__.py +4 -0
- westpa/oldtools/aframe/__init__.py +35 -0
- westpa/oldtools/aframe/atool.py +75 -0
- westpa/oldtools/aframe/base_mixin.py +26 -0
- westpa/oldtools/aframe/binning.py +178 -0
- westpa/oldtools/aframe/data_reader.py +560 -0
- westpa/oldtools/aframe/iter_range.py +200 -0
- westpa/oldtools/aframe/kinetics.py +117 -0
- westpa/oldtools/aframe/mcbs.py +146 -0
- westpa/oldtools/aframe/output.py +39 -0
- westpa/oldtools/aframe/plotting.py +90 -0
- westpa/oldtools/aframe/trajwalker.py +126 -0
- westpa/oldtools/aframe/transitions.py +469 -0
- westpa/oldtools/cmds/__init__.py +0 -0
- westpa/oldtools/cmds/w_ttimes.py +358 -0
- westpa/oldtools/files.py +34 -0
- westpa/oldtools/miscfn.py +23 -0
- westpa/oldtools/stats/__init__.py +4 -0
- westpa/oldtools/stats/accumulator.py +35 -0
- westpa/oldtools/stats/edfs.py +129 -0
- westpa/oldtools/stats/mcbs.py +89 -0
- westpa/tools/__init__.py +33 -0
- westpa/tools/binning.py +472 -0
- westpa/tools/core.py +340 -0
- westpa/tools/data_reader.py +159 -0
- westpa/tools/dtypes.py +31 -0
- westpa/tools/iter_range.py +198 -0
- westpa/tools/kinetics_tool.py +340 -0
- westpa/tools/plot.py +283 -0
- westpa/tools/progress.py +17 -0
- westpa/tools/selected_segs.py +154 -0
- westpa/tools/wipi.py +751 -0
- westpa/trajtree/__init__.py +4 -0
- westpa/trajtree/_trajtree.cpython-312-darwin.so +0 -0
- westpa/trajtree/trajtree.py +117 -0
- westpa/westext/__init__.py +0 -0
- westpa/westext/adaptvoronoi/__init__.py +3 -0
- westpa/westext/adaptvoronoi/adaptVor_driver.py +214 -0
- westpa/westext/hamsm_restarting/__init__.py +3 -0
- westpa/westext/hamsm_restarting/example_overrides.py +35 -0
- westpa/westext/hamsm_restarting/restart_driver.py +1165 -0
- westpa/westext/stringmethod/__init__.py +11 -0
- westpa/westext/stringmethod/fourier_fitting.py +69 -0
- westpa/westext/stringmethod/string_driver.py +253 -0
- westpa/westext/stringmethod/string_method.py +306 -0
- westpa/westext/weed/BinCluster.py +180 -0
- westpa/westext/weed/ProbAdjustEquil.py +100 -0
- westpa/westext/weed/UncertMath.py +247 -0
- westpa/westext/weed/__init__.py +10 -0
- westpa/westext/weed/weed_driver.py +182 -0
- westpa/westext/wess/ProbAdjust.py +101 -0
- westpa/westext/wess/__init__.py +6 -0
- westpa/westext/wess/wess_driver.py +207 -0
- westpa/work_managers/__init__.py +57 -0
- westpa/work_managers/core.py +396 -0
- westpa/work_managers/environment.py +134 -0
- westpa/work_managers/mpi.py +318 -0
- westpa/work_managers/processes.py +187 -0
- westpa/work_managers/serial.py +28 -0
- westpa/work_managers/threads.py +79 -0
- westpa/work_managers/zeromq/__init__.py +20 -0
- westpa/work_managers/zeromq/core.py +641 -0
- westpa/work_managers/zeromq/node.py +131 -0
- westpa/work_managers/zeromq/work_manager.py +526 -0
- westpa/work_managers/zeromq/worker.py +320 -0
- westpa-2022.10.dist-info/AUTHORS +22 -0
- westpa-2022.10.dist-info/LICENSE +21 -0
- westpa-2022.10.dist-info/METADATA +183 -0
- westpa-2022.10.dist-info/RECORD +150 -0
- westpa-2022.10.dist-info/WHEEL +5 -0
- westpa-2022.10.dist-info/entry_points.txt +29 -0
- westpa-2022.10.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This implementation is derived from the ``concurrent.futures``
|
|
3
|
+
# module of Python 3.2, by Brian Quinlan, (C) 2011 the Python Software
|
|
4
|
+
# Foundation. See http://docs.python.org/3/license.html for more information.
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import signal
|
|
8
|
+
import threading
|
|
9
|
+
import uuid
|
|
10
|
+
from itertools import islice
|
|
11
|
+
from contextlib import contextmanager
|
|
12
|
+
|
|
13
|
+
import h5py
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
log = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class WorkManager:
|
|
20
|
+
'''Base class for all work managers. At a minimum, work managers must provide a
|
|
21
|
+
``submit()`` function and a ``n_workers`` attribute (which may be a property),
|
|
22
|
+
though most will also override ``startup()`` and ``shutdown()``.'''
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_environ(cls, wmenv=None):
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def add_wm_args(cls, parser, wmenv=None):
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
def __repr__(self):
|
|
33
|
+
return '<{classname} at 0x{id:x}>'.format(classname=self.__class__.__name__, id=id(self))
|
|
34
|
+
|
|
35
|
+
def __init__(self):
|
|
36
|
+
self._sigint_handler_installed = False
|
|
37
|
+
self.prior_sigint_handler = None
|
|
38
|
+
self.running = False
|
|
39
|
+
|
|
40
|
+
def __enter__(self):
|
|
41
|
+
self.startup()
|
|
42
|
+
return self
|
|
43
|
+
|
|
44
|
+
def __exit__(self, exc_type, exc_val, exc_traceback):
|
|
45
|
+
self.shutdown()
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
def sigint_handler(self, signum, frame):
|
|
49
|
+
self.shutdown()
|
|
50
|
+
if self.prior_sigint_handler in (signal.SIG_IGN, None):
|
|
51
|
+
pass
|
|
52
|
+
elif self.prior_sigint_handler == signal.SIG_DFL:
|
|
53
|
+
raise KeyboardInterrupt
|
|
54
|
+
else:
|
|
55
|
+
self.prior_sigint_handler(signum, frame)
|
|
56
|
+
|
|
57
|
+
def install_sigint_handler(self):
|
|
58
|
+
if not self._sigint_handler_installed:
|
|
59
|
+
self._sigint_handler_installed = True
|
|
60
|
+
self.prior_sigint_handler = signal.signal(signal.SIGINT, self.sigint_handler)
|
|
61
|
+
|
|
62
|
+
def startup(self):
|
|
63
|
+
'''Perform any necessary startup work, such as spawning clients.'''
|
|
64
|
+
self.running = True
|
|
65
|
+
|
|
66
|
+
def shutdown(self):
|
|
67
|
+
'''Cleanly shut down any active workers.'''
|
|
68
|
+
self.running = False
|
|
69
|
+
|
|
70
|
+
def run(self):
|
|
71
|
+
'''Run the worker loop (in clients only).'''
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
def submit(self, fn, args=None, kwargs=None):
|
|
75
|
+
'''Submit a task to the work manager, returning a `WMFuture` object representing the pending
|
|
76
|
+
result. ``fn(*args,**kwargs)`` will be executed by a worker, and the return value assigned as the
|
|
77
|
+
result of the returned future. The function ``fn`` and all arguments must be picklable; note
|
|
78
|
+
particularly that off-path modules (like the system module and any active plugins) are not
|
|
79
|
+
picklable unless pre-loaded in the worker process (i.e. prior to forking the master).'''
|
|
80
|
+
raise NotImplementedError
|
|
81
|
+
|
|
82
|
+
def submit_many(self, tasks):
|
|
83
|
+
'''Submit a set of tasks to the work manager, returning a list of `WMFuture` objects representing
|
|
84
|
+
pending results. Each entry in ``tasks`` should be a triple (fn, args, kwargs), which will result in
|
|
85
|
+
fn(*args, **kwargs) being executed by a worker. The function ``fn`` and all arguments must be
|
|
86
|
+
picklable; note particularly that off-path modules are not picklable unless pre-loaded in the worker
|
|
87
|
+
process.'''
|
|
88
|
+
|
|
89
|
+
return [self.submit(fn, args, kwargs) for (fn, args, kwargs) in tasks]
|
|
90
|
+
|
|
91
|
+
def as_completed(self, futures):
|
|
92
|
+
'''Return a generator which yields results from the given ``futures`` as they become
|
|
93
|
+
available.'''
|
|
94
|
+
pending = set(futures)
|
|
95
|
+
|
|
96
|
+
# See which futures have results, and install a watcher on those that do not
|
|
97
|
+
with WMFuture.all_acquired(pending):
|
|
98
|
+
completed = {future for future in futures if future.done}
|
|
99
|
+
pending -= completed
|
|
100
|
+
watcher = FutureWatcher(pending, threshold=1)
|
|
101
|
+
|
|
102
|
+
# Yield available results immediately
|
|
103
|
+
for future in completed:
|
|
104
|
+
yield future
|
|
105
|
+
del completed
|
|
106
|
+
|
|
107
|
+
# Wait on any remaining results
|
|
108
|
+
while pending:
|
|
109
|
+
watcher.wait()
|
|
110
|
+
completed = watcher.reset()
|
|
111
|
+
for future in completed:
|
|
112
|
+
yield future
|
|
113
|
+
pending.remove(future)
|
|
114
|
+
|
|
115
|
+
def submit_as_completed(self, task_generator, queue_size=None):
|
|
116
|
+
'''Return a generator which yields results from a set of ``futures`` as they become
|
|
117
|
+
available. Futures are generated by the ``task_generator``, which must return a triple of the form
|
|
118
|
+
expected by ``submit``. The method also accepts an int ``queue_size`` that dictates the
|
|
119
|
+
maximum number of Futures that should be pending at any given time. The default value of
|
|
120
|
+
``None`` submits all of the tasks at once.'''
|
|
121
|
+
|
|
122
|
+
futures = [self.submit(fn, args, kwargs) for (fn, args, kwargs) in islice(task_generator, queue_size)]
|
|
123
|
+
pending = set(futures)
|
|
124
|
+
|
|
125
|
+
with WMFuture.all_acquired(pending):
|
|
126
|
+
watcher = FutureWatcher(pending, threshold=1)
|
|
127
|
+
|
|
128
|
+
while pending:
|
|
129
|
+
watcher.wait()
|
|
130
|
+
completed = watcher.reset()
|
|
131
|
+
new_futures = [self.submit(fn, args, kwargs) for (fn, args, kwargs) in islice(task_generator, len(completed))]
|
|
132
|
+
pending.update(new_futures)
|
|
133
|
+
|
|
134
|
+
with WMFuture.all_acquired(new_futures):
|
|
135
|
+
watcher.add(new_futures)
|
|
136
|
+
|
|
137
|
+
for future in completed:
|
|
138
|
+
yield future
|
|
139
|
+
pending.remove(future)
|
|
140
|
+
|
|
141
|
+
def wait_any(self, futures):
|
|
142
|
+
'''Wait on any of the given ``futures`` and return the first one which has a result available.
|
|
143
|
+
If more than one result is or becomes available simultaneously, any completed future may be returned.'''
|
|
144
|
+
pending = set(futures)
|
|
145
|
+
with WMFuture.all_acquired(pending):
|
|
146
|
+
completed = {future for future in futures if future.done}
|
|
147
|
+
|
|
148
|
+
if completed:
|
|
149
|
+
# If any futures are complete, then we don't need to do anything else
|
|
150
|
+
return completed.pop()
|
|
151
|
+
else:
|
|
152
|
+
# Otherwise, we need to install a watcher
|
|
153
|
+
watcher = FutureWatcher(futures, threshold=1)
|
|
154
|
+
|
|
155
|
+
watcher.wait()
|
|
156
|
+
completed = watcher.reset()
|
|
157
|
+
return completed.pop()
|
|
158
|
+
|
|
159
|
+
def wait_all(self, futures):
|
|
160
|
+
'''A convenience function which waits on all the given ``futures`` in order. This function returns
|
|
161
|
+
the same ``futures`` as submitted to the function as a list, indicating the order in which waits
|
|
162
|
+
occurred.'''
|
|
163
|
+
futures = list(futures)
|
|
164
|
+
results = []
|
|
165
|
+
for future in futures:
|
|
166
|
+
results.append(future.result)
|
|
167
|
+
return futures
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def is_master(self):
|
|
171
|
+
'''True if this is the master process for task distribution. This is necessary, e.g., for
|
|
172
|
+
MPI, where all processes start identically and then must branch depending on rank.'''
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class FutureWatcher:
|
|
177
|
+
'''A device to wait on multiple results and/or exceptions with only one lock.'''
|
|
178
|
+
|
|
179
|
+
def __init__(self, futures, threshold=1):
|
|
180
|
+
self.event = threading.Event()
|
|
181
|
+
self.lock = threading.RLock()
|
|
182
|
+
self.threshold = threshold
|
|
183
|
+
self.completed = []
|
|
184
|
+
|
|
185
|
+
for future in futures:
|
|
186
|
+
future._add_watcher(self)
|
|
187
|
+
|
|
188
|
+
def signal(self, future):
|
|
189
|
+
'''Signal this watcher that the given future has results available. If this
|
|
190
|
+
brings the number of available futures above signal_threshold, this watcher's
|
|
191
|
+
event object will be signalled as well.'''
|
|
192
|
+
with self.lock:
|
|
193
|
+
self.completed.append(future)
|
|
194
|
+
if len(self.completed) >= self.threshold:
|
|
195
|
+
self.event.set()
|
|
196
|
+
|
|
197
|
+
def wait(self):
|
|
198
|
+
'''Wait on one or more futures.'''
|
|
199
|
+
return self.event.wait()
|
|
200
|
+
|
|
201
|
+
def reset(self):
|
|
202
|
+
'''Reset this watcher's list of completed futures, returning the list of completed futures
|
|
203
|
+
prior to resetting it.'''
|
|
204
|
+
with self.lock:
|
|
205
|
+
self.event.clear()
|
|
206
|
+
completed = self.completed
|
|
207
|
+
self.completed = []
|
|
208
|
+
return completed
|
|
209
|
+
|
|
210
|
+
def add(self, futures):
|
|
211
|
+
'''Add watchers to all futures in the iterable of futures.'''
|
|
212
|
+
for future in futures:
|
|
213
|
+
future._add_watcher(self)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class WMFuture:
|
|
217
|
+
'''A "future", representing work which has been dispatched for completion asynchronously.'''
|
|
218
|
+
|
|
219
|
+
@staticmethod
|
|
220
|
+
@contextmanager
|
|
221
|
+
def all_acquired(futures):
|
|
222
|
+
'''Context manager to acquire all locks on the given ``futures``. Primarily for internal use.'''
|
|
223
|
+
futures = list(futures)
|
|
224
|
+
for future in futures:
|
|
225
|
+
future._condition.acquire()
|
|
226
|
+
|
|
227
|
+
yield # to contents of "with" block
|
|
228
|
+
|
|
229
|
+
for future in futures:
|
|
230
|
+
future._condition.release()
|
|
231
|
+
|
|
232
|
+
def __init__(self, task_id=None):
|
|
233
|
+
self.task_id = task_id or uuid.uuid4()
|
|
234
|
+
|
|
235
|
+
self._condition = threading.Condition()
|
|
236
|
+
self._done = False
|
|
237
|
+
self._result = None
|
|
238
|
+
self._exception = None
|
|
239
|
+
self._traceback = None
|
|
240
|
+
|
|
241
|
+
# a set of Events representing who is waiting on results from this future
|
|
242
|
+
# this set will be cleared after the result is updated and watchers are notified
|
|
243
|
+
self._watchers = set()
|
|
244
|
+
|
|
245
|
+
# a set of functions that will be called with this future as an argument when it is updated with a
|
|
246
|
+
# result. This list will be cleared after the result is updated and all callbacks invoked
|
|
247
|
+
self._update_callbacks = []
|
|
248
|
+
|
|
249
|
+
def __repr__(self):
|
|
250
|
+
return '<WMFuture 0x{id:x}: {self.task_id!s}>'.format(id=id(self), self=self)
|
|
251
|
+
|
|
252
|
+
def __hash__(self):
|
|
253
|
+
return hash(self.task_id)
|
|
254
|
+
|
|
255
|
+
def _notify_watchers(self):
|
|
256
|
+
'''Notify all watchers that this future has been updated, then deletes the list of update watchers.'''
|
|
257
|
+
with self._condition:
|
|
258
|
+
assert self._done
|
|
259
|
+
for watcher in self._watchers:
|
|
260
|
+
watcher.signal(self)
|
|
261
|
+
self._watchers.clear()
|
|
262
|
+
|
|
263
|
+
def _invoke_callbacks(self):
|
|
264
|
+
'''Invoke all callbacks which have been registered on this future. Exceptions in callbacks
|
|
265
|
+
will be logged and ignored.'''
|
|
266
|
+
with self._condition:
|
|
267
|
+
for callback in self._update_callbacks:
|
|
268
|
+
try:
|
|
269
|
+
callback(self)
|
|
270
|
+
except Exception:
|
|
271
|
+
# This may need to be a simple print to stderr, depending on the locking
|
|
272
|
+
# semantics of the logger.
|
|
273
|
+
log.exception('ignoring exception in result callback')
|
|
274
|
+
del self._update_callbacks
|
|
275
|
+
self._update_callbacks = []
|
|
276
|
+
|
|
277
|
+
def _add_watcher(self, watcher):
|
|
278
|
+
'''Add the given update watcher to the internal list of watchers. If a result is available,
|
|
279
|
+
returns immediately without updating the list of watchers.'''
|
|
280
|
+
with self._condition:
|
|
281
|
+
if self._done:
|
|
282
|
+
watcher.signal(self)
|
|
283
|
+
return
|
|
284
|
+
else:
|
|
285
|
+
self._watchers.add(watcher)
|
|
286
|
+
|
|
287
|
+
def _add_callback(self, callback):
|
|
288
|
+
'''Add the given update callback to the internal list of callbacks. If a result is available,
|
|
289
|
+
invokes the callback immediately without updating the list of callbacks.'''
|
|
290
|
+
with self._condition:
|
|
291
|
+
if self._done:
|
|
292
|
+
try:
|
|
293
|
+
callback(self)
|
|
294
|
+
except Exception:
|
|
295
|
+
log.exception('ignoring exception in result callback')
|
|
296
|
+
else:
|
|
297
|
+
self._update_callbacks.append(callback)
|
|
298
|
+
|
|
299
|
+
def _set_result(self, result):
|
|
300
|
+
'''Set the result of this future to the given value, invoke on-completion callbacks, and notify
|
|
301
|
+
watchers.'''
|
|
302
|
+
with self._condition:
|
|
303
|
+
self._result = result
|
|
304
|
+
self._done = True
|
|
305
|
+
self._condition.notify_all()
|
|
306
|
+
self._invoke_callbacks()
|
|
307
|
+
self._notify_watchers()
|
|
308
|
+
|
|
309
|
+
def _set_exception(self, exception, traceback=None):
|
|
310
|
+
'''Set the exception of this future to the given value, invoke on-completion callbacks, and notify
|
|
311
|
+
watchers.'''
|
|
312
|
+
|
|
313
|
+
with self._condition:
|
|
314
|
+
self._exception = exception
|
|
315
|
+
self._traceback = traceback
|
|
316
|
+
self._done = True
|
|
317
|
+
self._condition.notify_all()
|
|
318
|
+
self._invoke_callbacks()
|
|
319
|
+
self._notify_watchers()
|
|
320
|
+
|
|
321
|
+
def get_result(self, discard=True):
|
|
322
|
+
'''Get the result associated with this future, blocking until it is available.
|
|
323
|
+
If ``discard`` is true, then removes the reference to the result contained
|
|
324
|
+
in this instance, so that a collection of futures need not turn into a cache of
|
|
325
|
+
all associated results.'''
|
|
326
|
+
with self._condition:
|
|
327
|
+
if self._done:
|
|
328
|
+
if self._exception:
|
|
329
|
+
if isinstance(self._traceback, h5py.string_dtype(encoding='utf-8').type) or isinstance(self._traceback, str):
|
|
330
|
+
if self._traceback:
|
|
331
|
+
log.error('uncaught exception in remote function\n{}'.format(self._traceback))
|
|
332
|
+
raise self._exception
|
|
333
|
+
else:
|
|
334
|
+
raise self._exception.with_traceback(self._traceback)
|
|
335
|
+
else:
|
|
336
|
+
self._condition.wait()
|
|
337
|
+
assert self._done
|
|
338
|
+
if self._exception:
|
|
339
|
+
if isinstance(self._traceback, str):
|
|
340
|
+
log.error('uncaught exception in remote function\n{}'.format(self._traceback))
|
|
341
|
+
raise self._exception
|
|
342
|
+
else:
|
|
343
|
+
raise self._exception.with_traceback(self._traceback)
|
|
344
|
+
|
|
345
|
+
result = self._result
|
|
346
|
+
if discard:
|
|
347
|
+
del self._result
|
|
348
|
+
return result
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def result(self):
|
|
352
|
+
return self.get_result(discard=False)
|
|
353
|
+
|
|
354
|
+
def wait(self):
|
|
355
|
+
'''Wait until this future has a result or exception available.'''
|
|
356
|
+
with self._condition:
|
|
357
|
+
if self._done:
|
|
358
|
+
return
|
|
359
|
+
else:
|
|
360
|
+
self._condition.wait()
|
|
361
|
+
assert self._done
|
|
362
|
+
return
|
|
363
|
+
|
|
364
|
+
def get_exception(self):
|
|
365
|
+
'''Get the exception associated with this future, blocking until it is available.'''
|
|
366
|
+
with self._condition:
|
|
367
|
+
if self._done:
|
|
368
|
+
return self._exception
|
|
369
|
+
else:
|
|
370
|
+
self._condition.wait()
|
|
371
|
+
assert self._done
|
|
372
|
+
return self._exception
|
|
373
|
+
|
|
374
|
+
exception = property(get_exception, None, None, get_exception.__doc__)
|
|
375
|
+
|
|
376
|
+
def get_traceback(self):
|
|
377
|
+
'''Get the traceback object associated with this future, if any.'''
|
|
378
|
+
with self._condition:
|
|
379
|
+
if self._returned:
|
|
380
|
+
return self._traceback
|
|
381
|
+
else:
|
|
382
|
+
self._condition.wait()
|
|
383
|
+
assert self._done
|
|
384
|
+
return self._traceback
|
|
385
|
+
|
|
386
|
+
traceback = property(get_traceback, None, None, get_traceback.__doc__)
|
|
387
|
+
|
|
388
|
+
def is_done(self):
|
|
389
|
+
'Indicates whether this future is done executing (may block if this future is being updated).'
|
|
390
|
+
with self._condition:
|
|
391
|
+
return self._done
|
|
392
|
+
|
|
393
|
+
done = property(is_done, None, None, is_done.__doc__)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
# end class WMFuture
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'''Routines for configuring the work manager environment'''
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from . import _available_work_managers
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class WMEnvironment:
|
|
10
|
+
'''A class to encapsulate the environment in which work managers are instantiated;
|
|
11
|
+
this controls how environment variables and command-line arguments are used to
|
|
12
|
+
set up work managers. This could be used to cleanly instantiate two work managers
|
|
13
|
+
within one application, but is really more about providing facilities to make
|
|
14
|
+
it easier for individual work managers to configure themselves according to
|
|
15
|
+
precendence of configuration information:
|
|
16
|
+
1. command-line arguments
|
|
17
|
+
2. environment variables
|
|
18
|
+
3. defaults
|
|
19
|
+
'''
|
|
20
|
+
|
|
21
|
+
group_title = 'parallelization options'
|
|
22
|
+
group_description = None
|
|
23
|
+
|
|
24
|
+
env_prefix = 'WM'
|
|
25
|
+
arg_prefix = 'wm'
|
|
26
|
+
|
|
27
|
+
default_work_manager = 'serial'
|
|
28
|
+
default_parallel_work_manager = 'processes'
|
|
29
|
+
valid_work_managers = list(_available_work_managers.keys())
|
|
30
|
+
|
|
31
|
+
def __init__(self, use_arg_prefixes=False, valid_work_managers=None):
|
|
32
|
+
self.environ = os.environ
|
|
33
|
+
self.args = None
|
|
34
|
+
|
|
35
|
+
# copy from the class variable to permit modification
|
|
36
|
+
# this can be modified to disable certain work managers, for whatever reason
|
|
37
|
+
# mostly it's about having a valid list for enumeration
|
|
38
|
+
self.valid_work_managers = valid_work_managers or list(self.valid_work_managers)
|
|
39
|
+
|
|
40
|
+
self.use_arg_prefixes = use_arg_prefixes
|
|
41
|
+
|
|
42
|
+
def env_name(self, name):
|
|
43
|
+
return '{}_{}'.format(self.env_prefix, name.upper())
|
|
44
|
+
|
|
45
|
+
def arg_name(self, name):
|
|
46
|
+
if self.use_arg_prefixes:
|
|
47
|
+
return '{}_{}'.format(self.arg_prefix, name)
|
|
48
|
+
else:
|
|
49
|
+
return name
|
|
50
|
+
|
|
51
|
+
def arg_flag(self, name):
|
|
52
|
+
if self.use_arg_prefixes:
|
|
53
|
+
return '--{}-{}'.format(self.arg_prefix, re.sub('_', '-', name))
|
|
54
|
+
else:
|
|
55
|
+
return '--{}'.format(re.sub('_', '-', name))
|
|
56
|
+
|
|
57
|
+
def get_val(self, name, default=None, type_=None):
|
|
58
|
+
envname = self.env_name(name)
|
|
59
|
+
argname = self.arg_name(name)
|
|
60
|
+
|
|
61
|
+
val = getattr(self.args, argname, None)
|
|
62
|
+
if val is None:
|
|
63
|
+
try:
|
|
64
|
+
val = self.environ[envname]
|
|
65
|
+
except KeyError:
|
|
66
|
+
val = default
|
|
67
|
+
|
|
68
|
+
if type_ is None:
|
|
69
|
+
return val
|
|
70
|
+
else:
|
|
71
|
+
try:
|
|
72
|
+
return type_(val)
|
|
73
|
+
except ValueError as e:
|
|
74
|
+
raise ValueError('cannot convert {!r} to {!r}: {!s}'.format(val, type_, e))
|
|
75
|
+
|
|
76
|
+
def add_wm_args(self, parser):
|
|
77
|
+
wm_group = parser.add_argument_group(self.group_title, self.group_description)
|
|
78
|
+
wm_mutex = wm_group.add_mutually_exclusive_group()
|
|
79
|
+
wm_mutex.add_argument(
|
|
80
|
+
self.arg_flag('serial'),
|
|
81
|
+
dest=self.arg_name('work_manager'),
|
|
82
|
+
action='store_const',
|
|
83
|
+
const='serial',
|
|
84
|
+
help='run in serial mode',
|
|
85
|
+
)
|
|
86
|
+
wm_mutex.add_argument(
|
|
87
|
+
self.arg_flag('parallel'),
|
|
88
|
+
dest=self.arg_name('work_manager'),
|
|
89
|
+
action='store_const',
|
|
90
|
+
const=self.default_parallel_work_manager,
|
|
91
|
+
help='run in parallel mode (using {})'.format(self.default_parallel_work_manager),
|
|
92
|
+
)
|
|
93
|
+
wm_mutex.add_argument(
|
|
94
|
+
self.arg_flag('work_manager'),
|
|
95
|
+
metavar='WORK_MANAGER',
|
|
96
|
+
choices=self.valid_work_managers,
|
|
97
|
+
help='''use the given work manager for parallel task distribution. Available
|
|
98
|
+
work managers are {!r}; default is {!r}
|
|
99
|
+
'''.format(
|
|
100
|
+
tuple(self.valid_work_managers), self.default_work_manager
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
wm_group.add_argument(
|
|
105
|
+
self.arg_flag('n_workers'),
|
|
106
|
+
metavar='N_WORKERS',
|
|
107
|
+
type=int,
|
|
108
|
+
help='''Use up to N_WORKERS on this host, for work managers which support this option.
|
|
109
|
+
Use 0 for a dedicated server. (Ignored by work managers which do not support
|
|
110
|
+
this option.)''',
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
for wm in self.valid_work_managers:
|
|
114
|
+
_available_work_managers[wm].add_wm_args(parser, self)
|
|
115
|
+
|
|
116
|
+
def process_wm_args(self, args):
|
|
117
|
+
self.args = args
|
|
118
|
+
|
|
119
|
+
def make_work_manager(self):
|
|
120
|
+
'''Using cues from the environment, instantiate a pre-configured work manager.'''
|
|
121
|
+
|
|
122
|
+
work_manager_name = self.get_val('work_manager', '').lower()
|
|
123
|
+
work_manager_name = work_manager_name or self.default_work_manager
|
|
124
|
+
|
|
125
|
+
if work_manager_name not in self.valid_work_managers:
|
|
126
|
+
raise ValueError('work manager {!r} is invalid or unavailable'.format(work_manager_name))
|
|
127
|
+
else:
|
|
128
|
+
return _available_work_managers[work_manager_name].from_environ(self)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
default_env = WMEnvironment()
|
|
132
|
+
make_work_manager = default_env.make_work_manager
|
|
133
|
+
add_wm_args = default_env.add_wm_args
|
|
134
|
+
process_wm_args = default_env.process_wm_args
|