multiCMD 1.29__tar.gz → 1.31__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.29 → multicmd-1.31}/PKG-INFO +1 -1
- {multicmd-1.29 → multicmd-1.31}/multiCMD.egg-info/PKG-INFO +1 -1
- {multicmd-1.29 → multicmd-1.31}/multiCMD.py +74 -4
- {multicmd-1.29 → multicmd-1.31}/README.md +0 -0
- {multicmd-1.29 → multicmd-1.31}/multiCMD.egg-info/SOURCES.txt +0 -0
- {multicmd-1.29 → multicmd-1.31}/multiCMD.egg-info/dependency_links.txt +0 -0
- {multicmd-1.29 → multicmd-1.31}/multiCMD.egg-info/entry_points.txt +0 -0
- {multicmd-1.29 → multicmd-1.31}/multiCMD.egg-info/requires.txt +0 -0
- {multicmd-1.29 → multicmd-1.31}/multiCMD.egg-info/top_level.txt +0 -0
- {multicmd-1.29 → multicmd-1.31}/setup.cfg +0 -0
- {multicmd-1.29 → multicmd-1.31}/setup.py +0 -0
@@ -18,10 +18,11 @@ import re
|
|
18
18
|
import itertools
|
19
19
|
import signal
|
20
20
|
|
21
|
-
version = '1.
|
21
|
+
version = '1.31'
|
22
22
|
__version__ = version
|
23
23
|
|
24
24
|
__running_threads = []
|
25
|
+
__variables = {}
|
25
26
|
class Task:
|
26
27
|
def __init__(self, command):
|
27
28
|
self.command = command
|
@@ -226,6 +227,66 @@ class AsyncExecutor:
|
|
226
227
|
'''
|
227
228
|
return [task.returncode for task in self.tasks]
|
228
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: str, vars_: dict[str, str]) -> list[str]:
|
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: str) -> list[str]:
|
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
|
+
|
229
290
|
def _expand_ranges(inStr):
|
230
291
|
'''
|
231
292
|
Expand ranges in a string
|
@@ -236,6 +297,7 @@ def _expand_ranges(inStr):
|
|
236
297
|
@returns:
|
237
298
|
list[str]: The expanded string
|
238
299
|
'''
|
300
|
+
global __variables
|
239
301
|
expandingStr = [inStr]
|
240
302
|
expandedList = []
|
241
303
|
# all valid alphanumeric characters
|
@@ -250,14 +312,22 @@ def _expand_ranges(inStr):
|
|
250
312
|
parts = group.split(',')
|
251
313
|
for part in parts:
|
252
314
|
part = part.strip()
|
253
|
-
if '
|
315
|
+
if ':' in part:
|
316
|
+
variableName, _, part = part.partition(':')
|
317
|
+
__variables[variableName] = part
|
318
|
+
expandingStr.append(currentStr.replace(match.group(0), '', 1))
|
319
|
+
elif '-' in part:
|
254
320
|
try:
|
255
321
|
range_start,_, range_end = part.partition('-')
|
256
322
|
except ValueError:
|
257
323
|
expandedList.append(currentStr)
|
258
324
|
continue
|
259
325
|
range_start = range_start.strip()
|
326
|
+
if range_start in __variables:
|
327
|
+
range_start = __variables[range_start]
|
260
328
|
range_end = range_end.strip()
|
329
|
+
if range_end in __variables:
|
330
|
+
range_end = __variables[range_end]
|
261
331
|
if range_start.isdigit() and range_end.isdigit():
|
262
332
|
padding_length = min(len(range_start), len(range_end))
|
263
333
|
format_str = "{:0" + str(padding_length) + "d}"
|
@@ -528,7 +598,7 @@ def __format_command(command,expand = False):
|
|
528
598
|
'''
|
529
599
|
if isinstance(command,str):
|
530
600
|
if expand:
|
531
|
-
commands =
|
601
|
+
commands = _expand_ranges_fast(command)
|
532
602
|
else:
|
533
603
|
commands = [command]
|
534
604
|
return [command.split() for command in commands]
|
@@ -542,7 +612,7 @@ def __format_command(command,expand = False):
|
|
542
612
|
sanitized_command.append(repr(field))
|
543
613
|
if not expand:
|
544
614
|
return [sanitized_command]
|
545
|
-
sanitized_expanded_command = [
|
615
|
+
sanitized_expanded_command = [_expand_ranges_fast(field) for field in sanitized_command]
|
546
616
|
# now the command had been expanded to list of list of fields with each field expanded to all possible options
|
547
617
|
# we need to generate all possible combinations of the fields
|
548
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
|