Skip to content

Commit 2eb8ca9

Browse files
committed
Drop python 3.8 and fix code
1 parent e22d8eb commit 2eb8ca9

25 files changed

+179
-206
lines changed

.github/workflows/pythonpackage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ jobs:
104104
max-parallel: 6
105105
matrix:
106106
os: [ubuntu-latest, windows-latest]
107-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
107+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
108108

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

.pre-commit-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.6.0
3+
rev: v5.0.0
44
hooks:
55
- id: check-yaml
66
- id: end-of-file-fixer
77
- id: trailing-whitespace
88
- id: mixed-line-ending
99

1010
- repo: https://github.com/pycqa/isort
11-
rev: 5.13.2
11+
rev: 6.0.1
1212
hooks:
1313
- id: isort
1414
name: isort (python)
@@ -20,7 +20,7 @@ repos:
2020
types: [pyi]
2121

2222
- repo: https://github.com/psf/black
23-
rev: 24.4.2
23+
rev: 25.1.0
2424
hooks:
2525
- id: black
2626
# It is recommended to specify the latest version of Python
@@ -30,7 +30,7 @@ repos:
3030

3131
- repo: https://github.com/astral-sh/ruff-pre-commit
3232
# Ruff version.
33-
rev: v0.4.6
33+
rev: v0.11.8
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.8
87
Programming Language :: Python :: 3.9
98
Programming Language :: Python :: 3.10
109
Programming Language :: Python :: 3.11
1110
Programming Language :: Python :: 3.12
11+
Programming Language :: Python :: 3.19
1212
Programming Language :: Python :: Implementation :: CPython
1313
Programming Language :: Python :: Implementation :: PyPy

exec_helpers/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import typing
2121
import warnings
2222

23-
try: # noqa: FURB107,SIM105,RUF100
23+
try: # noqa:SIM105,RUF100
2424
from ._version import version as __version__ # noqa: F401
2525
except ImportError:
2626
pass

exec_helpers/_helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,5 @@ def chroot_command(command: str, chroot_path: str | None = None, chroot_exe: str
9494
if chroot_path and chroot_path != "/":
9595
chroot_dst: str = shlex.quote(chroot_path.strip())
9696
quoted_command = shlex.quote(command)
97-
return f'{chroot_exe} {chroot_dst} sh -c {shlex.quote(f"eval {quoted_command}")}'
97+
return f"{chroot_exe} {chroot_dst} sh -c {shlex.quote(f'eval {quoted_command}')}"
9898
return command

exec_helpers/_ssh_base.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from __future__ import annotations
2020

2121
import concurrent.futures
22+
import contextlib
2223
import copy
2324
import datetime
2425
import getpass
@@ -135,7 +136,7 @@ def stdout(self) -> paramiko.channel.ChannelFile | None:
135136
return super().stdout
136137

137138

138-
class _SSHExecuteContext(api.ExecuteContext, typing.ContextManager[SshExecuteAsyncResult]):
139+
class _SSHExecuteContext(api.ExecuteContext, contextlib.AbstractContextManager[SshExecuteAsyncResult]):
139140
"""SSH Execute context."""
140141

141142
__slots__ = (
@@ -358,7 +359,7 @@ def __exit__(
358359
self.__chan = None
359360

360361

361-
class _SudoContext(typing.ContextManager[None]):
362+
class _SudoContext(contextlib.AbstractContextManager[None]):
362363
"""Context manager for call commands with sudo.
363364
364365
:param ssh: Connection instance.
@@ -389,7 +390,7 @@ def __exit__(
389390
self.__ssh.sudo_mode = self.__sudo_status
390391

391392

392-
class _KeepAliveContext(typing.ContextManager[None]):
393+
class _KeepAliveContext(contextlib.AbstractContextManager[None]):
393394
"""Context manager for keepalive management.
394395
395396
:param ssh: Connection instance.
@@ -869,7 +870,8 @@ def __del__(self) -> None:
869870
try:
870871
self.__ssh.close()
871872
except BaseException as e: # pragma: no cover # NOSONAR
872-
self.logger.debug(f"Exception in {self!s} destructor call: {e}")
873+
with contextlib.suppress(AttributeError):
874+
self.logger.debug(f"Exception in {self!s} destructor call: {e}")
873875
self.__sftp = None
874876

875877
def __exit__(
@@ -995,12 +997,12 @@ def _prepare_command(self, cmd: str, chroot_path: str | None = None, chroot_exe:
995997
return super()._prepare_command(cmd=cmd, chroot_path=chroot_path, chroot_exe=chroot_exe)
996998
quoted_command: str = shlex.quote(cmd)
997999
if chroot_path is self._chroot_path is None:
998-
return f'sudo -S sh -c {shlex.quote(f"eval {quoted_command}")}'
1000+
return f"sudo -S sh -c {shlex.quote(f'eval {quoted_command}')}"
9991001
if chroot_path is not None:
10001002
target_path: str = shlex.quote(chroot_path)
10011003
else:
10021004
target_path = shlex.quote(self._chroot_path) # type: ignore[arg-type]
1003-
return f'{chroot_exe} {target_path} sudo sh -c {shlex.quote(f"eval {quoted_command}")}'
1005+
return f"{chroot_exe} {target_path} sudo sh -c {shlex.quote(f'eval {quoted_command}')}"
10041006

10051007
def _exec_command( # type: ignore[override]
10061008
self,

exec_helpers/_ssh_helpers.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55
import functools
66
import pathlib
77
import typing
8+
from collections import UserDict
9+
from collections.abc import Collection
810

911
import paramiko
1012

1113
if typing.TYPE_CHECKING:
12-
from collections.abc import Collection
13-
1414
# noinspection PyPackageRequirements
1515
import logwrap
1616

17-
SSHConfigDictLikeT = typing.Dict[str, typing.Union[str, int, bool, typing.Collection[str]]]
18-
SSHConfigsDictT = typing.Dict[str, SSHConfigDictLikeT]
17+
SSHConfigDictLikeT = dict[str, typing.Union[str, int, bool, Collection[str]]]
18+
SSHConfigsDictT = dict[str, SSHConfigDictLikeT]
1919

2020

2121
# Parse default SSHConfig if available
@@ -379,7 +379,7 @@ def controlmaster(self) -> bool | None:
379379
return self.__controlmaster
380380

381381

382-
class HostsSSHConfigs(typing.Dict[str, SSHConfig]):
382+
class HostsSSHConfigs(UserDict[str, SSHConfig]):
383383
"""Specific dictionary for managing SSHConfig records.
384384
385385
Instead of creating a new record by request, just generate default value and return if not exists.

exec_helpers/api.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@
2121
from __future__ import annotations
2222

2323
import abc
24+
import contextlib
2425
import logging
2526
import pathlib
27+
import re
2628
import threading
2729
import typing
30+
from collections.abc import Callable
31+
from collections.abc import Iterable
2832

2933
from exec_helpers import constants
3034
from exec_helpers import exceptions
@@ -55,13 +59,13 @@
5559
"OptionalTimeoutT",
5660
)
5761

58-
CommandT = typing.Union[str, typing.Iterable[str]]
59-
LogMaskReT = typing.Union[str, typing.Pattern[str], None]
62+
CommandT = typing.Union[str, Iterable[str]]
63+
LogMaskReT = typing.Union[str, re.Pattern[str], None]
6064
ErrorInfoT = typing.Optional[str]
6165
ChRootPathSetT = typing.Optional[typing.Union[str, pathlib.Path]]
62-
ExpectedExitCodesT = typing.Iterable[ExitCodeT]
66+
ExpectedExitCodesT = Iterable[ExitCodeT]
6367
OptionalTimeoutT = typing.Union[int, float, None]
64-
CalledProcessErrorSubClassT = typing.Type[exceptions.CalledProcessError]
68+
CalledProcessErrorSubClassT = type[exceptions.CalledProcessError]
6569

6670

6771
class ExecuteAsyncResult(typing.NamedTuple):
@@ -74,7 +78,7 @@ class ExecuteAsyncResult(typing.NamedTuple):
7478
started: datetime.datetime
7579

7680

77-
class ExecuteContext(typing.ContextManager[ExecuteAsyncResult], abc.ABC):
81+
class ExecuteContext(contextlib.AbstractContextManager[ExecuteAsyncResult], abc.ABC):
7882
"""Execute context manager."""
7983

8084
__slots__ = (
@@ -165,7 +169,7 @@ def open_stderr(self) -> bool:
165169

166170

167171
# noinspection PyProtectedMember
168-
class _ChRootContext(typing.ContextManager[None]):
172+
class _ChRootContext(contextlib.AbstractContextManager[None]):
169173
"""Context manager for call commands with chroot.
170174
171175
:param conn: Connection instance.
@@ -219,8 +223,8 @@ def __exit__(
219223

220224

221225
class ExecHelper(
222-
typing.Callable[..., exec_result.ExecResult], # type: ignore[misc]
223-
typing.ContextManager["ExecHelper"],
226+
Callable[..., exec_result.ExecResult], # type: ignore[misc]
227+
contextlib.AbstractContextManager["ExecHelper"],
224228
abc.ABC,
225229
):
226230
"""ExecHelper global API.

exec_helpers/async_api/api.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121

2222
import abc
2323
import asyncio
24+
import contextlib
2425
import logging
2526
import pathlib
2627
import typing
28+
from collections.abc import Awaitable
29+
from collections.abc import Callable
2730

2831
from exec_helpers import api
2932
from exec_helpers import constants
@@ -62,7 +65,7 @@
6265
)
6366

6467

65-
class ExecuteContext(typing.AsyncContextManager[api.ExecuteAsyncResult], abc.ABC):
68+
class ExecuteContext(contextlib.AbstractAsyncContextManager[api.ExecuteAsyncResult], abc.ABC):
6669
"""Execute context manager."""
6770

6871
__slots__ = (
@@ -153,7 +156,7 @@ def open_stderr(self) -> bool:
153156

154157

155158
# noinspection PyProtectedMember
156-
class _ChRootContext(typing.AsyncContextManager[None]):
159+
class _ChRootContext(contextlib.AbstractAsyncContextManager[None]):
157160
"""Async extension for chroot.
158161
159162
:param conn: Connection instance.
@@ -203,8 +206,8 @@ async def __aexit__(
203206

204207

205208
class ExecHelper(
206-
typing.Callable[..., typing.Awaitable["ExecHelper"]], # type: ignore[misc]
207-
typing.AsyncContextManager["ExecHelper"],
209+
Callable[..., Awaitable["ExecHelper"]], # type: ignore[misc]
210+
contextlib.AbstractAsyncContextManager["ExecHelper"],
208211
abc.ABC,
209212
):
210213
"""Subprocess helper with timeouts and lock-free FIFO.

exec_helpers/async_api/subprocess.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ def stdout(self) -> AsyncIterable[bytes] | None: # type: ignore[override]
106106
return super().stdout # type: ignore[return-value]
107107

108108

109-
class _SubprocessExecuteContext(api.ExecuteContext, typing.AsyncContextManager[SubprocessExecuteAsyncResult]):
109+
class _SubprocessExecuteContext(
110+
api.ExecuteContext, contextlib.AbstractAsyncContextManager[SubprocessExecuteAsyncResult]
111+
):
110112
"""Subprocess Execute context."""
111113

112114
__slots__ = ("__cwd", "__env", "__process")
@@ -390,7 +392,7 @@ def open_execute_context(
390392
if env_patch is not None:
391393
# make mutable copy
392394
env = dict(copy.deepcopy(os.environ) if env is None else copy.deepcopy(env)) # type: ignore[arg-type]
393-
env.update(env_patch) # type: ignore[arg-type]
395+
env.update(env_patch)
394396
return _SubprocessExecuteContext(
395397
command=f"{self._prepare_command(cmd=command, chroot_path=chroot_path, chroot_exe=chroot_exe)}\n",
396398
stdin=None if stdin is None else self._string_bytes_bytearray_as_bytes(stdin),

exec_helpers/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ def __init__(
274274
class ParallelCallExceptionsError(ParallelCallProcessError):
275275
"""Exception raised during parallel call as result of exceptions."""
276276

277-
__slots__ = ("cmd", "exceptions")
277+
__slots__ = ("exceptions",)
278278

279279
def __init__(
280280
self,

exec_helpers/exec_result.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -802,8 +802,8 @@ def __str__(self) -> str:
802802
if self.started:
803803
started = f"\tstarted={self.started.strftime('%Y-%m-%d %H:%M:%S')},\n"
804804
if self.timestamp:
805-
_spent = (self.timestamp - self.started).seconds
806-
spent = f"\tspent={_spent // (60 * 60):02d}:{_spent // 60:02d}:{_spent % 60:02d},\n"
805+
spent_ = (self.timestamp - self.started).seconds
806+
spent = f"\tspent={spent_ // (60 * 60):02d}:{spent_ // 60:02d}:{spent_ % 60:02d},\n"
807807
else:
808808
spent = ""
809809
else:

exec_helpers/ssh_auth.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import copy
2222
import logging
2323
import typing
24+
from collections import UserDict
2425

2526
import paramiko
2627

@@ -74,7 +75,7 @@ def __init__(
7475
self.__username: str | None = username
7576
self.__password: str | None = password
7677

77-
self.__keys: list[None | paramiko.PKey] = []
78+
self.__keys: list[paramiko.PKey | None] = []
7879

7980
if key is not None:
8081
# noinspection PyTypeChecker
@@ -298,22 +299,22 @@ def __repr__(self) -> str:
298299
:rtype: str
299300
"""
300301
if self.__keys[self.__key_index] is None:
301-
_key: str | None = None
302+
key: str | None = None
302303
else:
303-
_key = f"<private for pub: {self.public_key}>"
304-
_keys: list[str | None] = []
304+
key = f"<private for pub: {self.public_key}>"
305+
keys: list[str | None] = []
305306
for idx, k in enumerate(self.__keys):
306307
if idx == self.__key_index:
307308
continue
308309
# noinspection PyTypeChecker
309-
_keys.append(f"<private for pub: {self.__get_public_key(key=k)}>" if k is not None else None)
310+
keys.append(f"<private for pub: {self.__get_public_key(key=k)}>" if k is not None else None)
310311

311312
return (
312313
f"{self.__class__.__name__}("
313314
f"username={self.username!r}, "
314315
f"password=<*masked*>, "
315-
f"key={_key}, "
316-
f"keys={_keys}, "
316+
f"key={key}, "
317+
f"keys={keys}, "
317318
f"key_filename={self.key_filename!r}, "
318319
f"passphrase=<*masked*>,"
319320
f")"
@@ -328,7 +329,7 @@ def __str__(self) -> str:
328329
return f"{self.__class__.__name__} for {self.username}"
329330

330331

331-
class SSHAuthMapping(typing.Dict[str, SSHAuth]):
332+
class SSHAuthMapping(UserDict[str, SSHAuth]):
332333
"""Specific dictionary for ssh hostname - auth mapping.
333334
334335
Keys are always string and saved/collected lowercase.

0 commit comments

Comments
 (0)