multiSSH3 5.69__tar.gz → 5.72__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.

Potentially problematic release.


This version of multiSSH3 might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiSSH3
3
- Version: 5.69
3
+ Version: 5.72
4
4
  Summary: Run commands on multiple hosts via SSH
5
5
  Home-page: https://github.com/yufei-pan/multiSSH3
6
6
  Author: Yufei Pan
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multiSSH3
3
- Version: 5.69
3
+ Version: 5.72
4
4
  Summary: Run commands on multiple hosts via SSH
5
5
  Home-page: https://github.com/yufei-pan/multiSSH3
6
6
  Author: Yufei Pan
@@ -54,10 +54,10 @@ except AttributeError:
54
54
  # If neither is available, use a dummy decorator
55
55
  def cache_decorator(func):
56
56
  return func
57
- version = '5.69'
57
+ version = '5.72'
58
58
  VERSION = version
59
59
  __version__ = version
60
- COMMIT_DATE = '2025-05-09'
60
+ COMMIT_DATE = '2025-05-21'
61
61
 
62
62
  CONFIG_FILE_CHAIN = ['./multiSSH3.config.json',
63
63
  '~/multiSSH3.config.json',
@@ -1191,12 +1191,13 @@ def __handle_writing_stream(stream,stop_event,host):
1191
1191
  line = '> ' + ''.join(__keyPressesIn[sentInput]).encode().decode().replace('\n', '↵')
1192
1192
  host.output.append(line)
1193
1193
  host.stdout.append(line)
1194
+ host.lineNumToPrintSet.add(len(host.output)-1)
1194
1195
  sentInput += 1
1195
1196
  host.lastUpdateTime = time.monotonic()
1196
1197
  else:
1197
1198
  time.sleep(0.01) # sleep for 10ms
1198
1199
  if sentInput < len(__keyPressesIn) - 1 :
1199
- eprint(f"Warning: {len(__keyPressesIn)-sentInput} key presses are not sent before the process is terminated!")
1200
+ eprint(f"Warning: {len(__keyPressesIn)-sentInput} lines of key presses are not sent before the process is terminated!")
1200
1201
  # # send the last line
1201
1202
  # if __keyPressesIn and __keyPressesIn[-1]:
1202
1203
  # stream.write(''.join(__keyPressesIn[-1]).encode())
@@ -1853,19 +1854,19 @@ def _get_hosts_to_display (hosts, max_num_hosts, hosts_to_display = None):
1853
1854
  waiting_hosts = [host for host in hosts if host.returncode is None and not host.output]
1854
1855
  new_hosts_to_display = (running_hosts + failed_hosts + finished_hosts + waiting_hosts)[:max_num_hosts]
1855
1856
  if not hosts_to_display:
1856
- return new_hosts_to_display , {'running':len(running_hosts), 'failed':len(failed_hosts), 'finished':len(finished_hosts), 'waiting':len(waiting_hosts)}
1857
+ return new_hosts_to_display , {'running':len(running_hosts), 'failed':len(failed_hosts), 'finished':len(finished_hosts), 'waiting':len(waiting_hosts)}, set(new_hosts_to_display)
1857
1858
  # we will compare the new_hosts_to_display with the old one, if some hosts are not in their original position, we will reprint all lines
1859
+ rearrangedHosts = set()
1858
1860
  for i, host in enumerate(new_hosts_to_display):
1859
1861
  if host not in hosts_to_display or i != hosts_to_display.index(host):
1860
- host.lineNumToPrintSet.update(range(len(host.output)))
1861
- return new_hosts_to_display , {'running':len(running_hosts), 'failed':len(failed_hosts), 'finished':len(finished_hosts), 'waiting':len(waiting_hosts)}
1862
+ rearrangedHosts.add(host)
1863
+ return new_hosts_to_display , {'running':len(running_hosts), 'failed':len(failed_hosts), 'finished':len(finished_hosts), 'waiting':len(waiting_hosts)}, rearrangedHosts
1862
1864
 
1863
1865
  def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min_char_len = DEFAULT_CURSES_MINIMUM_CHAR_LEN, min_line_len = DEFAULT_CURSES_MINIMUM_LINE_LEN,single_window=DEFAULT_SINGLE_WINDOW, config_reason = 'New Configuration'):
1864
1866
  _ = config_reason
1865
1867
  try:
1866
1868
  box_ansi_color = None
1867
1869
  org_dim = stdscr.getmaxyx()
1868
- new_configured = True
1869
1870
  # To do this, first we need to know the size of the terminal
1870
1871
  max_y, max_x = org_dim
1871
1872
  # we will use one line to print the aggregated stats for the hosts.
@@ -1889,7 +1890,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
1889
1890
  max_num_hosts = max_num_hosts_x * max_num_hosts_y
1890
1891
  if max_num_hosts < 1:
1891
1892
  return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Terminal too small to display any hosts')
1892
- hosts_to_display , host_stats = _get_hosts_to_display(hosts, max_num_hosts)
1893
+ hosts_to_display , host_stats, rearrangedHosts = _get_hosts_to_display(hosts, max_num_hosts)
1893
1894
  if len(hosts_to_display) == 0:
1894
1895
  return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'No hosts to display')
1895
1896
  # Now we calculate the actual number of hosts we will display for x and y
@@ -2080,16 +2081,15 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
2080
2081
  if time.perf_counter() - last_refresh_time < 0.01:
2081
2082
  time.sleep(max(0,0.01 - time.perf_counter() + last_refresh_time))
2082
2083
  #stdscr.clear()
2083
- hosts_to_display, host_stats = _get_hosts_to_display(hosts, max_num_hosts,hosts_to_display)
2084
2084
  for host_window, host in zip(host_windows, hosts_to_display):
2085
2085
  # we will only update the window if there is new output or the window is not fully printed
2086
- if new_configured:
2087
- host.lineNumToPrintSet.update(range(len(host.output)))
2086
+ if host in rearrangedHosts:
2088
2087
  linePrintOut = f'{host.name}:[{host.command}]'.replace('\n', ' ').replace('\r', ' ').strip()
2089
2088
  _curses_add_string_to_window(window=host_window, y=0, line=linePrintOut, color_pair_list=[-1, -1, 1],centered=True,fill_char='─',lead_str='┼',box_ansi_color=box_ansi_color)
2090
2089
  # clear the window
2091
2090
  for i in range(host_window_height - 1):
2092
2091
  _curses_add_string_to_window(window=host_window, color_pair_list=[-1, -1, 1], y=i + 1,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color)
2092
+ host.lineNumToPrintSet.update(range(len(host.output)))
2093
2093
  # for i in range(host.printedLines, len(host.output)):
2094
2094
  # _curses_add_string_to_window(window=host_window, y=i + 1, line=host.output[i], color_pair_list=host.current_color_pair,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color)
2095
2095
  # host.printedLines = len(host.output)
@@ -2110,7 +2110,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
2110
2110
  if org_dim != stdscr.getmaxyx():
2111
2111
  return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Terminal resize detected')
2112
2112
  host_window.refresh()
2113
- new_configured = False
2113
+ hosts_to_display, host_stats,rearrangedHosts = _get_hosts_to_display(hosts, max_num_hosts,hosts_to_display)
2114
2114
  last_refresh_time = time.perf_counter()
2115
2115
  except Exception as e:
2116
2116
  import traceback
@@ -2263,7 +2263,7 @@ def generate_output(hosts, usejson = False, greppable = False):
2263
2263
  rtnStr += "User Inputs: \n "
2264
2264
  rtnStr += '\n '.join(CMDsOut)
2265
2265
  rtnStr += '\n'
2266
- __keyPressesIn = [[]]
2266
+ __keyPressesIn[-1].clear()
2267
2267
  if __global_suppress_printout and not outputs:
2268
2268
  rtnStr += 'Success'
2269
2269
  return rtnStr
@@ -2438,7 +2438,7 @@ def getStrCommand(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAULT_ONE_O
2438
2438
  copy_id = False, unavailable_host_expiry = DEFAULT_UNAVAILABLE_HOST_EXPIRY,no_history = DEFAULT_NO_HISTORY,
2439
2439
  history_file = DEFAULT_HISTORY_FILE, env_file = DEFAULT_ENV_FILE,
2440
2440
  repeat = DEFAULT_REPEAT,interval = DEFAULT_INTERVAL,
2441
- shortend = False):
2441
+ shortend = False,tabSeperated = False):
2442
2442
  _ = called
2443
2443
  _ = returnUnfinished
2444
2444
  _ = willUpdateUnreachableHosts
@@ -2462,7 +2462,10 @@ def getStrCommand(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAULT_ONE_O
2462
2462
  commandStr = '"' + '" "'.join(commands) + '"' if commands else ''
2463
2463
  filePath = os.path.abspath(__file__)
2464
2464
  programName = filePath if filePath else 'mssh'
2465
- return f'{programName} {argsStr} {hostStr} {commandStr}'
2465
+ if tabSeperated:
2466
+ return f'{programName}\t{argsStr}\t{hostStr}\t{commandStr}'
2467
+ else:
2468
+ return f'{programName} {argsStr} {hostStr} {commandStr}'
2466
2469
 
2467
2470
  #%% ------------ Record History Block ----------------
2468
2471
  def record_command_history(kwargs):
@@ -2486,7 +2489,7 @@ def record_command_history(kwargs):
2486
2489
  for name in sig.parameters
2487
2490
  if name in kwargs
2488
2491
  }
2489
- strCommand = getStrCommand(**wanted)
2492
+ strCommand = getStrCommand(**wanted,shortend=True,tabSeperated=True)
2490
2493
  with open(history_file, 'a') as f:
2491
2494
  # it follows <timestamp>\t<strCommand>\n
2492
2495
  f.write(f'{int(time.time())}\t{strCommand}\n')
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes