docs & fix

- improve method docstrings
- some fixes
This commit is contained in:
Daniel Langbein 2023-03-18 15:24:10 +01:00
parent ec472cf5aa
commit 326e3bf5e6
9 changed files with 116 additions and 62 deletions

View File

@ -7,6 +7,16 @@ def test():
class DataUnitConverter: 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): class DataUnit(Enum):
B = 'B' B = 'B'
KB = 'KB' KB = 'KB'

View File

@ -4,6 +4,10 @@ import subprocess
def execute_capture(command: list[str]) -> tuple[int, str, str]: 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( completed: subprocess.CompletedProcess = subprocess.run(
command, command,
capture_output=True, capture_output=True,

View File

@ -16,7 +16,16 @@ def execute_consume_chunks(command: list[str],
handle_chunks: Callable[[Queue.put], None], handle_chunks: Callable[[Queue.put], None],
) -> int: ) -> 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 command:
:param handle_chunks: Has one parameter, `queue_put`. `handle_chunks` must call queue_put.(chunk_path, last_chunk) for each saved chunk. :param handle_chunks: Has one parameter, `queue_put`. `handle_chunks` must call queue_put.(chunk_path, last_chunk) for each saved chunk.

View File

@ -7,37 +7,6 @@ import subprocess
from typing import AnyStr, IO 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. # 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. 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 command: Command to execute, e.g. ['ls', '-la', '/home']
:param encoding: :param encoding:
@ -127,3 +97,34 @@ def execute_print_capture(command: list[str], encoding='UTF-8') -> tuple[int, st
t_write.join() t_write.join()
return returncode, ''.join(out), ''.join(err) 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)

View File

@ -14,15 +14,15 @@ def execute_produce_chunks(command: list[str],
chunk_size: int = 1024 * 1024, chunk_size: int = 1024 * 1024,
) -> int: ) -> int:
""" """
Executes the given command in a subprocess. Executes the given `command` in a subprocess.
Stdout of the subprocess is saved in chunks. The stdout of the subprocess is saved in chunks.
The location of the chunks is determined by `get_chunk_path`. 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. 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 processed chunks from disk. 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 command:
:param get_chunk_path: :param get_chunk_path:

View File

@ -13,6 +13,19 @@ def receive_inform(in_pipe: IO[AnyStr],
get_chunk_file: Callable[[Path, int], Path], get_chunk_file: Callable[[Path, int], Path],
) -> None: ) -> 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 get_chunk_file:
:param in_pipe: :param in_pipe:
:param chunk_file_tmpl: :param chunk_file_tmpl:

View File

@ -10,30 +10,40 @@ def repeat_until_successful(command: list[str],
retries: int = 0, retries: int = 0,
retry_delay_seconds: float = 5) -> str: 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 If the command has failed, it is executed at most `retires` times again, until successful.
`socket_file` and waits for user input. 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: while True:
returncode: int returncode: int
out: str stdout: str
returncode, out, _err = execute_print_capture(command) returncode, stdout, _stderr = execute_print_capture(command)
# If no error occurred, return stdout of subprocess.
if returncode == 0: if returncode == 0:
time.sleep(retry_delay_seconds) return stdout
return out # Else, an error has occurred.
print(f'\n' print(f'\n'
f'Error while executing:\n' f'Error while executing:\n'
f'\t{command}\n' f'\t{command}\n'
f'\tFor details, see above output.') f'\tFor details, see above output.')
# If retry attempts are left,
# wait `retry_delay_seconds` and then execute the command again.
if retries > 0: if retries > 0:
print('\tRetrying the failed command.' print(f'\tRetrying the failed command in {retry_delay_seconds} seconds.'
'\n') f'\n')
retries = retries - 1 time.sleep(retry_delay_seconds)
retries -= 1
continue continue
# Else, no more retry attempts are left.
# Print message and wait for user input.
print(f'Info:\n' print(f'Info:\n'
f'\tPlease fix the above error first. Then continue here:\n' f'\tPlease fix the above error first. Then continue here:\n'
f'\tsudo pacman -S --needed openbsd-netcat\n' f'\tsudo pacman -S --needed openbsd-netcat\n'

View File

@ -14,7 +14,7 @@ def wait_for_message(socket_file: Path,
Closes the UNIX socket. Closes the UNIX socket.
:returns: The message that was received. :return: The message that was received.
""" """
# INSPIRATION: https://pymotw.com/3/socket/uds.html # INSPIRATION: https://pymotw.com/3/socket/uds.html

View File

@ -4,6 +4,13 @@ from pathlib import Path
def write_message(socket_file: Path, def write_message(socket_file: Path,
message: bytes): message: bytes):
"""
Writes `message` to the UNIX socket `socket_file`.
:param socket_file:
:param message:
:return:
"""
# INSPIRATION: https://pymotw.com/3/socket/uds.html # INSPIRATION: https://pymotw.com/3/socket/uds.html