multiSSH3 5.74__py3-none-any.whl → 5.76__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.
- multiSSH3.py +56 -22
- {multissh3-5.74.dist-info → multissh3-5.76.dist-info}/METADATA +1 -1
- multissh3-5.76.dist-info/RECORD +6 -0
- multissh3-5.74.dist-info/RECORD +0 -6
- {multissh3-5.74.dist-info → multissh3-5.76.dist-info}/WHEEL +0 -0
- {multissh3-5.74.dist-info → multissh3-5.76.dist-info}/entry_points.txt +0 -0
- {multissh3-5.74.dist-info → multissh3-5.76.dist-info}/top_level.txt +0 -0
multiSSH3.py
CHANGED
|
@@ -55,10 +55,10 @@ except AttributeError:
|
|
|
55
55
|
# If neither is available, use a dummy decorator
|
|
56
56
|
def cache_decorator(func):
|
|
57
57
|
return func
|
|
58
|
-
version = '5.
|
|
58
|
+
version = '5.76'
|
|
59
59
|
VERSION = version
|
|
60
60
|
__version__ = version
|
|
61
|
-
COMMIT_DATE = '2025-06-
|
|
61
|
+
COMMIT_DATE = '2025-06-25'
|
|
62
62
|
|
|
63
63
|
CONFIG_FILE_CHAIN = ['./multiSSH3.config.json',
|
|
64
64
|
'~/multiSSH3.config.json',
|
|
@@ -78,6 +78,24 @@ def eprint(*args, **kwargs):
|
|
|
78
78
|
print(f"Error: Cannot print to stderr: {e}")
|
|
79
79
|
print(*args, **kwargs)
|
|
80
80
|
|
|
81
|
+
def _exit_with_code(code, message=None):
|
|
82
|
+
'''
|
|
83
|
+
Exit the program with a specific code and print a message
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
code (int): The exit code
|
|
87
|
+
message (str, optional): The message to print. Defaults to None.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
None
|
|
91
|
+
'''
|
|
92
|
+
global __returnZero
|
|
93
|
+
if message:
|
|
94
|
+
eprint('Exiting: '+ message)
|
|
95
|
+
if __returnZero:
|
|
96
|
+
code = 0
|
|
97
|
+
sys.exit(code)
|
|
98
|
+
|
|
81
99
|
def signal_handler(sig, frame):
|
|
82
100
|
'''
|
|
83
101
|
Handle the Ctrl C signal
|
|
@@ -98,7 +116,7 @@ def signal_handler(sig, frame):
|
|
|
98
116
|
# wait for 0.1 seconds to allow the threads to exit
|
|
99
117
|
time.sleep(0.1)
|
|
100
118
|
os.system(f'pkill -ef {os.path.basename(__file__)}')
|
|
101
|
-
|
|
119
|
+
_exit_with_code(1, 'Exiting immediately due to Ctrl C')
|
|
102
120
|
|
|
103
121
|
# def input_with_timeout_and_countdown(timeout, prompt='Please enter your selection'):
|
|
104
122
|
# """
|
|
@@ -303,6 +321,7 @@ DEFAULT_CURSES_MINIMUM_LINE_LEN = 1
|
|
|
303
321
|
DEFAULT_SINGLE_WINDOW = False
|
|
304
322
|
DEFAULT_ERROR_ONLY = False
|
|
305
323
|
DEFAULT_NO_OUTPUT = False
|
|
324
|
+
DEFAULT_RETURN_ZERO = False
|
|
306
325
|
DEFAULT_NO_ENV = False
|
|
307
326
|
DEFAULT_ENV_FILE = '/etc/profile.d/hosts.sh'
|
|
308
327
|
DEFAULT_NO_HISTORY = False
|
|
@@ -313,6 +332,7 @@ DEFAULT_PRINT_SUCCESS_HOSTS = False
|
|
|
313
332
|
DEFAULT_GREPPABLE_MODE = False
|
|
314
333
|
DEFAULT_SKIP_UNREACHABLE = True
|
|
315
334
|
DEFAULT_SKIP_HOSTS = ''
|
|
335
|
+
DEFAULT_ENCODING = 'utf-8'
|
|
316
336
|
SSH_STRICT_HOST_KEY_CHECKING = False
|
|
317
337
|
ERROR_MESSAGES_TO_IGNORE = [
|
|
318
338
|
'Pseudo-terminal will not be allocated because stdin is not a terminal',
|
|
@@ -360,6 +380,8 @@ __curses_color_table = {}
|
|
|
360
380
|
__curses_current_color_index = 10
|
|
361
381
|
__max_connections_nofile_limit_supported = 0
|
|
362
382
|
__thread_start_delay = 0
|
|
383
|
+
_encoding = DEFAULT_ENCODING
|
|
384
|
+
__returnZero = DEFAULT_RETURN_ZERO
|
|
363
385
|
if __resource_lib_available:
|
|
364
386
|
# Get the current limits
|
|
365
387
|
_, __system_nofile_limit = resource.getrlimit(resource.RLIMIT_NOFILE)
|
|
@@ -1123,11 +1145,12 @@ def __handle_reading_stream(stream,target, host):
|
|
|
1123
1145
|
Returns:
|
|
1124
1146
|
None
|
|
1125
1147
|
'''
|
|
1148
|
+
global _encoding
|
|
1126
1149
|
def add_line(current_line,target, host, keepLastLine=True):
|
|
1127
1150
|
if not keepLastLine:
|
|
1128
1151
|
target.pop()
|
|
1129
1152
|
host.output.pop()
|
|
1130
|
-
current_line_str = current_line.decode(
|
|
1153
|
+
current_line_str = current_line.decode(_encoding,errors='backslashreplace')
|
|
1131
1154
|
target.append(current_line_str)
|
|
1132
1155
|
host.output.append(current_line_str)
|
|
1133
1156
|
host.lineNumToPrintSet.add(len(host.output)-1)
|
|
@@ -1158,7 +1181,7 @@ def __handle_reading_stream(stream,target, host):
|
|
|
1158
1181
|
current_line.append(char[0])
|
|
1159
1182
|
else:
|
|
1160
1183
|
# curser is bigger than the length of the line
|
|
1161
|
-
current_line += b' '*(curser_position - len(current_line)) + char
|
|
1184
|
+
current_line += b' '*(curser_position - len(current_line)) + char[0]
|
|
1162
1185
|
curser_position += 1
|
|
1163
1186
|
if time.monotonic() - previousUpdateTime > 0.1:
|
|
1164
1187
|
# if the time since the last update is more than 10ms, we update the output
|
|
@@ -1181,15 +1204,16 @@ def __handle_writing_stream(stream,stop_event,host):
|
|
|
1181
1204
|
None
|
|
1182
1205
|
'''
|
|
1183
1206
|
global __keyPressesIn
|
|
1207
|
+
global _encoding
|
|
1184
1208
|
# __keyPressesIn is a list of lists.
|
|
1185
1209
|
# Each list is a list of characters to be sent to the stdin of the process at once.
|
|
1186
1210
|
# We do not send the last line as it may be incomplete.
|
|
1187
1211
|
sentInput = 0
|
|
1188
1212
|
while not stop_event.is_set():
|
|
1189
1213
|
if sentInput < len(__keyPressesIn) - 1 :
|
|
1190
|
-
stream.write(''.join(__keyPressesIn[sentInput]).encode())
|
|
1214
|
+
stream.write(''.join(__keyPressesIn[sentInput]).encode(encoding=_encoding,errors='backslashreplace'))
|
|
1191
1215
|
stream.flush()
|
|
1192
|
-
line = '> ' + ''.join(__keyPressesIn[sentInput]).encode().decode().replace('\n', '↵')
|
|
1216
|
+
line = '> ' + ''.join(__keyPressesIn[sentInput]).encode(encoding=_encoding,errors='backslashreplace').decode(encoding=_encoding,errors='backslashreplace').replace('\n', '↵')
|
|
1193
1217
|
host.output.append(line)
|
|
1194
1218
|
host.stdout.append(line)
|
|
1195
1219
|
host.lineNumToPrintSet.add(len(host.output)-1)
|
|
@@ -1866,6 +1890,7 @@ def _get_hosts_to_display (hosts, max_num_hosts, hosts_to_display = None, indexO
|
|
|
1866
1890
|
return new_hosts_to_display , {'running':len(running_hosts), 'failed':len(failed_hosts), 'finished':len(finished_hosts), 'waiting':len(waiting_hosts)}, rearrangedHosts
|
|
1867
1891
|
|
|
1868
1892
|
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'):
|
|
1893
|
+
global _encoding
|
|
1869
1894
|
_ = config_reason
|
|
1870
1895
|
try:
|
|
1871
1896
|
box_ansi_color = None
|
|
@@ -2101,7 +2126,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
2101
2126
|
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} "
|
|
2102
2127
|
else:
|
|
2103
2128
|
# we use the stat bar to display the key presses
|
|
2104
|
-
encodedLine = ''.join(__keyPressesIn[lineToDisplay]).encode().decode().strip('\n') + ' '
|
|
2129
|
+
encodedLine = ''.join(__keyPressesIn[lineToDisplay]).encode(encoding=_encoding,errors='backslashreplace').decode(encoding=_encoding,errors='backslashreplace').strip('\n') + ' '
|
|
2105
2130
|
#stats = '┍'+ f"Send CMD: {encodedLine}"[:max_x - 2].center(max_x - 2, "━")
|
|
2106
2131
|
# format the stats line with chracter at curser position inverted using ansi escape sequence
|
|
2107
2132
|
# displayCurserPosition is needed as the curserPosition can be larger than the length of the encodedLine. This is wanted to keep scrolling through the history less painful
|
|
@@ -2253,6 +2278,7 @@ def curses_print(stdscr, hosts, threads, min_char_len = DEFAULT_CURSES_MINIMUM_C
|
|
|
2253
2278
|
def generate_output(hosts, usejson = False, greppable = False):
|
|
2254
2279
|
global __keyPressesIn
|
|
2255
2280
|
global __global_suppress_printout
|
|
2281
|
+
global __encoding
|
|
2256
2282
|
if __global_suppress_printout:
|
|
2257
2283
|
# remove hosts with returncode 0
|
|
2258
2284
|
hosts = [dict(host) for host in hosts if host.returncode != 0]
|
|
@@ -2286,7 +2312,7 @@ def generate_output(hosts, usejson = False, greppable = False):
|
|
|
2286
2312
|
rtnStr += pretty_format_table(rtnList)
|
|
2287
2313
|
rtnStr += '*'*80+'\n'
|
|
2288
2314
|
if __keyPressesIn[-1]:
|
|
2289
|
-
CMDsOut = [''.join(cmd).encode('
|
|
2315
|
+
CMDsOut = [''.join(cmd).encode(encoding=_encoding,errors='backslashreplace').decode(encoding=_encoding,errors='backslashreplace').replace('\\n', '↵') for cmd in __keyPressesIn if cmd]
|
|
2290
2316
|
rtnStr += 'User Inputs: '+ '\nUser Inputs: '.join(CMDsOut)
|
|
2291
2317
|
#rtnStr += '\n'
|
|
2292
2318
|
else:
|
|
@@ -2317,7 +2343,7 @@ def generate_output(hosts, usejson = False, greppable = False):
|
|
|
2317
2343
|
if not __global_suppress_printout or outputs:
|
|
2318
2344
|
rtnStr += '*'*80+'\n'
|
|
2319
2345
|
if __keyPressesIn[-1]:
|
|
2320
|
-
CMDsOut = [''.join(cmd).encode('
|
|
2346
|
+
CMDsOut = [''.join(cmd).encode(encoding=_encoding,errors='backslashreplace').decode(encoding=_encoding,errors='backslashreplace').replace('\\n', '↵') for cmd in __keyPressesIn if cmd]
|
|
2321
2347
|
#rtnStr += f"Key presses: {''.join(__keyPressesIn).encode('unicode_escape').decode()}\n"
|
|
2322
2348
|
#rtnStr += f"Key presses: {__keyPressesIn}\n"
|
|
2323
2349
|
rtnStr += "User Inputs: \n "
|
|
@@ -2755,7 +2781,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
2755
2781
|
else:
|
|
2756
2782
|
eprint(f"Warning: ssh-copy-id not found in {_binPaths} , skipping copy id to the hosts")
|
|
2757
2783
|
if not commands:
|
|
2758
|
-
|
|
2784
|
+
_exit_with_code(0, "Copy id finished, no commands to run")
|
|
2759
2785
|
if files and not commands:
|
|
2760
2786
|
# if files are specified but not target dir, we default to file sync mode
|
|
2761
2787
|
file_sync = True
|
|
@@ -2772,8 +2798,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
2772
2798
|
except:
|
|
2773
2799
|
pathSet.update(glob.glob(file,recursive=True))
|
|
2774
2800
|
if not pathSet:
|
|
2775
|
-
|
|
2776
|
-
sys.exit(66)
|
|
2801
|
+
_exit_with_code(66, f'No source files at {files!r} are found after resolving globs!')
|
|
2777
2802
|
else:
|
|
2778
2803
|
pathSet = set(files)
|
|
2779
2804
|
if file_sync:
|
|
@@ -2790,7 +2815,7 @@ def run_command_on_hosts(hosts = DEFAULT_HOSTS,commands = None,oneonone = DEFAUL
|
|
|
2790
2815
|
eprint("Error: the number of commands must be the same as the number of hosts")
|
|
2791
2816
|
eprint(f"Number of commands: {len(commands)}")
|
|
2792
2817
|
eprint(f"Number of hosts: {len(set(targetHostDic) - set(skipHostSet))}")
|
|
2793
|
-
|
|
2818
|
+
_exit_with_code(255, "Number of commands and hosts do not match")
|
|
2794
2819
|
if not __global_suppress_printout:
|
|
2795
2820
|
eprint('-'*80)
|
|
2796
2821
|
eprint("Running in one on one mode")
|
|
@@ -2905,6 +2930,7 @@ def generate_default_config(args):
|
|
|
2905
2930
|
'DEFAULT_SINGLE_WINDOW': args.single_window,
|
|
2906
2931
|
'DEFAULT_ERROR_ONLY': args.error_only,
|
|
2907
2932
|
'DEFAULT_NO_OUTPUT': args.no_output,
|
|
2933
|
+
'DEFAULT_RETURN_ZERO': args.return_zero,
|
|
2908
2934
|
'DEFAULT_NO_ENV': args.no_env,
|
|
2909
2935
|
'DEFAULT_ENV_FILE': args.env_file,
|
|
2910
2936
|
'DEFAULT_NO_HISTORY': args.no_history,
|
|
@@ -2915,6 +2941,7 @@ def generate_default_config(args):
|
|
|
2915
2941
|
'DEFAULT_GREPPABLE_MODE': args.greppable,
|
|
2916
2942
|
'DEFAULT_SKIP_UNREACHABLE': args.skip_unreachable,
|
|
2917
2943
|
'DEFAULT_SKIP_HOSTS': args.skip_hosts,
|
|
2944
|
+
'DEFAULT_ENCODING': args.encoding,
|
|
2918
2945
|
'SSH_STRICT_HOST_KEY_CHECKING': SSH_STRICT_HOST_KEY_CHECKING,
|
|
2919
2946
|
'ERROR_MESSAGES_TO_IGNORE': ERROR_MESSAGES_TO_IGNORE,
|
|
2920
2947
|
}
|
|
@@ -2938,8 +2965,7 @@ def write_default_config(args,CONFIG_FILE = None):
|
|
|
2938
2965
|
elif inStr.lower().strip().startswith('o'):
|
|
2939
2966
|
backup = False
|
|
2940
2967
|
else:
|
|
2941
|
-
|
|
2942
|
-
sys.exit(1)
|
|
2968
|
+
_exit_with_code(0, "Aborted by user, no config file written")
|
|
2943
2969
|
try:
|
|
2944
2970
|
if backup and os.path.exists(CONFIG_FILE):
|
|
2945
2971
|
os.rename(CONFIG_FILE,CONFIG_FILE+'.bak')
|
|
@@ -2948,8 +2974,7 @@ def write_default_config(args,CONFIG_FILE = None):
|
|
|
2948
2974
|
eprint(f"Do you want to continue writing the new config file to {CONFIG_FILE!r}? (y/n)")
|
|
2949
2975
|
inStr = input_with_timeout_and_countdown(10)
|
|
2950
2976
|
if not inStr or not inStr.lower().strip().startswith('y'):
|
|
2951
|
-
|
|
2952
|
-
sys.exit(1)
|
|
2977
|
+
_exit_with_code(0, "Aborted by user, no config file written")
|
|
2953
2978
|
try:
|
|
2954
2979
|
with open(CONFIG_FILE,'w') as f:
|
|
2955
2980
|
json.dump(__configs_from_file,f,indent=4)
|
|
@@ -2970,6 +2995,8 @@ def main():
|
|
|
2970
2995
|
global _env_file
|
|
2971
2996
|
global __DEBUG_MODE
|
|
2972
2997
|
global __configs_from_file
|
|
2998
|
+
global _encoding
|
|
2999
|
+
global __returnZero
|
|
2973
3000
|
_emo = False
|
|
2974
3001
|
# We handle the signal
|
|
2975
3002
|
signal.signal(signal.SIGINT, signal_handler)
|
|
@@ -3002,6 +3029,7 @@ def main():
|
|
|
3002
3029
|
parser.add_argument('-B','-sw','--single_window', action='store_true', help=f'Use a single window for all hosts. (default: {DEFAULT_SINGLE_WINDOW})', default=DEFAULT_SINGLE_WINDOW)
|
|
3003
3030
|
parser.add_argument('-R','-eo','--error_only', action='store_true', help=f'Only print the error output. (default: {DEFAULT_ERROR_ONLY})', default=DEFAULT_ERROR_ONLY)
|
|
3004
3031
|
parser.add_argument('-Q',"-no","--no_output", action='store_true', help=f"Do not print the output. (default: {DEFAULT_NO_OUTPUT})", default=DEFAULT_NO_OUTPUT)
|
|
3032
|
+
parser.add_argument('-Z','-rz','--return_zero', action='store_true', help=f"Return 0 even if there are errors. (default: {DEFAULT_RETURN_ZERO})", default=DEFAULT_RETURN_ZERO)
|
|
3005
3033
|
parser.add_argument('-C','--no_env', action='store_true', help=f'Do not load the command line environment variables. (default: {DEFAULT_NO_ENV})', default=DEFAULT_NO_ENV)
|
|
3006
3034
|
parser.add_argument("--env_file", type=str, help=f"The file to load the mssh file based environment variables from. ( Still work with --no_env ) (default: {DEFAULT_ENV_FILE})", default=DEFAULT_ENV_FILE)
|
|
3007
3035
|
parser.add_argument("-m","--max_connections", type=int, help=f"Max number of connections to use (default: 4 * cpu_count)", default=DEFAULT_MAX_CONNECTIONS)
|
|
@@ -3021,6 +3049,7 @@ def main():
|
|
|
3021
3049
|
parser.add_argument('-I','-nh','--no_history', action='store_true', help=f'Do not record the command to history. Default: {DEFAULT_NO_HISTORY}', default=DEFAULT_NO_HISTORY)
|
|
3022
3050
|
parser.add_argument('-hf','--history_file', type=str, help=f'The file to store the history. (default: {DEFAULT_HISTORY_FILE})', default=DEFAULT_HISTORY_FILE)
|
|
3023
3051
|
parser.add_argument('--script', action='store_true', help='Run the command in script mode, short for -SCRIPT or --no_watch --skip_unreachable --no_env --no_history --greppable --error_only')
|
|
3052
|
+
parser.add_argument('-e','--encoding', type=str, help=f'The encoding to use for the output. (default: {DEFAULT_ENCODING})', default=DEFAULT_ENCODING)
|
|
3024
3053
|
parser.add_argument("-V","--version", action='version', version=f'%(prog)s {version} @ {COMMIT_DATE} with [ {", ".join(_binPaths.keys())} ] by {AUTHOR} ({AUTHOR_EMAIL})')
|
|
3025
3054
|
|
|
3026
3055
|
# parser.add_argument('-u', '--user', metavar='user', type=str, nargs=1,
|
|
@@ -3046,7 +3075,10 @@ def main():
|
|
|
3046
3075
|
args.no_history = True
|
|
3047
3076
|
args.greppable = True
|
|
3048
3077
|
args.error_only = True
|
|
3049
|
-
|
|
3078
|
+
|
|
3079
|
+
if args.return_zero:
|
|
3080
|
+
__returnZero = True
|
|
3081
|
+
|
|
3050
3082
|
if args.generate_config_file or args.store_config_file:
|
|
3051
3083
|
if args.store_config_file:
|
|
3052
3084
|
configFileToWriteTo = args.store_config_file
|
|
@@ -3062,7 +3094,7 @@ def main():
|
|
|
3062
3094
|
if configFileToWriteTo:
|
|
3063
3095
|
with open(configFileToWriteTo,'r') as f:
|
|
3064
3096
|
eprint(f"Config file content: \n{f.read()}")
|
|
3065
|
-
|
|
3097
|
+
_exit_with_code(0)
|
|
3066
3098
|
if args.config_file:
|
|
3067
3099
|
if os.path.exists(args.config_file):
|
|
3068
3100
|
__configs_from_file.update(load_config_file(os.path.expanduser(args.config_file)))
|
|
@@ -3085,7 +3117,7 @@ def main():
|
|
|
3085
3117
|
elif inStr.lower().strip().startswith('m'):
|
|
3086
3118
|
eprint(f"\nRunning multiple commands: {', '.join(args.commands)!r} on all hosts")
|
|
3087
3119
|
else:
|
|
3088
|
-
|
|
3120
|
+
_exit_with_code(0, "Aborted by user, no commands to run")
|
|
3089
3121
|
|
|
3090
3122
|
if args.key or args.use_key:
|
|
3091
3123
|
if not args.key:
|
|
@@ -3109,6 +3141,8 @@ def main():
|
|
|
3109
3141
|
# set timeout to the default script timeout if timeout is not set
|
|
3110
3142
|
if args.timeout == DEFAULT_CLI_TIMEOUT:
|
|
3111
3143
|
args.timeout = DEFAULT_TIMEOUT
|
|
3144
|
+
|
|
3145
|
+
_encoding = args.encoding
|
|
3112
3146
|
|
|
3113
3147
|
if not __global_suppress_printout:
|
|
3114
3148
|
cmdStr = getStrCommand(args.hosts,args.commands,
|
|
@@ -3168,7 +3202,7 @@ def main():
|
|
|
3168
3202
|
# os.system(f'pkill -ef {os.path.basename(__file__)}')
|
|
3169
3203
|
# os._exit(mainReturnCode)
|
|
3170
3204
|
|
|
3171
|
-
|
|
3205
|
+
_exit_with_code(__mainReturnCode)
|
|
3172
3206
|
|
|
3173
3207
|
if __name__ == "__main__":
|
|
3174
3208
|
main()
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
multiSSH3.py,sha256=ioUj2vZW1RiOyXRm_yJizPp751_ggMZeVtDrM6Lvvjk,150851
|
|
2
|
+
multissh3-5.76.dist-info/METADATA,sha256=VYwjn_-6fQHyubKyY6M0cuh6DNJaI4hIkDh4RV0qE5k,18093
|
|
3
|
+
multissh3-5.76.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
+
multissh3-5.76.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
5
|
+
multissh3-5.76.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
6
|
+
multissh3-5.76.dist-info/RECORD,,
|
multissh3-5.74.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
multiSSH3.py,sha256=h8FnoCjcZGOjyPypzJ7H4XLOhMIKU2I-kN-m6PGdjJY,149182
|
|
2
|
-
multissh3-5.74.dist-info/METADATA,sha256=ErFNVhzY6qUCJAiI2-paOpyfNiLJKJayjXCYiQRHJPg,18093
|
|
3
|
-
multissh3-5.74.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
-
multissh3-5.74.dist-info/entry_points.txt,sha256=xi2rWWNfmHx6gS8Mmx0rZL2KZz6XWBYP3DWBpWAnnZ0,143
|
|
5
|
-
multissh3-5.74.dist-info/top_level.txt,sha256=tUwttxlnpLkZorSsroIprNo41lYSxjd2ASuL8-EJIJw,10
|
|
6
|
-
multissh3-5.74.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|