exec-helpers 8.0.0.post0__tar.gz → 8.1.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.
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/.pre-commit-config.yaml +4 -4
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/PKG-INFO +7 -6
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/README.rst +6 -5
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/__init__.py +18 -21
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/__init__.pyi +16 -19
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/_helpers.py +18 -16
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/_ssh_base.py +326 -308
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/_ssh_helpers.py +57 -57
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/_subprocess_helpers.py +3 -3
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/_version.py +2 -2
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/api.py +209 -186
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/async_api/__init__.py +2 -2
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/async_api/__init__.pyi +3 -5
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/async_api/api.py +191 -170
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/async_api/exec_result.py +12 -12
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/async_api/subprocess.py +120 -109
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/exceptions.py +37 -108
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/exec_result.py +150 -83
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/proc_enums.py +7 -7
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/ssh.py +12 -12
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/ssh_auth.py +51 -51
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/subprocess.py +120 -109
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers.egg-info/PKG-INFO +7 -6
- exec_helpers-8.1.0/flake8_requirements.txt +14 -0
- exec_helpers-8.1.0/mypy_requirements.txt +1 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/pyproject.toml +68 -6
- exec-helpers-8.0.0.post0/flake8_requirements.txt +0 -4
- exec-helpers-8.0.0.post0/mypy_requirements.txt +0 -1
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/.editorconfig +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/.readthedocs.yaml +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/CI_REQUIREMENTS.txt +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/LICENSE +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/MANIFEST.in +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/build_requirements.txt +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/classifiers.txt +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/_log_templates.py +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/constants.py +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers/py.typed +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers.egg-info/SOURCES.txt +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers.egg-info/dependency_links.txt +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers.egg-info/not-zip-safe +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers.egg-info/requires.txt +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/exec_helpers.egg-info/top_level.txt +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/pytest_requirements.txt +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/requirements.txt +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/setup.cfg +0 -0
- {exec-helpers-8.0.0.post0 → exec_helpers-8.1.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
repos:
|
|
2
2
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
-
rev: v4.
|
|
3
|
+
rev: v4.6.0
|
|
4
4
|
hooks:
|
|
5
5
|
- id: check-yaml
|
|
6
6
|
- id: end-of-file-fixer
|
|
@@ -8,7 +8,7 @@ repos:
|
|
|
8
8
|
- id: mixed-line-ending
|
|
9
9
|
|
|
10
10
|
- repo: https://github.com/pycqa/isort
|
|
11
|
-
rev: 5.
|
|
11
|
+
rev: 5.13.2
|
|
12
12
|
hooks:
|
|
13
13
|
- id: isort
|
|
14
14
|
name: isort (python)
|
|
@@ -20,7 +20,7 @@ repos:
|
|
|
20
20
|
types: [pyi]
|
|
21
21
|
|
|
22
22
|
- repo: https://github.com/psf/black
|
|
23
|
-
rev:
|
|
23
|
+
rev: 24.4.2
|
|
24
24
|
hooks:
|
|
25
25
|
- id: black
|
|
26
26
|
# It is recommended to specify the latest version of Python
|
|
@@ -30,7 +30,7 @@ repos:
|
|
|
30
30
|
|
|
31
31
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
32
32
|
# Ruff version.
|
|
33
|
-
rev: v0.
|
|
33
|
+
rev: v0.4.6
|
|
34
34
|
hooks:
|
|
35
35
|
- id: ruff
|
|
36
36
|
args: [ --fix, --exit-non-zero-on-fix ]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: exec-helpers
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.1.0
|
|
4
4
|
Summary: Execution helpers for simplified usage of subprocess and ssh.
|
|
5
5
|
Home-page: https://github.com/python-useful-helpers/exec-helpers
|
|
6
6
|
Author-email: Alexey Stepanov <penguinolog@gmail.com>
|
|
@@ -156,7 +156,7 @@ Creation from scratch:
|
|
|
156
156
|
password='password', # str | None
|
|
157
157
|
key=None, # type: paramiko.PKey | None
|
|
158
158
|
keys=None, # type: Iterable[paramiko.PKey] | None
|
|
159
|
-
key_filename=None, # type:
|
|
159
|
+
key_filename=None, # type: list[str] | None
|
|
160
160
|
passphrase=None, # str | None
|
|
161
161
|
)
|
|
162
162
|
|
|
@@ -286,9 +286,10 @@ Execution result object has a set of useful properties:
|
|
|
286
286
|
|
|
287
287
|
* `cmd` - Command
|
|
288
288
|
* `exit_code` - Command return code. If possible to decode using enumerators for Linux -> it used.
|
|
289
|
+
* `ok` -> `bool`. Command return code is 0 (EX_OK).
|
|
289
290
|
* `stdin` -> `str`. Text representation of stdin.
|
|
290
|
-
* `stdout` -> `
|
|
291
|
-
* `stderr` -> `
|
|
291
|
+
* `stdout` -> `tuple[bytes]`. Raw stdout output.
|
|
292
|
+
* `stderr` -> `tuple[bytes]`. Raw stderr output.
|
|
292
293
|
* `stdout_bin` -> `bytearray`. Binary stdout output.
|
|
293
294
|
* `stderr_bin` -> `bytearray`. Binary stderr output.
|
|
294
295
|
* `stdout_str` -> `str`. Text representation of output.
|
|
@@ -319,7 +320,7 @@ Possible to call commands in parallel on multiple hosts if it's not produce huge
|
|
|
319
320
|
|
|
320
321
|
.. code-block:: python
|
|
321
322
|
|
|
322
|
-
results:
|
|
323
|
+
results: dict[tuple[str, int], ExecResult] = SSHClient.execute_together(
|
|
323
324
|
remotes, # type: Iterable[SSHClient]
|
|
324
325
|
command, # type: str | Iterable[str]
|
|
325
326
|
timeout=1 * 60 * 60, # type: type: int | float | None
|
|
@@ -332,7 +333,7 @@ Possible to call commands in parallel on multiple hosts if it's not produce huge
|
|
|
332
333
|
log_mask_re=None, # str | None
|
|
333
334
|
exception_class=ParallelCallProcessError # type[ParallelCallProcessError]
|
|
334
335
|
)
|
|
335
|
-
results # type: dict[
|
|
336
|
+
results # type: dict[tuple[str, int], exec_result.ExecResult]
|
|
336
337
|
|
|
337
338
|
Results is a dict with keys = (hostname, port) and and results in values.
|
|
338
339
|
By default execute_together raises exception if unexpected return code on any remote.
|
|
@@ -110,7 +110,7 @@ Creation from scratch:
|
|
|
110
110
|
password='password', # str | None
|
|
111
111
|
key=None, # type: paramiko.PKey | None
|
|
112
112
|
keys=None, # type: Iterable[paramiko.PKey] | None
|
|
113
|
-
key_filename=None, # type:
|
|
113
|
+
key_filename=None, # type: list[str] | None
|
|
114
114
|
passphrase=None, # str | None
|
|
115
115
|
)
|
|
116
116
|
|
|
@@ -240,9 +240,10 @@ Execution result object has a set of useful properties:
|
|
|
240
240
|
|
|
241
241
|
* `cmd` - Command
|
|
242
242
|
* `exit_code` - Command return code. If possible to decode using enumerators for Linux -> it used.
|
|
243
|
+
* `ok` -> `bool`. Command return code is 0 (EX_OK).
|
|
243
244
|
* `stdin` -> `str`. Text representation of stdin.
|
|
244
|
-
* `stdout` -> `
|
|
245
|
-
* `stderr` -> `
|
|
245
|
+
* `stdout` -> `tuple[bytes]`. Raw stdout output.
|
|
246
|
+
* `stderr` -> `tuple[bytes]`. Raw stderr output.
|
|
246
247
|
* `stdout_bin` -> `bytearray`. Binary stdout output.
|
|
247
248
|
* `stderr_bin` -> `bytearray`. Binary stderr output.
|
|
248
249
|
* `stdout_str` -> `str`. Text representation of output.
|
|
@@ -273,7 +274,7 @@ Possible to call commands in parallel on multiple hosts if it's not produce huge
|
|
|
273
274
|
|
|
274
275
|
.. code-block:: python
|
|
275
276
|
|
|
276
|
-
results:
|
|
277
|
+
results: dict[tuple[str, int], ExecResult] = SSHClient.execute_together(
|
|
277
278
|
remotes, # type: Iterable[SSHClient]
|
|
278
279
|
command, # type: str | Iterable[str]
|
|
279
280
|
timeout=1 * 60 * 60, # type: type: int | float | None
|
|
@@ -286,7 +287,7 @@ Possible to call commands in parallel on multiple hosts if it's not produce huge
|
|
|
286
287
|
log_mask_re=None, # str | None
|
|
287
288
|
exception_class=ParallelCallProcessError # type[ParallelCallProcessError]
|
|
288
289
|
)
|
|
289
|
-
results # type: dict[
|
|
290
|
+
results # type: dict[tuple[str, int], exec_result.ExecResult]
|
|
290
291
|
|
|
291
292
|
Results is a dict with keys = (hostname, port) and and results in values.
|
|
292
293
|
By default execute_together raises exception if unexpected return code on any remote.
|
|
@@ -20,7 +20,7 @@ import importlib
|
|
|
20
20
|
import typing
|
|
21
21
|
import warnings
|
|
22
22
|
|
|
23
|
-
try: # noqa: SIM105
|
|
23
|
+
try: # noqa: FURB107,SIM105,RUF100
|
|
24
24
|
from ._version import version as __version__ # noqa: F401
|
|
25
25
|
except ImportError:
|
|
26
26
|
pass
|
|
@@ -28,31 +28,28 @@ except ImportError:
|
|
|
28
28
|
if typing.TYPE_CHECKING:
|
|
29
29
|
from collections.abc import Sequence
|
|
30
30
|
|
|
31
|
+
|
|
31
32
|
# noinspection PyUnresolvedReferences
|
|
32
33
|
__all__ = (
|
|
33
34
|
# pylint: disable=undefined-all-variable
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"async_api",
|
|
37
|
-
"ExitCodes",
|
|
38
|
-
"ExecResult",
|
|
35
|
+
"CalledProcessError",
|
|
36
|
+
"ExecCalledProcessError",
|
|
39
37
|
"ExecHelper",
|
|
40
|
-
"mask_command",
|
|
41
|
-
# Expensive
|
|
42
|
-
"Subprocess",
|
|
43
|
-
"SSHClient",
|
|
44
|
-
"SSHAuth",
|
|
45
|
-
"SSHConfig",
|
|
46
|
-
"HostsSSHConfigs",
|
|
47
|
-
# Exceptions
|
|
48
|
-
"exceptions",
|
|
49
38
|
"ExecHelperError",
|
|
50
|
-
"ExecCalledProcessError",
|
|
51
|
-
"CalledProcessError",
|
|
52
|
-
"ParallelCallExceptionsError",
|
|
53
|
-
"ParallelCallProcessError",
|
|
54
39
|
"ExecHelperNoKillError",
|
|
55
40
|
"ExecHelperTimeoutError",
|
|
41
|
+
"ExecResult",
|
|
42
|
+
"ExitCodes",
|
|
43
|
+
"HostsSSHConfigs",
|
|
44
|
+
"ParallelCallExceptionsError",
|
|
45
|
+
"ParallelCallProcessError",
|
|
46
|
+
"SSHAuth",
|
|
47
|
+
"SSHClient",
|
|
48
|
+
"SSHConfig",
|
|
49
|
+
"Subprocess",
|
|
50
|
+
"async_api",
|
|
51
|
+
"exceptions",
|
|
52
|
+
"mask_command",
|
|
56
53
|
)
|
|
57
54
|
|
|
58
55
|
__locals: dict[str, typing.Any] = locals() # use mutable access for pure lazy loading
|
|
@@ -90,8 +87,8 @@ _deprecated: dict[str, str] = {}
|
|
|
90
87
|
def __getattr__(name: str) -> typing.Any:
|
|
91
88
|
"""Get attributes lazy.
|
|
92
89
|
|
|
93
|
-
:return:
|
|
94
|
-
:raises AttributeError:
|
|
90
|
+
:return: Attribute by name.
|
|
91
|
+
:raises AttributeError: Attribute is not defined for the lazy load.
|
|
95
92
|
"""
|
|
96
93
|
if name in _deprecated:
|
|
97
94
|
warnings.warn(
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
# License for the specific language governing permissions and limitations
|
|
13
13
|
# under the License.
|
|
14
14
|
|
|
15
|
+
# pylint: disable=missing-module-docstring
|
|
16
|
+
|
|
15
17
|
import typing
|
|
16
18
|
|
|
17
19
|
from . import async_api
|
|
@@ -39,33 +41,28 @@ except ImportError:
|
|
|
39
41
|
|
|
40
42
|
# noinspection PyUnresolvedReferences
|
|
41
43
|
__all__ = (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# API
|
|
45
|
-
"async_api",
|
|
46
|
-
"ExitCodes",
|
|
47
|
-
"ExecResult",
|
|
44
|
+
"CalledProcessError",
|
|
45
|
+
"ExecCalledProcessError",
|
|
48
46
|
"ExecHelper",
|
|
49
|
-
"mask_command",
|
|
50
|
-
# Expensive
|
|
51
|
-
"Subprocess",
|
|
52
|
-
"SSHClient",
|
|
53
|
-
"SSHAuth",
|
|
54
|
-
"SSHConfig",
|
|
55
|
-
"HostsSSHConfigs",
|
|
56
|
-
# Exceptions
|
|
57
47
|
"ExecHelperError",
|
|
58
|
-
"ExecCalledProcessError",
|
|
59
|
-
"CalledProcessError",
|
|
60
|
-
"ParallelCallExceptionsError",
|
|
61
|
-
"ParallelCallProcessError",
|
|
62
48
|
"ExecHelperNoKillError",
|
|
63
49
|
"ExecHelperTimeoutError",
|
|
50
|
+
"ExecResult",
|
|
51
|
+
"ExitCodes",
|
|
52
|
+
"HostsSSHConfigs",
|
|
53
|
+
"ParallelCallExceptionsError",
|
|
54
|
+
"ParallelCallProcessError",
|
|
55
|
+
"SSHAuth",
|
|
56
|
+
"SSHClient",
|
|
57
|
+
"SSHConfig",
|
|
58
|
+
"Subprocess",
|
|
59
|
+
"async_api",
|
|
60
|
+
"mask_command",
|
|
64
61
|
)
|
|
65
62
|
|
|
66
63
|
_deprecated: dict[str, str] = ...
|
|
67
64
|
|
|
68
|
-
def __getattr__(name: str) -> typing.Any: ...
|
|
65
|
+
def __getattr__(name: str) -> typing.Any: ... # pylint: disable=unused-argument
|
|
69
66
|
|
|
70
67
|
__author__: str = ...
|
|
71
68
|
__author_email__: str = ...
|
|
@@ -12,10 +12,10 @@ if typing.TYPE_CHECKING:
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def string_bytes_bytearray_as_bytes(src: str | bytes | bytearray) -> bytes:
|
|
15
|
-
"""Get
|
|
15
|
+
"""Get byte string from string/bytes/bytearray union.
|
|
16
16
|
|
|
17
|
-
:param src:
|
|
18
|
-
:return: Byte string
|
|
17
|
+
:param src: Source string or bytes-like object.
|
|
18
|
+
:return: Byte string.
|
|
19
19
|
:rtype: bytes
|
|
20
20
|
:raises TypeError: unexpected source type.
|
|
21
21
|
"""
|
|
@@ -31,11 +31,11 @@ def string_bytes_bytearray_as_bytes(src: str | bytes | bytearray) -> bytes:
|
|
|
31
31
|
def _mask_command(text: str, rules: str | re.Pattern[str]) -> str:
|
|
32
32
|
"""Mask part of text using rules.
|
|
33
33
|
|
|
34
|
-
:param text:
|
|
34
|
+
:param text: Source text.
|
|
35
35
|
:type text: str
|
|
36
|
-
:param rules:
|
|
36
|
+
:param rules: Regex rules to mask.
|
|
37
37
|
:type rules: str | re.Pattern
|
|
38
|
-
:return:
|
|
38
|
+
:return: Source with all MATCHED groups replaced by '<*masked*>'.
|
|
39
39
|
:rtype: str
|
|
40
40
|
"""
|
|
41
41
|
masked: list[str] = []
|
|
@@ -55,11 +55,11 @@ def _mask_command(text: str, rules: str | re.Pattern[str]) -> str:
|
|
|
55
55
|
def mask_command(text: str, *rules: str | re.Pattern[str] | None) -> str:
|
|
56
56
|
"""Apply all rules to command.
|
|
57
57
|
|
|
58
|
-
:param text:
|
|
58
|
+
:param text: Source text.
|
|
59
59
|
:type text: str
|
|
60
|
-
:param rules:
|
|
60
|
+
:param rules: Regex rules to mask.
|
|
61
61
|
:type rules: str | re.Pattern[str] | None
|
|
62
|
-
:return:
|
|
62
|
+
:return: Source with all MATCHED groups replaced by '<*masked*>'.
|
|
63
63
|
:rtype: str
|
|
64
64
|
"""
|
|
65
65
|
return functools.reduce(_mask_command, (rule for rule in rules if rule is not None), text)
|
|
@@ -68,9 +68,9 @@ def mask_command(text: str, *rules: str | re.Pattern[str] | None) -> str:
|
|
|
68
68
|
def cmd_to_string(command: str | Iterable[str]) -> str:
|
|
69
69
|
"""Convert command to string for usage with shell.
|
|
70
70
|
|
|
71
|
-
:param command:
|
|
71
|
+
:param command: Original command.
|
|
72
72
|
:type command: str | Iterable[str]
|
|
73
|
-
:return:
|
|
73
|
+
:return: Command as single string.
|
|
74
74
|
:rtype: str
|
|
75
75
|
"""
|
|
76
76
|
if isinstance(command, str):
|
|
@@ -78,18 +78,20 @@ def cmd_to_string(command: str | Iterable[str]) -> str:
|
|
|
78
78
|
return shlex.join(command)
|
|
79
79
|
|
|
80
80
|
|
|
81
|
-
def chroot_command(command: str, chroot_path: str | None = None) -> str:
|
|
81
|
+
def chroot_command(command: str, chroot_path: str | None = None, chroot_exe: str = "chroot") -> str:
|
|
82
82
|
"""Prepare command for chroot execution.
|
|
83
83
|
|
|
84
|
-
:param command:
|
|
84
|
+
:param command: Original command.
|
|
85
85
|
:type command: str
|
|
86
|
-
:param chroot_path: chroot path
|
|
86
|
+
:param chroot_path: chroot path.
|
|
87
87
|
:type chroot_path: str | None
|
|
88
|
-
:
|
|
88
|
+
:param chroot_exe: chroot executable.
|
|
89
|
+
:type chroot_exe: str
|
|
90
|
+
:return: Command to be executed with chroot rules if applicable.
|
|
89
91
|
:rtype: str
|
|
90
92
|
"""
|
|
91
93
|
if chroot_path and chroot_path != "/":
|
|
92
94
|
chroot_dst: str = shlex.quote(chroot_path.strip())
|
|
93
95
|
quoted_command = shlex.quote(command)
|
|
94
|
-
return f'
|
|
96
|
+
return f'{chroot_exe} {chroot_dst} sh -c {shlex.quote(f"eval {quoted_command}")}'
|
|
95
97
|
return command
|