sshreader 6.2.0__tar.gz → 7.0.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-7.0.0/PKG-INFO +131 -0
- sshreader-7.0.0/README.md +103 -0
- {sshreader-6.2.0 → sshreader-7.0.0}/pyproject.toml +19 -14
- {sshreader-6.2.0 → sshreader-7.0.0}/sshreader/__init__.py +1 -1
- {sshreader-6.2.0 → sshreader-7.0.0}/sshreader/customtypes.py +10 -10
- {sshreader-6.2.0 → sshreader-7.0.0}/sshreader/scripts/__init__.py +1 -1
- {sshreader-6.2.0 → sshreader-7.0.0}/sshreader/scripts/pydsh.py +245 -108
- {sshreader-6.2.0 → sshreader-7.0.0}/sshreader/ssh.py +65 -27
- {sshreader-6.2.0 → sshreader-7.0.0}/sshreader/utils.py +184 -104
- sshreader-7.0.0/tests/test_customtypes.py +15 -0
- sshreader-7.0.0/tests/test_pydsh.py +413 -0
- sshreader-7.0.0/tests/test_ssh.py +337 -0
- sshreader-7.0.0/tests/test_utils.py +423 -0
- sshreader-6.2.0/PKG-INFO +0 -86
- sshreader-6.2.0/README.md +0 -58
- sshreader-6.2.0/tests/keys/id_rsa +0 -27
- sshreader-6.2.0/tests/keys/id_rsa.pub +0 -1
- sshreader-6.2.0/tests/test_pydsh.py +0 -83
- sshreader-6.2.0/tests/test_sshreader.py +0 -242
- {sshreader-6.2.0 → sshreader-7.0.0}/LICENSE +0 -0
- {sshreader-6.2.0 → sshreader-7.0.0}/LICENSE.lesser +0 -0
sshreader-7.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: sshreader
|
|
3
|
+
Version: 7.0.0
|
|
4
|
+
Summary: Multi-threading/processing wrapper for Paramiko
|
|
5
|
+
Author-Email: Jesse Almanrode <jesse@almanrode.com>
|
|
6
|
+
License: LGPL-3.0-or-later
|
|
7
|
+
Classifier: Programming Language :: Python
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Project-URL: homepage, https://sshreader.readthedocs.io/
|
|
20
|
+
Project-URL: repository, https://github.com/isaiah1112/sshreader
|
|
21
|
+
Requires-Python: <4.0,>=3.11
|
|
22
|
+
Requires-Dist: click>=8.4.2
|
|
23
|
+
Requires-Dist: colorama>=0.4.6
|
|
24
|
+
Requires-Dist: paramiko>=5.0.0
|
|
25
|
+
Requires-Dist: progressbar2>=4.5.0
|
|
26
|
+
Requires-Dist: python-hostlist>=2.3.0
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
[](https://github.com/isaiah1112/sshreader/actions/workflows/lint_and_test.yml)
|
|
32
|
+
|
|
33
|
+
# SSHreader
|
|
34
|
+
|
|
35
|
+
SSHreader is a Python library for running commands on multiple servers in parallel. It wraps [Paramiko](https://www.paramiko.org/) and provides both a direct SSH client and a job-based interface for parallel SSH and local shell work. It also includes `pydsh`, a command-line tool for running commands across host lists.
|
|
36
|
+
|
|
37
|
+
SSHreader supports Python 3.11 and later.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
Install the latest release from PyPI:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
python -m pip install sshreader
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick start
|
|
48
|
+
|
|
49
|
+
Use `SSH` to run commands in a single SSH session. SSHreader can use an SSH key, an SSH agent, or a password:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from sshreader import SSH
|
|
53
|
+
|
|
54
|
+
with SSH("myhost.example.com", username="jdoe", keyfile="~/.ssh/id_ed25519") as ssh:
|
|
55
|
+
result = ssh.ssh_command("uname -a")
|
|
56
|
+
print(result.stdout)
|
|
57
|
+
print(result.return_code)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Use `ServerJob` and `sshread` to run jobs on multiple hosts. A job can contain multiple commands, which run sequentially on that host:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from sshreader import ServerJob, sshread
|
|
64
|
+
|
|
65
|
+
hosts = ["server1.example.com", "server2.example.com"]
|
|
66
|
+
jobs = [
|
|
67
|
+
ServerJob(host, ["hostname", "uptime"], username="jdoe", keyfile="~/.ssh/id_ed25519")
|
|
68
|
+
for host in hosts
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
finished_jobs = sshread(jobs, tcount=0)
|
|
72
|
+
for job in finished_jobs:
|
|
73
|
+
print(job.name, job.status)
|
|
74
|
+
for result in job.results:
|
|
75
|
+
print(result.cmd, result.stdout, result.stderr, result.return_code)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`tcount=0` selects a thread count based on the available CPUs and number of jobs. Use `pcount=0` to enable multiprocessing; you can also combine process and thread workers. When using multiprocessing, put the call to `sshread` inside the standard `if __name__ == "__main__":` guard.
|
|
79
|
+
|
|
80
|
+
Each command result has `cmd`, `stdout`, `stderr`, and `return_code` fields. A `ServerJob` stores its command results in `results` and the sum of their return codes in `status`.
|
|
81
|
+
|
|
82
|
+
## Other features
|
|
83
|
+
|
|
84
|
+
- Run one or more commands over a reusable SSH connection with `SSH.ssh_command`.
|
|
85
|
+
- Transfer files over SFTP with `SSH.sftp_put` and `SSH.sftp_get`.
|
|
86
|
+
- Run local commands with `shell_command` (shell-free by default; use `shell=True` only when needed) or create local `ServerJob` instances with `run_local=True`.
|
|
87
|
+
- Use `Hook` objects to run functions before or after a job, including hooks that use its established SSH connection.
|
|
88
|
+
- Discover the current username, SSH keys, and agent keys with `envvars()`.
|
|
89
|
+
- Configure connection and command timeouts, SSH ports, output handling, and worker counts.
|
|
90
|
+
|
|
91
|
+
See the [documentation](https://sshreader.readthedocs.io/) for detailed API guides and examples.
|
|
92
|
+
|
|
93
|
+
## `pydsh`
|
|
94
|
+
|
|
95
|
+
Installing SSHreader also installs the `pydsh` command. It accepts [hostlist expressions](https://pypi.org/project/python-hostlist/) and runs a command across the expanded hosts:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pydsh -w 'web[01-10].example.com' 'uname -r'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
By default, `pydsh` uses the current username and available SSH keys or agent, prompting for a password when needed. You can specify credentials and group or coalesce output:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pydsh -u jdoe -k ~/.ssh/id_ed25519 -D -w 'web[01-10].example.com' 'uname -r'
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Use `pydsh --help` to see all options. With `-F`, the command argument is treated as a script file; the script must start with a shebang (`#!`), and `--confirm` skips the execution confirmation prompt.
|
|
108
|
+
|
|
109
|
+
## Development
|
|
110
|
+
|
|
111
|
+
To set up a local development environment and run the tests:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv sync --group test
|
|
115
|
+
uv run --group test pytest
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Run lint and type checks with:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uv run --group test ruff check sshreader tests
|
|
122
|
+
uv run --group test ty check sshreader
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Contributing
|
|
126
|
+
|
|
127
|
+
Bug reports, feature requests, and code contributions are welcome. Please [open an issue](https://github.com/isaiah1112/sshreader/issues) with a minimal example and relevant traceback when reporting a problem. Contributions can be submitted as pull requests to the [GitHub repository](https://github.com/isaiah1112/sshreader).
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
SSHreader is released under the [GNU Lesser General Public License v3.0 or later](https://www.gnu.org/licenses/lgpl-3.0.html). See [LICENSE](./LICENSE) and [LICENSE.lesser](./LICENSE.lesser) for the license texts.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
[](https://github.com/isaiah1112/sshreader/actions/workflows/lint_and_test.yml)
|
|
4
|
+
|
|
5
|
+
# SSHreader
|
|
6
|
+
|
|
7
|
+
SSHreader is a Python library for running commands on multiple servers in parallel. It wraps [Paramiko](https://www.paramiko.org/) and provides both a direct SSH client and a job-based interface for parallel SSH and local shell work. It also includes `pydsh`, a command-line tool for running commands across host lists.
|
|
8
|
+
|
|
9
|
+
SSHreader supports Python 3.11 and later.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Install the latest release from PyPI:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
python -m pip install sshreader
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
Use `SSH` to run commands in a single SSH session. SSHreader can use an SSH key, an SSH agent, or a password:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from sshreader import SSH
|
|
25
|
+
|
|
26
|
+
with SSH("myhost.example.com", username="jdoe", keyfile="~/.ssh/id_ed25519") as ssh:
|
|
27
|
+
result = ssh.ssh_command("uname -a")
|
|
28
|
+
print(result.stdout)
|
|
29
|
+
print(result.return_code)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use `ServerJob` and `sshread` to run jobs on multiple hosts. A job can contain multiple commands, which run sequentially on that host:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from sshreader import ServerJob, sshread
|
|
36
|
+
|
|
37
|
+
hosts = ["server1.example.com", "server2.example.com"]
|
|
38
|
+
jobs = [
|
|
39
|
+
ServerJob(host, ["hostname", "uptime"], username="jdoe", keyfile="~/.ssh/id_ed25519")
|
|
40
|
+
for host in hosts
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
finished_jobs = sshread(jobs, tcount=0)
|
|
44
|
+
for job in finished_jobs:
|
|
45
|
+
print(job.name, job.status)
|
|
46
|
+
for result in job.results:
|
|
47
|
+
print(result.cmd, result.stdout, result.stderr, result.return_code)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`tcount=0` selects a thread count based on the available CPUs and number of jobs. Use `pcount=0` to enable multiprocessing; you can also combine process and thread workers. When using multiprocessing, put the call to `sshread` inside the standard `if __name__ == "__main__":` guard.
|
|
51
|
+
|
|
52
|
+
Each command result has `cmd`, `stdout`, `stderr`, and `return_code` fields. A `ServerJob` stores its command results in `results` and the sum of their return codes in `status`.
|
|
53
|
+
|
|
54
|
+
## Other features
|
|
55
|
+
|
|
56
|
+
- Run one or more commands over a reusable SSH connection with `SSH.ssh_command`.
|
|
57
|
+
- Transfer files over SFTP with `SSH.sftp_put` and `SSH.sftp_get`.
|
|
58
|
+
- Run local commands with `shell_command` (shell-free by default; use `shell=True` only when needed) or create local `ServerJob` instances with `run_local=True`.
|
|
59
|
+
- Use `Hook` objects to run functions before or after a job, including hooks that use its established SSH connection.
|
|
60
|
+
- Discover the current username, SSH keys, and agent keys with `envvars()`.
|
|
61
|
+
- Configure connection and command timeouts, SSH ports, output handling, and worker counts.
|
|
62
|
+
|
|
63
|
+
See the [documentation](https://sshreader.readthedocs.io/) for detailed API guides and examples.
|
|
64
|
+
|
|
65
|
+
## `pydsh`
|
|
66
|
+
|
|
67
|
+
Installing SSHreader also installs the `pydsh` command. It accepts [hostlist expressions](https://pypi.org/project/python-hostlist/) and runs a command across the expanded hosts:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pydsh -w 'web[01-10].example.com' 'uname -r'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
By default, `pydsh` uses the current username and available SSH keys or agent, prompting for a password when needed. You can specify credentials and group or coalesce output:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pydsh -u jdoe -k ~/.ssh/id_ed25519 -D -w 'web[01-10].example.com' 'uname -r'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Use `pydsh --help` to see all options. With `-F`, the command argument is treated as a script file; the script must start with a shebang (`#!`), and `--confirm` skips the execution confirmation prompt.
|
|
80
|
+
|
|
81
|
+
## Development
|
|
82
|
+
|
|
83
|
+
To set up a local development environment and run the tests:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
uv sync --group test
|
|
87
|
+
uv run --group test pytest
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Run lint and type checks with:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
uv run --group test ruff check sshreader tests
|
|
94
|
+
uv run --group test ty check sshreader
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Contributing
|
|
98
|
+
|
|
99
|
+
Bug reports, feature requests, and code contributions are welcome. Please [open an issue](https://github.com/isaiah1112/sshreader/issues) with a minimal example and relevant traceback when reporting a problem. Contributions can be submitted as pull requests to the [GitHub repository](https://github.com/isaiah1112/sshreader).
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
SSHreader is released under the [GNU Lesser General Public License v3.0 or later](https://www.gnu.org/licenses/lgpl-3.0.html). See [LICENSE](./LICENSE) and [LICENSE.lesser](./LICENSE.lesser) for the license texts.
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
authors = [
|
|
3
3
|
{ name = "Jesse Almanrode", email = "jesse@almanrode.com" },
|
|
4
4
|
]
|
|
5
|
-
requires-python = "
|
|
5
|
+
requires-python = ">=3.11,<4.0"
|
|
6
6
|
dependencies = [
|
|
7
|
-
"click
|
|
8
|
-
"colorama
|
|
9
|
-
"paramiko
|
|
10
|
-
"progressbar2
|
|
11
|
-
"python-hostlist
|
|
7
|
+
"click>=8.4.2",
|
|
8
|
+
"colorama>=0.4.6",
|
|
9
|
+
"paramiko>=5.0.0",
|
|
10
|
+
"progressbar2>=4.5.0",
|
|
11
|
+
"python-hostlist>=2.3.0",
|
|
12
12
|
]
|
|
13
13
|
name = "sshreader"
|
|
14
|
-
version = "
|
|
14
|
+
version = "7.0.0"
|
|
15
15
|
description = "Multi-threading/processing wrapper for Paramiko"
|
|
16
16
|
readme = "README.md"
|
|
17
17
|
classifiers = [
|
|
@@ -20,11 +20,11 @@ classifiers = [
|
|
|
20
20
|
"License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)",
|
|
21
21
|
"Development Status :: 5 - Production/Stable",
|
|
22
22
|
"Programming Language :: Python",
|
|
23
|
-
"Programming Language :: Python :: 3.9",
|
|
24
|
-
"Programming Language :: Python :: 3.10",
|
|
25
23
|
"Programming Language :: Python :: 3.11",
|
|
26
24
|
"Programming Language :: Python :: 3.12",
|
|
27
25
|
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Python :: 3.14",
|
|
27
|
+
"Programming Language :: Python :: 3.15",
|
|
28
28
|
"Topic :: Software Development :: Libraries",
|
|
29
29
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
30
30
|
]
|
|
@@ -33,8 +33,8 @@ classifiers = [
|
|
|
33
33
|
text = "LGPL-3.0-or-later"
|
|
34
34
|
|
|
35
35
|
[project.urls]
|
|
36
|
-
homepage = "
|
|
37
|
-
repository = "https://
|
|
36
|
+
homepage = "https://sshreader.readthedocs.io/"
|
|
37
|
+
repository = "https://github.com/isaiah1112/sshreader"
|
|
38
38
|
|
|
39
39
|
[project.scripts]
|
|
40
40
|
pydsh = "sshreader.scripts.pydsh:cli"
|
|
@@ -44,6 +44,9 @@ includes = [
|
|
|
44
44
|
"sshreader",
|
|
45
45
|
"sshreader/scripts",
|
|
46
46
|
]
|
|
47
|
+
excludes = [
|
|
48
|
+
"**/.DS_Store",
|
|
49
|
+
]
|
|
47
50
|
|
|
48
51
|
[tool.ruff.lint]
|
|
49
52
|
select = [
|
|
@@ -74,9 +77,11 @@ build-backend = "pdm.backend"
|
|
|
74
77
|
|
|
75
78
|
[dependency-groups]
|
|
76
79
|
test = [
|
|
77
|
-
"coverage
|
|
78
|
-
"discover
|
|
79
|
-
"
|
|
80
|
+
"coverage>=7.15.2",
|
|
81
|
+
"discover>=0.4.0",
|
|
82
|
+
"pytest>=9.1.1",
|
|
83
|
+
"ruff>=0.15.22",
|
|
84
|
+
"ty>=0.0.60",
|
|
80
85
|
]
|
|
81
86
|
docs = [
|
|
82
87
|
"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
|
|
@@ -14,23 +14,23 @@
|
|
|
14
14
|
#
|
|
15
15
|
# You should have received a copy of the GNU Lesser General Public License
|
|
16
16
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
-
from typing import NamedTuple
|
|
17
|
+
from typing import NamedTuple
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class Command(NamedTuple):
|
|
21
21
|
cmd: str
|
|
22
|
-
stdout:
|
|
23
|
-
stderr:
|
|
22
|
+
stdout: str | bytes | None
|
|
23
|
+
stderr: str | bytes | None
|
|
24
24
|
return_code: int
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class EnvVars(NamedTuple):
|
|
28
|
-
username:
|
|
29
|
-
agent_keys:
|
|
30
|
-
dsa_key:
|
|
31
|
-
ecdsa_key:
|
|
32
|
-
rsa_key:
|
|
28
|
+
username: str | None
|
|
29
|
+
agent_keys: list[object] | None
|
|
30
|
+
dsa_key: str | None
|
|
31
|
+
ecdsa_key: str | None
|
|
32
|
+
rsa_key: str | None
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
Timeout =
|
|
35
|
+
Timeout = int | float
|
|
36
36
|
TimeoutTuple = tuple[Timeout, Timeout]
|
|
@@ -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
|