iripau 0.1.0__py3-none-any.whl → 1.1.0__py3-none-any.whl

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/requests.py ADDED
@@ -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
iripau/subprocess.py CHANGED
@@ -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: Iterable[TeeStream] = [], add_global_stdout_tees=True,
128
- stderr_tees: Iterable[TeeStream] = [], add_global_stderr_tees=True,
129
- prompt_tees: Iterable[TeeStream] = [], add_global_prompt_tees=True,
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: Iterable[TeeStream]):
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
- ["sudo", "kill"] + self.get_pids(),
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
- ["sudo", "kill", "-9"] + self.get_pids(),
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[str], cmd, cwd=None, env=None, err2out=False, comment=None):
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[str], cmd, cwd=None, env=None, err2out=False, comment=None):
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 None
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: 0.1.0
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 :: 4 - Beta
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
 
@@ -3,11 +3,12 @@ iripau/executable.py,sha256=W00UNJfMWwZ7cZkEvppwVWy8GaGfbr36eRx0BwaI6mA,3109
3
3
  iripau/functools.py,sha256=Jyi0LdOx36Ih6s9TF4aFS3Xk-RW78YE2znB1PBWTAIo,2901
4
4
  iripau/logging.py,sha256=dA-69y5MN8a_c95e_DOlULO0tc7t267okFi9iqJzrmg,2542
5
5
  iripau/random.py,sha256=zC5GxmBdfVa6nAUZcv5gCLwBpZU3sA1TB71Cn311lHo,1023
6
+ iripau/requests.py,sha256=BJIlz2UzB7wNEHx0IZ-BF1UNfmRxXBhRaW86HBO0jII,2597
6
7
  iripau/shutil.py,sha256=HwU4sUrStzAZjqbIMK7xm-NfjxMXHZx4lWAjwfBf4rw,2558
7
- iripau/subprocess.py,sha256=T4BF5pdvLo8bhLccA1Oy8p0VZSoHvWD1YWSJpxYry4E,16358
8
+ iripau/subprocess.py,sha256=mQDLrBgTOEJ_Ueb1nqIbRkrzMlnYWjfXS8kJsspMcsE,17684
8
9
  iripau/threading.py,sha256=q-a6OuDrz3hg_5u2Q0eMG3hzu_IgGNBfEw-cI_pyaDo,8955
9
- iripau-0.1.0.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
10
- iripau-0.1.0.dist-info/METADATA,sha256=yzxsS5gTUZDFlqfTDlU_Nqjk4jXwBuZ3A9cXh2Q4mnY,774
11
- iripau-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
- iripau-0.1.0.dist-info/top_level.txt,sha256=y2HMFLCoP2EP7kbRIqkyCVIf_YRZfA0cYYLNK28U9cY,7
13
- iripau-0.1.0.dist-info/RECORD,,
10
+ iripau-1.1.0.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
11
+ iripau-1.1.0.dist-info/METADATA,sha256=U60idKE5l07kZz3VlMhNPWoHXZsco1DdkpKGGCgGqC0,810
12
+ iripau-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
+ iripau-1.1.0.dist-info/top_level.txt,sha256=y2HMFLCoP2EP7kbRIqkyCVIf_YRZfA0cYYLNK28U9cY,7
14
+ iripau-1.1.0.dist-info/RECORD,,
File without changes