multiCMD 1.27__py3-none-any.whl → 1.28__py3-none-any.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.
multiCMD.py CHANGED
@@ -18,7 +18,7 @@ import re
18
18
  import itertools
19
19
  import signal
20
20
 
21
- version = '1.27'
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
  '''
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiCMD
3
- Version: 1.27
3
+ Version: 1.28
4
4
  Summary: Run commands simultaneously
5
5
  Home-page: https://github.com/yufei-pan/multiCMD
6
6
  Author: Yufei Pan
@@ -0,0 +1,6 @@
1
+ multiCMD.py,sha256=fgT8HBpqVnoT8HZ9bHQwFGOM5ONuJIn88ey4ITYiRP8,25050
2
+ multicmd-1.28.dist-info/METADATA,sha256=7EK4qvJSK1DheNstmVJTOq1qK9r0oGhZrlcL0JWIAEE,5640
3
+ multicmd-1.28.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
4
+ multicmd-1.28.dist-info/entry_points.txt,sha256=nSLBkYrcUCQxt1w3LIJkvgOhpRYEC0xAPqNG7u4OYs8,89
5
+ multicmd-1.28.dist-info/top_level.txt,sha256=DSqgftD40G09F3qEjpHRCUNUsGUvGZZG69Sm3YEUiWI,9
6
+ multicmd-1.28.dist-info/RECORD,,
@@ -1,6 +0,0 @@
1
- multiCMD.py,sha256=YroqJCPegx_Ea-FSNWPzNRdlFWSEmiqt2YN94sqVPw8,18903
2
- multicmd-1.27.dist-info/METADATA,sha256=TF_qAeuDkNtIgC70KCUZbEwf5PftyStFW_TIyfFU4Go,5640
3
- multicmd-1.27.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
4
- multicmd-1.27.dist-info/entry_points.txt,sha256=nSLBkYrcUCQxt1w3LIJkvgOhpRYEC0xAPqNG7u4OYs8,89
5
- multicmd-1.27.dist-info/top_level.txt,sha256=DSqgftD40G09F3qEjpHRCUNUsGUvGZZG69Sm3YEUiWI,9
6
- multicmd-1.27.dist-info/RECORD,,