sshreader 6.2.0__tar.gz → 6.3.0__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.
- {sshreader-6.2.0 → sshreader-6.3.0}/PKG-INFO +9 -7
- {sshreader-6.2.0 → sshreader-6.3.0}/README.md +7 -6
- {sshreader-6.2.0 → sshreader-6.3.0}/pyproject.toml +4 -1
- {sshreader-6.2.0 → sshreader-6.3.0}/sshreader/__init__.py +1 -1
- {sshreader-6.2.0 → sshreader-6.3.0}/sshreader/customtypes.py +1 -1
- {sshreader-6.2.0 → sshreader-6.3.0}/sshreader/scripts/__init__.py +1 -1
- {sshreader-6.2.0 → sshreader-6.3.0}/sshreader/scripts/pydsh.py +6 -5
- {sshreader-6.2.0 → sshreader-6.3.0}/sshreader/ssh.py +4 -4
- {sshreader-6.2.0 → sshreader-6.3.0}/sshreader/utils.py +8 -8
- {sshreader-6.2.0 → sshreader-6.3.0}/LICENSE +0 -0
- {sshreader-6.2.0 → sshreader-6.3.0}/LICENSE.lesser +0 -0
- {sshreader-6.2.0 → sshreader-6.3.0}/tests/keys/id_rsa +0 -0
- {sshreader-6.2.0 → sshreader-6.3.0}/tests/keys/id_rsa.pub +0 -0
- {sshreader-6.2.0 → sshreader-6.3.0}/tests/test_pydsh.py +0 -0
- {sshreader-6.2.0 → sshreader-6.3.0}/tests/test_sshreader.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sshreader
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.3.0
|
|
4
4
|
Summary: Multi-threading/processing wrapper for Paramiko
|
|
5
5
|
Author-Email: Jesse Almanrode <jesse@almanrode.com>
|
|
6
6
|
License: LGPL-3.0-or-later
|
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
18
|
Classifier: Topic :: Software Development :: Libraries
|
|
18
19
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
20
|
Project-URL: homepage, http://sshreader.readthedocs.io/
|
|
@@ -28,6 +29,8 @@ Description-Content-Type: text/markdown
|
|
|
28
29
|
|
|
29
30
|

|
|
30
31
|
|
|
32
|
+
[](https://github.com/isaiah1112/sshreader/actions/workflows/lint_and_test.yml)
|
|
33
|
+
|
|
31
34
|
A Python Module for multiprocessing/threading ssh connections in order to make ssh operations
|
|
32
35
|
across multiple servers parallel. It utilizes the [Paramiko](http://www.paramiko.org/) module
|
|
33
36
|
for its ssh client.
|
|
@@ -62,14 +65,13 @@ The documentation for [SSHreader][] can be found at [ReadTheDocs.](https://sshre
|
|
|
62
65
|
|
|
63
66
|
Comments and enhancements are very welcome.
|
|
64
67
|
|
|
65
|
-
Report any issues or feature requests on the [
|
|
66
|
-
|
|
67
|
-
not-working example which reproduces the bug and, if appropriate, the
|
|
68
|
+
Report any issues or feature requests on the [GitHub Issue Tracker](https://github.com/isaiah1112/sshreader/issues).
|
|
69
|
+
Please include a minimal not-working example which reproduces the bug and, if appropriate, the
|
|
68
70
|
traceback information. Please do not request features already being worked
|
|
69
71
|
towards.
|
|
70
72
|
|
|
71
|
-
Code contributions are encouraged: please feel free to [fork the
|
|
72
|
-
|
|
73
|
+
Code contributions are encouraged: please feel free to [fork the project](https://github.com/isaiah1112/sshreader)
|
|
74
|
+
and submit pull requests to the develop branch.
|
|
73
75
|
|
|
74
76
|
## Extras
|
|
75
77
|
|
|
@@ -83,4 +85,4 @@ to process.
|
|
|
83
85
|
|
|
84
86
|
[GNU Lesser General Public License v3.0]: http://choosealicense.com/licenses/lgpl-3.0/ "LGPL v3"
|
|
85
87
|
|
|
86
|
-
[sshreader]: https://
|
|
88
|
+
[sshreader]: https://github.com/isaiah1112/sshreader "SSHreader Package"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
+
[](https://github.com/isaiah1112/sshreader/actions/workflows/lint_and_test.yml)
|
|
4
|
+
|
|
3
5
|
A Python Module for multiprocessing/threading ssh connections in order to make ssh operations
|
|
4
6
|
across multiple servers parallel. It utilizes the [Paramiko](http://www.paramiko.org/) module
|
|
5
7
|
for its ssh client.
|
|
@@ -34,14 +36,13 @@ The documentation for [SSHreader][] can be found at [ReadTheDocs.](https://sshre
|
|
|
34
36
|
|
|
35
37
|
Comments and enhancements are very welcome.
|
|
36
38
|
|
|
37
|
-
Report any issues or feature requests on the [
|
|
38
|
-
|
|
39
|
-
not-working example which reproduces the bug and, if appropriate, the
|
|
39
|
+
Report any issues or feature requests on the [GitHub Issue Tracker](https://github.com/isaiah1112/sshreader/issues).
|
|
40
|
+
Please include a minimal not-working example which reproduces the bug and, if appropriate, the
|
|
40
41
|
traceback information. Please do not request features already being worked
|
|
41
42
|
towards.
|
|
42
43
|
|
|
43
|
-
Code contributions are encouraged: please feel free to [fork the
|
|
44
|
-
|
|
44
|
+
Code contributions are encouraged: please feel free to [fork the project](https://github.com/isaiah1112/sshreader)
|
|
45
|
+
and submit pull requests to the develop branch.
|
|
45
46
|
|
|
46
47
|
## Extras
|
|
47
48
|
|
|
@@ -55,4 +56,4 @@ to process.
|
|
|
55
56
|
|
|
56
57
|
[GNU Lesser General Public License v3.0]: http://choosealicense.com/licenses/lgpl-3.0/ "LGPL v3"
|
|
57
58
|
|
|
58
|
-
[sshreader]: https://
|
|
59
|
+
[sshreader]: https://github.com/isaiah1112/sshreader "SSHreader Package"
|
|
@@ -11,7 +11,7 @@ dependencies = [
|
|
|
11
11
|
"python-hostlist<2,>=1",
|
|
12
12
|
]
|
|
13
13
|
name = "sshreader"
|
|
14
|
-
version = "6.
|
|
14
|
+
version = "6.3.0"
|
|
15
15
|
description = "Multi-threading/processing wrapper for Paramiko"
|
|
16
16
|
readme = "README.md"
|
|
17
17
|
classifiers = [
|
|
@@ -25,6 +25,7 @@ classifiers = [
|
|
|
25
25
|
"Programming Language :: Python :: 3.11",
|
|
26
26
|
"Programming Language :: Python :: 3.12",
|
|
27
27
|
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Programming Language :: Python :: 3.14",
|
|
28
29
|
"Topic :: Software Development :: Libraries",
|
|
29
30
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
30
31
|
]
|
|
@@ -76,7 +77,9 @@ build-backend = "pdm.backend"
|
|
|
76
77
|
test = [
|
|
77
78
|
"coverage<8,>=7",
|
|
78
79
|
"discover<1.0,>=0.4",
|
|
80
|
+
"pytest>=8.4.2",
|
|
79
81
|
"ruff<1.0.0,>=0.9.9",
|
|
82
|
+
"ty>=0.0.5",
|
|
80
83
|
]
|
|
81
84
|
docs = [
|
|
82
85
|
"sphinx<8,>=7",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""A Python Package for parallelizing ssh connections via threading and multiprocessing.
|
|
2
2
|
"""
|
|
3
|
-
# Copyright (C) 2015-
|
|
3
|
+
# Copyright (C) 2015-2026 Jesse Almanrode
|
|
4
4
|
#
|
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
|
6
6
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
""" Typing module for sshreader
|
|
2
2
|
"""
|
|
3
|
-
# Copyright (C) 2015-
|
|
3
|
+
# Copyright (C) 2015-2026 Jesse Almanrode
|
|
4
4
|
#
|
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
|
6
6
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
""" Scripts portion of sshreader package
|
|
2
2
|
"""
|
|
3
|
-
# Copyright (C) 2015-
|
|
3
|
+
# Copyright (C) 2015-2026 Jesse Almanrode
|
|
4
4
|
#
|
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
|
6
6
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
""" A Pythonic implementation of pdsh powered by sshreader
|
|
2
2
|
"""
|
|
3
|
-
# Copyright (C) 2015-
|
|
3
|
+
# Copyright (C) 2015-2026 Jesse Almanrode
|
|
4
4
|
#
|
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
|
6
6
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
@@ -162,10 +162,11 @@ def cli(**kwargs):
|
|
|
162
162
|
if kwargs['file']:
|
|
163
163
|
mkpath = click.Path(exists=True, dir_okay=False)
|
|
164
164
|
script_path = mkpath(kwargs['cmd'])
|
|
165
|
-
|
|
165
|
+
# Ensure script_path is a str for os.path functions (type-checker safe)
|
|
166
|
+
script_name = os.path.basename(str(script_path))
|
|
166
167
|
log.info('Creating copy_script prehook for: ' + script_name)
|
|
167
168
|
prehook = sshreader.Hook(copy_script, args=[script_path], ssh_established=True)
|
|
168
|
-
with open(script_path) as s:
|
|
169
|
+
with open(str(script_path)) as s:
|
|
169
170
|
script = s.readline()
|
|
170
171
|
if script.startswith('#!') is False:
|
|
171
172
|
raise click.UsageError('Script must start with #!')
|
|
@@ -196,7 +197,7 @@ def cli(**kwargs):
|
|
|
196
197
|
kwargs['keyfile'] = sshenv.dsa_key
|
|
197
198
|
log.info('Using DSA private key file')
|
|
198
199
|
else:
|
|
199
|
-
if len(sshenv.agent_keys) == 0:
|
|
200
|
+
if sshenv.agent_keys is None or len(sshenv.agent_keys) == 0:
|
|
200
201
|
if not all((kwargs['username'], kwargs['password'])):
|
|
201
202
|
raise click.ClickException('Unable to find ssh key to use and password not supplied.')
|
|
202
203
|
else:
|
|
@@ -214,7 +215,7 @@ def cli(**kwargs):
|
|
|
214
215
|
# If you specify a password or prompt for one it overrides the ssh key
|
|
215
216
|
if not kwargs['password']:
|
|
216
217
|
if not kwargs['prompt']:
|
|
217
|
-
if not kwargs['keyfile'] and len(sshenv.agent_keys) == 0:
|
|
218
|
+
if not kwargs['keyfile'] and (sshenv.agent_keys is None or len(sshenv.agent_keys) == 0):
|
|
218
219
|
raise click.ClickException('Unable to find ssh key to use and password not supplied or prompt enabled.')
|
|
219
220
|
else:
|
|
220
221
|
log.info('Prompting for password and disabling discovered SSH keyfiles')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""A wrapper for Paramiko that attempts to make ssh sessions easier to work with.
|
|
2
2
|
"""
|
|
3
|
-
# Copyright (C) 2015-
|
|
3
|
+
# Copyright (C) 2015-2026 Jesse Almanrode
|
|
4
4
|
#
|
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
|
6
6
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
@@ -173,7 +173,7 @@ class SSH:
|
|
|
173
173
|
else:
|
|
174
174
|
result = Command(cmd=command, stdout=stdout.read().strip(), stderr=None,
|
|
175
175
|
return_code=stdout.channel.recv_exit_status())
|
|
176
|
-
except (paramiko.buffered_pipe.PipeTimeout, socket.timeout):
|
|
176
|
+
except (paramiko.buffered_pipe.PipeTimeout, socket.timeout): # ty:ignore[possibly-missing-submodule]
|
|
177
177
|
result = Command(cmd=command, stdout='command timed out', stderr=None, return_code=124)
|
|
178
178
|
else:
|
|
179
179
|
try:
|
|
@@ -184,7 +184,7 @@ class SSH:
|
|
|
184
184
|
else:
|
|
185
185
|
result = Command(cmd=command, stdout=stdout.read().strip(), stderr=stderr.read().strip(),
|
|
186
186
|
return_code=stdout.channel.recv_exit_status())
|
|
187
|
-
except (paramiko.buffered_pipe.PipeTimeout, socket.timeout):
|
|
187
|
+
except (paramiko.buffered_pipe.PipeTimeout, socket.timeout): # ty:ignore[possibly-missing-submodule]
|
|
188
188
|
result = Command(cmd=command, stdout='', stderr='command timed out', return_code=124)
|
|
189
189
|
return result
|
|
190
190
|
|
|
@@ -226,7 +226,7 @@ class SSH:
|
|
|
226
226
|
"""
|
|
227
227
|
if self.__alive():
|
|
228
228
|
raise paramiko.SSHException(f"connection to {self.host} already established")
|
|
229
|
-
paramiko.util.logging.getLogger().setLevel(logging.CRITICAL) # Keeping paramiko from logging errors to stdout
|
|
229
|
+
paramiko.util.logging.getLogger().setLevel(logging.CRITICAL) # Keeping paramiko from logging errors to stdout # ty:ignore[possibly-missing-submodule]
|
|
230
230
|
if not self.keyfile and len(paramiko.Agent().get_keys()) == 0 and not all((self.username, self.password)):
|
|
231
231
|
paramiko.SSHException('username and password or keyfile not provided')
|
|
232
232
|
if self.keyfile:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
""" All the classes and functions that make sshreader tick
|
|
2
2
|
"""
|
|
3
|
-
# Copyright (C) 2015-
|
|
3
|
+
# Copyright (C) 2015-2026 Jesse Almanrode
|
|
4
4
|
#
|
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
|
6
6
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
@@ -175,8 +175,8 @@ class ServerJob:
|
|
|
175
175
|
self.ssh_timeout = timeout[0]
|
|
176
176
|
self.cmd_timeout = timeout[1]
|
|
177
177
|
else:
|
|
178
|
-
self.ssh_timeout = timeout
|
|
179
|
-
self.cmd_timeout = timeout
|
|
178
|
+
self.ssh_timeout = timeout if timeout is not None else 0.5
|
|
179
|
+
self.cmd_timeout = timeout if timeout is not None else 0.5
|
|
180
180
|
if pre_hook:
|
|
181
181
|
if isinstance(pre_hook, Hook):
|
|
182
182
|
self.pre_hook = pre_hook
|
|
@@ -220,8 +220,8 @@ class ServerJob:
|
|
|
220
220
|
log.debug(f'{self.name}: running prehook')
|
|
221
221
|
self.pre_hook.run(self)
|
|
222
222
|
try:
|
|
223
|
-
self._conn = SSH(self.name, username=self.username, password=self.password, keyfile=self.key,
|
|
224
|
-
port=self.ssh_port, connect=False, rsa_sha2=self.rsa_sha2)
|
|
223
|
+
self._conn = SSH(self.name, username=self.username or '', password=self.password or '', keyfile=self.key,
|
|
224
|
+
port=self.ssh_port, connect=False, rsa_sha2=self.rsa_sha2 or True)
|
|
225
225
|
self._conn.connect(timeout=self.ssh_timeout)
|
|
226
226
|
log.debug(f'{self.name}: ssh connection established')
|
|
227
227
|
except Exception as errorMsg:
|
|
@@ -354,7 +354,7 @@ def sshread(serverjobs: list, pcount: Optional[int] = None, tcount: Optional[int
|
|
|
354
354
|
|
|
355
355
|
if pcount is None:
|
|
356
356
|
# Limit the number of threads to spawn
|
|
357
|
-
if tcount == 0:
|
|
357
|
+
if tcount == 0 or tcount is None:
|
|
358
358
|
tcount = int(min(totaljobs, cpu_limit()))
|
|
359
359
|
else:
|
|
360
360
|
tcount = int(min(tcount, totaljobs))
|
|
@@ -399,10 +399,10 @@ def sshread(serverjobs: list, pcount: Optional[int] = None, tcount: Optional[int
|
|
|
399
399
|
log.debug(f'main waiting for {totaljobs} ServerJobs to finish')
|
|
400
400
|
while result_queue.full() is False:
|
|
401
401
|
if progress_bar:
|
|
402
|
-
bar.update(item_counter.value)
|
|
402
|
+
bar.update(item_counter.value) # ty:ignore[unresolved-attribute]
|
|
403
403
|
time.sleep(1)
|
|
404
404
|
if progress_bar:
|
|
405
|
-
bar.finish()
|
|
405
|
+
bar.finish() # ty:ignore[unresolved-attribute]
|
|
406
406
|
|
|
407
407
|
if len(threads) > 0:
|
|
408
408
|
log.info(f'joining {len(threads)} threads')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|