multiSSH3 5.71__tar.gz → 5.73__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.71
3
+ Version: 5.73
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.71
3
+ Version: 5.73
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.71'
57
+ version = '5.73'
58
58
  VERSION = version
59
59
  __version__ = version
60
- COMMIT_DATE = '2025-05-13'
60
+ COMMIT_DATE = '2025-05-21'
61
61
 
62
62
  CONFIG_FILE_CHAIN = ['./multiSSH3.config.json',
63
63
  '~/multiSSH3.config.json',
@@ -1824,14 +1824,14 @@ def _curses_add_string_to_window(window, line = '', y = 0, x = 0, number_of_char
1824
1824
  charsWritten += min(len(segment), numChar - charsWritten)
1825
1825
  # if we have finished printing segments but we still have space, we will fill it with fill_char
1826
1826
  trail_fill_length = numChar - charsWritten - len(trail_str)
1827
- if trail_fill_length > 0:
1828
- window.addnstr(y, x + charsWritten,fill_char * (trail_fill_length // len(fill_char) + 1), trail_fill_length, boxAttr)
1827
+ if trail_fill_length > 0 and fill_char:
1828
+ window.addnstr(y, x + charsWritten,fill_char * (trail_fill_length // len(fill_char) + 1), trail_fill_length , boxAttr)
1829
1829
  charsWritten += trail_fill_length
1830
1830
  if len(trail_str) > 0 and charsWritten < numChar:
1831
1831
  window.addnstr(y, x + charsWritten, trail_str, numChar - charsWritten, boxAttr)
1832
1832
  charsWritten += min(len(trail_str), numChar - charsWritten)
1833
1833
 
1834
- def _get_hosts_to_display (hosts, max_num_hosts, hosts_to_display = None):
1834
+ def _get_hosts_to_display (hosts, max_num_hosts, hosts_to_display = None, indexOffset = 0):
1835
1835
  '''
1836
1836
  Generate a list for the hosts to be displayed on the screen. This is used to display as much relevant information as possible.
1837
1837
 
@@ -1852,21 +1852,23 @@ def _get_hosts_to_display (hosts, max_num_hosts, hosts_to_display = None):
1852
1852
  failed_hosts = [host for host in hosts if host.returncode is not None and host.returncode != 0]
1853
1853
  finished_hosts = [host for host in hosts if host.returncode is not None and host.returncode == 0]
1854
1854
  waiting_hosts = [host for host in hosts if host.returncode is None and not host.output]
1855
- new_hosts_to_display = (running_hosts + failed_hosts + finished_hosts + waiting_hosts)[:max_num_hosts]
1855
+ new_hosts_to_display = (running_hosts + failed_hosts + finished_hosts + waiting_hosts)
1856
+ new_hosts_to_display = new_hosts_to_display[indexOffset:] + new_hosts_to_display[:indexOffset]
1857
+ new_hosts_to_display = new_hosts_to_display[:max_num_hosts]
1856
1858
  if not hosts_to_display:
1857
- return new_hosts_to_display , {'running':len(running_hosts), 'failed':len(failed_hosts), 'finished':len(finished_hosts), 'waiting':len(waiting_hosts)}
1859
+ 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)
1858
1860
  # 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
1861
+ rearrangedHosts = set()
1859
1862
  for i, host in enumerate(new_hosts_to_display):
1860
1863
  if host not in hosts_to_display or i != hosts_to_display.index(host):
1861
- host.lineNumToPrintSet.update(range(len(host.output)))
1862
- return new_hosts_to_display , {'running':len(running_hosts), 'failed':len(failed_hosts), 'finished':len(finished_hosts), 'waiting':len(waiting_hosts)}
1864
+ rearrangedHosts.add(host)
1865
+ return new_hosts_to_display , {'running':len(running_hosts), 'failed':len(failed_hosts), 'finished':len(finished_hosts), 'waiting':len(waiting_hosts)}, rearrangedHosts
1863
1866
 
1864
1867
  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'):
1865
1868
  _ = config_reason
1866
1869
  try:
1867
1870
  box_ansi_color = None
1868
1871
  org_dim = stdscr.getmaxyx()
1869
- new_configured = True
1870
1872
  # To do this, first we need to know the size of the terminal
1871
1873
  max_y, max_x = org_dim
1872
1874
  # we will use one line to print the aggregated stats for the hosts.
@@ -1890,7 +1892,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
1890
1892
  max_num_hosts = max_num_hosts_x * max_num_hosts_y
1891
1893
  if max_num_hosts < 1:
1892
1894
  return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Terminal too small to display any hosts')
1893
- hosts_to_display , host_stats = _get_hosts_to_display(hosts, max_num_hosts)
1895
+ hosts_to_display , host_stats, rearrangedHosts = _get_hosts_to_display(hosts, max_num_hosts)
1894
1896
  if len(hosts_to_display) == 0:
1895
1897
  return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'No hosts to display')
1896
1898
  # Now we calculate the actual number of hosts we will display for x and y
@@ -1949,6 +1951,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
1949
1951
  #bottom_border.addnstr(0, 0, '-' * (max_x - 1), max_x - 1)
1950
1952
  _curses_add_string_to_window(window=bottom_border, y=0, line='-' * (max_x - 1),fill_char='-',box_ansi_color=box_ansi_color)
1951
1953
  bottom_border.refresh()
1954
+ indexOffset = 0
1952
1955
  while host_stats['running'] > 0 or host_stats['waiting'] > 0:
1953
1956
  # Check for keypress
1954
1957
  key = stdscr.getch()
@@ -1981,6 +1984,10 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
1981
1984
  elif key == 125 and not __keyPressesIn[-1]: # 125 is the key code for }
1982
1985
  # if last line is empty, we will reconfigure the ww to be larger
1983
1986
  return (lineToDisplay,curserPosition , min_char_len +1, min_line_len, single_window, 'Increase character length')
1987
+ elif key == 60 and not __keyPressesIn[-1]: # 60 is the key code for <
1988
+ indexOffset = (indexOffset - 1 ) % len(hosts)
1989
+ elif key == 62 and not __keyPressesIn[-1]: # 62 is the key code for >
1990
+ indexOffset = (indexOffset +1 ) % len(hosts)
1984
1991
  # We handle positional keys
1985
1992
  # if the key is up arrow, we will move the line to display up
1986
1993
  elif key == 259: # 259 is the key code for up arrow
@@ -1989,7 +1996,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
1989
1996
  elif key == 258: # 258 is the key code for down arrow
1990
1997
  lineToDisplay = min(lineToDisplay + 1, -1)
1991
1998
  # if the key is left arrow, we will move the cursor left
1992
- elif key == 260: # 260 is the key code for left arrow
1999
+ elif key == 260: # 260 is the key code for left arrow
1993
2000
  curserPosition = min(max(curserPosition - 1, 0), len(__keyPressesIn[lineToDisplay]) -1)
1994
2001
  # if the key is right arrow, we will move the cursor right
1995
2002
  elif key == 261: # 261 is the key code for right arrow
@@ -2042,7 +2049,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
2042
2049
  # We generate the aggregated stats if user did not input anything
2043
2050
  if not __keyPressesIn[lineToDisplay]:
2044
2051
  #stats = '┍'+ f" Total: {len(hosts)} Running: {host_stats['running']} Failed: {host_stats['failed']} Finished: {host_stats['finished']} Waiting: {host_stats['waiting']} ww: {min_char_len} wh:{min_line_len} "[:max_x - 2].center(max_x - 2, "━")
2045
- stats = f"Total: {len(hosts)} Running: {host_stats['running']} Failed: {host_stats['failed']} Finished: {host_stats['finished']} Waiting: {host_stats['waiting']} ww: {min_char_len} wh:{min_line_len} "
2052
+ stats = f"Total: {len(hosts)} Running: {host_stats['running']} Failed: {host_stats['failed']} Finished: {host_stats['finished']} Waiting: {host_stats['waiting']} ww: {min_char_len} wh:{min_line_len} i:{indexOffset} "
2046
2053
  else:
2047
2054
  # we use the stat bar to display the key presses
2048
2055
  encodedLine = ''.join(__keyPressesIn[lineToDisplay]).encode().decode().strip('\n') + ' '
@@ -2081,16 +2088,15 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
2081
2088
  if time.perf_counter() - last_refresh_time < 0.01:
2082
2089
  time.sleep(max(0,0.01 - time.perf_counter() + last_refresh_time))
2083
2090
  #stdscr.clear()
2084
- hosts_to_display, host_stats = _get_hosts_to_display(hosts, max_num_hosts,hosts_to_display)
2085
2091
  for host_window, host in zip(host_windows, hosts_to_display):
2086
2092
  # we will only update the window if there is new output or the window is not fully printed
2087
- if new_configured:
2088
- host.lineNumToPrintSet.update(range(len(host.output)))
2093
+ if host in rearrangedHosts:
2089
2094
  linePrintOut = f'{host.name}:[{host.command}]'.replace('\n', ' ').replace('\r', ' ').strip()
2090
2095
  _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)
2091
2096
  # clear the window
2092
2097
  for i in range(host_window_height - 1):
2093
2098
  _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)
2099
+ host.lineNumToPrintSet.update(range(len(host.output)))
2094
2100
  # for i in range(host.printedLines, len(host.output)):
2095
2101
  # _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)
2096
2102
  # host.printedLines = len(host.output)
@@ -2103,15 +2109,16 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
2103
2109
  for lineNumToReprint in sorted(lineNumToPrintSet):
2104
2110
  # if the line is visible, we will reprint it
2105
2111
  if visibleLowerBound <= lineNumToReprint <= len(host.output):
2106
- _curses_add_string_to_window(window=host_window, y=lineNumToReprint + 1, line=host.output[lineNumToReprint], color_pair_list=host.current_color_pair,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color)
2112
+ _curses_add_string_to_window(window=host_window, y=lineNumToReprint + 1, line=host.output[lineNumToReprint], color_pair_list=host.current_color_pair,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color,fill_char='')
2107
2113
  except Exception as e:
2108
2114
  # import traceback
2109
2115
  # print(str(e).strip())
2110
2116
  # print(traceback.format_exc().strip())
2111
2117
  if org_dim != stdscr.getmaxyx():
2112
2118
  return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Terminal resize detected')
2113
- host_window.refresh()
2114
- new_configured = False
2119
+ host_window.noutrefresh()
2120
+ hosts_to_display, host_stats,rearrangedHosts = _get_hosts_to_display(hosts, max_num_hosts,hosts_to_display, indexOffset)
2121
+ curses.doupdate()
2115
2122
  last_refresh_time = time.perf_counter()
2116
2123
  except Exception as e:
2117
2124
  import traceback
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes