westpa 2022.10__cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.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.

Files changed (150) hide show
  1. westpa/__init__.py +14 -0
  2. westpa/_version.py +21 -0
  3. westpa/analysis/__init__.py +5 -0
  4. westpa/analysis/core.py +746 -0
  5. westpa/analysis/statistics.py +27 -0
  6. westpa/analysis/trajectories.py +360 -0
  7. westpa/cli/__init__.py +0 -0
  8. westpa/cli/core/__init__.py +0 -0
  9. westpa/cli/core/w_fork.py +152 -0
  10. westpa/cli/core/w_init.py +230 -0
  11. westpa/cli/core/w_run.py +77 -0
  12. westpa/cli/core/w_states.py +212 -0
  13. westpa/cli/core/w_succ.py +99 -0
  14. westpa/cli/core/w_truncate.py +59 -0
  15. westpa/cli/tools/__init__.py +0 -0
  16. westpa/cli/tools/ploterr.py +506 -0
  17. westpa/cli/tools/plothist.py +706 -0
  18. westpa/cli/tools/w_assign.py +596 -0
  19. westpa/cli/tools/w_bins.py +166 -0
  20. westpa/cli/tools/w_crawl.py +119 -0
  21. westpa/cli/tools/w_direct.py +547 -0
  22. westpa/cli/tools/w_dumpsegs.py +94 -0
  23. westpa/cli/tools/w_eddist.py +506 -0
  24. westpa/cli/tools/w_fluxanl.py +378 -0
  25. westpa/cli/tools/w_ipa.py +833 -0
  26. westpa/cli/tools/w_kinavg.py +127 -0
  27. westpa/cli/tools/w_kinetics.py +96 -0
  28. westpa/cli/tools/w_multi_west.py +414 -0
  29. westpa/cli/tools/w_ntop.py +213 -0
  30. westpa/cli/tools/w_pdist.py +515 -0
  31. westpa/cli/tools/w_postanalysis_matrix.py +82 -0
  32. westpa/cli/tools/w_postanalysis_reweight.py +53 -0
  33. westpa/cli/tools/w_red.py +486 -0
  34. westpa/cli/tools/w_reweight.py +780 -0
  35. westpa/cli/tools/w_select.py +226 -0
  36. westpa/cli/tools/w_stateprobs.py +111 -0
  37. westpa/cli/tools/w_trace.py +599 -0
  38. westpa/core/__init__.py +0 -0
  39. westpa/core/_rc.py +673 -0
  40. westpa/core/binning/__init__.py +55 -0
  41. westpa/core/binning/_assign.cpython-312-x86_64-linux-gnu.so +0 -0
  42. westpa/core/binning/assign.py +449 -0
  43. westpa/core/binning/binless.py +96 -0
  44. westpa/core/binning/binless_driver.py +54 -0
  45. westpa/core/binning/binless_manager.py +190 -0
  46. westpa/core/binning/bins.py +47 -0
  47. westpa/core/binning/mab.py +427 -0
  48. westpa/core/binning/mab_driver.py +54 -0
  49. westpa/core/binning/mab_manager.py +198 -0
  50. westpa/core/data_manager.py +1694 -0
  51. westpa/core/extloader.py +74 -0
  52. westpa/core/h5io.py +995 -0
  53. westpa/core/kinetics/__init__.py +24 -0
  54. westpa/core/kinetics/_kinetics.cpython-312-x86_64-linux-gnu.so +0 -0
  55. westpa/core/kinetics/events.py +147 -0
  56. westpa/core/kinetics/matrates.py +156 -0
  57. westpa/core/kinetics/rate_averaging.py +266 -0
  58. westpa/core/progress.py +218 -0
  59. westpa/core/propagators/__init__.py +54 -0
  60. westpa/core/propagators/executable.py +715 -0
  61. westpa/core/reweight/__init__.py +14 -0
  62. westpa/core/reweight/_reweight.cpython-312-x86_64-linux-gnu.so +0 -0
  63. westpa/core/reweight/matrix.py +126 -0
  64. westpa/core/segment.py +119 -0
  65. westpa/core/sim_manager.py +830 -0
  66. westpa/core/states.py +359 -0
  67. westpa/core/systems.py +93 -0
  68. westpa/core/textio.py +74 -0
  69. westpa/core/trajectory.py +330 -0
  70. westpa/core/we_driver.py +908 -0
  71. westpa/core/wm_ops.py +43 -0
  72. westpa/core/yamlcfg.py +391 -0
  73. westpa/fasthist/__init__.py +34 -0
  74. westpa/fasthist/__main__.py +110 -0
  75. westpa/fasthist/_fasthist.cpython-312-x86_64-linux-gnu.so +0 -0
  76. westpa/mclib/__init__.py +264 -0
  77. westpa/mclib/__main__.py +28 -0
  78. westpa/mclib/_mclib.cpython-312-x86_64-linux-gnu.so +0 -0
  79. westpa/oldtools/__init__.py +4 -0
  80. westpa/oldtools/aframe/__init__.py +35 -0
  81. westpa/oldtools/aframe/atool.py +75 -0
  82. westpa/oldtools/aframe/base_mixin.py +26 -0
  83. westpa/oldtools/aframe/binning.py +178 -0
  84. westpa/oldtools/aframe/data_reader.py +560 -0
  85. westpa/oldtools/aframe/iter_range.py +200 -0
  86. westpa/oldtools/aframe/kinetics.py +117 -0
  87. westpa/oldtools/aframe/mcbs.py +146 -0
  88. westpa/oldtools/aframe/output.py +39 -0
  89. westpa/oldtools/aframe/plotting.py +90 -0
  90. westpa/oldtools/aframe/trajwalker.py +126 -0
  91. westpa/oldtools/aframe/transitions.py +469 -0
  92. westpa/oldtools/cmds/__init__.py +0 -0
  93. westpa/oldtools/cmds/w_ttimes.py +358 -0
  94. westpa/oldtools/files.py +34 -0
  95. westpa/oldtools/miscfn.py +23 -0
  96. westpa/oldtools/stats/__init__.py +4 -0
  97. westpa/oldtools/stats/accumulator.py +35 -0
  98. westpa/oldtools/stats/edfs.py +129 -0
  99. westpa/oldtools/stats/mcbs.py +89 -0
  100. westpa/tools/__init__.py +33 -0
  101. westpa/tools/binning.py +472 -0
  102. westpa/tools/core.py +340 -0
  103. westpa/tools/data_reader.py +159 -0
  104. westpa/tools/dtypes.py +31 -0
  105. westpa/tools/iter_range.py +198 -0
  106. westpa/tools/kinetics_tool.py +340 -0
  107. westpa/tools/plot.py +283 -0
  108. westpa/tools/progress.py +17 -0
  109. westpa/tools/selected_segs.py +154 -0
  110. westpa/tools/wipi.py +751 -0
  111. westpa/trajtree/__init__.py +4 -0
  112. westpa/trajtree/_trajtree.cpython-312-x86_64-linux-gnu.so +0 -0
  113. westpa/trajtree/trajtree.py +117 -0
  114. westpa/westext/__init__.py +0 -0
  115. westpa/westext/adaptvoronoi/__init__.py +3 -0
  116. westpa/westext/adaptvoronoi/adaptVor_driver.py +214 -0
  117. westpa/westext/hamsm_restarting/__init__.py +3 -0
  118. westpa/westext/hamsm_restarting/example_overrides.py +35 -0
  119. westpa/westext/hamsm_restarting/restart_driver.py +1165 -0
  120. westpa/westext/stringmethod/__init__.py +11 -0
  121. westpa/westext/stringmethod/fourier_fitting.py +69 -0
  122. westpa/westext/stringmethod/string_driver.py +253 -0
  123. westpa/westext/stringmethod/string_method.py +306 -0
  124. westpa/westext/weed/BinCluster.py +180 -0
  125. westpa/westext/weed/ProbAdjustEquil.py +100 -0
  126. westpa/westext/weed/UncertMath.py +247 -0
  127. westpa/westext/weed/__init__.py +10 -0
  128. westpa/westext/weed/weed_driver.py +182 -0
  129. westpa/westext/wess/ProbAdjust.py +101 -0
  130. westpa/westext/wess/__init__.py +6 -0
  131. westpa/westext/wess/wess_driver.py +207 -0
  132. westpa/work_managers/__init__.py +57 -0
  133. westpa/work_managers/core.py +396 -0
  134. westpa/work_managers/environment.py +134 -0
  135. westpa/work_managers/mpi.py +318 -0
  136. westpa/work_managers/processes.py +187 -0
  137. westpa/work_managers/serial.py +28 -0
  138. westpa/work_managers/threads.py +79 -0
  139. westpa/work_managers/zeromq/__init__.py +20 -0
  140. westpa/work_managers/zeromq/core.py +641 -0
  141. westpa/work_managers/zeromq/node.py +131 -0
  142. westpa/work_managers/zeromq/work_manager.py +526 -0
  143. westpa/work_managers/zeromq/worker.py +320 -0
  144. westpa-2022.10.dist-info/AUTHORS +22 -0
  145. westpa-2022.10.dist-info/LICENSE +21 -0
  146. westpa-2022.10.dist-info/METADATA +183 -0
  147. westpa-2022.10.dist-info/RECORD +150 -0
  148. westpa-2022.10.dist-info/WHEEL +6 -0
  149. westpa-2022.10.dist-info/entry_points.txt +29 -0
  150. 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