Skip to content

Commit 0b32a78

Browse files
gjkloostermanGeert Kloostermanpenguinolog
authored
Fix chroot_exe when used with context manager (#156)
* Fix chroot_exe when used with context manager Use None for `chroot_exe` default argument. This allows us to use similar logic as `chroot_path`. * Update exec_helpers/api.py Co-authored-by: Alexey Stepanov <penguinolog@users.noreply.github.com> * Update exec_helpers/async_api/api.py Co-authored-by: Alexey Stepanov <penguinolog@users.noreply.github.com> --------- Co-authored-by: Geert Kloosterman <geert.kloosterman@brightcomputing.com> Co-authored-by: Alexey Stepanov <penguinolog@users.noreply.github.com>
1 parent 3129fc4 commit 0b32a78

File tree

6 files changed

+86
-74
lines changed

6 files changed

+86
-74
lines changed

exec_helpers/_helpers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,19 @@ def cmd_to_string(command: str | Iterable[str]) -> str:
7878
return shlex.join(command)
7979

8080

81-
def chroot_command(command: str, chroot_path: str | None = None, chroot_exe: str = "chroot") -> str:
81+
def chroot_command(command: str, chroot_path: str | None = None, chroot_exe: str | None = None) -> str:
8282
"""Prepare command for chroot execution.
8383
8484
:param command: Original command.
8585
:type command: str
8686
:param chroot_path: chroot path.
8787
:type chroot_path: str | None
8888
:param chroot_exe: chroot executable.
89-
:type chroot_exe: str
89+
:type chroot_exe: str | None
9090
:return: Command to be executed with chroot rules if applicable.
9191
:rtype: str
9292
"""
93+
chroot_exe = chroot_exe or "chroot"
9394
if chroot_path and chroot_path != "/":
9495
chroot_dst: str = shlex.quote(chroot_path.strip())
9596
quoted_command = shlex.quote(command)

exec_helpers/_ssh_base.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -982,14 +982,15 @@ def keepalive(self, enforce: KeepAlivePeriodT = 1) -> _KeepAliveContext:
982982
"""
983983
return _KeepAliveContext(ssh=self, enforce=int(enforce))
984984

985-
def _prepare_command(self, cmd: str, chroot_path: str | None = None, chroot_exe: str = "chroot") -> str:
985+
def _prepare_command(self, cmd: str, chroot_path: str | None = None, chroot_exe: str | None = None) -> str:
986986
"""Prepare command: cover chroot and other cases.
987987
988988
:param cmd: Main command.
989989
:param chroot_path: Path to make chroot for execution.
990990
:param chroot_exe: chroot executable, default "chroot".
991991
:return: The final command includes chroot, if required.
992992
"""
993+
chroot_exe = chroot_exe or "chroot"
993994
if not self.sudo_mode:
994995
return super()._prepare_command(cmd=cmd, chroot_path=chroot_path, chroot_exe=chroot_exe)
995996
quoted_command: str = shlex.quote(cmd)
@@ -1096,7 +1097,7 @@ def open_execute_context(
10961097
open_stdout: bool = True,
10971098
open_stderr: bool = True,
10981099
chroot_path: str | None = None,
1099-
chroot_exe: str = "chroot",
1100+
chroot_exe: str | None = None,
11001101
get_pty: bool = False,
11011102
width: int = 80,
11021103
height: int = 24,
@@ -1116,7 +1117,7 @@ def open_execute_context(
11161117
:param chroot_path: chroot path override.
11171118
:type chroot_path: str | None
11181119
:param chroot_exe: chroot exe override.
1119-
:type chroot_exe: str
1120+
:type chroot_exe: str | None
11201121
:param get_pty: Get PTY for connection.
11211122
:type get_pty: bool
11221123
:param width: PTY width.
@@ -1160,7 +1161,7 @@ def execute(
11601161
open_stderr: bool = True,
11611162
log_stderr: bool = True,
11621163
chroot_path: str | None = None,
1163-
chroot_exe: str = "chroot",
1164+
chroot_exe: str | None = None,
11641165
get_pty: bool = False,
11651166
width: int = 80,
11661167
height: int = 24,
@@ -1190,7 +1191,7 @@ def execute(
11901191
:param chroot_path: chroot path override.
11911192
:type chroot_path: str | None
11921193
:param chroot_exe: chroot exe override.
1193-
:type chroot_exe: str
1194+
:type chroot_exe: str | None
11941195
:param get_pty: Get PTY for connection.
11951196
:type get_pty: bool
11961197
:param width: PTY width.
@@ -1240,7 +1241,7 @@ def __call__(
12401241
open_stderr: bool = True,
12411242
log_stderr: bool = True,
12421243
chroot_path: str | None = None,
1243-
chroot_exe: str = "chroot",
1244+
chroot_exe: str | None = None,
12441245
get_pty: bool = False,
12451246
width: int = 80,
12461247
height: int = 24,
@@ -1270,7 +1271,7 @@ def __call__(
12701271
:param chroot_path: Chroot path override.
12711272
:type chroot_path: str | None
12721273
:param chroot_exe: Chroot exe override.
1273-
:type chroot_exe: str
1274+
:type chroot_exe: str | None
12741275
:param get_pty: Get PTY for connection.
12751276
:type get_pty: bool
12761277
:param width: PTY width.
@@ -1658,7 +1659,7 @@ def execute_together(
16581659
open_stdout: bool = True,
16591660
open_stderr: bool = True,
16601661
chroot_path: str | None = None,
1661-
chroot_exe: str = "chroot",
1662+
chroot_exe: str | None = None,
16621663
verbose: bool = False,
16631664
log_mask_re: LogMaskReT = None,
16641665
exception_class: type[exceptions.ParallelCallProcessError] = exceptions.ParallelCallProcessError,
@@ -1685,7 +1686,7 @@ def execute_together(
16851686
:param chroot_path: chroot path override.
16861687
:type chroot_path: str | None
16871688
:param chroot_exe: chroot exe override.
1688-
:type chroot_exe: str
1689+
:type chroot_exe: str | None
16891690
:param verbose: Produce verbose log record on command call.
16901691
:type verbose: bool
16911692
:param log_mask_re: Regex lookup rule to mask command for logger.

exec_helpers/api.py

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -173,32 +173,32 @@ class _ChRootContext(typing.ContextManager[None]):
173173
:param path: chroot path or None for no chroot.
174174
:type path: str | pathlib.Path | None
175175
:param chroot_exe: chroot executable.
176-
:type chroot_exe: str
176+
:type chroot_exe: str | None
177177
:raises TypeError: Incorrect type of path or chroot_exe variable.
178178
179179
.. versionadded:: 4.1.0
180180
"""
181181

182182
__slots__ = ("_chroot_exe_status", "_chroot_status", "_conn", "_exe", "_path")
183183

184-
def __init__(self, conn: ExecHelper, path: ChRootPathSetT = None, chroot_exe: str = "chroot") -> None:
184+
def __init__(self, conn: ExecHelper, path: ChRootPathSetT = None, chroot_exe: str | None = None) -> None:
185185
"""Context manager for call commands with sudo.
186186
187187
:raises TypeError: Incorrect type of path or chroot_exe variable.
188188
"""
189189
self._conn: ExecHelper = conn
190190
self._chroot_status: str | None = conn._chroot_path
191-
self._chroot_exe_status: str = conn._chroot_exe
191+
self._chroot_exe_status: str | None = conn._chroot_exe
192192
if path is None or isinstance(path, str):
193193
self._path: str | None = path
194194
elif isinstance(path, pathlib.Path):
195195
self._path = path.as_posix() # get absolute path
196196
else:
197197
raise TypeError(f"path={path!r} is not instance of {ChRootPathSetT}")
198-
if isinstance(chroot_exe, str):
199-
self._exe: str = chroot_exe
198+
if chroot_exe is None or isinstance(chroot_exe, str):
199+
self._exe: str | None = chroot_exe
200200
else:
201-
raise TypeError(f"chroot_exe={chroot_exe!r} is not instance of str")
201+
raise TypeError(f"chroot_exe={chroot_exe!r} is not None or instance of str")
202202

203203
def __enter__(self) -> None:
204204
self._conn.__enter__()
@@ -252,7 +252,7 @@ def __init__(self, log_mask_re: LogMaskReT = None, *, logger: logging.Logger) ->
252252
self.__logger: logging.Logger = logger
253253
self.log_mask_re: LogMaskReT = log_mask_re
254254
self.__chroot_path: str | None = None
255-
self.__chroot_exe: str = "chroot"
255+
self.__chroot_exe: str | None = None
256256
self.__context_count = 0
257257

258258
@property
@@ -306,43 +306,43 @@ def _chroot_path(self) -> None:
306306
self.__chroot_path = None
307307

308308
@property
309-
def _chroot_exe(self) -> str:
309+
def _chroot_exe(self) -> str | None:
310310
"""Exe for chroot
311311
312-
:rtype: str
312+
:rtype: str | None
313313
.. versionadded:: 8.1.0
314314
"""
315315
return self.__chroot_exe
316316

317317
@_chroot_exe.setter
318-
def _chroot_exe(self, new_state: str) -> None:
318+
def _chroot_exe(self, new_state: str | None) -> None:
319319
"""Executable for chroot if set.
320320
321321
:param new_state: New exe.
322-
:type new_state: str
322+
:type new_state: str | None
323323
:raises TypeError: Not supported exe information.
324324
.. versionadded:: 8.1.0
325325
"""
326-
if isinstance(new_state, str):
326+
if new_state is None or isinstance(new_state, str):
327327
self.__chroot_exe = new_state
328328
else:
329-
raise TypeError(f"chroot_exe is expected to be string, but set {new_state!r}")
329+
raise TypeError(f"chroot_exe is expected to be None or string, but set {new_state!r}")
330330

331331
@_chroot_exe.deleter
332332
def _chroot_exe(self) -> None:
333333
"""Restore chroot executable.
334334
335335
.. versionadded:: 8.1.0
336336
"""
337-
self.__chroot_exe = "chroot"
337+
self.__chroot_exe = None
338338

339-
def chroot(self, path: ChRootPathSetT, chroot_exe: str = "chroot") -> _ChRootContext:
339+
def chroot(self, path: ChRootPathSetT, chroot_exe: str | None = None) -> _ChRootContext:
340340
"""Context manager for changing chroot rules.
341341
342342
:param path: chroot path or none for working without chroot.
343343
:type path: str | pathlib.Path | None
344344
:param chroot_exe: chroot exe.
345-
:type chroot_exe: str
345+
:type chroot_exe: str | None
346346
:return: Context manager with selected chroot state inside.
347347
:rtype: typing.ContextManager
348348
@@ -394,7 +394,7 @@ def _mask_command(self, cmd: str, log_mask_re: LogMaskReT = None) -> str:
394394

395395
return _helpers.mask_command(cmd.rstrip(), self.log_mask_re, log_mask_re)
396396

397-
def _prepare_command(self, cmd: str, chroot_path: str | None = None, chroot_exe: str = "chroot") -> str:
397+
def _prepare_command(self, cmd: str, chroot_path: str | None = None, chroot_exe: str | None = None) -> str:
398398
"""Prepare command: cower chroot and other cases.
399399
400400
:param cmd: Main command.
@@ -404,7 +404,11 @@ def _prepare_command(self, cmd: str, chroot_path: str | None = None, chroot_exe:
404404
:return: Final command, includes chroot, if required.
405405
:rtype: str
406406
"""
407-
return _helpers.chroot_command(cmd, chroot_path=chroot_path or self._chroot_path, chroot_exe=chroot_exe)
407+
return _helpers.chroot_command(
408+
cmd,
409+
chroot_path=chroot_path or self._chroot_path,
410+
chroot_exe=chroot_exe or self._chroot_exe,
411+
)
408412

409413
@abc.abstractmethod
410414
def _exec_command(
@@ -473,7 +477,7 @@ def open_execute_context(
473477
open_stdout: bool = True,
474478
open_stderr: bool = True,
475479
chroot_path: str | None = None,
476-
chroot_exe: str = "chroot",
480+
chroot_exe: str | None = None,
477481
**kwargs: typing.Any,
478482
) -> ExecuteContext:
479483
"""Get execution context manager.
@@ -489,7 +493,7 @@ def open_execute_context(
489493
:param chroot_path: chroot path override.
490494
:type chroot_path: str | None
491495
:param chroot_exe: chroot exe override.
492-
:type chroot_exe: str
496+
:type chroot_exe: str | None
493497
:param kwargs: Additional parameters for call.
494498
:type kwargs: typing.Any
495499
@@ -509,7 +513,7 @@ def execute(
509513
open_stderr: bool = True,
510514
log_stderr: bool = True,
511515
chroot_path: str | None = None,
512-
chroot_exe: str = "chroot",
516+
chroot_exe: str | None = None,
513517
**kwargs: typing.Any,
514518
) -> exec_result.ExecResult:
515519
"""Execute command and wait for return code.
@@ -536,7 +540,7 @@ def execute(
536540
:param chroot_path: chroot path override.
537541
:type chroot_path: str | None
538542
:param chroot_exe: chroot exe override.
539-
:type chroot_exe: str
543+
:type chroot_exe: str | None
540544
:param kwargs: Additional parameters for call.
541545
:type kwargs: typing.Any
542546
:return: Execution result.
@@ -594,7 +598,7 @@ def __call__(
594598
open_stderr: bool = True,
595599
log_stderr: bool = True,
596600
chroot_path: str | None = None,
597-
chroot_exe: str = "chroot",
601+
chroot_exe: str | None = None,
598602
**kwargs: typing.Any,
599603
) -> exec_result.ExecResult:
600604
"""Execute command and wait for return code.
@@ -621,7 +625,7 @@ def __call__(
621625
:param chroot_path: chroot path override.
622626
:type chroot_path: str | None
623627
:param chroot_exe: chroot exe override.
624-
:type chroot_exe: str
628+
:type chroot_exe: str | None
625629
:param kwargs: Additional parameters for call.
626630
:type kwargs: typing.Any
627631
:return: Execution result.

0 commit comments

Comments
 (0)