Skip to content

Commit 1e53a8e

Browse files
committed
Maintenance: python 3.8+ only, reduce amount of deprecated code
* drop `_execute_async`, which was long time protected and deprecated * use `datetime.now(tz=timezone.utc)` instead of more naive `datetime.utcnow()` * remove imports headers
1 parent a448724 commit 1e53a8e

39 files changed

+304
-1083
lines changed

.github/workflows/pythonpackage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ jobs:
108108
max-parallel: 6
109109
matrix:
110110
os: [ubuntu-latest, windows-latest]
111-
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
111+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
112112

113113
name: "Script based python ${{ matrix.python-version }} on ${{ matrix.os }}"
114114
steps:

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.4.0
3+
rev: v4.5.0
44
hooks:
55
- id: check-yaml
66
- id: end-of-file-fixer
@@ -30,7 +30,7 @@ repos:
3030

3131
- repo: https://github.com/astral-sh/ruff-pre-commit
3232
# Ruff version.
33-
rev: v0.0.285
33+
rev: v0.1.5
3434
hooks:
3535
- id: ruff
3636
args: [ --fix, --exit-non-zero-on-fix ]

classifiers.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ Topic :: Software Development :: Libraries :: Python Modules
44
License :: OSI Approved :: Apache Software License
55
Programming Language :: Python :: 3
66
Programming Language :: Python :: 3 :: Only
7-
Programming Language :: Python :: 3.7
87
Programming Language :: Python :: 3.8
98
Programming Language :: Python :: 3.9
109
Programming Language :: Python :: 3.10
1110
Programming Language :: Python :: 3.11
11+
Programming Language :: Python :: 3.12
1212
Programming Language :: Python :: Implementation :: CPython
1313
Programming Language :: Python :: Implementation :: PyPy

exec_helpers/__init__.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,16 @@
1616

1717
from __future__ import annotations
1818

19-
# Standard Library
2019
import importlib
2120
import typing
2221
import warnings
2322

2423
try: # noqa: SIM105
25-
# Local Implementation
2624
from ._version import version as __version__ # noqa: F401
2725
except ImportError:
2826
pass
2927

3028
if typing.TYPE_CHECKING:
31-
# Standard Library
3229
from collections.abc import Sequence
3330

3431
# noinspection PyUnresolvedReferences

exec_helpers/__init__.pyi

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
1414

15-
# Standard Library
1615
import typing
1716

18-
# Local Implementation
1917
from . import async_api
2018
from ._helpers import mask_command
2119
from ._ssh_helpers import HostsSSHConfigs
@@ -36,7 +34,6 @@ from .ssh_auth import SSHAuth
3634
from .subprocess import Subprocess # nosec # Expected
3735

3836
try: # noqa: SIM105
39-
# Local Implementation
4037
from ._version import version as __version__ # noqa: F401
4138
except ImportError:
4239
pass

exec_helpers/_helpers.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@
22

33
from __future__ import annotations
44

5-
# Standard Library
65
import functools
76
import re
87
import shlex
98
import typing
109

1110
if typing.TYPE_CHECKING:
12-
# Standard Library
1311
from collections.abc import Iterable
1412

1513

@@ -47,8 +45,7 @@ def _mask_command(text: str, rules: str | re.Pattern[str]) -> str:
4745
for match in re.finditer(rules, text):
4846
for idx, _ in enumerate(match.groups(), start=1):
4947
start, end = match.span(idx)
50-
masked.append(text[prev:start])
51-
masked.append("<*masked*>")
48+
masked.extend((text[prev:start], "<*masked*>"))
5249
prev = end
5350
masked.append(text[prev:])
5451

@@ -78,7 +75,7 @@ def cmd_to_string(command: str | Iterable[str]) -> str:
7875
"""
7976
if isinstance(command, str):
8077
return command
81-
return " ".join(shlex.quote(elem) for elem in command)
78+
return shlex.join(command)
8279

8380

8481
def chroot_command(command: str, chroot_path: str | None = None) -> str:

exec_helpers/_ssh_base.py

Lines changed: 3 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
from __future__ import annotations
2020

21-
# Standard Library
2221
import concurrent.futures
2322
import copy
2423
import datetime
@@ -31,34 +30,28 @@
3130
import typing
3231
import warnings
3332

34-
# External Dependencies
3533
import paramiko
3634
import tenacity
3735

38-
# Package Implementation
3936
from exec_helpers import api
4037
from exec_helpers import constants
4138
from exec_helpers import exceptions
4239
from exec_helpers import exec_result
4340
from exec_helpers import proc_enums
4441
from exec_helpers import ssh_auth
4542

46-
# Local Implementation
4743
from . import _helpers
4844
from . import _log_templates
4945
from . import _ssh_helpers
5046

5147
if typing.TYPE_CHECKING:
52-
# Standard Library
5348
import socket
5449
from collections.abc import Iterable
5550
from collections.abc import Sequence
5651
from types import TracebackType
5752

58-
# External Dependencies
5953
from typing_extensions import Self
6054

61-
# Package Implementation
6255
from exec_helpers.api import CalledProcessErrorSubClassT
6356
from exec_helpers.api import CommandT
6457
from exec_helpers.api import ErrorInfoT
@@ -68,7 +61,6 @@
6861
from exec_helpers.api import OptionalTimeoutT
6962
from exec_helpers.proc_enums import ExitCodeT
7063

71-
# Local Implementation
7264
from ._ssh_helpers import SSHConfigsDictT
7365

7466
__all__ = ("SSHClientBase", "SshExecuteAsyncResult", "SupportPathT")
@@ -316,7 +308,7 @@ def __enter__(self) -> SshExecuteAsyncResult:
316308
stderr = None
317309
_stdin: paramiko.channel.ChannelFile
318310
with chan.makefile("wb") as _stdin:
319-
started = datetime.datetime.utcnow()
311+
started = datetime.datetime.now(tz=datetime.timezone.utc)
320312
if self.__sudo_mode:
321313
chan.exec_command(self.command) # nosec # Sanitize on caller side
322314
if not stdout.channel.closed:
@@ -868,7 +860,7 @@ def sudo_mode(self, mode: bool) -> None:
868860
:param mode: sudo status: enabled | disabled
869861
:type mode: bool
870862
"""
871-
self.__sudo_mode = bool(mode)
863+
self.__sudo_mode = mode
872864

873865
@property
874866
def keepalive_period(self) -> int:
@@ -957,122 +949,14 @@ def _prepare_command(self, cmd: str, chroot_path: str | None = None) -> str:
957949
if not self.sudo_mode:
958950
return super()._prepare_command(cmd=cmd, chroot_path=chroot_path)
959951
quoted_command: str = shlex.quote(cmd)
960-
if chroot_path is None and self._chroot_path is None:
952+
if chroot_path is self._chroot_path is None:
961953
return f'sudo -S sh -c {shlex.quote(f"eval {quoted_command}")}'
962954
if chroot_path is not None:
963955
target_path: str = shlex.quote(chroot_path)
964956
else:
965957
target_path = shlex.quote(self._chroot_path) # type: ignore[arg-type]
966958
return f'chroot {target_path} sudo sh -c {shlex.quote(f"eval {quoted_command}")}'
967959

968-
# noinspection PyMethodOverriding
969-
def _execute_async(
970-
self,
971-
command: str,
972-
*,
973-
stdin: OptionalStdinT = None,
974-
open_stdout: bool = True,
975-
open_stderr: bool = True,
976-
chroot_path: str | None = None,
977-
get_pty: bool = False,
978-
width: int = 80,
979-
height: int = 24,
980-
timeout: OptionalTimeoutT = None,
981-
**kwargs: typing.Any,
982-
) -> SshExecuteAsyncResult:
983-
"""Execute command in async mode and return channel with IO objects.
984-
985-
:param command: Command for execution
986-
:type command: str
987-
:param stdin: pass STDIN text to the process
988-
:type stdin: bytes | str | bytearray | None
989-
:param open_stdout: open STDOUT stream for read
990-
:type open_stdout: bool
991-
:param open_stderr: open STDERR stream for read
992-
:type open_stderr: bool
993-
:param chroot_path: chroot path override
994-
:type chroot_path: str | None
995-
:param get_pty: Get PTY for connection
996-
:type get_pty: bool
997-
:param width: PTY width
998-
:type width: int
999-
:param height: PTY height
1000-
:type height: int
1001-
:param timeout: timeout before stop execution with TimeoutError (will be set on channel)
1002-
:type timeout: int | float | None
1003-
:param kwargs: additional parameters for call.
1004-
:type kwargs: typing.Any
1005-
:return: Tuple with control interface and file-like objects for STDIN/STDERR/STDOUT
1006-
:rtype: typing.NamedTuple(
1007-
'SshExecuteAsyncResult',
1008-
[
1009-
('interface', paramiko.Channel),
1010-
('stdin', paramiko.ChannelFile),
1011-
('stderr', paramiko.ChannelFile | None),
1012-
('stdout', paramiko.ChannelFile | None),
1013-
("started", datetime.datetime),
1014-
]
1015-
)
1016-
1017-
.. versionchanged:: 1.2.0 open_stdout and open_stderr flags
1018-
.. versionchanged:: 1.2.0 stdin data
1019-
.. versionchanged:: 1.2.0 get_pty moved to `**kwargs`
1020-
.. versionchanged:: 2.1.0 Use typed NamedTuple as result
1021-
.. versionchanged:: 3.2.0 Expose pty options as optional keyword-only arguments
1022-
.. versionchanged:: 4.1.0 support chroot
1023-
"""
1024-
warnings.warn("_execute_async is deprecated and will be removed soon", DeprecationWarning, stacklevel=2)
1025-
chan: paramiko.Channel = self._ssh_transport.open_session()
1026-
if timeout is not None:
1027-
chan.settimeout(timeout)
1028-
1029-
if get_pty:
1030-
# Open PTY
1031-
chan.get_pty(term="vt100", width=width, height=height, width_pixels=0, height_pixels=0)
1032-
1033-
_stdin: paramiko.ChannelFile = chan.makefile("wb") # type: ignore[name-defined]
1034-
stdout: paramiko.ChannelFile = chan.makefile("rb") # type: ignore[name-defined]
1035-
if open_stderr:
1036-
stderr: paramiko.ChannelFile | None = chan.makefile_stderr("rb") # type: ignore[name-defined]
1037-
else:
1038-
stderr = None
1039-
1040-
cmd = f"{self._prepare_command(cmd=command, chroot_path=chroot_path)}\n"
1041-
1042-
started = datetime.datetime.utcnow()
1043-
if self.sudo_mode:
1044-
chan.exec_command(cmd) # nosec # Sanitize on caller side
1045-
if not stdout.channel.closed:
1046-
# noinspection PyTypeChecker
1047-
self.auth.enter_password(_stdin)
1048-
_stdin.flush()
1049-
else:
1050-
chan.exec_command(cmd) # nosec # Sanitize on caller side
1051-
1052-
if stdin is not None:
1053-
if not _stdin.channel.closed:
1054-
stdin_str: bytes = self._string_bytes_bytearray_as_bytes(stdin)
1055-
1056-
_stdin.write(stdin_str)
1057-
_stdin.flush()
1058-
else:
1059-
self.logger.warning("STDIN Send failed: closed channel")
1060-
1061-
if open_stdout:
1062-
res_stdout = stdout
1063-
else:
1064-
stdout.close()
1065-
res_stdout = None
1066-
1067-
# noinspection PyArgumentList
1068-
return SshExecuteAsyncResult(
1069-
interface=chan,
1070-
stdin=_stdin,
1071-
stderr=stderr,
1072-
stdout=res_stdout,
1073-
started=started,
1074-
)
1075-
1076960
def _exec_command( # type: ignore[override]
1077961
self,
1078962
command: str,

exec_helpers/_ssh_helpers.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,15 @@
22

33
from __future__ import annotations
44

5-
# Standard Library
65
import functools
76
import pathlib
87
import typing
98

10-
# External Dependencies
119
import paramiko
1210

1311
if typing.TYPE_CHECKING:
14-
# Standard Library
1512
from collections.abc import Collection
1613

17-
# External Dependencies
1814
# noinspection PyPackageRequirements
1915
import logwrap
2016

exec_helpers/_subprocess_helpers.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@
1616

1717
from __future__ import annotations
1818

19-
# Standard Library
2019
import contextlib
2120
import platform
2221
import typing
2322

24-
# External Dependencies
2523
import psutil
2624

2725
__all__ = ("kill_proc_tree", "subprocess_kw")

0 commit comments

Comments
 (0)