ae-shell 0.3.16__tar.gz → 0.3.17__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.
- {ae_shell-0.3.16/ae_shell.egg-info → ae_shell-0.3.17}/PKG-INFO +4 -4
- {ae_shell-0.3.16 → ae_shell-0.3.17}/README.md +3 -3
- {ae_shell-0.3.16 → ae_shell-0.3.17}/ae/shell.py +83 -36
- {ae_shell-0.3.16 → ae_shell-0.3.17/ae_shell.egg-info}/PKG-INFO +4 -4
- {ae_shell-0.3.16 → ae_shell-0.3.17}/setup.py +1 -1
- {ae_shell-0.3.16 → ae_shell-0.3.17}/tests/test_shell.py +42 -10
- {ae_shell-0.3.16 → ae_shell-0.3.17}/LICENSE.md +0 -0
- {ae_shell-0.3.16 → ae_shell-0.3.17}/ae_shell.egg-info/SOURCES.txt +0 -0
- {ae_shell-0.3.16 → ae_shell-0.3.17}/ae_shell.egg-info/dependency_links.txt +0 -0
- {ae_shell-0.3.16 → ae_shell-0.3.17}/ae_shell.egg-info/requires.txt +0 -0
- {ae_shell-0.3.16 → ae_shell-0.3.17}/ae_shell.egg-info/top_level.txt +0 -0
- {ae_shell-0.3.16 → ae_shell-0.3.17}/ae_shell.egg-info/zip-safe +0 -0
- {ae_shell-0.3.16 → ae_shell-0.3.17}/pyproject.toml +0 -0
- {ae_shell-0.3.16 → ae_shell-0.3.17}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ae_shell
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.17
|
|
4
4
|
Summary: ae namespace module portion shell: shell execution and environment helpers
|
|
5
5
|
Home-page: https://gitlab.com/ae-group/ae_shell
|
|
6
6
|
Author: AndiEcker
|
|
@@ -64,13 +64,13 @@ Dynamic: summary
|
|
|
64
64
|
|
|
65
65
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.110 -->
|
|
66
66
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.33 -->
|
|
67
|
-
# shell 0.3.
|
|
67
|
+
# shell 0.3.17
|
|
68
68
|
|
|
69
69
|
[](
|
|
70
70
|
https://gitlab.com/ae-group/ae_shell)
|
|
71
71
|
[](
|
|
73
|
+
https://gitlab.com/ae-group/ae_shell/-/tree/release0.3.17)
|
|
74
74
|
[](
|
|
75
75
|
https://pypi.org/project/ae-shell/#history)
|
|
76
76
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.110 -->
|
|
2
2
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.33 -->
|
|
3
|
-
# shell 0.3.
|
|
3
|
+
# shell 0.3.17
|
|
4
4
|
|
|
5
5
|
[](
|
|
6
6
|
https://gitlab.com/ae-group/ae_shell)
|
|
7
7
|
[](
|
|
9
|
+
https://gitlab.com/ae-group/ae_shell/-/tree/release0.3.17)
|
|
10
10
|
[](
|
|
11
11
|
https://pypi.org/project/ae-shell/#history)
|
|
12
12
|
|
|
@@ -5,17 +5,19 @@ shell execution and environment helpers
|
|
|
5
5
|
this module is designed to provide a comprehensive set of constants and helper functions
|
|
6
6
|
to manage shell printouts, OS environment variables and to execute shell commands.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
* :func:`debug_or_verbose`: checks if the application is running in debug or verbose mode.
|
|
9
|
+
* :func:`get_domain_user_var`: retrieves an OS environment variable value for a specific domain and/or user.
|
|
10
|
+
* :func:`hint`: provides a hint message for console printouts based on the provided arguments.
|
|
11
|
+
* :func:`in_os_env`: context manager to temporarily add environment variables from the ``.env`` files onto `os.environ`.
|
|
12
|
+
* :func:`mask_token`: hide/mask tokens in a text block, to prevent to show them in logs and printouts.
|
|
13
|
+
* :func:`output_line_split`: decode and split the specified shell/console output streams into line chunks.
|
|
14
|
+
* :func:`output_zero_split`: decode and split the specified shell/console output streams separated by `NUL` (\\0) chars.
|
|
15
|
+
* :func:`sh_exec`: execute command in the current working directory of the OS console/shell.
|
|
16
|
+
* :func:`sh_exit_if_exec_err`: extended version of :func:`sh_exec` with automatically checks for errors
|
|
17
|
+
after a command is executed and handles application shutdown/termination gracefully.
|
|
18
|
+
|
|
19
|
+
* :data:`STDERR_BEG_MARKER`: marker used in the console/shell printouts for the beginning of merged-in stderr output.
|
|
20
|
+
* :data:`STDERR_END_MARKER`: marker used in the console/shell printouts for the end of merged-in stderr output.
|
|
19
21
|
"""
|
|
20
22
|
import os
|
|
21
23
|
import shlex
|
|
@@ -25,20 +27,20 @@ from collections.abc import Callable, Iterable, Iterator, MutableMapping
|
|
|
25
27
|
from contextlib import contextmanager
|
|
26
28
|
from typing import Any, cast, overload
|
|
27
29
|
|
|
28
|
-
from ae.base import UNSET, dummy_function, env_str, norm_name
|
|
29
|
-
from ae.system import
|
|
30
|
+
from ae.base import UNSET, UnsetType, dummy_function, env_str, norm_name # type: ignore
|
|
31
|
+
from ae.system import load_env_var_defaults, os_env_venv # type: ignore
|
|
30
32
|
from ae.core import main_app_instance, AppBase # type: ignore
|
|
31
33
|
from ae.console import MAIN_SECTION_NAME, ConsoleApp # type: ignore
|
|
32
34
|
|
|
33
35
|
|
|
34
|
-
__version__ = '0.3.
|
|
36
|
+
__version__ = '0.3.17'
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
STDERR_BEG_MARKER = 'vvv STDERR vvv' #: :paramref:`ae.shell.sh_exec.output_lines` begin stderr lines marker
|
|
38
40
|
STDERR_END_MARKER = '^^^ STDERR ^^^' #: end stderr lines marker in :paramref:`ae.shell.sh_exec.output_lines`
|
|
39
41
|
|
|
40
42
|
|
|
41
|
-
def debug_or_verbose(app_obj: ConsoleApp | None = None) -> bool:
|
|
43
|
+
def debug_or_verbose(app_obj: ConsoleApp | UnsetType | None = None) -> bool:
|
|
42
44
|
""" determine if the current app runs in debug|verbose mode, while preventing early .get_option() call an app init.
|
|
43
45
|
|
|
44
46
|
:param app_obj: optional ConsoleApp instance (def=main_app_instance()).
|
|
@@ -52,9 +54,11 @@ def debug_or_verbose(app_obj: ConsoleApp | None = None) -> bool:
|
|
|
52
54
|
the debug mode can be activated via the :class:`~ae.console.ConsoleApp` option `debug_level`, specified either
|
|
53
55
|
in a config file or via the command line options. the verbose mode get activated via the `more_verbose` option.
|
|
54
56
|
"""
|
|
55
|
-
|
|
57
|
+
if app_obj is None:
|
|
58
|
+
app_obj = main_app_instance()
|
|
59
|
+
|
|
56
60
|
return bool(
|
|
57
|
-
not app_obj
|
|
61
|
+
not isinstance(app_obj, AppBase) # prevent exception in early app startup and unit test runs
|
|
58
62
|
or app_obj.debug # main_app.debug_level > DEBUG_LEVEL_DISABLED
|
|
59
63
|
or not isinstance(app_obj, ConsoleApp) # return True for pure ae.core.AppBase instances
|
|
60
64
|
or not getattr(app_obj, '_parsed_arguments', None) # ConsoleApp._parsed_arguments args Namespace not created
|
|
@@ -101,10 +105,10 @@ def hint(command: str, action: Callable | str, message_suffix: str = "") -> str:
|
|
|
101
105
|
|
|
102
106
|
@contextmanager
|
|
103
107
|
def in_os_env(start_dir: str = "") -> Iterator[MutableMapping[str, str]]:
|
|
104
|
-
""" temporarily add environment variables from the
|
|
108
|
+
""" temporarily add environment variables from the ``.env`` files that not exist in :attr:`os.environ` to it.
|
|
105
109
|
|
|
106
110
|
:param start_dir: path to the folder where the first dotenv file (with the highest priority) is stored.
|
|
107
|
-
:return: yielding the
|
|
111
|
+
:return: yielding the OS env variables that got added to os.environ in this temporary context.
|
|
108
112
|
"""
|
|
109
113
|
loaded_env_vars = load_env_var_defaults(start_dir, os.environ)
|
|
110
114
|
try:
|
|
@@ -122,6 +126,10 @@ def mask_token(text: str) -> str: ...
|
|
|
122
126
|
def mask_token(text: list[str]) -> list[str]: ...
|
|
123
127
|
|
|
124
128
|
|
|
129
|
+
@overload
|
|
130
|
+
def mask_token(text: str | list[str]) -> str | list[str]: ...
|
|
131
|
+
|
|
132
|
+
|
|
125
133
|
def mask_token(text: str | list[str]) -> str | list[str]:
|
|
126
134
|
""" hide most parts of any Codeberg/GitHub/GitHub URL tokens found in the specified text/-lines.
|
|
127
135
|
|
|
@@ -150,10 +158,29 @@ def mask_token(text: str | list[str]) -> str | list[str]:
|
|
|
150
158
|
return lines[0] if is_str_arg else lines
|
|
151
159
|
|
|
152
160
|
|
|
161
|
+
def output_line_split(output: bytes) -> list[str]:
|
|
162
|
+
""" decode and split the specified shell/console output streams into line chunks.
|
|
163
|
+
|
|
164
|
+
:param output: captured stdout/stderr output from an executed OS shell command.
|
|
165
|
+
:return: list of non-empty shell/console output lines, decoded into string.
|
|
166
|
+
"""
|
|
167
|
+
return [line for line in output.decode().splitlines() if line.strip()]
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def output_zero_split(output: bytes) -> list[str]:
|
|
171
|
+
""" decode and split the specified shell/console output streams separated by `NUL` (\\0) characters.
|
|
172
|
+
|
|
173
|
+
:param output: captured stdout/stderr output from an executed OS shell command (e.g. `env -0`).
|
|
174
|
+
:return: list of non-empty shell/console output chunks, decoded into string.
|
|
175
|
+
"""
|
|
176
|
+
return [line for line in output.decode().split('\0') if line.strip()]
|
|
177
|
+
|
|
178
|
+
|
|
153
179
|
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
|
|
154
180
|
def sh_exec(command_line: str, extra_args: Iterable[str] = (), console_input: str = "",
|
|
155
|
-
output_lines: list[str] | None = None, app_obj: AppBase | None = None, shell: bool = False,
|
|
156
|
-
env_vars: dict[str, str] | None =
|
|
181
|
+
output_lines: list[str] | None = None, app_obj: AppBase | UnsetType | None = None, shell: bool = False,
|
|
182
|
+
env_vars: dict[str, str] | UnsetType | None = UNSET, err_redirect: int | None = None,
|
|
183
|
+
decoder_splitter: Callable[[bytes], list[str]] = output_line_split) -> int:
|
|
157
184
|
""" execute command in the current working directory of the OS console/shell.
|
|
158
185
|
|
|
159
186
|
:param command_line: command line string to execute on the console/shell. could contain command line args
|
|
@@ -170,8 +197,18 @@ def sh_exec(command_line: str, extra_args: Iterable[str] = (), console_input: st
|
|
|
170
197
|
specify :data:`~ae.base.UNSET` to suppress any printing/logging output.
|
|
171
198
|
:param shell: pass True to execute command in the default OS shell (for more info check the
|
|
172
199
|
documentation of the parameter :paramref:`~subprocess.run.shell` of the
|
|
173
|
-
:
|
|
200
|
+
:func:`subprocess.run` function).
|
|
174
201
|
:param env_vars: OS shell environment variables to be used instead of the console/bash defaults.
|
|
202
|
+
if not specified or `UNSET` then an isolated dict copy of :attr:`os.environ` will get
|
|
203
|
+
passed onto :func:`subprocess.run` in order to avoid potential runtime errors, if the
|
|
204
|
+
parent process (or a concurrent thread) modifies :attr:`os.environ` during the creation
|
|
205
|
+
of the subprocess. Additionally, with the convertion of the special :class:`_Environ`
|
|
206
|
+
object into a standard dict, the execution will result slightly faster and avoids
|
|
207
|
+
any special method overrides interfering with the child process creation. it also is
|
|
208
|
+
preventing rare errors with multithread-processes that try to change OS env variable
|
|
209
|
+
(e.g. Conda in relation with pip could lead to raise a `RuntimeError: dictionary
|
|
210
|
+
changed size during iteration` exception).
|
|
211
|
+
specify `None` in order to use the original/unisolated :attr:`os.environ` object.
|
|
175
212
|
:param err_redirect: this argument controls if and how the output of the executed command onto the console
|
|
176
213
|
stderr stream gets captured/redirected. it gets passed directly onto the
|
|
177
214
|
:paramref:`~subprocess.run.stderr` argument of :func:`subprocess.run` function.
|
|
@@ -184,18 +221,23 @@ def sh_exec(command_line: str, extra_args: Iterable[str] = (), console_input: st
|
|
|
184
221
|
specify :data:`subprocess.DEVNULL` to hide any stderr output onto the console/shell
|
|
185
222
|
as well as in the :paramref:`.output_lines` list. if you specify `None`
|
|
186
223
|
(the default argument) then the stderr output will be printed only on the console.
|
|
224
|
+
:param decoder_splitter: callable to decode and split the output from the stdout/stderr streams into a list
|
|
225
|
+
of string chunks/lines, to be added to and returned by :paramref:`.output_lines`.
|
|
187
226
|
:return: return code of the executed command or 126 if execution raised any other exception.
|
|
188
227
|
"""
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
228
|
+
if shell:
|
|
229
|
+
all_args: str | list[str] = command_line + (" " + " ".join(extra_args) if extra_args else "")
|
|
230
|
+
else:
|
|
231
|
+
all_args = shlex.split(command_line) + list(extra_args)
|
|
192
232
|
masked_args = mask_token(all_args)
|
|
193
233
|
if app_obj is None:
|
|
194
234
|
app_obj = main_app_instance()
|
|
195
|
-
print_out =
|
|
196
|
-
debug_out =
|
|
235
|
+
print_out = dummy_function if app_obj is UNSET else app_obj.print_out if isinstance(app_obj, AppBase) else print
|
|
236
|
+
debug_out = dummy_function if app_obj is UNSET else app_obj.debug_out if isinstance(app_obj, AppBase) else print
|
|
237
|
+
if env_vars is UNSET:
|
|
238
|
+
env_vars = os.environ.copy()
|
|
197
239
|
|
|
198
|
-
debug_out(f" . executing {masked_args} at {os.getcwd()=} in {
|
|
240
|
+
debug_out(f" . executing {masked_args} at {os.getcwd()=} in {os_env_venv()=}")
|
|
199
241
|
result: subprocess.CompletedProcess | subprocess.CalledProcessError # having: stdout/stderr/returncode
|
|
200
242
|
try:
|
|
201
243
|
result = subprocess.run(all_args,
|
|
@@ -214,10 +256,10 @@ def sh_exec(command_line: str, extra_args: Iterable[str] = (), console_input: st
|
|
|
214
256
|
|
|
215
257
|
if isinstance(output_lines, list):
|
|
216
258
|
if result.stdout:
|
|
217
|
-
output_lines.extend(
|
|
259
|
+
output_lines.extend(decoder_splitter(result.stdout))
|
|
218
260
|
if err_redirect == subprocess.PIPE and result.stderr:
|
|
219
261
|
output_lines.append(STDERR_BEG_MARKER)
|
|
220
|
-
output_lines.extend(
|
|
262
|
+
output_lines.extend(decoder_splitter(result.stderr))
|
|
221
263
|
output_lines.append(STDERR_END_MARKER)
|
|
222
264
|
|
|
223
265
|
return result.returncode
|
|
@@ -226,8 +268,10 @@ def sh_exec(command_line: str, extra_args: Iterable[str] = (), console_input: st
|
|
|
226
268
|
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
|
|
227
269
|
def sh_exit_if_exec_err(err_code: int, command_line: str,
|
|
228
270
|
extra_args: Iterable[str] = (), output_lines: list[str] | None = None, exit_on_err: bool = True,
|
|
229
|
-
exit_msg: str = "", app_obj: ConsoleApp | None = None, shell: bool = False,
|
|
230
|
-
env_vars: dict[str, str] |
|
|
271
|
+
exit_msg: str = "", app_obj: ConsoleApp | UnsetType | None = None, shell: bool = False,
|
|
272
|
+
env_vars: dict[str, str] | UnsetType | None = UNSET,
|
|
273
|
+
err_redirect: int | None = subprocess.DEVNULL,
|
|
274
|
+
decoder_splitter: Callable[[bytes], list[str]] = output_line_split) -> int:
|
|
231
275
|
""" execute command in the current working directory, optionally capturing console output and exit app on error.
|
|
232
276
|
|
|
233
277
|
:param err_code: error code to pass to the console as exit code if the command set an error code and
|
|
@@ -242,13 +286,15 @@ def sh_exit_if_exec_err(err_code: int, command_line: str,
|
|
|
242
286
|
:param exit_msg: additional text to print on stdout/console if the app debug level is greater or equal
|
|
243
287
|
to 1 (:data:`~ae.core.DEBUG_LEVEL_ENABLED`) or if an error occurred.
|
|
244
288
|
:param app_obj: :class:`~ae.console.ConsoleApp` instance, used for logging/force-ignorable error.
|
|
245
|
-
:param shell: pass True to execute command in the default OS shell (see :
|
|
289
|
+
:param shell: pass True to execute command in the default OS shell (see :func:`sh_exec`).
|
|
246
290
|
:param env_vars: OS shell environment variables to be used instead of the console/bash defaults.
|
|
247
291
|
:param err_redirect: control how the output on stderr gets captured, redirected and returned. see also
|
|
248
292
|
:paramref:`sh_exec.err_redirect` for more details to the supported argument values.
|
|
249
293
|
if this argument is not specified or has the value :data:`subprocess.DEVNULL` then the
|
|
250
294
|
stderr output will get suppressed on the console and will also not get added to the
|
|
251
295
|
list argument in :paramref:`.output_lines`.
|
|
296
|
+
:param decoder_splitter: callable to decode and split the output from the stdout/stderr streams into a list
|
|
297
|
+
of string chunks/lines, to be added to and returned by :paramref:`.output_lines`.
|
|
252
298
|
:return: 0 on success, or if an error occurred the error number set by the executed command.
|
|
253
299
|
"""
|
|
254
300
|
if output_lines is None:
|
|
@@ -256,12 +302,13 @@ def sh_exit_if_exec_err(err_code: int, command_line: str,
|
|
|
256
302
|
output_len = 0
|
|
257
303
|
else:
|
|
258
304
|
output_len = len(output_lines)
|
|
259
|
-
|
|
305
|
+
if app_obj is None:
|
|
306
|
+
app_obj = cast(ConsoleApp, main_app_instance()) # calls app_obj./ConsoleApp.chk() method
|
|
260
307
|
|
|
261
308
|
sh_err = sh_exec(command_line, extra_args=extra_args, output_lines=output_lines, app_obj=app_obj, shell=shell,
|
|
262
|
-
env_vars=env_vars, err_redirect=err_redirect)
|
|
309
|
+
env_vars=env_vars, err_redirect=err_redirect, decoder_splitter=decoder_splitter)
|
|
263
310
|
|
|
264
|
-
if app_obj and (app_obj.debug or sh_err and exit_on_err):
|
|
311
|
+
if isinstance(app_obj, ConsoleApp) and (app_obj.debug or sh_err and exit_on_err):
|
|
265
312
|
for line in output_lines[output_len:]:
|
|
266
313
|
if app_obj.verbose or not line.startswith("LOG: "): # if verbose show mypy's endless (stderr) log entries
|
|
267
314
|
app_obj.po(" " * 6 + line)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ae_shell
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.17
|
|
4
4
|
Summary: ae namespace module portion shell: shell execution and environment helpers
|
|
5
5
|
Home-page: https://gitlab.com/ae-group/ae_shell
|
|
6
6
|
Author: AndiEcker
|
|
@@ -64,13 +64,13 @@ Dynamic: summary
|
|
|
64
64
|
|
|
65
65
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.110 -->
|
|
66
66
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.33 -->
|
|
67
|
-
# shell 0.3.
|
|
67
|
+
# shell 0.3.17
|
|
68
68
|
|
|
69
69
|
[](
|
|
70
70
|
https://gitlab.com/ae-group/ae_shell)
|
|
71
71
|
[](
|
|
73
|
+
https://gitlab.com/ae-group/ae_shell/-/tree/release0.3.17)
|
|
74
74
|
[](
|
|
75
75
|
https://pypi.org/project/ae-shell/#history)
|
|
76
76
|
|
|
@@ -6,14 +6,15 @@ import subprocess
|
|
|
6
6
|
from unittest.mock import PropertyMock, patch
|
|
7
7
|
|
|
8
8
|
from ae.base import UNSET, camel_to_snake, norm_name, os_path_join, write_file
|
|
9
|
-
from ae.system import load_env_var_defaults,
|
|
9
|
+
from ae.system import load_env_var_defaults, os_env_venv
|
|
10
10
|
from ae.core import DEBUG_LEVEL_DISABLED, DEBUG_LEVEL_ENABLED, DEBUG_LEVEL_VERBOSE
|
|
11
11
|
from ae.console import MAIN_SECTION_NAME, ConsoleApp
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
from ae.shell import (
|
|
15
15
|
STDERR_BEG_MARKER, STDERR_END_MARKER,
|
|
16
|
-
debug_or_verbose, get_domain_user_var, hint, in_os_env, mask_token,
|
|
16
|
+
debug_or_verbose, get_domain_user_var, hint, in_os_env, mask_token,
|
|
17
|
+
output_line_split, output_zero_split, sh_exec, sh_exit_if_exec_err)
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class TestHelpers:
|
|
@@ -257,6 +258,30 @@ STDERR_LINE = b'std___err'
|
|
|
257
258
|
|
|
258
259
|
|
|
259
260
|
class TestShellExecutions:
|
|
261
|
+
def test_output_line_split(self):
|
|
262
|
+
assert output_line_split(STDOUT_LINE) == [STDOUT_LINE.decode()]
|
|
263
|
+
|
|
264
|
+
assert output_line_split(STDOUT_LINE + b"\n" + STDERR_LINE + b"\n") == [STDOUT_LINE.decode(),
|
|
265
|
+
STDERR_LINE.decode()]
|
|
266
|
+
|
|
267
|
+
assert output_line_split(STDOUT_LINE + b"\n\0" + STDERR_LINE + b"\0") == [
|
|
268
|
+
STDOUT_LINE.decode(), "\0" + STDERR_LINE.decode() + "\0"]
|
|
269
|
+
|
|
270
|
+
assert output_line_split(STDOUT_LINE + b"\r" + STDERR_LINE + b"\r\n\r\0\r") == [
|
|
271
|
+
STDOUT_LINE.decode(), STDERR_LINE.decode(), "\0"]
|
|
272
|
+
|
|
273
|
+
def test_output_zero_split(self):
|
|
274
|
+
assert output_zero_split(STDOUT_LINE) == [STDOUT_LINE.decode()]
|
|
275
|
+
|
|
276
|
+
assert output_zero_split(STDOUT_LINE + b"\0" + STDERR_LINE + b"\0") == [STDOUT_LINE.decode(),
|
|
277
|
+
STDERR_LINE.decode()]
|
|
278
|
+
|
|
279
|
+
assert output_zero_split(STDOUT_LINE + b"\0" + STDERR_LINE + b" = multi\nval\rlines\r\n" + STDERR_LINE) == [
|
|
280
|
+
STDOUT_LINE.decode(), STDERR_LINE.decode() + " = multi\nval\rlines\r\n" + STDERR_LINE.decode()]
|
|
281
|
+
|
|
282
|
+
assert output_zero_split(STDOUT_LINE + b"\n" + STDERR_LINE + b"\r\n\r\0\r") == [
|
|
283
|
+
STDOUT_LINE.decode() + "\n" + STDERR_LINE.decode() + "\r\n\r"]
|
|
284
|
+
|
|
260
285
|
def test_sh_exec_catch_any_exception(self, capsys):
|
|
261
286
|
with patch("subprocess.run", side_effect=Exception('broad tst exception')):
|
|
262
287
|
assert sh_exec('any_cmd') == (126, )[0]
|
|
@@ -283,7 +308,7 @@ class TestShellExecutions:
|
|
|
283
308
|
out, err = capsys.readouterr()
|
|
284
309
|
assert " . executing ['any', 'command']" in out
|
|
285
310
|
assert os.getcwd() in out
|
|
286
|
-
assert
|
|
311
|
+
assert os_env_venv() in out
|
|
287
312
|
assert "**** subprocess.run(['any', 'command']) raised exception" in out
|
|
288
313
|
assert err == ""
|
|
289
314
|
|
|
@@ -329,40 +354,41 @@ class TestShellExecutions:
|
|
|
329
354
|
def test_sh_exec_run_args(self, mock_method):
|
|
330
355
|
cmd_line = "cmd arg1 arg2"
|
|
331
356
|
extra_args = ['extra_arg1', 'extra_arg2']
|
|
357
|
+
env = os.environ.copy()
|
|
332
358
|
|
|
333
359
|
sh_exec(cmd_line, tuple(extra_args))
|
|
334
360
|
|
|
335
361
|
mock_method.assert_called_with(
|
|
336
|
-
shlex.split(cmd_line) + extra_args, stdout=None, stderr=None, input=b'', check=True, shell=False, env=
|
|
362
|
+
shlex.split(cmd_line) + extra_args, stdout=None, stderr=None, input=b'', check=True, shell=False, env=env)
|
|
337
363
|
|
|
338
364
|
sh_exec(cmd_line, shell=True)
|
|
339
365
|
|
|
340
366
|
mock_method.assert_called_with(
|
|
341
|
-
cmd_line, stdout=None, stderr=None, input=b'', check=True, shell=True, env=
|
|
367
|
+
cmd_line, stdout=None, stderr=None, input=b'', check=True, shell=True, env=env)
|
|
342
368
|
|
|
343
369
|
sh_exec(cmd_line, {_: "any" for _ in extra_args}, shell=True, err_redirect=subprocess.DEVNULL)
|
|
344
370
|
|
|
345
371
|
mock_method.assert_called_with(
|
|
346
372
|
cmd_line + " " + " ".join(extra_args), stdout=None, stderr=subprocess.DEVNULL, input=b'', check=True,
|
|
347
|
-
shell=True, env=
|
|
373
|
+
shell=True, env=env)
|
|
348
374
|
|
|
349
375
|
sh_exec(cmd_line, extra_args, console_input='con_inp')
|
|
350
376
|
|
|
351
377
|
mock_method.assert_called_with(
|
|
352
378
|
shlex.split(cmd_line) + extra_args, stdout=None, stderr=None, input=b'con_inp',
|
|
353
|
-
check=True, shell=False, env=
|
|
379
|
+
check=True, shell=False, env=env)
|
|
354
380
|
|
|
355
381
|
sh_exec(cmd_line, extra_args, output_lines=[])
|
|
356
382
|
|
|
357
383
|
mock_method.assert_called_with(
|
|
358
384
|
shlex.split(cmd_line) + extra_args, stdout=subprocess.PIPE, stderr=None, input=b'',
|
|
359
|
-
check=True, shell=False, env=
|
|
385
|
+
check=True, shell=False, env=env)
|
|
360
386
|
|
|
361
387
|
sh_exec(cmd_line, extra_args, console_input='con_inp', output_lines=[], err_redirect=subprocess.STDOUT)
|
|
362
388
|
|
|
363
389
|
mock_method.assert_called_with(
|
|
364
390
|
shlex.split(cmd_line) + extra_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, input=b'con_inp',
|
|
365
|
-
check=True, shell=False, env=
|
|
391
|
+
check=True, shell=False, env=env)
|
|
366
392
|
|
|
367
393
|
env_vars = {'A': "1", 'C': "tst_string value"}
|
|
368
394
|
|
|
@@ -372,6 +398,12 @@ class TestShellExecutions:
|
|
|
372
398
|
shlex.split(cmd_line) + extra_args, stdout=None, stderr=None, input=b'',
|
|
373
399
|
check=True, shell=False, env=env_vars)
|
|
374
400
|
|
|
401
|
+
sh_exec(cmd_line, extra_args, env_vars=None)
|
|
402
|
+
|
|
403
|
+
mock_method.assert_called_with(
|
|
404
|
+
shlex.split(cmd_line) + extra_args, stdout=None, stderr=None, input=b'',
|
|
405
|
+
check=True, shell=False, env=None)
|
|
406
|
+
|
|
375
407
|
def test_sh_exec_run_returned_values(self):
|
|
376
408
|
def _run_return(*_args, **_kwargs):
|
|
377
409
|
""" mock to simulate subprocess.run return object. """
|
|
@@ -505,7 +537,7 @@ class TestShellExecutions:
|
|
|
505
537
|
assert ret == (126, )[0]
|
|
506
538
|
assert output == ['old output']
|
|
507
539
|
out, err = capsys.readouterr()
|
|
508
|
-
assert f". executing [] at os.getcwd()='{os.getcwd()}' in
|
|
540
|
+
assert f". executing [] at os.getcwd()='{os.getcwd()}' in os_env_venv()='{os_env_venv()}'" in out
|
|
509
541
|
assert 'tst exit message' in out
|
|
510
542
|
assert err == ""
|
|
511
543
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|