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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiCMD
3
- Version: 1.30
3
+ Version: 1.32
4
4
  Summary: Run commands simultaneously
5
5
  Home-page: https://github.com/yufei-pan/multiCMD
6
6
  Author: Yufei Pan
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiCMD
3
- Version: 1.30
3
+ Version: 1.32
4
4
  Summary: Run commands simultaneously
5
5
  Home-page: https://github.com/yufei-pan/multiCMD
6
6
  Author: Yufei Pan
@@ -18,7 +18,7 @@ import re
18
18
  import itertools
19
19
  import signal
20
20
 
21
- version = '1.30'
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 = _expand_ranges(command)
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 = [_expand_ranges(field) for field in sanitized_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