multiSSH3 5.48__tar.gz → 5.51__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.2
2
2
  Name: multiSSH3
3
- Version: 5.48
3
+ Version: 5.51
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: multiSSH3
3
- Version: 5.48
3
+ Version: 5.51
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
@@ -1,4 +1,11 @@
1
1
  #!/usr/bin/env python3
2
+ # /// script
3
+ # requires-python = ">=3.6"
4
+ # dependencies = [
5
+ # "argparse",
6
+ # "ipaddress",
7
+ # ]
8
+ # ///
2
9
  __curses_available = False
3
10
  __resource_lib_available = False
4
11
  try:
@@ -47,8 +54,9 @@ except AttributeError:
47
54
  # If neither is available, use a dummy decorator
48
55
  def cache_decorator(func):
49
56
  return func
50
- version = '5.48'
57
+ version = '5.51'
51
58
  VERSION = version
59
+ __version__ = version
52
60
  COMMIT_DATE = '2025-01-30'
53
61
 
54
62
  CONFIG_FILE_CHAIN = ['./multiSSH3.config.json',
@@ -1358,7 +1366,7 @@ def run_command(host, sem, timeout=60,passwds=None, retry_limit = 5):
1358
1366
  # Monitor the subprocess and terminate it after the timeout
1359
1367
  host.lastUpdateTime = time.time()
1360
1368
  timeoutLineAppended = False
1361
- sleep_interval = 1.0e-8 # 10 nanoseconds
1369
+ sleep_interval = 1.0e-7 # 100 nanoseconds
1362
1370
  while proc.poll() is None: # while the process is still running
1363
1371
  if timeout > 0:
1364
1372
  if time.time() - host.lastUpdateTime > timeout:
@@ -2175,18 +2183,7 @@ def curses_print(stdscr, hosts, threads, min_char_len = DEFAULT_CURSES_MINIMUM_C
2175
2183
  #time.sleep(0.25)
2176
2184
 
2177
2185
  # ------------ Generate Output Block ----------------
2178
- def print_output(hosts,usejson = False,quiet = False,greppable = False):
2179
- '''
2180
- Print / generate the output of the hosts to the terminal
2181
-
2182
- Args:
2183
- hosts (list): A list of Host objects
2184
- usejson (bool, optional): Whether to print the output in JSON format. Defaults to False.
2185
- quiet (bool, optional): Whether to print the output. Defaults to False.
2186
-
2187
- Returns:
2188
- str: The pretty output generated
2189
- '''
2186
+ def generate_output(hosts, usejson = False, greppable = False):
2190
2187
  global __keyPressesIn
2191
2188
  global __global_suppress_printout
2192
2189
  hosts = [dict(host) for host in hosts]
@@ -2251,6 +2248,21 @@ def print_output(hosts,usejson = False,quiet = False,greppable = False):
2251
2248
  __keyPressesIn = [[]]
2252
2249
  if __global_suppress_printout and not outputs:
2253
2250
  rtnStr += 'Success'
2251
+ return rtnStr
2252
+
2253
+ def print_output(hosts,usejson = False,quiet = False,greppable = False):
2254
+ '''
2255
+ Print / generate the output of the hosts to the terminal
2256
+
2257
+ Args:
2258
+ hosts (list): A list of Host objects
2259
+ usejson (bool, optional): Whether to print the output in JSON format. Defaults to False.
2260
+ quiet (bool, optional): Whether to print the output. Defaults to False.
2261
+
2262
+ Returns:
2263
+ str: The pretty output generated
2264
+ '''
2265
+ rtnStr = generate_output(hosts,usejson,greppable)
2254
2266
  if not quiet:
2255
2267
  print(rtnStr)
2256
2268
  return rtnStr
@@ -2259,13 +2271,27 @@ def print_output(hosts,usejson = False,quiet = False,greppable = False):
2259
2271
  def processRunOnHosts(timeout, password, max_connections, hosts, returnUnfinished, nowatch, json, called, greppable,unavailableHosts,willUpdateUnreachableHosts,curses_min_char_len = DEFAULT_CURSES_MINIMUM_CHAR_LEN, curses_min_line_len = DEFAULT_CURSES_MINIMUM_LINE_LEN,single_window = DEFAULT_SINGLE_WINDOW):
2260
2272
  global __globalUnavailableHosts
2261
2273
  global _no_env
2274
+ sleep_interval = 1.0e-7 # 0.1 microseconds
2262
2275
  threads = start_run_on_hosts(hosts, timeout=timeout,password=password,max_connections=max_connections)
2263
- if __curses_available and not nowatch and threads and not returnUnfinished and any([thread.is_alive() for thread in threads]) and sys.stdout.isatty() and os.get_terminal_size() and os.get_terminal_size().columns > 10:
2264
- curses.wrapper(curses_print, hosts, threads, min_char_len = curses_min_char_len, min_line_len = curses_min_line_len, single_window = single_window)
2276
+ if __curses_available and not nowatch and threads and not returnUnfinished and sys.stdout.isatty() and os.get_terminal_size() and os.get_terminal_size().columns > 10:
2277
+ total_sleeped = 0
2278
+ while any([host.returncode is None for host in hosts]):
2279
+ time.sleep(sleep_interval) # avoid busy-waiting
2280
+ total_sleeped += sleep_interval
2281
+ if sleep_interval < 0.001:
2282
+ sleep_interval *= 2
2283
+ elif sleep_interval < 0.01:
2284
+ sleep_interval *= 1.1
2285
+ if total_sleeped > 0.1:
2286
+ break
2287
+ if any([host.returncode is None for host in hosts]):
2288
+ curses.wrapper(curses_print, hosts, threads, min_char_len = curses_min_char_len, min_line_len = curses_min_line_len, single_window = single_window)
2265
2289
  if not returnUnfinished:
2266
2290
  # wait until all hosts have a return code
2267
2291
  while any([host.returncode is None for host in hosts]):
2268
- time.sleep(0.01)
2292
+ time.sleep(sleep_interval) # avoid busy-waiting
2293
+ if sleep_interval < 0.01:
2294
+ sleep_interval *= 1.1
2269
2295
  for thread in threads:
2270
2296
  thread.join(timeout=3)
2271
2297
  # update the unavailable hosts and global unavailable hosts
@@ -2448,6 +2474,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
2448
2474
  global __DEBUG_MODE
2449
2475
  global __thread_start_delay
2450
2476
  global __max_connections_nofile_limit_supported
2477
+ global __keyPressesIn
2451
2478
  _emo = False
2452
2479
  _no_env = no_env
2453
2480
  if os.path.exists(os.path.join(tempfile.gettempdir(),'__multiSSH3_UNAVAILABLE_HOSTS.csv')):
@@ -2511,6 +2538,8 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
2511
2538
  unavailableHosts = __globalUnavailableHosts
2512
2539
  else:
2513
2540
  unavailableHosts = set()
2541
+ # set global input to empty
2542
+ __keyPressesIn = [[]]
2514
2543
  else:
2515
2544
  # if run in command line ( or emulating running in command line, we default to skip unreachable hosts within one command call )
2516
2545
  if skipUnreachable:
@@ -28,6 +28,8 @@ setup(
28
28
  'Programming Language :: Python :: 3',
29
29
  'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
30
30
  'Operating System :: POSIX :: Linux',
31
+ 'Operating System :: MacOS',
32
+ 'Operating System :: Microsoft :: Windows',
31
33
  ],
32
34
  python_requires='>=3.6',
33
35
  license='GPLv3+',
File without changes
File without changes
File without changes
File without changes
File without changes