Edit on GitHub

pipewire_python._utils

Here we store internal functions, don't expect to see something here in documentation html version.

View Source
"""
Here we store internal functions, don't expect
to see something here in documentation html version.
"""
import subprocess
import asyncio
import re

# Loading constants Constants.py
from ._constants import MESSAGES_ERROR


def _print_std(
    stdout: bytes,
    stderr: bytes,
    # Debug
    verbose: bool = False,
):
    """
    Print terminal output if are different to None and verbose activated
    """

    if stdout is not None and verbose:
        print(f"[_print_std][stdout][type={type(stdout)}]\n{stdout.decode()}")
    if stderr is not None and verbose:
        print(f"[_print_std][stderr][type={type(stderr)}]\n{stderr.decode()}")


def _get_dict_from_stdout(
    stdout: str,
    # Debug
    verbose: bool = False,
):
    """
    Converts shell output (str) to dictionary looking for
    "default" and "--" values
    """

    rows = stdout.split("\n")
    config_dict = {}
    for row in rows:
        if "default" in row:
            key = "--" + row.split("--")[1].split(" ")[0]
            value = row.split("default ")[1].replace(")", "")
            config_dict[key] = value
    if verbose:
        print(config_dict)
    return config_dict


def _update_dict_by_dict(
    main_dict: dict,
    secondary_dict: dict,
):
    """
    Update values of one dictionary with values of another dictionary
    based on keys
    """
    return main_dict.update(([(key, secondary_dict[key]) for key in secondary_dict.keys()]))


def _drop_keys_with_none_values(main_dict: dict):
    """
    Drop keys with None values to parse safe dictionary config
    """
    return {k: v for k, v in main_dict.items() if v is not None}


def _generate_command_by_dict(
    mydict: dict,
    # Debug
    verbose: bool = False,
):
    """
    Generate an array based on dictionary with keys and values
    """
    array_command = []
    # append to a list
    for key, value in mydict.items():
        array_command.extend([key, value])
    if verbose:
        print(array_command)
    # return values
    return array_command


def _execute_shell_command(
    command: list[str],
    timeout: int = -1,  # *default= no limit
    # Debug
    verbose: bool = False,
):
    """
    Execute command on terminal via subprocess

    Args:
        - command (str): command line to execute. Example: 'ls -l'
        - timeout (int): (seconds) time to end the terminal process
        - verbose (bool): print variables for debug purposes
    Return:
        - stdout (str): terminal response to the command
        - stderr (str): terminal response to the command
    """
    # Create subprocess
    # NO-RESOURCE-ALLOCATING
    # terminal_subprocess = subprocess.Popen(
    #     command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT  # Example ['ls ','l']
    # )

    with subprocess.Popen(
        command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT  # Example ['ls ','l']
    ) as terminal_subprocess:
        # Execute command depending or not in timeout
        try:
            if timeout == -1:
                stdout, stderr = terminal_subprocess.communicate()
            else:
                stdout, stderr = terminal_subprocess.communicate(timeout=timeout)
        except subprocess.TimeoutExpired:  # When script finish in time
            terminal_subprocess.kill()
            stdout, stderr = terminal_subprocess.communicate()

        # Print terminal output
        _print_std(stdout, stderr, verbose=verbose)

        # Return terminal output
        return stdout, stderr


async def _execute_shell_command_async(
    command,
    timeout: int = -1,
    # Debug
    verbose: bool = False,
):
    """[ASYNC] Function that execute terminal commands in asyncio way

    Args:
        - command (str): command line to execute. Example: 'ls -l'
    Return:
        - stdout (str): terminal response to the command.
        - stderr (str): terminal response to the command.
    """
    if timeout == -1:
        # No timeout
        terminal_process_async = await asyncio.create_subprocess_shell(
            command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
        )
        stdout, stderr = await terminal_process_async.communicate()
        print(
            f"[_execute_shell_command_async]\
                [{command!r} exited with\
                {terminal_process_async.returncode}]"
        )
        _print_std(stdout, stderr, verbose=verbose)

    else:
        raise NotImplementedError(MESSAGES_ERROR["NotImplementedError"])

    return stdout, stderr


def _generate_dict_list_targets(
    longstring: str,  # string output of shell
    # Debug
    verbose: bool = False,
):
    """
    Function that transform long string of list targets
    to a `dict`
    """

    regex_id = r"(\d.*):"
    regex_desc = r'description="([^"]*)"'
    regex_prio = r"prio=(-?\d.*)"
    regex_default_node = r"[*]\t(\d\d)"
    regex_alsa_node = r"(alsa_[a-zA-Z].*)"

    results_regex_id = re.findall(regex_id, longstring)
    results_regex_desc = re.findall(regex_desc, longstring)
    results_regex_prio = re.findall(regex_prio, longstring)
    results_regex_default_node = re.findall(regex_default_node, longstring)
    results_regex_alsa_mode = re.findall(regex_alsa_node, longstring)

    mydict = {}
    for idx, _ in enumerate(results_regex_id):
        mydict[results_regex_id[idx]] = {
            "description": results_regex_desc[idx],
            "prior": results_regex_prio[idx],
        }
    mydict["_list_nodes"] = results_regex_id
    mydict["_node_default"] = results_regex_default_node
    mydict["_alsa_node"] = results_regex_alsa_mode

    if verbose:
        print(mydict)

    return mydict


def _generate_dict_interfaces(
    longstring: str,  # string output of shell
    # Debug
    verbose: bool = False,
):
    """
    Function that transform long string of list interfaces
    to a `dict`
    """

    mydict = {}
    text_in_lines = longstring.split("\n")
    first_level = "X"

    for line in text_in_lines:
        try:
            is_interface = True
            if "id: " in line:
                # when interface starts
                regex_id = r"\tid: ([0-9]*)"
                results_regex_id = re.findall(regex_id, line)
                is_interface = False

            if is_interface:
                if "*" in line[:1]:
                    # delete * on each line at the beginning
                    line = line[1:]
                if "\t\t" in line:
                    # third level data
                    data = line.replace("\t\t", "")
                    data = data.split(" = ")
                    third_level = str(data[0])
                    data_to_place = " ".join(data[1:]).replace('"', "")

                    if "properties" not in mydict[first_level]:
                        mydict[first_level]["properties"] = {}
                    if third_level not in mydict[first_level]["properties"]:
                        mydict[first_level]["properties"][third_level] = {}
                    mydict[first_level]["properties"][third_level] = data_to_place

                elif "\t  " in line:
                    # second level data: params

                    data = line.replace("\t  ", "").split(" ")
                    third_level = str(data[0])
                    if type(mydict[first_level]["params"]) != dict:
                        mydict[first_level]["params"] = {}
                    mydict[first_level]["params"][third_level] = {
                        "spa": data[1],
                        "permissions": data[2],
                    }

                elif "\t" in line:
                    # first level data
                    data = line.replace("\t", "")
                    data = data.split(": ")
                    first_level = str(results_regex_id[0])
                    second_level = str(data[0])
                    data_to_place = " ".join(data[1:]).replace('"', "")

                    # to_dict
                    if first_level not in mydict:
                        mydict[str(first_level)] = {}

                    mydict[first_level][second_level] = data_to_place
        except Exception as e:
            print(e)

    if verbose:
        print(mydict)

    return mydict


def _filter_by_type(
    dict_interfaces: dict,  # interfaecs dict
    type_interfaces: str,  # string with type
    # Debug
    verbose: bool = False,
):
    """
    Function that filters a `dict` by type of interface
    """

    dict_filtered = {}
    for key in dict_interfaces:
        # Filter
        if type_interfaces in dict_interfaces[key]["type"]:
            dict_filtered[key] = dict_interfaces[key]

    if verbose:
        print(dict_filtered)

    return dict_filtered