multiCMD 1.26__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.26 → multicmd-1.28}/PKG-INFO +2 -2
- {multicmd-1.26 → multicmd-1.28}/multiCMD.egg-info/PKG-INFO +2 -2
- {multicmd-1.26 → multicmd-1.28}/multiCMD.py +181 -5
- {multicmd-1.26 → multicmd-1.28}/README.md +0 -0
- {multicmd-1.26 → multicmd-1.28}/multiCMD.egg-info/SOURCES.txt +0 -0
- {multicmd-1.26 → multicmd-1.28}/multiCMD.egg-info/dependency_links.txt +0 -0
- {multicmd-1.26 → multicmd-1.28}/multiCMD.egg-info/entry_points.txt +0 -0
- {multicmd-1.26 → multicmd-1.28}/multiCMD.egg-info/requires.txt +0 -0
- {multicmd-1.26 → multicmd-1.28}/multiCMD.egg-info/top_level.txt +0 -0
- {multicmd-1.26 → multicmd-1.28}/setup.cfg +0 -0
- {multicmd-1.26 → 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
|
'''
|
@@ -307,7 +480,7 @@ def ping(hosts,timeout=1,max_threads=0,quiet=True,dry_run=False,with_stdErr=Fals
|
|
307
480
|
|
308
481
|
|
309
482
|
def run_command(command, timeout=0,max_threads=1,quiet=False,dry_run=False,with_stdErr=False,
|
310
|
-
return_code_only=False,return_object=False,wait_for_return=True):
|
483
|
+
return_code_only=False,return_object=False,wait_for_return=True, sem = None):
|
311
484
|
'''
|
312
485
|
Run a command
|
313
486
|
|
@@ -321,13 +494,14 @@ def run_command(command, timeout=0,max_threads=1,quiet=False,dry_run=False,with_
|
|
321
494
|
return_code_only: Whether to return only the return code
|
322
495
|
return_object: Whether to return the Task object
|
323
496
|
wait_for_return: Whether to wait for the return of the command
|
497
|
+
sem: The semaphore to use for threading
|
324
498
|
|
325
499
|
@returns:
|
326
500
|
None | int | list[str] | Task: The output of the command
|
327
501
|
'''
|
328
502
|
return run_commands(commands=[command], timeout=timeout, max_threads=max_threads, quiet=quiet,
|
329
503
|
dry_run=dry_run, with_stdErr=with_stdErr, return_code_only=return_code_only,
|
330
|
-
return_object=return_object,parse=False,wait_for_return=wait_for_return)[0]
|
504
|
+
return_object=return_object,parse=False,wait_for_return=wait_for_return,sem=sem)[0]
|
331
505
|
|
332
506
|
def __format_command(command,expand = False):
|
333
507
|
'''
|
@@ -366,7 +540,7 @@ def __format_command(command,expand = False):
|
|
366
540
|
return __format_command(str(command),expand=expand)
|
367
541
|
|
368
542
|
def run_commands(commands, timeout=0,max_threads=1,quiet=False,dry_run=False,with_stdErr=False,
|
369
|
-
return_code_only=False,return_object=False, parse = False, wait_for_return = True):
|
543
|
+
return_code_only=False,return_object=False, parse = False, wait_for_return = True, sem : threading.Semaphore = None):
|
370
544
|
'''
|
371
545
|
Run multiple commands in parallel
|
372
546
|
|
@@ -381,6 +555,7 @@ def run_commands(commands, timeout=0,max_threads=1,quiet=False,dry_run=False,wit
|
|
381
555
|
return_object: Whether to return the Task object
|
382
556
|
parse: Whether to parse ranged input
|
383
557
|
wait_for_return: Whether to wait for the return of the commands
|
558
|
+
sem: The semaphore to use for threading
|
384
559
|
|
385
560
|
@returns:
|
386
561
|
list: The output of the commands ( list[None] | list[int] | list[list[str]] | list[Task] )
|
@@ -395,7 +570,8 @@ def run_commands(commands, timeout=0,max_threads=1,quiet=False,dry_run=False,wit
|
|
395
570
|
if max_threads < 1:
|
396
571
|
max_threads = len(formatedCommands)
|
397
572
|
if max_threads > 1 or not wait_for_return:
|
398
|
-
|
573
|
+
if not sem:
|
574
|
+
sem = threading.Semaphore(max_threads) # Limit concurrent sessions
|
399
575
|
threads = [threading.Thread(target=__run_command, args=(task,sem,timeout,quiet,dry_run,...),daemon=True) for task in tasks]
|
400
576
|
for thread,task in zip(threads,tasks):
|
401
577
|
task.thread = thread
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|