[PATCH 05/36] tuna: Add comprehensive docstrings to converter functions

John Kacur <[email protected]> Fri, 10 Jul 2026 10:14:43 -0400
Newsgroups org.kernel.vger.linux-rt-users
Message-ID <[email protected]>
Add detailed docstrings to all argparse converter functions to improve
code documentation and maintainability:

- threadstring_to_list(): Documents flexible input formats (PIDs,
  patterns, mixed), parameters, and the match_requested return value
- irqstring_to_list(): Explains IRQ number and pattern matching
- socketstring_to_list(): Documents socket-to-CPU expansion and
  validation behavior
- pick_op(): Explains operation prefix extraction (+, -, or None)
- get_loglevel(): Documents numeric (0-4) and string format conversion
- get_policy_and_rtprio(): Explains POLICY:RTPRIO parsing

Each docstring includes Args, Returns, and Notes sections with clear
examples of accepted input formats. This completes task #3 from the
argparse enhancements tracking document.

Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: John Kacur <[email protected]>
---
 tuna-cmd.py  | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++
 tuna/tuna.py |  30 +++++++++++++
 2 files changed, 149 insertions(+)

diff --git a/tuna-cmd.py b/tuna-cmd.py
index 6cb0a679cc60..f1605896c33c 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -28,6 +28,28 @@ import shutil
 import tuna.cpupower as cpw
 
 def get_loglevel(level):
+    """Convert a logging level string to a Python logging level constant.
+
+    Accepts either numeric (0-4) or string (DEBUG, INFO, WARNING, ERROR) formats.
+    Numeric values are multiplied by 10 to match Python logging constants.
+
+    Args:
+        level: Logging level as string. Can be:
+              - Numeric: "0" (NOTSET), "1" (DEBUG), "2" (INFO), "3" (WARNING), "4" (ERROR)
+              - String: "DEBUG", "INFO", "WARNING", "ERROR" (case-insensitive)
+
+    Returns:
+        Integer logging level constant compatible with Python's logging module.
+        - Numeric input: Returns level * 10 (e.g., "1" -> 10 for DEBUG)
+        - String input: Returns the uppercase string (e.g., "debug" -> "DEBUG")
+
+    Raises:
+        ValueError: If level is "CRITICAL" (not supported by tuna)
+
+    Note:
+        Python logging levels: 0=NOTSET, 10=DEBUG, 20=INFO, 30=WARNING, 40=ERROR, 50=CRITICAL
+        CRITICAL level is explicitly rejected as tuna does not support it.
+    """
     if level.isdigit() and int(level) in range(0,5):
         # logging built-in module levels:
         # 0 - NOTSET
@@ -541,6 +563,31 @@ def do_list_op(op, current_list, op_list):
     return list(set(op_list))
 
 def threadstring_to_list(threadstr, ps=None):
+    """Convert a thread specifier string to a list of thread IDs.
+
+    Accepts flexible input formats with comma and/or space separators.
+    Thread specifiers can be numeric PIDs or name patterns with wildcards.
+
+    Args:
+        threadstr: String of thread specifiers. Can be:
+                  - Numeric PIDs: "1", "1,2,3", "1, 2, 3", "1 2 3"
+                  - Thread name patterns: "systemd*", "kworker/*"
+                  - Mixed: "1,systemd*,100", "1 systemd* 100"
+                  Patterns use shell-style wildcards (* and ?).
+        ps: Optional procfs.pidstats() object. If None, will be created
+            on demand when needed to resolve name patterns.
+
+    Returns:
+        Tuple of (thread_list, match_requested) where:
+        - thread_list: List of integer PIDs that match the specifiers
+        - match_requested: Boolean indicating if any filtering was requested
+                          (True if threadstr is non-empty, False otherwise)
+
+    Note:
+        The match_requested flag is used by display functions to determine
+        whether to show only matched threads or all threads in the system.
+        Empty input returns ([], False) to indicate "show all threads".
+    """
     match_requested = bool(threadstr)
     thread_list = []
     if not threadstr:
@@ -561,6 +608,30 @@ def threadstring_to_list(threadstr, ps=None):
     return thread_list, match_requested
 
 def irqstring_to_list(irqstr):
+    """Convert an IRQ specifier string to a list of IRQ numbers.
+
+    Accepts flexible input formats with comma and/or space separators.
+    IRQ specifiers can be numeric IRQ numbers or IRQ user patterns with wildcards.
+
+    Args:
+        irqstr: String of IRQ specifiers. Can be:
+                - Numeric IRQ numbers: "50", "50,51,52", "50, 51, 52", "50 51 52"
+                - IRQ user patterns: "eth0*", "nvme*"
+                - Mixed: "50,eth0*,100", "50 eth0* 100"
+                Patterns use shell-style wildcards (* and ?) matched against
+                IRQ user names from /proc/interrupts.
+
+    Returns:
+        Tuple of (irq_list, match_requested) where:
+        - irq_list: List of integer IRQ numbers that match the specifiers
+        - match_requested: Boolean indicating if any filtering was requested
+                          (True if irqstr is non-empty, False otherwise)
+
+    Note:
+        The match_requested flag is used by display functions to determine
+        whether to show only matched IRQs or all IRQs in the system.
+        Empty input returns ([], False) to indicate "show all IRQs".
+    """
     match_requested = bool(irqstr)
     irq_list = []
     if not irqstr:
@@ -578,6 +649,30 @@ def irqstring_to_list(irqstr):
     return irq_list, match_requested
 
 def socketstring_to_list(socketstr):
+    """Convert a CPU socket specifier string to a list of CPU numbers.
+
+    Accepts flexible input formats with comma and/or space separators.
+    Socket specifiers are socket IDs that get expanded to all CPUs in those sockets.
+
+    Args:
+        socketstr: String of socket specifiers. Can be:
+                  - Single socket: "0"
+                  - Multiple sockets: "0,1", "0, 1", "0 1"
+                  Socket IDs must be valid socket numbers present in the system.
+
+    Returns:
+        List of integer CPU numbers belonging to the specified sockets.
+        CPUs are returned in the order they appear in sysfs.
+
+    Raises:
+        SystemExit: If any socket ID is invalid. Prints available sockets
+                   and exits with code 2.
+
+    Note:
+        This function queries sysfs to discover CPU topology and validate
+        socket IDs. Invalid socket IDs cause immediate program termination
+        with an error message showing valid options.
+    """
     cpu_list = []
     # Split by both comma and whitespace
     import re
@@ -593,6 +688,30 @@ def socketstring_to_list(socketstr):
     return cpu_list
 
 def pick_op(argument):
+    """Extract an operation prefix from an argument string.
+
+    Checks if the argument starts with a '+' or '-' operation prefix.
+    If present, returns the operation and the remaining string.
+
+    Args:
+        argument: String to check for operation prefix.
+
+    Returns:
+        Tuple of (operation, remaining_string) where:
+        - operation: '+' for addition, '-' for removal, None for replacement
+        - remaining_string: The argument with the operation prefix removed,
+                           or the original argument if no prefix was found
+
+    Examples:
+        >>> pick_op("+1,2,3")
+        ('+', '1,2,3')
+        >>> pick_op("-0,1")
+        ('-', '0,1')
+        >>> pick_op("1,2,3")
+        (None, '1,2,3')
+        >>> pick_op("")
+        (None, '')
+    """
     if argument == "":
         return (None, argument)
     if argument[0] in ('+', '-'):
diff --git a/tuna/tuna.py b/tuna/tuna.py
index be3ca5017ef0..1cb538575b17 100755
--- a/tuna/tuna.py
+++ b/tuna/tuna.py
@@ -530,6 +530,36 @@ def get_irq_affinity_text(irqs, irq):
         return ""
 
 def get_policy_and_rtprio(parm):
+    """Parse a scheduling policy and priority from a POLICY:RTPRIO string.
+
+    Accepts flexible input formats for specifying scheduler policy and real-time priority.
+    Can specify just a policy, just a priority, or both separated by a colon.
+
+    Args:
+        parm: String specifying policy and/or priority. Can be:
+             - Policy only: "FIFO", "RR", "OTHER", "BATCH", "IDLE"
+             - Priority only: "50" (assumes current policy)
+             - Both: "FIFO:50", "RR:99"
+             Policy names are case-insensitive.
+
+    Returns:
+        Tuple of (policy, rtprio) where:
+        - policy: Integer scheduling policy constant (e.g., SCHED_FIFO, SCHED_RR)
+                 or None if only priority was specified
+        - rtprio: Integer real-time priority (1-99 for RT policies, 0 for others)
+                 Defaults to 1 for RT policies if not specified
+
+    Raises:
+        ValueError: If the policy name is invalid or cannot be parsed
+
+    Examples:
+        >>> get_policy_and_rtprio("FIFO:50")
+        (SCHED_FIFO, 50)
+        >>> get_policy_and_rtprio("RR")
+        (SCHED_RR, 1)
+        >>> get_policy_and_rtprio("50")
+        (None, 50)
+    """
     parms = parm.split(":")
     rtprio = 0
     policy = None
-- 
2.54.0