multiCMD 1.30__tar.gz → 1.32__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.30 → multicmd-1.32}/PKG-INFO +1 -1
- {multicmd-1.30 → multicmd-1.32}/multiCMD.egg-info/PKG-INFO +1 -1
- {multicmd-1.30 → multicmd-1.32}/multiCMD.py +63 -3
- {multicmd-1.30 → multicmd-1.32}/README.md +0 -0
- {multicmd-1.30 → multicmd-1.32}/multiCMD.egg-info/SOURCES.txt +0 -0
- {multicmd-1.30 → multicmd-1.32}/multiCMD.egg-info/dependency_links.txt +0 -0
- {multicmd-1.30 → multicmd-1.32}/multiCMD.egg-info/entry_points.txt +0 -0
- {multicmd-1.30 → multicmd-1.32}/multiCMD.egg-info/requires.txt +0 -0
- {multicmd-1.30 → multicmd-1.32}/multiCMD.egg-info/top_level.txt +0 -0
- {multicmd-1.30 → multicmd-1.32}/setup.cfg +0 -0
- {multicmd-1.30 → multicmd-1.32}/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.32'
|
22
22
|
__version__ = version
|
23
23
|
|
24
24
|
__running_threads = []
|
@@ -227,6 +227,66 @@ class AsyncExecutor:
|
|
227
227
|
'''
|
228
228
|
return [task.returncode for task in self.tasks]
|
229
229
|
|
230
|
+
# immutable helpers compiled once at import time
|
231
|
+
_BRACKET_RX = re.compile(r'\[([^\]]+)\]')
|
232
|
+
_ALPHANUM = string.digits + string.ascii_letters
|
233
|
+
_ALPHA_IDX = {c: i for i, c in enumerate(_ALPHANUM)}
|
234
|
+
|
235
|
+
def _expand_piece(piece, vars_):
|
236
|
+
"""Turn one comma-separated element from inside [...] into a list of strings."""
|
237
|
+
piece = piece.strip()
|
238
|
+
|
239
|
+
# variable assignment foo:BAR
|
240
|
+
if ':' in piece:
|
241
|
+
var, _, value = piece.partition(':')
|
242
|
+
vars_[var] = value
|
243
|
+
return # bracket disappears
|
244
|
+
|
245
|
+
# explicit range start-end
|
246
|
+
if '-' in piece:
|
247
|
+
start, _, end = (p.strip() for p in piece.partition('-'))
|
248
|
+
|
249
|
+
start = vars_.get(start, start)
|
250
|
+
end = vars_.get(end, end)
|
251
|
+
|
252
|
+
if start.isdigit() and end.isdigit(): # decimal range
|
253
|
+
pad = max(len(start), len(end))
|
254
|
+
return [f"{i:0{pad}d}" for i in range(int(start), int(end) + 1)]
|
255
|
+
|
256
|
+
if all(c in string.hexdigits for c in start + end): # hex range
|
257
|
+
return [format(i, 'x') for i in range(int(start, 16),
|
258
|
+
int(end, 16) + 1)]
|
259
|
+
|
260
|
+
# alphanumeric range (0-9a-zA-Z)
|
261
|
+
try:
|
262
|
+
return [_ALPHANUM[i]
|
263
|
+
for i in range(_ALPHA_IDX[start], _ALPHA_IDX[end] + 1)]
|
264
|
+
except KeyError:
|
265
|
+
pass # fall through
|
266
|
+
|
267
|
+
# plain token or ${var}
|
268
|
+
return [vars_.get(piece, piece)]
|
269
|
+
|
270
|
+
def _expand_ranges_fast(inStr):
|
271
|
+
global __variables
|
272
|
+
segments: list[list[str]] = []
|
273
|
+
pos = 0
|
274
|
+
# split the template into literal pieces + expandable pieces
|
275
|
+
for m in _BRACKET_RX.finditer(inStr):
|
276
|
+
if m.start() > pos:
|
277
|
+
segments.append([inStr[pos:m.start()]]) # literal
|
278
|
+
choices: list[str] = []
|
279
|
+
for sub in m.group(1).split(','):
|
280
|
+
expandedPieces = _expand_piece(sub, __variables)
|
281
|
+
if expandedPieces:
|
282
|
+
choices.extend(expandedPieces)
|
283
|
+
segments.append(choices or ['']) # keep length
|
284
|
+
pos = m.end()
|
285
|
+
segments.append([inStr[pos:]]) # tail
|
286
|
+
|
287
|
+
# cartesian product of all segments
|
288
|
+
return [''.join(parts) for parts in itertools.product(*segments)]
|
289
|
+
|
230
290
|
def _expand_ranges(inStr):
|
231
291
|
'''
|
232
292
|
Expand ranges in a string
|
@@ -538,7 +598,7 @@ def __format_command(command,expand = False):
|
|
538
598
|
'''
|
539
599
|
if isinstance(command,str):
|
540
600
|
if expand:
|
541
|
-
commands =
|
601
|
+
commands = _expand_ranges_fast(command)
|
542
602
|
else:
|
543
603
|
commands = [command]
|
544
604
|
return [command.split() for command in commands]
|
@@ -552,7 +612,7 @@ def __format_command(command,expand = False):
|
|
552
612
|
sanitized_command.append(repr(field))
|
553
613
|
if not expand:
|
554
614
|
return [sanitized_command]
|
555
|
-
sanitized_expanded_command = [
|
615
|
+
sanitized_expanded_command = [_expand_ranges_fast(field) for field in sanitized_command]
|
556
616
|
# now the command had been expanded to list of list of fields with each field expanded to all possible options
|
557
617
|
# we need to generate all possible combinations of the fields
|
558
618
|
# we will use the cartesian product to do this
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|