mirror of
https://codeberg.org/privacy1st/subprocess-util
synced 2024-12-22 22:06:05 +01:00
docs & fix
- improve method docstrings - some fixes
This commit is contained in:
parent
ec472cf5aa
commit
326e3bf5e6
@ -7,6 +7,16 @@ def test():
|
||||
|
||||
|
||||
class DataUnitConverter:
|
||||
"""
|
||||
A class to convert between data units.
|
||||
|
||||
Example: 1.000.000 B == 1 MB
|
||||
|
||||
The main purpose is to convert a number with many digits
|
||||
to a "larger" unit so that the number is shorter
|
||||
and more readable.
|
||||
"""
|
||||
|
||||
class DataUnit(Enum):
|
||||
B = 'B'
|
||||
KB = 'KB'
|
||||
|
@ -4,6 +4,10 @@ import subprocess
|
||||
|
||||
|
||||
def execute_capture(command: list[str]) -> tuple[int, str, str]:
|
||||
"""
|
||||
:param command: Executes the given `command` in a subprocess.
|
||||
:return: (returncode, stdout, stderr)
|
||||
"""
|
||||
completed: subprocess.CompletedProcess = subprocess.run(
|
||||
command,
|
||||
capture_output=True,
|
||||
|
@ -16,7 +16,16 @@ def execute_consume_chunks(command: list[str],
|
||||
handle_chunks: Callable[[Queue.put], None],
|
||||
) -> int:
|
||||
"""
|
||||
Local chunks are deleted after they are fed to stdin of subprocess.
|
||||
The `command` is executed in a subprocess.
|
||||
|
||||
`handle_chunks` is executed in a separate thread.
|
||||
After `handle_chunks` has saved a new chunk,
|
||||
the path of the saved chunk is added to a `Queue`.
|
||||
|
||||
For each chunk that has been added to the `Queue`, we
|
||||
- read the chunk from disk
|
||||
- write the chunk to the stdin of the subprocess
|
||||
- and delete the chunk
|
||||
|
||||
:param command:
|
||||
:param handle_chunks: Has one parameter, `queue_put`. `handle_chunks` must call queue_put.(chunk_path, last_chunk) for each saved chunk.
|
||||
|
@ -7,37 +7,6 @@ import subprocess
|
||||
from typing import AnyStr, IO
|
||||
|
||||
|
||||
class _Assert:
|
||||
@staticmethod
|
||||
def true(a):
|
||||
if not a:
|
||||
raise ValueError(f'Expected a to be true: {a}')
|
||||
|
||||
@staticmethod
|
||||
def equal(a, b):
|
||||
if a != b:
|
||||
raise ValueError(f'Expected a and b to be equal: {a}, {b}')
|
||||
|
||||
|
||||
def _read_output(str_pipe: IO[AnyStr],
|
||||
queue_put: Queue.put,
|
||||
list_append: list.append,
|
||||
prefix: str = ''):
|
||||
line: str
|
||||
for line in str_pipe:
|
||||
queue_put(f'{prefix}{line}')
|
||||
list_append(line)
|
||||
|
||||
# TODO: Has this any effect?
|
||||
# str_pipe.close()
|
||||
|
||||
|
||||
def _write_output(queue_get: Queue.get):
|
||||
# Take items out of queue until taken item is None.
|
||||
for line in iter(queue_get, None):
|
||||
sys.stdout.write(line)
|
||||
|
||||
|
||||
# Goal: We want to **capture** and **print** stdout/stderr while running the command.
|
||||
#
|
||||
#
|
||||
@ -79,7 +48,8 @@ def execute_print_capture(command: list[str], encoding='UTF-8') -> tuple[int, st
|
||||
"""
|
||||
Executes the given command.
|
||||
|
||||
Stdout and stderr are printed in real time.
|
||||
The stdout and stderr are printed in real time
|
||||
and returned after the command has finished.
|
||||
|
||||
:param command: Command to execute, e.g. ['ls', '-la', '/home']
|
||||
:param encoding:
|
||||
@ -127,3 +97,34 @@ def execute_print_capture(command: list[str], encoding='UTF-8') -> tuple[int, st
|
||||
t_write.join()
|
||||
|
||||
return returncode, ''.join(out), ''.join(err)
|
||||
|
||||
|
||||
class _Assert:
|
||||
@staticmethod
|
||||
def true(a):
|
||||
if not a:
|
||||
raise ValueError(f'Expected a to be true: {a}')
|
||||
|
||||
@staticmethod
|
||||
def equal(a, b):
|
||||
if a != b:
|
||||
raise ValueError(f'Expected a and b to be equal: {a}, {b}')
|
||||
|
||||
|
||||
def _read_output(str_pipe: IO[AnyStr],
|
||||
queue_put: Queue.put,
|
||||
list_append: list.append,
|
||||
prefix: str = ''):
|
||||
line: str
|
||||
for line in str_pipe:
|
||||
queue_put(f'{prefix}{line}')
|
||||
list_append(line)
|
||||
|
||||
# TODO: Has this any effect?
|
||||
# str_pipe.close()
|
||||
|
||||
|
||||
def _write_output(queue_get: Queue.get):
|
||||
# Take items out of queue until taken item is None.
|
||||
for line in iter(queue_get, None):
|
||||
sys.stdout.write(line)
|
||||
|
@ -14,15 +14,15 @@ def execute_produce_chunks(command: list[str],
|
||||
chunk_size: int = 1024 * 1024,
|
||||
) -> int:
|
||||
"""
|
||||
Executes the given command in a subprocess.
|
||||
Executes the given `command` in a subprocess.
|
||||
|
||||
Stdout of the subprocess is saved in chunks.
|
||||
The location of the chunks is determined by `get_chunk_path`.
|
||||
The stdout of the subprocess is saved in chunks.
|
||||
The location of the chunks is determined by `get_chunk_path(chunk_no)`.
|
||||
|
||||
A separate thread calls `handle_chunk` once a new chunk was saved.
|
||||
It is the duty of `handle_chunk` to delete processed chunks from disk.
|
||||
A separate thread calls `handle_chunk(chunk_no, last_chunk)` once a new chunk was saved.
|
||||
It is the duty of `handle_chunk` to delete the processed chunk.
|
||||
|
||||
Stderr of the subprocess is printed to sys.stderr.
|
||||
The stderr of the subprocess is printed to sys.stderr.
|
||||
|
||||
:param command:
|
||||
:param get_chunk_path:
|
||||
|
@ -13,6 +13,19 @@ def receive_inform(in_pipe: IO[AnyStr],
|
||||
get_chunk_file: Callable[[Path, int], Path],
|
||||
) -> None:
|
||||
"""
|
||||
Creates a listening UNIX socket at `socket_file`.
|
||||
|
||||
Waits for incoming messages `OK` and `EOF` on the socket.
|
||||
|
||||
Once a message is received,
|
||||
the file returned by `get_chunk_file(chunk_file_tmpl, chunk_no)`
|
||||
is read and then written to `in_pipe`.
|
||||
|
||||
If the received message was `EOF`, then the listening loop ends.
|
||||
If the received message was `OK`, then chunk_no is increased by one and the loop continues.
|
||||
|
||||
Finally, the socket is closed.
|
||||
|
||||
:param get_chunk_file:
|
||||
:param in_pipe:
|
||||
:param chunk_file_tmpl:
|
||||
|
@ -10,30 +10,40 @@ def repeat_until_successful(command: list[str],
|
||||
retries: int = 0,
|
||||
retry_delay_seconds: float = 5) -> str:
|
||||
"""
|
||||
Executes the given `command`.
|
||||
Executes the given `command` and returns its stdout.
|
||||
|
||||
If an error occurs, it creates a UNIX socket at
|
||||
`socket_file` and waits for user input.
|
||||
If the command has failed, it is executed at most `retires` times again, until successful.
|
||||
Between each retry we wait for `retry_delay_seconds`.
|
||||
|
||||
If no more retries are left, a UNIX socket is created at `socket_file`
|
||||
and we wait for user input.
|
||||
"""
|
||||
while True:
|
||||
returncode: int
|
||||
out: str
|
||||
returncode, out, _err = execute_print_capture(command)
|
||||
stdout: str
|
||||
returncode, stdout, _stderr = execute_print_capture(command)
|
||||
|
||||
# If no error occurred, return stdout of subprocess.
|
||||
if returncode == 0:
|
||||
time.sleep(retry_delay_seconds)
|
||||
return out
|
||||
return stdout
|
||||
# Else, an error has occurred.
|
||||
|
||||
print(f'\n'
|
||||
f'Error while executing:\n'
|
||||
f'\t{command}\n'
|
||||
f'\tFor details, see above output.')
|
||||
|
||||
# If retry attempts are left,
|
||||
# wait `retry_delay_seconds` and then execute the command again.
|
||||
if retries > 0:
|
||||
print('\tRetrying the failed command.'
|
||||
'\n')
|
||||
retries = retries - 1
|
||||
print(f'\tRetrying the failed command in {retry_delay_seconds} seconds.'
|
||||
f'\n')
|
||||
time.sleep(retry_delay_seconds)
|
||||
retries -= 1
|
||||
continue
|
||||
# Else, no more retry attempts are left.
|
||||
|
||||
# Print message and wait for user input.
|
||||
print(f'Info:\n'
|
||||
f'\tPlease fix the above error first. Then continue here:\n'
|
||||
f'\tsudo pacman -S --needed openbsd-netcat\n'
|
||||
|
@ -14,7 +14,7 @@ def wait_for_message(socket_file: Path,
|
||||
|
||||
Closes the UNIX socket.
|
||||
|
||||
:returns: The message that was received.
|
||||
:return: The message that was received.
|
||||
"""
|
||||
|
||||
# INSPIRATION: https://pymotw.com/3/socket/uds.html
|
||||
|
@ -4,6 +4,13 @@ from pathlib import Path
|
||||
|
||||
def write_message(socket_file: Path,
|
||||
message: bytes):
|
||||
"""
|
||||
Writes `message` to the UNIX socket `socket_file`.
|
||||
|
||||
:param socket_file:
|
||||
:param message:
|
||||
:return:
|
||||
"""
|
||||
|
||||
# INSPIRATION: https://pymotw.com/3/socket/uds.html
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user