multiCMD 1.27__tar.gz → 1.28__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.
- {multicmd-1.27 → multicmd-1.28}/PKG-INFO +1 -1
- {multicmd-1.27 → multicmd-1.28}/multiCMD.egg-info/PKG-INFO +1 -1
- {multicmd-1.27 → multicmd-1.28}/multiCMD.py +174 -1
- {multicmd-1.27 → multicmd-1.28}/README.md +0 -0
- {multicmd-1.27 → multicmd-1.28}/multiCMD.egg-info/SOURCES.txt +0 -0
- {multicmd-1.27 → multicmd-1.28}/multiCMD.egg-info/dependency_links.txt +0 -0
- {multicmd-1.27 → multicmd-1.28}/multiCMD.egg-info/entry_points.txt +0 -0
- {multicmd-1.27 → multicmd-1.28}/multiCMD.egg-info/requires.txt +0 -0
- {multicmd-1.27 → multicmd-1.28}/multiCMD.egg-info/top_level.txt +0 -0
- {multicmd-1.27 → multicmd-1.28}/setup.cfg +0 -0
- {multicmd-1.27 → multicmd-1.28}/setup.py +0 -0
@@ -18,7 +18,7 @@ import re
|
|
18
18
|
import itertools
|
19
19
|
import signal
|
20
20
|
|
21
|
-
version = '1.
|
21
|
+
version = '1.28'
|
22
22
|
__version__ = version
|
23
23
|
|
24
24
|
__running_threads = []
|
@@ -40,6 +40,179 @@ class Task:
|
|
40
40
|
if self.thread is not None:
|
41
41
|
return self.thread.is_alive()
|
42
42
|
return False
|
43
|
+
|
44
|
+
class AsyncExecutor:
|
45
|
+
def __init__(self, max_threads=1,semaphore=None,timeout=0,quiet=True,dry_run=False,parse=False):
|
46
|
+
'''
|
47
|
+
AsyncExecutor class to run commands in parallel asynchronously
|
48
|
+
@params:
|
49
|
+
max_threads: The maximum number of threads to use ( int ) ( Note: if passing semaphore, this likely will be ignored )
|
50
|
+
semaphore: The semaphore to use for threading ( threading.Semaphore )
|
51
|
+
timeout: The timeout for each command ( int )
|
52
|
+
quiet: Whether to suppress output ( bool )
|
53
|
+
dry_run: Whether to simulate running the commands ( bool )
|
54
|
+
parse: Whether to parse ranged input ( bool )
|
55
|
+
'''
|
56
|
+
self.max_threads = max_threads
|
57
|
+
self.semaphore = semaphore
|
58
|
+
self.runningThreads = []
|
59
|
+
self.tasks = []
|
60
|
+
self.timeout = timeout
|
61
|
+
self.quiet = quiet
|
62
|
+
self.dry_run = dry_run
|
63
|
+
self.parse = parse
|
64
|
+
|
65
|
+
def __iter__(self):
|
66
|
+
return iter(self.tasks)
|
67
|
+
|
68
|
+
def __repr__(self):
|
69
|
+
return f'AsyncExecutor(max_threads={self.max_threads}, semaphore={self.semaphore}, runningThreads={self.runningThreads}, tasks={self.tasks}, timeout={self.timeout}, quiet={self.quiet}, dry_run={self.dry_run}, parse={self.parse})'
|
70
|
+
|
71
|
+
def __str__(self):
|
72
|
+
return str(self.tasks)
|
73
|
+
|
74
|
+
def __len__(self):
|
75
|
+
return len(self.tasks)
|
76
|
+
|
77
|
+
def __bool__(self):
|
78
|
+
return bool(self.tasks)
|
79
|
+
|
80
|
+
def run_commands(self, commands, timeout=...,max_threads=...,quiet=...,dry_run=...,parse = ...,sem = ...):
|
81
|
+
'''
|
82
|
+
Run multiple commands in parallel asynchronously
|
83
|
+
@params:
|
84
|
+
commands: A list of commands to run ( list[str] | list[list[str]] )
|
85
|
+
timeout: The timeout for each command to override the object default
|
86
|
+
max_threads: The maximum number of threads to use to override the object default
|
87
|
+
quiet: Whether to suppress output to override the object default
|
88
|
+
dry_run: Whether to simulate running the commands to override the object default
|
89
|
+
parse: Whether to parse ranged input to override the object default
|
90
|
+
sem: The semaphore to use for threading to override the object default
|
91
|
+
@returns:
|
92
|
+
list: The Task Object of the commands ( list[Task] )
|
93
|
+
'''
|
94
|
+
if timeout is ...:
|
95
|
+
timeout = self.timeout
|
96
|
+
if max_threads is ...:
|
97
|
+
max_threads = self.max_threads
|
98
|
+
if quiet is ...:
|
99
|
+
quiet = self.quiet
|
100
|
+
if dry_run is ...:
|
101
|
+
dry_run = self.dry_run
|
102
|
+
if parse is ...:
|
103
|
+
parse = self.parse
|
104
|
+
if sem is ...:
|
105
|
+
sem = self.semaphore
|
106
|
+
taskObjects: list[Task] = run_commands(commands,timeout=timeout,max_threads=max_threads,quiet=quiet,dry_run=dry_run,with_stdErr=False,
|
107
|
+
return_code_only=False,return_object=True, parse = parse, wait_for_return = False, sem = sem)
|
108
|
+
self.tasks.extend(taskObjects)
|
109
|
+
self.runningThreads.extend([task.thread for task in taskObjects])
|
110
|
+
return taskObjects
|
111
|
+
|
112
|
+
def run_command(self, command, timeout=...,max_threads=...,quiet=...,dry_run=...,parse = ...,sem = ...):
|
113
|
+
'''
|
114
|
+
Run a command in parallel asynchronously
|
115
|
+
@params:
|
116
|
+
command: The command to run ( str | list[str] )
|
117
|
+
timeout: The timeout for each command to override the object default
|
118
|
+
max_threads: The maximum number of threads to use to override the object default
|
119
|
+
quiet: Whether to suppress output to override the object default
|
120
|
+
dry_run: Whether to simulate running the commands to override the object default
|
121
|
+
parse: Whether to parse ranged input to override the object default
|
122
|
+
sem: The semaphore to use for threading to override the object default
|
123
|
+
@returns:
|
124
|
+
Task: The Task Object of the command
|
125
|
+
'''
|
126
|
+
return self.run_commands([command],timeout=timeout,max_threads=max_threads,quiet=quiet,dry_run=dry_run,parse=parse,sem=sem)[0]
|
127
|
+
|
128
|
+
def wait(self, timeout=..., threads = ...):
|
129
|
+
'''
|
130
|
+
Wait for the threads to finish
|
131
|
+
@params:
|
132
|
+
timeout: The timeout for each command to override the object default
|
133
|
+
threads: The threads to join, default to all running threads managed by this object
|
134
|
+
@returns:
|
135
|
+
list[threading.Thread]: The list of running threads that are still running
|
136
|
+
'''
|
137
|
+
if threads is ...:
|
138
|
+
threads = self.runningThreads
|
139
|
+
if timeout is ...:
|
140
|
+
timeout = self.timeout
|
141
|
+
for thread in threads:
|
142
|
+
if timeout > 0:
|
143
|
+
thread.join(timeout=timeout)
|
144
|
+
else:
|
145
|
+
thread.join()
|
146
|
+
self.runningThreads = [thread for thread in self.runningThreads if thread.is_alive()]
|
147
|
+
return self.runningThreads
|
148
|
+
|
149
|
+
def stop(self,timeout=...):
|
150
|
+
'''
|
151
|
+
Stop all running threads. This signals all threads to stop and joins them
|
152
|
+
@params:
|
153
|
+
None
|
154
|
+
@returns:
|
155
|
+
list[Task]: The list of tasks that are managed by this object
|
156
|
+
'''
|
157
|
+
for task in self.tasks:
|
158
|
+
task.stop = True
|
159
|
+
self.wait(timeout)
|
160
|
+
return self.tasks
|
161
|
+
|
162
|
+
def cleanup(self,timeout=...):
|
163
|
+
'''
|
164
|
+
Cleanup the tasks and threads. This calls stop() and clears the tasks and threads
|
165
|
+
@params:
|
166
|
+
None
|
167
|
+
@returns:
|
168
|
+
list[Task]: The list of tasks that are managed by this object
|
169
|
+
'''
|
170
|
+
self.stop(timeout)
|
171
|
+
self.tasks = []
|
172
|
+
self.runningThreads = []
|
173
|
+
return self.tasks
|
174
|
+
|
175
|
+
def join(self, timeout=..., threads = ..., print_error=True):
|
176
|
+
'''
|
177
|
+
Wait for the threads to finish and print error if there is any
|
178
|
+
@params:
|
179
|
+
timeout: The timeout for each command to override the object default
|
180
|
+
threads: The threads to wait for, default to all running threads managed by this object
|
181
|
+
@returns:
|
182
|
+
list[Task]: The list of tasks that are managed by this object
|
183
|
+
'''
|
184
|
+
self.wait(timeout=timeout, threads=threads)
|
185
|
+
for task in self.tasks:
|
186
|
+
if task.returncode != 0 and print_error:
|
187
|
+
print(f'Command: {task.command} failed with return code: {task.returncode}')
|
188
|
+
print('Stdout:')
|
189
|
+
print('\n '.join(task.stdout))
|
190
|
+
print('Stderr:')
|
191
|
+
print('\n '.join(task.stderr))
|
192
|
+
return self.tasks
|
193
|
+
|
194
|
+
def get_results(self, with_stdErr=False):
|
195
|
+
'''
|
196
|
+
Get the results of the tasks
|
197
|
+
@params:
|
198
|
+
with_stdErr: Whether to append the standard error output to the standard output
|
199
|
+
@returns:
|
200
|
+
list[list[str]]: The output of the tasks ( list[list[str]] )
|
201
|
+
'''
|
202
|
+
if with_stdErr:
|
203
|
+
return [task.stdout + task.stderr for task in self.tasks]
|
204
|
+
else:
|
205
|
+
return [task.stdout for task in self.tasks]
|
206
|
+
|
207
|
+
def get_return_codes(self):
|
208
|
+
'''
|
209
|
+
Get the return codes of the tasks
|
210
|
+
@params:
|
211
|
+
None
|
212
|
+
@returns:
|
213
|
+
list[int]: The return codes of the tasks ( list[int] )
|
214
|
+
'''
|
215
|
+
return [task.returncode for task in self.tasks]
|
43
216
|
|
44
217
|
def _expand_ranges(inStr):
|
45
218
|
'''
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|