multiSSH3 5.30__tar.gz → 5.33__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.
- {multissh3-5.30 → multissh3-5.33}/PKG-INFO +1 -1
- {multissh3-5.30 → multissh3-5.33}/multiSSH3.egg-info/PKG-INFO +1 -1
- {multissh3-5.30 → multissh3-5.33}/multiSSH3.py +202 -100
- {multissh3-5.30 → multissh3-5.33}/setup.py +1 -1
- {multissh3-5.30 → multissh3-5.33}/LICENSE +0 -0
- {multissh3-5.30 → multissh3-5.33}/README.md +0 -0
- {multissh3-5.30 → multissh3-5.33}/multiSSH3.egg-info/SOURCES.txt +0 -0
- {multissh3-5.30 → multissh3-5.33}/multiSSH3.egg-info/dependency_links.txt +0 -0
- {multissh3-5.30 → multissh3-5.33}/multiSSH3.egg-info/entry_points.txt +0 -0
- {multissh3-5.30 → multissh3-5.33}/multiSSH3.egg-info/requires.txt +0 -0
- {multissh3-5.30 → multissh3-5.33}/multiSSH3.egg-info/top_level.txt +0 -0
- {multissh3-5.30 → multissh3-5.33}/setup.cfg +0 -0
|
@@ -37,7 +37,7 @@ except AttributeError:
|
|
|
37
37
|
# If neither is available, use a dummy decorator
|
|
38
38
|
def cache_decorator(func):
|
|
39
39
|
return func
|
|
40
|
-
version = '5.
|
|
40
|
+
version = '5.33'
|
|
41
41
|
VERSION = version
|
|
42
42
|
|
|
43
43
|
CONFIG_FILE = '/etc/multiSSH3.config.json'
|
|
@@ -166,6 +166,7 @@ class Host:
|
|
|
166
166
|
self.stdout = [] # the stdout of the command
|
|
167
167
|
self.stderr = [] # the stderr of the command
|
|
168
168
|
self.printedLines = -1 # the number of lines printed on the screen
|
|
169
|
+
self.lineNumToReprintSet = set() # whether to reprint the last line
|
|
169
170
|
self.lastUpdateTime = time.time() # the last time the output was updated
|
|
170
171
|
self.files = files # the files to be copied to the host
|
|
171
172
|
self.ipmi = ipmi # whether to use ipmi to connect to the host
|
|
@@ -187,7 +188,7 @@ class Host:
|
|
|
187
188
|
def __repr__(self):
|
|
188
189
|
# return the complete data structure
|
|
189
190
|
return f"Host(name={self.name}, command={self.command}, returncode={self.returncode}, stdout={self.stdout}, stderr={self.stderr}, \
|
|
190
|
-
output={self.output}, printedLines={self.printedLines}, files={self.files}, ipmi={self.ipmi}, \
|
|
191
|
+
output={self.output}, printedLines={self.printedLines}, lineNumToReprintSet={self.lineNumToReprintSet}, files={self.files}, ipmi={self.ipmi}, \
|
|
191
192
|
interface_ip_prefix={self.interface_ip_prefix}, scp={self.scp}, gatherMode={self.gatherMode}, \
|
|
192
193
|
extraargs={self.extraargs}, resolvedName={self.resolvedName}, i={self.i}, uuid={self.uuid}), \
|
|
193
194
|
identity_file={self.identity_file}, ip={self.ip}, current_color_pair={self.current_color_pair}"
|
|
@@ -1085,10 +1086,10 @@ def __handle_reading_stream(stream,target, host):
|
|
|
1085
1086
|
if not keepLastLine:
|
|
1086
1087
|
target.pop()
|
|
1087
1088
|
host.output.pop()
|
|
1088
|
-
host.printedLines -= 1
|
|
1089
1089
|
current_line_str = current_line.decode('utf-8',errors='backslashreplace')
|
|
1090
1090
|
target.append(current_line_str)
|
|
1091
1091
|
host.output.append(current_line_str)
|
|
1092
|
+
host.lineNumToReprintSet.add(len(host.output)-1)
|
|
1092
1093
|
host.lastUpdateTime = time.time()
|
|
1093
1094
|
current_line = bytearray()
|
|
1094
1095
|
lastLineCommited = True
|
|
@@ -1355,12 +1356,13 @@ def run_command(host, sem, timeout=60,passwds=None):
|
|
|
1355
1356
|
# remove last line if it is a countdown
|
|
1356
1357
|
if host.output and timeoutLineAppended and host.output[-1].strip().endswith('] seconds!') and host.output[-1].strip().startswith('Timeout in ['):
|
|
1357
1358
|
host.output.pop()
|
|
1358
|
-
host.printedLines -= 1
|
|
1359
1359
|
host.output.append(timeoutLine)
|
|
1360
|
+
host.lineNumToReprintSet.add(len(host.output)-1)
|
|
1360
1361
|
timeoutLineAppended = True
|
|
1361
1362
|
elif host.output and timeoutLineAppended and host.output[-1].strip().endswith('] seconds!') and host.output[-1].strip().startswith('Timeout in ['):
|
|
1362
1363
|
host.output.pop()
|
|
1363
|
-
host.
|
|
1364
|
+
host.output.append('')
|
|
1365
|
+
host.lineNumToReprintSet.add(len(host.output)-1)
|
|
1364
1366
|
timeoutLineAppended = False
|
|
1365
1367
|
if _emo:
|
|
1366
1368
|
host.stderr.append('Ctrl C detected, Emergency Stop!')
|
|
@@ -1525,43 +1527,6 @@ def __approximate_color_24bit(r, g, b):
|
|
|
1525
1527
|
best_match = color
|
|
1526
1528
|
return best_match
|
|
1527
1529
|
|
|
1528
|
-
def __parse_ansi_escape_sequence_to_curses_color(escape_code):
|
|
1529
|
-
"""
|
|
1530
|
-
Parse ANSI escape codes to extract foreground and background colors.
|
|
1531
|
-
|
|
1532
|
-
Args:
|
|
1533
|
-
escape_code: ANSI escape sequence for color
|
|
1534
|
-
|
|
1535
|
-
Returns:
|
|
1536
|
-
Tuple of (foreground, background) curses color pairs.
|
|
1537
|
-
If the escape code is a reset code, return (-1, -1).
|
|
1538
|
-
None values indicate that the color should not be changed.
|
|
1539
|
-
"""
|
|
1540
|
-
if not escape_code:
|
|
1541
|
-
return None, None
|
|
1542
|
-
color_match = re.match(r"\x1b\[(\d+)(?:;(\d+))?(?:;(\d+))?(?:;(\d+);(\d+);(\d+))?m", escape_code)
|
|
1543
|
-
if color_match:
|
|
1544
|
-
params = color_match.groups()
|
|
1545
|
-
if params[0] == "0" and not any(params[1:]): # Reset code
|
|
1546
|
-
return -1, -1
|
|
1547
|
-
if params[0] == "38" and params[1] == "5": # 8-bit foreground
|
|
1548
|
-
return __approximate_color_8bit(int(params[2])), None
|
|
1549
|
-
elif params[0] == "38" and params[1] == "2": # 24-bit foreground
|
|
1550
|
-
return __approximate_color_24bit(int(params[3]), int(params[4]), int(params[5])), None
|
|
1551
|
-
elif params[0] == "48" and params[1] == "5": # 8-bit background
|
|
1552
|
-
return None , __approximate_color_8bit(int(params[2]))
|
|
1553
|
-
elif params[0] == "48" and params[1] == "2": # 24-bit background
|
|
1554
|
-
return None, __approximate_color_24bit(int(params[3]), int(params[4]), int(params[5]))
|
|
1555
|
-
else:
|
|
1556
|
-
fg = None
|
|
1557
|
-
bg = None
|
|
1558
|
-
if params[0] and params[0].isdigit(): # 4-bit color
|
|
1559
|
-
fg = ANSI_TO_CURSES_COLOR.get(int(params[0]), curses.COLOR_WHITE)
|
|
1560
|
-
if params[1] and params[1].isdigit():
|
|
1561
|
-
bg = ANSI_TO_CURSES_COLOR.get(int(params[1]), curses.COLOR_BLACK)
|
|
1562
|
-
return fg, bg
|
|
1563
|
-
return None, None
|
|
1564
|
-
|
|
1565
1530
|
def __get_curses_color_pair(fg, bg):
|
|
1566
1531
|
"""
|
|
1567
1532
|
Use curses color int values to create a curses color pair.
|
|
@@ -1587,7 +1552,123 @@ def __get_curses_color_pair(fg, bg):
|
|
|
1587
1552
|
__curses_current_color_pair_index += 1
|
|
1588
1553
|
return curses.color_pair(__curses_global_color_pairs[(fg, bg)])
|
|
1589
1554
|
|
|
1590
|
-
def
|
|
1555
|
+
def __parse_ansi_escape_sequence_to_curses_attr(escape_code,color_pair_list = None):
|
|
1556
|
+
"""
|
|
1557
|
+
Parse ANSI escape codes to extract foreground and background colors.
|
|
1558
|
+
|
|
1559
|
+
Args:
|
|
1560
|
+
escape_code: ANSI escape sequence for color
|
|
1561
|
+
color_pair_list: List of [foreground, background, color_pair] curses color pair values
|
|
1562
|
+
|
|
1563
|
+
Returns:
|
|
1564
|
+
Curses color pair / attribute code
|
|
1565
|
+
"""
|
|
1566
|
+
if not escape_code:
|
|
1567
|
+
return 1
|
|
1568
|
+
if not color_pair_list:
|
|
1569
|
+
color_pair_list = [-1,-1,1]
|
|
1570
|
+
color_match = escape_code.lstrip("\x1b[").rstrip("m").split(";")
|
|
1571
|
+
color_match = [x if x else '0' for x in color_match] # Replace empty strings with '0' (reset)
|
|
1572
|
+
if color_match:
|
|
1573
|
+
processed_index = -1
|
|
1574
|
+
for i, param in enumerate(color_match):
|
|
1575
|
+
if processed_index >= i:
|
|
1576
|
+
# if the index has been processed, skip
|
|
1577
|
+
continue
|
|
1578
|
+
if param.isdigit():
|
|
1579
|
+
if int(param) == 0:
|
|
1580
|
+
color_pair_list[0] = -1
|
|
1581
|
+
color_pair_list[1] = -1
|
|
1582
|
+
color_pair_list[2] = 1
|
|
1583
|
+
elif int(param) == 38:
|
|
1584
|
+
if i + 1 >= len(color_match):
|
|
1585
|
+
# Invalid color code, skip
|
|
1586
|
+
continue
|
|
1587
|
+
if color_match[i + 1] == "5":
|
|
1588
|
+
# 8-bit foreground color
|
|
1589
|
+
if i + 2 >= len(color_match) or not color_match[i + 2].isdigit():
|
|
1590
|
+
# Invalid color code, skip
|
|
1591
|
+
processed_index = i + 1
|
|
1592
|
+
continue
|
|
1593
|
+
color_pair_list[0] = __approximate_color_8bit(int(color_match[i + 2]))
|
|
1594
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1595
|
+
processed_index = i + 2
|
|
1596
|
+
elif color_match[i + 1] == "2":
|
|
1597
|
+
# 24-bit foreground color
|
|
1598
|
+
if i + 4 >= len(color_match) or not all(x.isdigit() for x in color_match[i + 2:i + 5]):
|
|
1599
|
+
# Invalid color code, skip
|
|
1600
|
+
processed_index = i + 1
|
|
1601
|
+
continue
|
|
1602
|
+
color_pair_list[0] = __approximate_color_24bit(int(color_match[i + 2]), int(color_match[i + 3]), int(color_match[i + 4]))
|
|
1603
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1604
|
+
processed_index = i + 4
|
|
1605
|
+
elif int(param) == 48:
|
|
1606
|
+
if i + 1 >= len(color_match):
|
|
1607
|
+
# Invalid color code, skip
|
|
1608
|
+
continue
|
|
1609
|
+
if color_match[i + 1] == "5":
|
|
1610
|
+
# 8-bit background color
|
|
1611
|
+
if i + 2 >= len(color_match) or not color_match[i + 2].isdigit():
|
|
1612
|
+
# Invalid color code, skip
|
|
1613
|
+
processed_index = i + 1
|
|
1614
|
+
continue
|
|
1615
|
+
color_pair_list[1] = __approximate_color_8bit(int(color_match[i + 2]))
|
|
1616
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1617
|
+
processed_index = i + 2
|
|
1618
|
+
elif color_match[i + 1] == "2":
|
|
1619
|
+
# 24-bit background color
|
|
1620
|
+
if i + 4 >= len(color_match) or not all(x.isdigit() for x in color_match[i + 2:i + 5]):
|
|
1621
|
+
# Invalid color code, skip
|
|
1622
|
+
processed_index = i + 1
|
|
1623
|
+
continue
|
|
1624
|
+
color_pair_list[1] = __approximate_color_24bit(int(color_match[i + 2]), int(color_match[i + 3]), int(color_match[i + 4]))
|
|
1625
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1626
|
+
processed_index = i + 4
|
|
1627
|
+
elif 30 <= int(param) <= 37 or 90 <= int(param) <= 97:
|
|
1628
|
+
# 4-bit foreground color
|
|
1629
|
+
color_pair_list[0] = ANSI_TO_CURSES_COLOR.get(int(param), curses.COLOR_WHITE)
|
|
1630
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1631
|
+
elif 40 <= int(param) <= 47 or 100 <= int(param) <= 107:
|
|
1632
|
+
# 4-bit background color
|
|
1633
|
+
color_pair_list[1] = ANSI_TO_CURSES_COLOR.get(int(param)-10, curses.COLOR_BLACK)
|
|
1634
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1635
|
+
elif int(param) == 1:
|
|
1636
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_BOLD
|
|
1637
|
+
elif int(param) == 2:
|
|
1638
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_DIM
|
|
1639
|
+
elif int(param) == 4:
|
|
1640
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_UNDERLINE
|
|
1641
|
+
elif int(param) == 5:
|
|
1642
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_BLINK
|
|
1643
|
+
elif int(param) == 7:
|
|
1644
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_REVERSE
|
|
1645
|
+
elif int(param) == 8:
|
|
1646
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_INVIS
|
|
1647
|
+
elif int(param) == 21:
|
|
1648
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_BOLD
|
|
1649
|
+
elif int(param) == 22:
|
|
1650
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_DIM
|
|
1651
|
+
elif int(param) == 24:
|
|
1652
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_UNDERLINE
|
|
1653
|
+
elif int(param) == 25:
|
|
1654
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_BLINK
|
|
1655
|
+
elif int(param) == 27:
|
|
1656
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_REVERSE
|
|
1657
|
+
elif int(param) == 28:
|
|
1658
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_INVIS
|
|
1659
|
+
elif int(param) == 39:
|
|
1660
|
+
color_pair_list[0] = -1
|
|
1661
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1662
|
+
elif int(param) == 49:
|
|
1663
|
+
color_pair_list[1] = -1
|
|
1664
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1665
|
+
else:
|
|
1666
|
+
color_pair_list[0] = -1
|
|
1667
|
+
color_pair_list[1] = -1
|
|
1668
|
+
color_pair_list[2] = 1
|
|
1669
|
+
return color_pair_list[2]
|
|
1670
|
+
|
|
1671
|
+
def _curses_add_string_to_window(window, line = '', y = 0, x = 0, number_of_char_to_write = -1, color_pair_list = [-1,-1,1],fill_char=' ',parse_ansi_colors = True,centered = False,lead_str = '', trail_str = '',box_ansi_color = None, keep_top_n_lines = 0):
|
|
1591
1672
|
"""
|
|
1592
1673
|
Add a string to a curses window with / without ANSI color escape sequences translated to curses color pairs.
|
|
1593
1674
|
|
|
@@ -1599,10 +1680,12 @@ def _curses_add_string_to_window(window, line, y = 0, x = 0, number_of_char_to_w
|
|
|
1599
1680
|
number_of_char_to_write: Number of characters to write. -1 for all remaining space in line, 0 for no characters, and a positive integer for a specific number of characters.
|
|
1600
1681
|
color_pair_list: List of [foreground, background, color_pair] curses color pair values
|
|
1601
1682
|
fill_char: Character to fill the remaining space in the line
|
|
1602
|
-
|
|
1683
|
+
parse_ansi_colors: Parse ASCII color codes
|
|
1603
1684
|
centered: Center the text in the window
|
|
1604
1685
|
lead_str: Leading string to add to the line
|
|
1605
1686
|
trail_str: Trailing string to add to the line
|
|
1687
|
+
box_ansi_color: ANSI color escape sequence for the box color
|
|
1688
|
+
keep_top_n_lines: Number of lines to keep at the top of the window
|
|
1606
1689
|
|
|
1607
1690
|
Returns:
|
|
1608
1691
|
None
|
|
@@ -1622,41 +1705,35 @@ def _curses_add_string_to_window(window, line, y = 0, x = 0, number_of_char_to_w
|
|
|
1622
1705
|
if numChar < 0:
|
|
1623
1706
|
return
|
|
1624
1707
|
if y < 0 or y >= window.getmaxyx()[0]:
|
|
1625
|
-
window.
|
|
1708
|
+
if keep_top_n_lines > window.getmaxyx()[0] -1:
|
|
1709
|
+
keep_top_n_lines = window.getmaxyx()[0] -1
|
|
1710
|
+
if keep_top_n_lines < 0:
|
|
1711
|
+
keep_top_n_lines = 0
|
|
1712
|
+
window.move(keep_top_n_lines,0)
|
|
1626
1713
|
window.deleteln()
|
|
1627
1714
|
y = window.getmaxyx()[0] - 1
|
|
1628
|
-
|
|
1715
|
+
line = line.replace('\n', ' ').replace('\r', ' ')
|
|
1716
|
+
if parse_ansi_colors:
|
|
1629
1717
|
segments = re.split(r"(\x1b\[[\d;]*m)", line) # Split line by ANSI escape codes
|
|
1630
1718
|
else:
|
|
1631
1719
|
segments = [line]
|
|
1632
1720
|
charsWritten = 0
|
|
1633
|
-
|
|
1634
|
-
newBoxFrontColor, newBoxBackColor = __parse_ansi_escape_sequence_to_curses_color(box_ansi_color)
|
|
1635
|
-
if newBoxFrontColor:
|
|
1636
|
-
boxFrontColor = newBoxFrontColor
|
|
1637
|
-
if newBoxBackColor:
|
|
1638
|
-
boxBackColor = newBoxBackColor
|
|
1639
|
-
boxColorPair = __get_curses_color_pair(boxFrontColor, boxBackColor)
|
|
1721
|
+
boxAttr = __parse_ansi_escape_sequence_to_curses_attr(box_ansi_color)
|
|
1640
1722
|
# first add the lead_str
|
|
1641
|
-
window.addnstr(y, x, lead_str, numChar,
|
|
1723
|
+
window.addnstr(y, x, lead_str, numChar, boxAttr)
|
|
1642
1724
|
charsWritten = min(len(lead_str), numChar)
|
|
1643
1725
|
# process centering
|
|
1644
1726
|
if centered:
|
|
1645
1727
|
fill_length = numChar - len(lead_str) - len(trail_str) - sum([len(segment) for segment in segments if not segment.startswith("\x1b[")])
|
|
1646
|
-
window.addnstr(y, x + charsWritten, fill_char * (fill_length // 2), numChar - charsWritten,
|
|
1728
|
+
window.addnstr(y, x + charsWritten, fill_char * (fill_length // 2), numChar - charsWritten, boxAttr)
|
|
1647
1729
|
charsWritten += min(fill_length // 2, numChar - charsWritten)
|
|
1648
1730
|
# add the segments
|
|
1649
1731
|
for segment in segments:
|
|
1650
|
-
if
|
|
1732
|
+
if not segment:
|
|
1733
|
+
continue
|
|
1734
|
+
if parse_ansi_colors and segment.startswith("\x1b["):
|
|
1651
1735
|
# Parse ANSI escape sequence
|
|
1652
|
-
|
|
1653
|
-
if newFrontColor is not None:
|
|
1654
|
-
color_pair_list[0] = newFrontColor
|
|
1655
|
-
if newBackColor is not None:
|
|
1656
|
-
color_pair_list[1] = newBackColor
|
|
1657
|
-
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1658
|
-
#window.addnstr(y, x + charsWritten, str(color_pair_list[2]), numChar - charsWritten, color_pair_list[2])
|
|
1659
|
-
#charsWritten += min(len(str(color_pair_list[2])), numChar - charsWritten)
|
|
1736
|
+
newAttr = __parse_ansi_escape_sequence_to_curses_attr(segment,color_pair_list)
|
|
1660
1737
|
else:
|
|
1661
1738
|
# Add text with current color
|
|
1662
1739
|
if charsWritten < numChar:
|
|
@@ -1666,10 +1743,10 @@ def _curses_add_string_to_window(window, line, y = 0, x = 0, number_of_char_to_w
|
|
|
1666
1743
|
if charsWritten + len(trail_str) < numChar:
|
|
1667
1744
|
fillStr = fill_char * (numChar - charsWritten - len(trail_str))
|
|
1668
1745
|
#fillStr = f'{color_pair_list}'
|
|
1669
|
-
window.addnstr(y, x + charsWritten, fillStr + trail_str, numChar - charsWritten,
|
|
1746
|
+
window.addnstr(y, x + charsWritten, fillStr + trail_str, numChar - charsWritten, boxAttr)
|
|
1670
1747
|
charsWritten += numChar - charsWritten
|
|
1671
1748
|
else:
|
|
1672
|
-
window.addnstr(y, x + charsWritten, trail_str, numChar - charsWritten,
|
|
1749
|
+
window.addnstr(y, x + charsWritten, trail_str, numChar - charsWritten, boxAttr)
|
|
1673
1750
|
|
|
1674
1751
|
def _get_hosts_to_display (hosts, max_num_hosts, hosts_to_display = None):
|
|
1675
1752
|
'''
|
|
@@ -1705,6 +1782,7 @@ def _get_hosts_to_display (hosts, max_num_hosts, hosts_to_display = None):
|
|
|
1705
1782
|
|
|
1706
1783
|
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'):
|
|
1707
1784
|
try:
|
|
1785
|
+
box_ansi_color = None
|
|
1708
1786
|
org_dim = stdscr.getmaxyx()
|
|
1709
1787
|
new_configured = True
|
|
1710
1788
|
# To do this, first we need to know the size of the terminal
|
|
@@ -1757,7 +1835,6 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
1757
1835
|
|
|
1758
1836
|
old_stat = ''
|
|
1759
1837
|
old_bottom_stat = ''
|
|
1760
|
-
old_cursor_position = -1
|
|
1761
1838
|
# we refresh the screen every 0.1 seconds
|
|
1762
1839
|
last_refresh_time = time.perf_counter()
|
|
1763
1840
|
stdscr.clear()
|
|
@@ -1788,7 +1865,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
1788
1865
|
bottom_border.leaveok(True)
|
|
1789
1866
|
#bottom_border.clear()
|
|
1790
1867
|
#bottom_border.addnstr(0, 0, '-' * (max_x - 1), max_x - 1)
|
|
1791
|
-
_curses_add_string_to_window(window=bottom_border, y=0, line='-' * (max_x - 1),fill_char='-')
|
|
1868
|
+
_curses_add_string_to_window(window=bottom_border, y=0, line='-' * (max_x - 1),fill_char='-',box_ansi_color=box_ansi_color)
|
|
1792
1869
|
bottom_border.refresh()
|
|
1793
1870
|
while host_stats['running'] > 0 or host_stats['waiting'] > 0:
|
|
1794
1871
|
# Check for keypress
|
|
@@ -1879,40 +1956,40 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
1879
1956
|
return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Terminal resize detected')
|
|
1880
1957
|
# We generate the aggregated stats if user did not input anything
|
|
1881
1958
|
if not __keyPressesIn[lineToDisplay]:
|
|
1882
|
-
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, "━")
|
|
1959
|
+
#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, "━")
|
|
1960
|
+
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} "
|
|
1883
1961
|
else:
|
|
1884
1962
|
# we use the stat bar to display the key presses
|
|
1885
1963
|
encodedLine = ''.join(__keyPressesIn[lineToDisplay]).encode().decode().strip('\n') + ' '
|
|
1886
|
-
#
|
|
1887
|
-
|
|
1888
|
-
#
|
|
1964
|
+
#stats = '┍'+ f"Send CMD: {encodedLine}"[:max_x - 2].center(max_x - 2, "━")
|
|
1965
|
+
stats = f"Send CMD: {encodedLine}"
|
|
1966
|
+
# format the stats line with chracter at curser position inverted using ansi escape sequence
|
|
1967
|
+
stats = f'{stats[:curserPosition]}\x1b[7m{stats[curserPosition]}\x1b[0m{stats[curserPosition + 1:]}'
|
|
1968
|
+
if stats != old_stat :
|
|
1969
|
+
old_stat = stats
|
|
1970
|
+
# calculate the real curser position in stats as we centered the stats
|
|
1971
|
+
# if 'Send CMD: ' in stats:
|
|
1972
|
+
# curserPositionStats = min(min(curserPosition,len(encodedLine) -1) + stats.find('Send CMD: ')+len('Send CMD: '), max_x -2)
|
|
1889
1973
|
# else:
|
|
1890
|
-
#
|
|
1891
|
-
|
|
1974
|
+
# curserPositionStats = max_x -2
|
|
1975
|
+
#stat_window.clear()
|
|
1976
|
+
#stat_window.addstr(0, 0, stats)
|
|
1977
|
+
# add the line with curser that inverses the color at the curser position
|
|
1978
|
+
# stat_window.addstr(0, 0, stats[:curserPositionStats])
|
|
1979
|
+
# stat_window.addch(0,curserPositionStats, stats[curserPositionStats], curses.A_REVERSE)
|
|
1980
|
+
# stat_window.addnstr(0, curserPositionStats + 1, stats[curserPositionStats + 1:], max_x - 1 - curserPositionStats)
|
|
1981
|
+
# stat_window.refresh()
|
|
1982
|
+
_curses_add_string_to_window(window=stat_window, y=0, line=stats, color_pair_list=[-1, -1, 1],centered=True,fill_char='━',lead_str='┍',box_ansi_color=box_ansi_color)
|
|
1892
1983
|
if bottom_border:
|
|
1893
|
-
target_length = max_x - 2 + len('\x1b[33m\x1b[0m\x1b[31m\x1b[0m\x1b[32m\x1b[0m')
|
|
1894
|
-
bottom_stats = '└'+ f" Total: {len(hosts)} Running: \x1b[33m{host_stats['running']}\x1b[0m Failed: \x1b[31m{host_stats['failed']}\x1b[0m Finished: \x1b[32m{host_stats['finished']}\x1b[0m Waiting: {host_stats['waiting']} "[:target_length].center(target_length, "─")
|
|
1984
|
+
#target_length = max_x - 2 + len('\x1b[33m\x1b[0m\x1b[31m\x1b[0m\x1b[32m\x1b[0m')
|
|
1985
|
+
#bottom_stats = '└'+ f" Total: {len(hosts)} Running: \x1b[33m{host_stats['running']}\x1b[0m Failed: \x1b[31m{host_stats['failed']}\x1b[0m Finished: \x1b[32m{host_stats['finished']}\x1b[0m Waiting: {host_stats['waiting']} "[:target_length].center(target_length, "─")
|
|
1986
|
+
bottom_stats = f" Total: {len(hosts)} Running: \x1b[33m{host_stats['running']}\x1b[0m Failed: \x1b[31m{host_stats['failed']}\x1b[0m Finished: \x1b[32m{host_stats['finished']}\x1b[0m Waiting: {host_stats['waiting']} "
|
|
1895
1987
|
if bottom_stats != old_bottom_stat:
|
|
1896
1988
|
old_bottom_stat = bottom_stats
|
|
1897
1989
|
#bottom_border.clear()
|
|
1898
1990
|
#bottom_border.addnstr(0, 0, bottom_stats, max_x - 1)
|
|
1899
|
-
_curses_add_string_to_window(window=bottom_border, y=0, line=bottom_stats)
|
|
1991
|
+
_curses_add_string_to_window(window=bottom_border, y=0, line=bottom_stats,fill_char='─',centered=True,lead_str='└',box_ansi_color=box_ansi_color)
|
|
1900
1992
|
bottom_border.refresh()
|
|
1901
|
-
if stats != old_stat or curserPosition != old_cursor_position:
|
|
1902
|
-
old_stat = stats
|
|
1903
|
-
old_cursor_position = curserPosition
|
|
1904
|
-
# calculate the real curser position in stats as we centered the stats
|
|
1905
|
-
if 'Send CMD: ' in stats:
|
|
1906
|
-
curserPositionStats = min(min(curserPosition,len(encodedLine) -1) + stats.find('Send CMD: ')+len('Send CMD: '), max_x -2)
|
|
1907
|
-
else:
|
|
1908
|
-
curserPositionStats = max_x -2
|
|
1909
|
-
#stat_window.clear()
|
|
1910
|
-
#stat_window.addstr(0, 0, stats)
|
|
1911
|
-
# add the line with curser that inverses the color at the curser position
|
|
1912
|
-
stat_window.addstr(0, 0, stats[:curserPositionStats])
|
|
1913
|
-
stat_window.addch(0,curserPositionStats, stats[curserPositionStats], curses.A_REVERSE)
|
|
1914
|
-
stat_window.addnstr(0, curserPositionStats + 1, stats[curserPositionStats + 1:], max_x - 1 - curserPositionStats)
|
|
1915
|
-
stat_window.refresh()
|
|
1916
1993
|
# set the maximum refresh rate to 100 Hz
|
|
1917
1994
|
if time.perf_counter() - last_refresh_time < 0.01:
|
|
1918
1995
|
time.sleep(max(0,0.01 - time.perf_counter() + last_refresh_time))
|
|
@@ -1924,20 +2001,27 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
1924
2001
|
try:
|
|
1925
2002
|
#host_window.clear()
|
|
1926
2003
|
# we will try to center the name of the host with ┼ at the beginning and end and ─ in between
|
|
1927
|
-
linePrintOut = f'┼{(host.name+":["+host.command+"]")[:host_window_width - 2].center(host_window_width - 1, "─")}'.replace('\n', ' ').replace('\r', ' ').strip()
|
|
1928
|
-
|
|
1929
|
-
#
|
|
2004
|
+
#linePrintOut = f'┼{(host.name+":["+host.command+"]")[:host_window_width - 2].center(host_window_width - 1, "─")}'.replace('\n', ' ').replace('\r', ' ').strip()
|
|
2005
|
+
#linePrintOut = f'┼{(host.name+":["+host.command+"]")[:host_window_width - 2].center(host_window_width - 1, "─")}'.replace('\n', ' ').replace('\r', ' ').strip()
|
|
2006
|
+
#host_window.addnstr(0, 0, linePrintOut, host_window_width - 1)
|
|
2007
|
+
linePrintOut = f'{host.name}:[{host.command}]'.replace('\n', ' ').replace('\r', ' ').strip()
|
|
2008
|
+
_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)
|
|
2009
|
+
#_add_line_with_ansi_colors(window=host_window, y=0, x=0, line=linePrintOut, n=host_window_width - 1, color_pair_list = host.current_color_pair)
|
|
1930
2010
|
# we will display the latest outputs of the host as much as we can
|
|
1931
|
-
for i, line in enumerate(host.output[-(host_window_height - 1):]):
|
|
2011
|
+
#for i, line in enumerate(host.output[-(host_window_height - 1):]):
|
|
1932
2012
|
# print(f"Printng a line at {i + 1} with length of {len('│'+line[:host_window_width - 1])}")
|
|
1933
2013
|
# time.sleep(10)
|
|
1934
|
-
linePrintOut = ('│'+line[:host_window_width - 2].replace('\n', ' ').replace('\r', ' ')).strip()
|
|
2014
|
+
#linePrintOut = ('│'+line[:host_window_width - 2].replace('\n', ' ').replace('\r', ' ')).strip()
|
|
1935
2015
|
#host_window.addnstr(i + 1, 0, linePrintOut, host_window_width - 1)
|
|
1936
|
-
_curses_add_string_to_window(window=host_window, y=i + 1, line=
|
|
2016
|
+
#_curses_add_string_to_window(window=host_window, y=i + 1, line=line, color_pair_list=host.current_color_pair,lead_str='│')
|
|
1937
2017
|
# we draw the rest of the available lines
|
|
2018
|
+
# for i in range(len(host.output), host_window_height - 1):
|
|
2019
|
+
# # print(f"Printng a line at {i + 1} with length of {len('│')}")
|
|
2020
|
+
# host_window.addnstr(i + 1, 0, '│'.ljust(host_window_width - 1, ' '), host_window_width - 1)
|
|
2021
|
+
for i in range(host.printedLines, len(host.output)):
|
|
2022
|
+
_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)
|
|
1938
2023
|
for i in range(len(host.output), host_window_height - 1):
|
|
1939
|
-
|
|
1940
|
-
host_window.addnstr(i + 1, 0, '│'.ljust(host_window_width - 1, ' '), host_window_width - 1)
|
|
2024
|
+
_curses_add_string_to_window(window=host_window, y=i + 1,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color)
|
|
1941
2025
|
host.printedLines = len(host.output)
|
|
1942
2026
|
host_window.refresh()
|
|
1943
2027
|
except Exception as e:
|
|
@@ -1946,6 +2030,24 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
1946
2030
|
# print(traceback.format_exc().strip())
|
|
1947
2031
|
if org_dim != stdscr.getmaxyx():
|
|
1948
2032
|
return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Terminal resize detected')
|
|
2033
|
+
if host.lineNumToReprintSet:
|
|
2034
|
+
# visible range is from host.printedLines - host_window_height + 1 to host.printedLines
|
|
2035
|
+
visibleLowerBound = host.printedLines - host_window_height + 1
|
|
2036
|
+
for lineNumToReprint in host.lineNumToReprintSet:
|
|
2037
|
+
# if the line is visible, we will reprint it
|
|
2038
|
+
if visibleLowerBound <= lineNumToReprint <= host.printedLines:
|
|
2039
|
+
if visibleLowerBound <= 0:
|
|
2040
|
+
# this means all lines are visible
|
|
2041
|
+
linePos = lineNumToReprint
|
|
2042
|
+
else:
|
|
2043
|
+
# calculate the position of the line to reprint
|
|
2044
|
+
linePos = lineNumToReprint - visibleLowerBound
|
|
2045
|
+
# Note: color can be incorrect if repainting an old line with new colors already initialized,
|
|
2046
|
+
# Thus we will not use any presistent color pair for old lines
|
|
2047
|
+
cpl = host.current_color_pair if lineNumToReprint == host.printedLines else [-1,-1,1]
|
|
2048
|
+
_curses_add_string_to_window(window=host_window, y=linePos + 1, line=host.output[lineNumToReprint], color_pair_list=cpl,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color)
|
|
2049
|
+
host.lineNumToReprintSet = set()
|
|
2050
|
+
host_window.refresh()
|
|
1949
2051
|
new_configured = False
|
|
1950
2052
|
last_refresh_time = time.perf_counter()
|
|
1951
2053
|
except Exception as e:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|