multiCMD 1.29__py3-none-any.whl → 1.31__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,10 +18,11 @@ import re
18
18
  import itertools
19
19
  import signal
20
20
 
21
- version = '1.29'
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 '-' in part:
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 = _expand_ranges(command)
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 = [_expand_ranges(field) for field in sanitized_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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiCMD
3
- Version: 1.29
3
+ Version: 1.31
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=H_jetcGuVpIsaBvCvMEcHaoAEK0XNtDPO705Arb1LgE,27994
2
+ multicmd-1.31.dist-info/METADATA,sha256=xa3SHJ-2Z_hvE3ePlbQiwzfrsDnKLT8tfOXAQeoHp6I,5640
3
+ multicmd-1.31.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
4
+ multicmd-1.31.dist-info/entry_points.txt,sha256=nSLBkYrcUCQxt1w3LIJkvgOhpRYEC0xAPqNG7u4OYs8,89
5
+ multicmd-1.31.dist-info/top_level.txt,sha256=DSqgftD40G09F3qEjpHRCUNUsGUvGZZG69Sm3YEUiWI,9
6
+ multicmd-1.31.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,6 +0,0 @@
1
- multiCMD.py,sha256=5jTOEqw-pch56VkKQjVu9Ylw4zR8ZDCcEGwwCS9utHE,25571
2
- multicmd-1.29.dist-info/METADATA,sha256=-2O5pV3TjRGb4kVF3GLX83_IEKuj23hFTqiM6bjqZZA,5640
3
- multicmd-1.29.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
4
- multicmd-1.29.dist-info/entry_points.txt,sha256=nSLBkYrcUCQxt1w3LIJkvgOhpRYEC0xAPqNG7u4OYs8,89
5
- multicmd-1.29.dist-info/top_level.txt,sha256=DSqgftD40G09F3qEjpHRCUNUsGUvGZZG69Sm3YEUiWI,9
6
- multicmd-1.29.dist-info/RECORD,,