multiSSH3 5.47__py3-none-any.whl → 5.50__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.

Potentially problematic release.


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

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: multiSSH3
3
- Version: 5.47
3
+ Version: 5.50
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
@@ -9,6 +9,8 @@ License: GPLv3+
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
11
11
  Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
12
14
  Requires-Python: >=3.6
13
15
  Description-Content-Type: text/markdown
14
16
  Requires-Dist: argparse
@@ -0,0 +1,6 @@
1
+ multiSSH3.py,sha256=4XPEcfGf7Gqq0YvVvsAve4x5Afb4Nse_0_nRJTm5T6A,137371
2
+ multiSSH3-5.50.dist-info/METADATA,sha256=63ZfVMNdX55SxVFWL8EogyLiOHWDerdjsVovEbNCBf8,18092
3
+ multiSSH3-5.50.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
4
+ multiSSH3-5.50.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
5
+ multiSSH3-5.50.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
6
+ multiSSH3-5.50.dist-info/RECORD,,
multiSSH3.py CHANGED
@@ -34,6 +34,7 @@ import uuid
34
34
  import tempfile
35
35
  import math
36
36
  from itertools import count
37
+ import queue
37
38
 
38
39
  try:
39
40
  # Check if functiools.cache is available
@@ -46,8 +47,9 @@ except AttributeError:
46
47
  # If neither is available, use a dummy decorator
47
48
  def cache_decorator(func):
48
49
  return func
49
- version = '5.47'
50
+ version = '5.50'
50
51
  VERSION = version
52
+ __version__ = version
51
53
  COMMIT_DATE = '2025-01-30'
52
54
 
53
55
  CONFIG_FILE_CHAIN = ['./multiSSH3.config.json',
@@ -90,31 +92,68 @@ def signal_handler(sig, frame):
90
92
  os.system(f'pkill -ef {os.path.basename(__file__)}')
91
93
  sys.exit(0)
92
94
 
95
+ # def input_with_timeout_and_countdown(timeout, prompt='Please enter your selection'):
96
+ # """
97
+ # Read an input from the user with a timeout and a countdown.
98
+
99
+ # Parameters:
100
+ # timeout (int): The timeout value in seconds.
101
+ # prompt (str): The prompt message to display to the user. Default is 'Please enter your selection'.
102
+
103
+ # Returns:
104
+ # str or None: The user input if received within the timeout, or None if no input is received.
105
+ # """
106
+ # import select
107
+ # # Print the initial prompt with the countdown
108
+ # eprint(f"{prompt} [{timeout}s]: ", end='', flush=True)
109
+ # # Loop until the timeout
110
+ # for remaining in range(timeout, 0, -1):
111
+ # # If there is an input, return it
112
+ # # this only works on linux
113
+ # if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
114
+ # return input().strip()
115
+ # # Print the remaining time
116
+ # eprint(f"\r{prompt} [{remaining}s]: ", end='', flush=True)
117
+ # # Wait a second
118
+ # time.sleep(1)
119
+ # # If there is no input, return None
120
+ # return None
121
+
93
122
  def input_with_timeout_and_countdown(timeout, prompt='Please enter your selection'):
94
123
  """
95
- Read an input from the user with a timeout and a countdown.
96
-
97
- Parameters:
98
- timeout (int): The timeout value in seconds.
99
- prompt (str): The prompt message to display to the user. Default is 'Please enter your selection'.
100
-
101
- Returns:
102
- str or None: The user input if received within the timeout, or None if no input is received.
124
+ Read input from the user with a timeout (cross-platform).
125
+ If the user does not enter any input within `timeout` seconds, return None.
126
+ Otherwise, return the input string.
103
127
  """
104
- import select
105
- # Print the initial prompt with the countdown
128
+ # Queue to receive user input from the background thread
129
+ input_queue = queue.Queue()
130
+ def read_input():
131
+ # Read line from stdin and put it in the queue
132
+ user_input = sys.stdin.readline()
133
+ input_queue.put(user_input)
134
+ # Start a thread that will block on input()
135
+ input_thread = threading.Thread(target=read_input, daemon=True)
136
+ input_thread.start()
137
+ # Print the initial prompt
106
138
  eprint(f"{prompt} [{timeout}s]: ", end='', flush=True)
107
- # Loop until the timeout
108
- for remaining in range(timeout, 0, -1):
109
- # If there is an input, return it
110
- if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
111
- return input().strip()
112
- # Print the remaining time
139
+ # Countdown loop
140
+ start_time = time.time()
141
+ while True:
142
+ # Check if the input thread has finished (i.e., user pressed Enter)
143
+ if not input_queue.empty():
144
+ # We got user input
145
+ user_input = input_queue.get().strip()
146
+ eprint() # move to the next line
147
+ return user_input
148
+ elapsed = int(time.time() - start_time)
149
+ remaining = timeout - elapsed
150
+ if remaining <= 0:
151
+ # Time is up, no input
152
+ eprint() # move to the next line
153
+ return None
154
+ # Update prompt countdown
113
155
  eprint(f"\r{prompt} [{remaining}s]: ", end='', flush=True)
114
- # Wait a second
115
156
  time.sleep(1)
116
- # If there is no input, return None
117
- return None
118
157
 
119
158
  @cache_decorator
120
159
  def getIP(hostname: str,local=False):
@@ -2137,18 +2176,7 @@ def curses_print(stdscr, hosts, threads, min_char_len = DEFAULT_CURSES_MINIMUM_C
2137
2176
  #time.sleep(0.25)
2138
2177
 
2139
2178
  # ------------ Generate Output Block ----------------
2140
- def print_output(hosts,usejson = False,quiet = False,greppable = False):
2141
- '''
2142
- Print / generate the output of the hosts to the terminal
2143
-
2144
- Args:
2145
- hosts (list): A list of Host objects
2146
- usejson (bool, optional): Whether to print the output in JSON format. Defaults to False.
2147
- quiet (bool, optional): Whether to print the output. Defaults to False.
2148
-
2149
- Returns:
2150
- str: The pretty output generated
2151
- '''
2179
+ def generate_output(hosts, usejson = False, greppable = False):
2152
2180
  global __keyPressesIn
2153
2181
  global __global_suppress_printout
2154
2182
  hosts = [dict(host) for host in hosts]
@@ -2213,6 +2241,21 @@ def print_output(hosts,usejson = False,quiet = False,greppable = False):
2213
2241
  __keyPressesIn = [[]]
2214
2242
  if __global_suppress_printout and not outputs:
2215
2243
  rtnStr += 'Success'
2244
+ return rtnStr
2245
+
2246
+ def print_output(hosts,usejson = False,quiet = False,greppable = False):
2247
+ '''
2248
+ Print / generate the output of the hosts to the terminal
2249
+
2250
+ Args:
2251
+ hosts (list): A list of Host objects
2252
+ usejson (bool, optional): Whether to print the output in JSON format. Defaults to False.
2253
+ quiet (bool, optional): Whether to print the output. Defaults to False.
2254
+
2255
+ Returns:
2256
+ str: The pretty output generated
2257
+ '''
2258
+ rtnStr = generate_output(hosts,usejson,greppable)
2216
2259
  if not quiet:
2217
2260
  print(rtnStr)
2218
2261
  return rtnStr
@@ -2410,6 +2453,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
2410
2453
  global __DEBUG_MODE
2411
2454
  global __thread_start_delay
2412
2455
  global __max_connections_nofile_limit_supported
2456
+ global __keyPressesIn
2413
2457
  _emo = False
2414
2458
  _no_env = no_env
2415
2459
  if os.path.exists(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv')):
@@ -2473,6 +2517,8 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
2473
2517
  unavailableHosts = __globalUnavailableHosts
2474
2518
  else:
2475
2519
  unavailableHosts = set()
2520
+ # set global input to empty
2521
+ __keyPressesIn = [[]]
2476
2522
  else:
2477
2523
  # if run in command line ( or emulating running in command line, we default to skip unreachable hosts within one command call )
2478
2524
  if skipUnreachable:
@@ -1,6 +0,0 @@
1
- multiSSH3.py,sha256=fLoImfnt8GQDs9kLWsySLcFFcB7yXyprRMfntEz_-jk,135786
2
- multiSSH3-5.47.dist-info/METADATA,sha256=b9DlbOVp-wi3Lu7nU5MuVcSk6CFZpAcdBDV02N_e328,18001
3
- multiSSH3-5.47.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
4
- multiSSH3-5.47.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
5
- multiSSH3-5.47.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
6
- multiSSH3-5.47.dist-info/RECORD,,