iripau 0.1.0__tar.gz → 1.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.
- {iripau-0.1.0 → iripau-1.1.0}/PKG-INFO +3 -2
- iripau-1.1.0/iripau/requests.py +81 -0
- {iripau-0.1.0 → iripau-1.1.0}/iripau/subprocess.py +55 -21
- {iripau-0.1.0 → iripau-1.1.0}/iripau.egg-info/PKG-INFO +3 -2
- {iripau-0.1.0 → iripau-1.1.0}/iripau.egg-info/SOURCES.txt +2 -0
- iripau-1.1.0/iripau.egg-info/requires.txt +2 -0
- {iripau-0.1.0 → iripau-1.1.0}/pyproject.toml +3 -2
- iripau-1.1.0/tests/test_requests.py +72 -0
- {iripau-0.1.0 → iripau-1.1.0}/tests/test_subprocess.py +48 -22
- iripau-0.1.0/iripau.egg-info/requires.txt +0 -1
- {iripau-0.1.0 → iripau-1.1.0}/LICENSE +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/README.md +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/iripau/__init__.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/iripau/executable.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/iripau/functools.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/iripau/logging.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/iripau/random.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/iripau/shutil.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/iripau/threading.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/iripau.egg-info/dependency_links.txt +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/iripau.egg-info/top_level.txt +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/setup.cfg +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/tests/test_command.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/tests/test_executable.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/tests/test_functools.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/tests/test_logging.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/tests/test_random.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/tests/test_shutil.py +0 -0
- {iripau-0.1.0 → iripau-1.1.0}/tests/test_threading.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: iripau
|
3
|
-
Version:
|
3
|
+
Version: 1.1.0
|
4
4
|
Summary: Python utilities focused on command execution
|
5
5
|
Author: Ricardo Quezada
|
6
6
|
Maintainer: Ricardo Quezada
|
@@ -9,11 +9,12 @@ Project-URL: Repository, https://github.com/ricardo-galo/iripau
|
|
9
9
|
Project-URL: Bug Tracker, https://github.com/ricardo-galo/iripau/issues
|
10
10
|
Project-URL: Changelog, https://github.com/ricardo-galo/iripau/blob/master/CHANGELOG.md
|
11
11
|
Keywords: subprocess,command,local,remote,ssh,Linux,real-time,log,logging,threading,utilities
|
12
|
-
Classifier: Development Status ::
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
13
13
|
Classifier: Programming Language :: Python
|
14
14
|
Requires-Python: >=3.7
|
15
15
|
Description-Content-Type: text/markdown
|
16
16
|
License-File: LICENSE
|
17
|
+
Requires-Dist: curlify
|
17
18
|
Requires-Dist: psutil
|
18
19
|
Dynamic: license-file
|
19
20
|
|
@@ -0,0 +1,81 @@
|
|
1
|
+
"""
|
2
|
+
Utilities for the requests module
|
3
|
+
"""
|
4
|
+
|
5
|
+
import requests
|
6
|
+
|
7
|
+
from curlify import to_curl
|
8
|
+
|
9
|
+
from iripau.subprocess import TeeStreams, Popen
|
10
|
+
|
11
|
+
|
12
|
+
def curlify(
|
13
|
+
response, compressed=False, verify=True, pretty=False,
|
14
|
+
hide_output=False, headers_to_hide=[], headers_to_omit=[],
|
15
|
+
stdout_tees: TeeStreams = [], add_global_stdout_tees=True,
|
16
|
+
stderr_tees: TeeStreams = [], add_global_stderr_tees=True,
|
17
|
+
prompt_tees: TeeStreams = [], add_global_prompt_tees=True,
|
18
|
+
echo=None
|
19
|
+
):
|
20
|
+
""" Simulate the request was executed by a curl subprocess.
|
21
|
+
The command and output can be echoed and/or sent to files as described
|
22
|
+
in subprocess.run
|
23
|
+
"""
|
24
|
+
request = response.request
|
25
|
+
if headers_to_hide or headers_to_omit:
|
26
|
+
request = request.copy()
|
27
|
+
|
28
|
+
for header in headers_to_omit:
|
29
|
+
if header in request.headers:
|
30
|
+
del request.headers[header]
|
31
|
+
|
32
|
+
for header in headers_to_hide:
|
33
|
+
if header in request.headers:
|
34
|
+
request.headers[header] = "***"
|
35
|
+
|
36
|
+
stdout = hide_output and b"***" or response.content
|
37
|
+
if not stdout.endswith(b"\n"):
|
38
|
+
stdout += b"\n"
|
39
|
+
stderr = ""
|
40
|
+
|
41
|
+
Popen.simulate(
|
42
|
+
cmd=to_curl(request, compressed, verify, pretty),
|
43
|
+
stdout=stdout,
|
44
|
+
stderr=stderr,
|
45
|
+
comment=f"{response.status_code} - {response.reason}",
|
46
|
+
stdout_tees=stdout_tees,
|
47
|
+
stderr_tees=stderr_tees,
|
48
|
+
prompt_tees=prompt_tees,
|
49
|
+
add_global_stdout_tees=add_global_stdout_tees,
|
50
|
+
add_global_stderr_tees=add_global_stderr_tees,
|
51
|
+
add_global_prompt_tees=add_global_prompt_tees,
|
52
|
+
echo=echo
|
53
|
+
)
|
54
|
+
|
55
|
+
|
56
|
+
class Session(requests.Session):
|
57
|
+
""" A requests.Session that accepts curlify arguments in the request method """
|
58
|
+
|
59
|
+
def request(
|
60
|
+
self, *args, compressed=False, pretty=False,
|
61
|
+
hide_output=False, headers_to_hide=[], headers_to_omit=[],
|
62
|
+
stdout_tees: TeeStreams = [], add_global_stdout_tees=True,
|
63
|
+
stderr_tees: TeeStreams = [], add_global_stderr_tees=True,
|
64
|
+
prompt_tees: TeeStreams = [], add_global_prompt_tees=True,
|
65
|
+
echo=None, **kwargs
|
66
|
+
):
|
67
|
+
response = super().request(*args, **kwargs)
|
68
|
+
|
69
|
+
verify = kwargs.get("verify")
|
70
|
+
if verify is None:
|
71
|
+
verify = self.verify
|
72
|
+
|
73
|
+
curlify(
|
74
|
+
response, compressed, verify, pretty,
|
75
|
+
hide_output, headers_to_hide, headers_to_omit,
|
76
|
+
stdout_tees, add_global_stdout_tees,
|
77
|
+
stderr_tees, add_global_stderr_tees,
|
78
|
+
prompt_tees, add_global_prompt_tees,
|
79
|
+
echo
|
80
|
+
)
|
81
|
+
return response
|
@@ -5,7 +5,6 @@ This module relies on the following system utilities being installed:
|
|
5
5
|
* bash
|
6
6
|
* kill
|
7
7
|
* pstree
|
8
|
-
* sudo
|
9
8
|
* tee
|
10
9
|
"""
|
11
10
|
|
@@ -38,6 +37,7 @@ GLOBAL_PROMPTS = set()
|
|
38
37
|
|
39
38
|
|
40
39
|
TeeStream = Union[io.IOBase, Callable[[], io.IOBase]]
|
40
|
+
TeeStreams = Iterable[TeeStream]
|
41
41
|
|
42
42
|
|
43
43
|
class PipeFile(SpooledTemporaryFile):
|
@@ -124,27 +124,21 @@ class Popen(subprocess.Popen):
|
|
124
124
|
|
125
125
|
def __init__(
|
126
126
|
self, args, *, cwd=None, env=None, encoding=None, errors=None, text=None,
|
127
|
-
stdout_tees:
|
128
|
-
stderr_tees:
|
129
|
-
prompt_tees:
|
127
|
+
stdout_tees: TeeStreams = [], add_global_stdout_tees=True,
|
128
|
+
stderr_tees: TeeStreams = [], add_global_stderr_tees=True,
|
129
|
+
prompt_tees: TeeStreams = [], add_global_prompt_tees=True,
|
130
130
|
echo=None, alias=None, comment=None, **kwargs
|
131
131
|
):
|
132
132
|
stdout = kwargs.get("stdout")
|
133
133
|
stderr = kwargs.get("stderr")
|
134
134
|
|
135
|
-
stdout_tees, stderr_tees, prompt_tees = self._get_tee_sets(
|
135
|
+
stdout_tees, stderr_tees, prompt_tees, err2out = self._get_tee_sets(
|
136
136
|
stdout_tees, add_global_stdout_tees,
|
137
137
|
stderr_tees, add_global_stderr_tees,
|
138
138
|
prompt_tees, add_global_prompt_tees,
|
139
139
|
echo, stdout, stderr
|
140
140
|
)
|
141
141
|
|
142
|
-
if stderr is STDOUT:
|
143
|
-
err2out = True
|
144
|
-
stderr_tees = set()
|
145
|
-
else:
|
146
|
-
err2out = False
|
147
|
-
|
148
142
|
stdout_fds = {tee.fileno() for tee in stdout_tees}
|
149
143
|
stderr_fds = {tee.fileno() for tee in stderr_tees}
|
150
144
|
prompt_fds = {tee.fileno() for tee in prompt_tees}
|
@@ -176,7 +170,7 @@ class Popen(subprocess.Popen):
|
|
176
170
|
self.stderr_process = stderr_process
|
177
171
|
|
178
172
|
@staticmethod
|
179
|
-
def _get_tee_files(tees:
|
173
|
+
def _get_tee_files(tees: TeeStreams):
|
180
174
|
return set(callable(tee) and tee() or tee for tee in tees)
|
181
175
|
|
182
176
|
@classmethod
|
@@ -218,12 +212,52 @@ class Popen(subprocess.Popen):
|
|
218
212
|
if stderr_tees:
|
219
213
|
stderr_tees.add(sys.stderr)
|
220
214
|
|
215
|
+
if stderr is STDOUT:
|
216
|
+
err2out = True
|
217
|
+
stderr_tees = set()
|
218
|
+
else:
|
219
|
+
err2out = False
|
220
|
+
|
221
221
|
return (
|
222
222
|
cls._get_tee_files(stdout_tees),
|
223
223
|
cls._get_tee_files(stderr_tees),
|
224
|
-
cls._get_tee_files(prompt_tees)
|
224
|
+
cls._get_tee_files(prompt_tees),
|
225
|
+
err2out
|
226
|
+
)
|
227
|
+
|
228
|
+
@classmethod
|
229
|
+
def simulate(
|
230
|
+
cls, cmd, stdout, stderr, encoding=None, errors=None, text=None, comment=None,
|
231
|
+
stdout_tees: TeeStreams = [], add_global_stdout_tees=True,
|
232
|
+
stderr_tees: TeeStreams = [], add_global_stderr_tees=True,
|
233
|
+
prompt_tees: TeeStreams = [], add_global_prompt_tees=True,
|
234
|
+
echo=None
|
235
|
+
):
|
236
|
+
stdout_tees, stderr_tees, prompt_tees, err2out = cls._get_tee_sets(
|
237
|
+
stdout_tees, add_global_stdout_tees,
|
238
|
+
stderr_tees, add_global_stderr_tees,
|
239
|
+
prompt_tees, add_global_prompt_tees,
|
240
|
+
echo, DEVNULL, DEVNULL
|
225
241
|
)
|
226
242
|
|
243
|
+
if not (stdout_tees or stderr_tees or prompt_tees):
|
244
|
+
return
|
245
|
+
|
246
|
+
stdout_fds = {tee.fileno() for tee in stdout_tees}
|
247
|
+
stderr_fds = {tee.fileno() for tee in stderr_tees}
|
248
|
+
prompt_fds = {tee.fileno() for tee in prompt_tees}
|
249
|
+
|
250
|
+
if prompt_fds:
|
251
|
+
stream_prompts(prompt_fds, cmd, None, None, err2out, comment)
|
252
|
+
|
253
|
+
if stdout_fds:
|
254
|
+
with Tee(PIPE, stdout_fds, DEVNULL, encoding, errors, text) as tee:
|
255
|
+
tee.communicate(stdout)
|
256
|
+
|
257
|
+
if stderr_fds:
|
258
|
+
with Tee(PIPE, stderr_fds, DEVNULL, encoding, errors, text) as tee:
|
259
|
+
tee.communicate(stderr)
|
260
|
+
|
227
261
|
def get_pids(self):
|
228
262
|
""" Return the pid for all of the processes in the tree """
|
229
263
|
output = run(
|
@@ -239,13 +273,13 @@ class Popen(subprocess.Popen):
|
|
239
273
|
|
240
274
|
def terminate_tree(self):
|
241
275
|
run(
|
242
|
-
["
|
276
|
+
["kill"] + self.get_pids(),
|
243
277
|
stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL,
|
244
278
|
)
|
245
279
|
|
246
280
|
def kill_tree(self):
|
247
281
|
run(
|
248
|
-
["
|
282
|
+
["kill", "-9"] + self.get_pids(),
|
249
283
|
stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL,
|
250
284
|
)
|
251
285
|
|
@@ -383,7 +417,7 @@ if subprocess.run(
|
|
383
417
|
stderr=DEVNULL
|
384
418
|
).stdout.splitlines()[-2:]
|
385
419
|
|
386
|
-
def stream_prompts(fds: Iterable[
|
420
|
+
def stream_prompts(fds: Iterable[int], cmd, cwd=None, env=None, err2out=False, comment=None):
|
387
421
|
""" Write shell prompt and command into file descriptors fds """
|
388
422
|
fds = normalize_outerr_fds(fds)
|
389
423
|
custom_env = {"CPS1": PS1, "CPS2": PS2}
|
@@ -393,7 +427,7 @@ if subprocess.run(
|
|
393
427
|
"(\n"
|
394
428
|
" IFS= read -r \"line\"\n"
|
395
429
|
" echo \"${CPS1@P}${line}\"\n"
|
396
|
-
" while IFS= read line; do\n"
|
430
|
+
" while IFS= read -r \"line\"; do\n"
|
397
431
|
" echo \"${CPS2@P}${line}\"\n"
|
398
432
|
" done\n"
|
399
433
|
") | " + quote(Tee.get_cmd(fds - {1}))
|
@@ -401,7 +435,7 @@ if subprocess.run(
|
|
401
435
|
subprocess.run(
|
402
436
|
["bash", "-c", script],
|
403
437
|
text=True,
|
404
|
-
input=shellify(cmd, err2out, comment),
|
438
|
+
input=shellify(cmd, err2out, comment) + "\n",
|
405
439
|
stdout=None if 1 in fds else DEVNULL,
|
406
440
|
stderr=None if 2 in fds else DEVNULL,
|
407
441
|
pass_fds=fds - {1, 2},
|
@@ -410,7 +444,7 @@ if subprocess.run(
|
|
410
444
|
check=True
|
411
445
|
)
|
412
446
|
else: # Use hard-coded PS1 and PS2 strings
|
413
|
-
def stream_prompts(fds: Iterable[
|
447
|
+
def stream_prompts(fds: Iterable[int], cmd, cwd=None, env=None, err2out=False, comment=None):
|
414
448
|
""" Write shell prompt and command into file descriptors fds """
|
415
449
|
cmd = shellify(cmd, err2out, comment) + "\n"
|
416
450
|
input = "$ " + "> ".join(cmd.splitlines(keepends=True))
|
@@ -450,7 +484,7 @@ def _output_context(kwargs, key, encoding, errors, text):
|
|
450
484
|
|
451
485
|
def run(
|
452
486
|
args, *, input=None, capture_output=False, timeout=None, check=False,
|
453
|
-
encoding=None, errors=None, text=None, sigterm_timeout=10, **kwargs
|
487
|
+
encoding=None, errors=None, text=None, sigterm_timeout=10, comment=None, **kwargs
|
454
488
|
):
|
455
489
|
""" A subprocess.run that instantiates this module's Popen """
|
456
490
|
if input is not None:
|
@@ -464,7 +498,7 @@ def run(
|
|
464
498
|
kwargs["stdout"] = FILE
|
465
499
|
kwargs["stderr"] = FILE
|
466
500
|
|
467
|
-
comment = f"timeout={timeout}" if timeout else
|
501
|
+
comment = " ".join((comment or "", f"timeout={timeout}" if timeout else "")).strip()
|
468
502
|
with (
|
469
503
|
_output_context(kwargs, "stdout", encoding, errors, text) as stdout_file,
|
470
504
|
_output_context(kwargs, "stderr", encoding, errors, text) as stderr_file,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: iripau
|
3
|
-
Version:
|
3
|
+
Version: 1.1.0
|
4
4
|
Summary: Python utilities focused on command execution
|
5
5
|
Author: Ricardo Quezada
|
6
6
|
Maintainer: Ricardo Quezada
|
@@ -9,11 +9,12 @@ Project-URL: Repository, https://github.com/ricardo-galo/iripau
|
|
9
9
|
Project-URL: Bug Tracker, https://github.com/ricardo-galo/iripau/issues
|
10
10
|
Project-URL: Changelog, https://github.com/ricardo-galo/iripau/blob/master/CHANGELOG.md
|
11
11
|
Keywords: subprocess,command,local,remote,ssh,Linux,real-time,log,logging,threading,utilities
|
12
|
-
Classifier: Development Status ::
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
13
13
|
Classifier: Programming Language :: Python
|
14
14
|
Requires-Python: >=3.7
|
15
15
|
Description-Content-Type: text/markdown
|
16
16
|
License-File: LICENSE
|
17
|
+
Requires-Dist: curlify
|
17
18
|
Requires-Dist: psutil
|
18
19
|
Dynamic: license-file
|
19
20
|
|
@@ -6,6 +6,7 @@ iripau/executable.py
|
|
6
6
|
iripau/functools.py
|
7
7
|
iripau/logging.py
|
8
8
|
iripau/random.py
|
9
|
+
iripau/requests.py
|
9
10
|
iripau/shutil.py
|
10
11
|
iripau/subprocess.py
|
11
12
|
iripau/threading.py
|
@@ -19,6 +20,7 @@ tests/test_executable.py
|
|
19
20
|
tests/test_functools.py
|
20
21
|
tests/test_logging.py
|
21
22
|
tests/test_random.py
|
23
|
+
tests/test_requests.py
|
22
24
|
tests/test_shutil.py
|
23
25
|
tests/test_subprocess.py
|
24
26
|
tests/test_threading.py
|
@@ -4,8 +4,9 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "iripau"
|
7
|
-
version = "
|
7
|
+
version = "1.1.0"
|
8
8
|
dependencies = [
|
9
|
+
"curlify",
|
9
10
|
"psutil"
|
10
11
|
]
|
11
12
|
requires-python = ">=3.7"
|
@@ -21,7 +22,7 @@ license = "MPL-2.0"
|
|
21
22
|
license-files = ["LICENSE"]
|
22
23
|
keywords = ["subprocess", "command", "local", "remote", "ssh", "Linux", "real-time", "log", "logging", "threading", "utilities"]
|
23
24
|
classifiers = [
|
24
|
-
"Development Status ::
|
25
|
+
"Development Status :: 5 - Production/Stable",
|
25
26
|
"Programming Language :: Python"
|
26
27
|
]
|
27
28
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
"""
|
2
|
+
Tests to validate iripau.requests module
|
3
|
+
"""
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
|
7
|
+
from iripau.requests import Session
|
8
|
+
|
9
|
+
|
10
|
+
class TestRequests:
|
11
|
+
|
12
|
+
@pytest.mark.parametrize("hide_output", [False, True], ids=["show_output", "hide_output"])
|
13
|
+
@pytest.mark.parametrize("session_verify", [False, True], ids=["request", "session"])
|
14
|
+
@pytest.mark.parametrize("verify", [False, True], ids=["insecure", "secure"])
|
15
|
+
def test_curlify(self, verify, session_verify, hide_output, capfd):
|
16
|
+
session = Session()
|
17
|
+
|
18
|
+
if session_verify:
|
19
|
+
session.verify = verify
|
20
|
+
|
21
|
+
response = session.post(
|
22
|
+
"https://dummyjson.com/test",
|
23
|
+
verify=None if session_verify else verify,
|
24
|
+
headers={
|
25
|
+
"API-Key": "QWERTY123",
|
26
|
+
"Accept": "application/json",
|
27
|
+
"Authorization": "Bearer 23U746F5R23745RG78345EDR3"
|
28
|
+
},
|
29
|
+
data={
|
30
|
+
"name": "The Name",
|
31
|
+
"status": "Old$"
|
32
|
+
},
|
33
|
+
hide_output=hide_output,
|
34
|
+
headers_to_hide=["API-Key", "Authorization"],
|
35
|
+
headers_to_omit=["User-Agent", "Accept-Encoding", "Connection"],
|
36
|
+
echo=True
|
37
|
+
)
|
38
|
+
|
39
|
+
out, err = capfd.readouterr()
|
40
|
+
if verify:
|
41
|
+
assert "--insecure" not in out
|
42
|
+
else:
|
43
|
+
assert "--insecure" in out
|
44
|
+
|
45
|
+
if hide_output:
|
46
|
+
assert out.endswith("***\n")
|
47
|
+
else:
|
48
|
+
assert not out.endswith("***\n")
|
49
|
+
|
50
|
+
# Omitted headers
|
51
|
+
assert "-H 'Accept: application/json'" in out
|
52
|
+
assert "-H 'API-Key: ***'" in out
|
53
|
+
assert "-H 'Authorization: ***'" in out
|
54
|
+
|
55
|
+
# Hidden headers
|
56
|
+
assert "-H 'Accept-Encoding: gzip, deflate'" not in out
|
57
|
+
assert "-H 'Connection: keep-alive'" not in out
|
58
|
+
assert "-H 'User-Agent: python-requests/2.32.3'" not in out
|
59
|
+
|
60
|
+
# Because of using data argument in the request
|
61
|
+
assert "-H 'Content-Type: application/x-www-form-urlencoded'" in out
|
62
|
+
assert "-d 'name=The+Name&status=Old%24'" in out
|
63
|
+
|
64
|
+
assert not err
|
65
|
+
|
66
|
+
# The request object was not touched
|
67
|
+
assert "Accept-Encoding" in response.request.headers
|
68
|
+
assert "Connection" in response.request.headers
|
69
|
+
assert "User-Agent" in response.request.headers
|
70
|
+
|
71
|
+
assert response.request.headers["API-Key"] != "***"
|
72
|
+
assert response.request.headers["Authorization"] != "***"
|
@@ -292,7 +292,8 @@ class TestSubprocess:
|
|
292
292
|
@pytest.mark.parametrize("stderr", [None, STDOUT], ids=["no_redirect", "redirect"])
|
293
293
|
@pytest.mark.parametrize("stdout", [None, PIPE, FILE], ids=["no_capture", "pipe", "file"])
|
294
294
|
@pytest.mark.parametrize("echo", [False, True], ids=["no_echo", "echo"])
|
295
|
-
|
295
|
+
@pytest.mark.parametrize("simulation", [False, True], ids=["reality", "simulation"])
|
296
|
+
def test_run_tee(self, simulation, echo, stdout, stderr, extra_tees, manual_echo, capfd):
|
296
297
|
redirect = stderr is STDOUT
|
297
298
|
stdout_command = "echo This goes to stdout"
|
298
299
|
stderr_command = "echo This goes to stderr >&2"
|
@@ -314,9 +315,6 @@ class TestSubprocess:
|
|
314
315
|
stderr_tees.add(sys.stderr)
|
315
316
|
|
316
317
|
kwargs = {
|
317
|
-
"stdout": stdout,
|
318
|
-
"stderr": stderr,
|
319
|
-
"shell": True,
|
320
318
|
"text": True,
|
321
319
|
"echo": echo,
|
322
320
|
"stdout_tees": stdout_tees | all_tees,
|
@@ -324,21 +322,6 @@ class TestSubprocess:
|
|
324
322
|
"prompt_tees": prompt_tees | all_tees
|
325
323
|
}
|
326
324
|
|
327
|
-
expected_stdout_1 = expected_stderr_1 = expected_stdout_2 = expected_stderr_2 = None
|
328
|
-
if stdout is not None:
|
329
|
-
expected_stdout_1 = "This goes to stdout\n"
|
330
|
-
expected_stdout_2 = "This goes to stderr\n" if redirect else ""
|
331
|
-
|
332
|
-
count = 30
|
333
|
-
for _ in range(count):
|
334
|
-
output = run(stdout_command, **kwargs)
|
335
|
-
assert expected_stdout_1 == output.stdout
|
336
|
-
assert expected_stderr_1 == output.stderr
|
337
|
-
|
338
|
-
output = run(stderr_command, **kwargs)
|
339
|
-
assert expected_stdout_2 == output.stdout
|
340
|
-
assert expected_stderr_2 == output.stderr
|
341
|
-
|
342
325
|
expected_stdout_command_prompt = get_prompt_and_command(stdout_command, redirect) + "\n"
|
343
326
|
expected_stderr_command_prompt = get_prompt_and_command(stderr_command, redirect) + "\n"
|
344
327
|
expected_prompt = expected_stdout_command_prompt + expected_stderr_command_prompt
|
@@ -352,8 +335,11 @@ class TestSubprocess:
|
|
352
335
|
expected_stderr = ""
|
353
336
|
if echo or manual_echo:
|
354
337
|
expected_captured_stdout = expected_output
|
338
|
+
elif stdout is None:
|
339
|
+
expected_captured_stdout = "" if simulation else expected_stdout
|
355
340
|
else:
|
356
|
-
expected_captured_stdout =
|
341
|
+
expected_captured_stdout = ""
|
342
|
+
expected_captured_stderr = ""
|
357
343
|
else:
|
358
344
|
expected_stdout = "This goes to stdout\n"
|
359
345
|
expected_stderr = "This goes to stderr\n"
|
@@ -362,8 +348,48 @@ class TestSubprocess:
|
|
362
348
|
expected_stdout_command_prompt + "This goes to stdout\n" +
|
363
349
|
expected_stderr_command_prompt
|
364
350
|
)
|
351
|
+
expected_captured_stderr = expected_stderr
|
352
|
+
elif stdout is None:
|
353
|
+
if simulation:
|
354
|
+
expected_captured_stdout = ""
|
355
|
+
expected_captured_stderr = ""
|
356
|
+
else:
|
357
|
+
expected_captured_stdout = expected_stdout
|
358
|
+
expected_captured_stderr = expected_stderr
|
365
359
|
else:
|
366
|
-
expected_captured_stdout =
|
360
|
+
expected_captured_stdout = ""
|
361
|
+
expected_captured_stderr = "" if simulation else expected_stderr
|
362
|
+
|
363
|
+
count = 1
|
364
|
+
if simulation:
|
365
|
+
fake_stdout_command = stdout_command
|
366
|
+
fake_stderr_command = stderr_command
|
367
|
+
fake_stdout_1 = "This goes to stdout\n"
|
368
|
+
fake_stderr_1 = ""
|
369
|
+
fake_stdout_2 = ""
|
370
|
+
fake_stderr_2 = "This goes to stderr\n"
|
371
|
+
if redirect: # Simulate redirection
|
372
|
+
fake_stdout_command += " 2>&1"
|
373
|
+
fake_stderr_command += " 2>&1"
|
374
|
+
fake_stdout_2, fake_stderr_2 = fake_stderr_2, fake_stdout_2
|
375
|
+
|
376
|
+
for _ in range(count):
|
377
|
+
Popen.simulate(fake_stdout_command, fake_stdout_1, fake_stderr_1, **kwargs)
|
378
|
+
Popen.simulate(fake_stderr_command, fake_stdout_2, fake_stderr_2, **kwargs)
|
379
|
+
else:
|
380
|
+
expected_stdout_1 = expected_stderr_1 = expected_stdout_2 = expected_stderr_2 = None
|
381
|
+
if stdout is not None:
|
382
|
+
expected_stdout_1 = "This goes to stdout\n"
|
383
|
+
expected_stdout_2 = "This goes to stderr\n" if redirect else ""
|
384
|
+
|
385
|
+
for _ in range(count):
|
386
|
+
output = run(stdout_command, stdout=stdout, stderr=stderr, shell=True, **kwargs)
|
387
|
+
assert expected_stdout_1 == output.stdout
|
388
|
+
assert expected_stderr_1 == output.stderr
|
389
|
+
|
390
|
+
output = run(stderr_command, stdout=stdout, stderr=stderr, shell=True, **kwargs)
|
391
|
+
assert expected_stdout_2 == output.stdout
|
392
|
+
assert expected_stderr_2 == output.stderr
|
367
393
|
|
368
394
|
expected_content = expected_stdout * count
|
369
395
|
for file in stdout_tees - {sys.stdout}:
|
@@ -383,7 +409,7 @@ class TestSubprocess:
|
|
383
409
|
|
384
410
|
out, err = capfd.readouterr()
|
385
411
|
assert expected_captured_stdout * count == out
|
386
|
-
assert
|
412
|
+
assert expected_captured_stderr * count == err
|
387
413
|
|
388
414
|
@pytest.mark.parametrize("echo", [False, True], ids=["no_echo", "echo"])
|
389
415
|
def test_run_alias(self, echo, capfd):
|
@@ -1 +0,0 @@
|
|
1
|
-
psutil
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|