Reply To: antiX Diagnostic Suite For Hardware Testing (proposal)

Forum › Forums › antiX-development › Development › antiX Diagnostic Suite For Hardware Testing (proposal) › Reply To: antiX Diagnostic Suite For Hardware Testing (proposal)

#185970
sybok
Member

    I am not sure how much time I could dedicate to this project above occasional “mentoring”.

    IMHO, the start should be at least a basic idea of strategy for the modular framework.

    First, consider a single tool (ST) workflow:
    ST.1) Detect presence of a tool:
    ST.1.A) a package name – may change over time; parsing output of subprocess.call(), ‘apt-cache policy’ or ‘dpkg -l’ or use python3-apt* as a dependency?
    ST.1.B) a command – shutil.which() is not None
    ST.1.C) a binary file – os.path.isfile()

    If a tool is missing, is there a plan to enable initiating installation from within the tool?
    What if the tool is not in repository – provide a link?

    ST.2) Collect switches|options for the tool.
    This should most likely be version aware.
    In case of GUI, combination of several types of widgets – verbosity (spinbox?), feature on|off (checkbuttons)

    ST.3) Call the tool with appropriate switches – subprocess.call()?

    Handle errors, root privileges required.
    ‘lshw’ works without root but it provides less information.
    Is that OK? Should there be a switch to select or require full-run by default?
    Some tools may run for a long time, need to repeatedly insert the root password! (Is there a safe workaround?)

    ST.4) Parse the output.

    What if there is no dedicated output file for the tool (and no option to specify one) – suitable redirection or or store in Python’s ‘*.stdout’ attribute directly?

    ST.5) Create a corresponding block of a report.

    The above leads me to propose the following for consideration – there should be a configuration table (a CSV file?) matching a tool (command or full-path binary) to a package and its output file, i.e.
    A) tool = command|binary file
    B) package in repository (if available),
    C) (package|program) version (parsing program version could get tricky, package is easier but beware of both Debian and antiX-specific naming suffices),
    D) output files where the info is stored (if empty, redirect STDOUT + STDERR to a temporary file or files such as ‘/tmp/aux_hw_test_<tool name>_$(date +%Y-%m-%d).tmp’ or store in Python’s ‘*.stdout’ attribute directly?)

    D) Beware of Debian 13+ (Trixie and possibly ongoing) on low-RAM systems https://www.debian.org/releases/trixie/release-notes/issues.html#the-temporary-files-directory-tmp-is-now-stored-in-a-tmpfs

    If I were to write it, here’s how I (think I) would proceed:
    Each tool a single Python file-name script (or even one file for each version of a given tool) and the overall “master” script.

    # Global variables in this script may partially replace the "configuration" script
    class SingleTool():
        """ Single class for a single tool; if version specific, then consider reducing code duplication using inheritance """
        def __init__():
        def compose_command(<object containing collected switches>):
        def parse_output():
        def partial_report() -> str:
        def does_all_that() -> str:
            """ Composes the above functions """
    TOOL_MAP: dict[str, Any] = {
        'ToolName':<SingleToolClass>, # This might be duplicated if the string ToolName includes version
    }
    
    class OverAllStuff():
        """ OverAll stuff """
        def __init__():
            partial_report: str = '' # Can be a HTML tree instead
            self.tool_set: dict[str, Any] = {} # To store tool "configuration"|switches
        def detect_tools():
            """ Iterate over the tool-table """
        def ask_to_install_missing_tools():
            """ It may not be needed """
        def build_display_or_gui_based_on_tools():
        def collect_settings_for_each_tool():
            """ Get current values of widgets - fills the 'self.tool_set' """
    
        def initiate_dummy_html():
            """ Start an empty report, e.g. HTML """
        def finalise_report():
            """ Properly end the report, e.g. HTML """
        def iterate_over_tools():
            for ToolName, <object containing collected switches> in self.tool_set.items():
                partial_report += TOOL_MAP[ToolName].does_all_that(<object containing collected switches>)
        def does_all_that():
            """ Composes all the above functions """

    EDIT:
    The configuration object for each tool could be a list-valued dictionary:

    BIN_TOOL: str = <path to the binary file OR command calling the tool>
    DAT_TOOL: dict[str, list] = {
        <text label (of the tool in the GUI|TUI>:[<cli option text>, <unitialised widget>, <a dummy value>]
    }

    Once the GUI is built from the individual DAT_TOOLs, the widget is initialised and its value can be set in the GUI.
    When calling the individual tool, one simply passes the set values instead of individual widgets, i.e.
    dat_tool: dict[str, list[str]] = {<text label (of the tool in the GUI|TUI>:[<str: cli option text>, <str: value set to the widget>, , <str: a dummy value as a string>]}
    When composing the command, a general approach is to build it up from the ‘dat_tool’ acquired from the GUI

    cmd_runs: list[str] = [BIN_TOOL]
    for val_item in dat_tool.values():
         if val_item[-2] != val_item[-1]:
             cmd_runs += val_item[:-1]

    Iterations in code (and there will be many) are said to be slow but expecting that running the tools will take significantly more time.

    Somehow, I believe that this could actually be reinventing the wheel…

    • This reply was modified 12 months ago by sybok. Reason: Dictionary based object describing CLI-options for a given tool suggested
    • This reply was modified 12 months ago by sybok.