Skip to content

Fix code style of jtop/gui/pgpu_thor.py.#754

Merged
johnnynunez merged 1 commit into
rbonghi:masterfrom
whitesscott:fix-code-style-pgpu-thor
Nov 9, 2025
Merged

Fix code style of jtop/gui/pgpu_thor.py.#754
johnnynunez merged 1 commit into
rbonghi:masterfrom
whitesscott:fix-code-style-pgpu-thor

Conversation

@whitesscott
Copy link
Copy Markdown
Contributor

@whitesscott whitesscott commented Nov 8, 2025

Fixed code style.

Summary by Sourcery

Refactor pgpu_thor.py to improve code style, enforce PEP8 conventions, and enhance readability by adding type annotations, docstrings, and consistent formatting

Enhancements:

  • Apply PEP8-compliant formatting across imports, dict literals, and function definitions
  • Add Python type hints and docstrings for helper functions, methods, and constants
  • Centralize environment variable names into constants and streamline NVML/CUDA memory utilities
  • Refactor gauge‐drawing and chart update functions for clearer naming and consistent styling

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Nov 8, 2025

Reviewer's Guide

This PR applies a broad code-style overhaul to pgpu_thor.py by introducing type annotations, docstrings, consistent formatting (quotes, spacing, dicts), reorganizing imports, and refactoring helper functions and class methods for clarity and maintainability.

Class diagram for updated GPU Page class structure

classDiagram
class GPU {
  +__init__(stdscr, jetson)
  +_get_cuda_mem_cached(ttl: float = 3.0, stale_ok: float = 10.0) Optional[Tuple[int, int]]
  +action_railgate(info, selected) None
  +action_scaling_3D(info, selected) None
  +update_chart(jetson, name: str) Dict[str, List[float]]
  +update_chart_ram(jetson, name: str) Dict[str, object]
  +_handle_mouse(mouse: Optional[Tuple[int, int]]) bool
  +_handle_hotkeys(key) bool
  +draw(key, mouse) None
  -draw_gpus: Dict[str, Dict[str, object]]
  -process_table: ProcessTable
  -_click_regions: Dict[str, List[Tuple[int, int, int]]]
  -_cuda_mem_last: Optional[Tuple[int, int]]
  -_cuda_mem_last_ts: float
}

class Page
class ProcessTable
class Chart
class SmallButton

GPU --|> Page
GPU --> ProcessTable
GPU --> Chart
GPU --> SmallButton
Loading

Class diagram for updated helper functions and their types

classDiagram
class _size_human {
  +_size_human(nbytes: int) Tuple[float, str, int]
}
class _to_gib_series {
  +_to_gib_series(used_b: int, shared_b: int, total_b: int) Dict[str, object]
}
class _read_podgov_params_gui {
  +_read_podgov_params_gui() Optional[Dict[str, int]]
}
class _thor_nvml_mem_summary {
  +_thor_nvml_mem_summary() Optional[Dict[str, int]]
}
class _thor_nvml_graphics_process_rows {
  +_thor_nvml_graphics_process_rows() List[Dict[str, Optional[int]]]
}
class _dbg {
  +_dbg(msg: str) None
}
class _fmt_bytes_mib {
  +_fmt_bytes_mib(n: Optional[int]) str
}
class gpu_gauge {
  +gpu_gauge(stdscr, pos_y: int, pos_x: int, size: int, gpu_data: dict, idx: int) None
}
class compact_gpu {
  +compact_gpu(stdscr, pos_y: int, pos_x: int, width: int, jetson, mouse: Optional[Tuple[int, int]] = None) int
}
Loading

File-Level Changes

Change Details Files
Reorganized imports and constants
  • Added from __future__ import annotations and typing imports
  • Grouped and reordered imports for readability
  • Introduced _ENV_ENABLE_NVML and _ENV_MEM_LOG_PATH constants
jtop/gui/pgpu_thor.py
Typed and documented helper functions
  • Added return-type annotations and docstrings to _size_human, _to_gib_series, _read_podgov_params_gui
  • Refactored logic inside these helpers for clarity
  • Standardized binary-size calculations
jtop/gui/pgpu_thor.py
Unified NVML and CUDA import handling
  • Renamed NVML variables (hhandle, bbar) and added # type: ignore where needed
  • Wrapped imports in try/except with consistent flag updates
  • Maintained optional CUDA probe assignment with type ignores
jtop/gui/pgpu_thor.py
Refactored GPU gauge and compact display functions
  • Expanded function signatures with type hints and return annotations
  • Added docstrings and multiline parameter formatting
  • Reformatted dict literals and replaced single quotes with double quotes
jtop/gui/pgpu_thor.py
Overhauled class GPU initializer
  • Extracted color_grey variable and annotated self.draw_gpus dict
  • Reformatted Chart, SmallButton, and optional chart_ram instantiation with multiline args
  • Standardized GPU-type logic (i vs d)
jtop/gui/pgpu_thor.py
Added type hints and cleaned method signatures
  • Annotated action_railgate, action_scaling_3D, update_chart, update_chart_ram, input handlers, draw
  • Simplified docstrings and comments for hotkeys and mouse handling
  • Ensured consistent return types
jtop/gui/pgpu_thor.py
Streamlined chart-update logic and formatting
  • Refactored update_chart_ram series selection and formatted debug logs
  • Used consistent f-string style and dict unpacking
  • Removed legacy commented code and inline comments updated
jtop/gui/pgpu_thor.py
General style improvements
  • Replaced mixed quote usage with double quotes
  • Adjusted blank lines, indentation, and wrapping to PEP8
  • Consolidated click-region tuple construction and draw loops
jtop/gui/pgpu_thor.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The NVML and CUDA probing logic adds a lot of complexity to this GUI file—consider extracting that into dedicated helper modules or classes so the page implementation stays focused and easier to maintain.
  • The draw method is quite large and handles multiple responsibilities; breaking it into smaller, focused helper methods (e.g., for drawing blocks and updating click regions) would improve readability.
  • Environment variables like _ENV_ENABLE_NVML and _ENV_MEM_LOG_PATH could be centralized in a shared constants or configuration module to avoid duplication and make them easier to manage.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The NVML and CUDA probing logic adds a lot of complexity to this GUI file—consider extracting that into dedicated helper modules or classes so the page implementation stays focused and easier to maintain.
- The draw method is quite large and handles multiple responsibilities; breaking it into smaller, focused helper methods (e.g., for drawing blocks and updating click regions) would improve readability.
- Environment variables like `_ENV_ENABLE_NVML` and `_ENV_MEM_LOG_PATH` could be centralized in a shared constants or configuration module to avoid duplication and make them easier to manage.

## Individual Comments

### Comment 1
<location> `jtop/gui/pgpu_thor.py:320` </location>
<code_context>
+# ---------------------------------------------------------------------------
+
+
 class GPU(Page):
     """Thor-specific GPU page (clickable 3D scaling & Railgate)."""

</code_context>

<issue_to_address>
**suggestion:** The draw_gpus dictionary is now typed, but its value type is Dict[str, object], which is very broad.

Consider specifying a more precise value type for draw_gpus to improve static analysis and code clarity.

Suggested implementation:

```python
from typing import Dict, Iterable, List, Optional, Tuple

```

```python
# draw_gpus: Dict[str, Dict[str, float]]

```

If the actual value type of `draw_gpus` is different (e.g., a custom class or another dictionary structure), you should replace `Dict[str, float]` with the correct type. You may also need to update any function signatures or usages of `draw_gpus` elsewhere in the file to match the new type annotation.
</issue_to_address>

### Comment 2
<location> `jtop/gui/pgpu_thor.py:498` </location>
<code_context>
     # ---------- main draw ----------

-    def draw(self, key, mouse):
+    def draw(self, key, mouse) -> None:
         if self._handle_mouse(mouse) or self._handle_hotkeys(key):
             pass
</code_context>

<issue_to_address>
**issue (bug_risk):** The calculation of gpu_height may result in a value less than 1 if height is small.

Add a minimum value check for gpu_height to prevent zero or negative values and avoid rendering issues.
</issue_to_address>

### Comment 3
<location> `jtop/gui/pgpu_thor.py:129` </location>
<code_context>
def _thor_nvml_mem_summary() -> Optional[Dict[str, int]]:
    """
    Return dict with {fb_total, fb_used, bar1_total, bar1_used} (bytes),
    or None if NVML is disabled/unavailable.
    """
    if not (_ENABLE_NVML and _HAS_NVML):
        return None
    try:
        nvmlInit()
        try:
            if nvmlDeviceGetCount() < 1:
                return None
            handle = nvmlDeviceGetHandleByIndex(0)
            fb = nvmlDeviceGetMemoryInfo(handle)
            fb_total, fb_used = int(fb.total), int(fb.used)
            try:
                bar = nvmlDeviceGetBAR1MemoryInfo(handle)
                bar1_total = int(getattr(bar, "bar1Total", 0))
                bar1_used = int(getattr(bar, "bar1Used", 0))
            except Exception:
                bar1_total, bar1_used = 0, 0
            return {
                "fb_total": fb_total,
                "fb_used": fb_used,
                "bar1_total": bar1_total,
                "bar1_used": bar1_used,
            }
        finally:
            try:
                nvmlShutdown()
            except Exception:
                pass
    except Exception:
        return None

</code_context>

<issue_to_address>
**issue (code-quality):** We've found these issues:

- Simplify logical expression using De Morgan identities ([`de-morgan`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/de-morgan/))
- Extract code out into function ([`extract-method`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/extract-method/))
</issue_to_address>

### Comment 4
<location> `jtop/gui/pgpu_thor.py:174-187` </location>
<code_context>
def _thor_nvml_graphics_process_rows() -> List[Dict[str, Optional[int]]]:
    """
    Return a list of dicts for graphics processes from NVML v3, or [] if unsupported/disabled.
    Each row: {pid, name (maybe None), used_bytes (or None)}.
    """
    if not (_ENABLE_NVML and _HAS_NVML):
        return []
    try:
        nvmlInit()
        try:
            if nvmlDeviceGetCount() < 1:
                return []
            handle = nvmlDeviceGetHandleByIndex(0)
            procs = nvmlDeviceGetGraphicsRunningProcesses(handle)
            rows = []
            for p in procs:
                rows.append(
                    {
                        "pid": int(p.pid),
                        "name": getattr(p, "name", None),
                        "used_bytes": (
                            None
                            if getattr(p, "usedGpuMemory", None) is None
                            else int(p.usedGpuMemory)
                        ),
                    }
                )
            return rows
        finally:
            try:
                nvmlShutdown()
            except Exception:
                pass
    except Exception:
        return []

</code_context>

<issue_to_address>
**suggestion (code-quality):** We've found these issues:

- Convert for loop into list comprehension ([`list-comprehension`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/list-comprehension/))
- Inline variable that is immediately returned ([`inline-immediately-returned-variable`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/inline-immediately-returned-variable/))

```suggestion
            return [
                {
                    "pid": int(p.pid),
                    "name": getattr(p, "name", None),
                    "used_bytes": (
                        None
                        if getattr(p, "usedGpuMemory", None) is None
                        else int(p.usedGpuMemory)
                    ),
                }
                for p in procs
            ]
```
</issue_to_address>

### Comment 5
<location> `jtop/gui/pgpu_thor.py:231` </location>
<code_context>
def compact_gpu(
    stdscr,
    pos_y: int,
    pos_x: int,
    width: int,
    jetson,
    mouse: Optional[Tuple[int, int]] = None,
) -> int:
    """
    Compact GPU display for the summary view.
    Returns number of terminal lines consumed.
    """
    if not jetson.gpu:
        data = {
            "name": "GPU",
            "color": NColors.green() | curses.A_BOLD,
            "online": False,
            "coffline": NColors.igreen(),
            "message": "NVIDIA GPU NOT DETECTED/AVAILABLE",
        }
        basic_gauge(stdscr, pos_y, pos_x, width - 2, data)
        return 1

    line_counter = 0
    for idx, gpu in enumerate(jetson.gpu.values()):
        gpu_gauge(stdscr, pos_y + line_counter, pos_x, width, gpu, idx)
        line_counter += 1

    # Read Thor runtime states
    try:
        gov = (current_governor() or "").strip()
        val3d = "Enabled" if gov != "performance" else "Disabled"
    except Exception:
        val3d = "Unknown"

    try:
        rs = rail_status()
        control = rs.get("control_value")
        valrg = (
            "Enabled"
            if control == "auto"
            else ("Disabled" if control == "on" else "Unknown")
        )
    except Exception:
        valrg = "Unknown"

    y = pos_y + line_counter
    label1_x = pos_x + 1
    label1 = "3D scaling: "
    field1 = "{" + val3d + "}"
    field1_x = label1_x + len(label1)
    field1_x_end = field1_x + len(field1) - 1

    label2_x = pos_x + max(width // 2, field1_x_end + 3)
    label2 = "Railgate: "
    field2 = "{" + valrg + "}"
    field2_x = label2_x + len(label2)
    field2_x_end = field2_x + len(field2) - 1

    # Mouse interaction
    if mouse:
        mx, my = mouse
        if my == y:
            if label1_x <= mx <= field1_x_end:
                toggle_governor()
            elif label2_x <= mx <= field2_x_end:
                toggle_rail()

    # Draw labels with color highlighting
    try:
        stdscr.addstr(y, label1_x, label1, curses.A_BOLD)
        color_3d = NColors.green() if val3d == "Enabled" else curses.A_NORMAL
        stdscr.addstr(y, field1_x, field1, color_3d)

        if field2_x_end < pos_x + width:
            stdscr.addstr(y, label2_x, label2, curses.A_BOLD)
            color_rail = NColors.green() if valrg == "Enabled" else curses.A_NORMAL
            stdscr.addstr(y, field2_x, field2, color_rail)
    except curses.error:
        pass

    return line_counter + 1

</code_context>

<issue_to_address>
**issue (code-quality):** Low code quality found in compact\_gpu - 21% ([`low-code-quality`](https://docs.sourcery.ai/Reference/Default-Rules/comments/low-code-quality/))

<br/><details><summary>Explanation</summary>The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.

How can you solve this?

It might be worth refactoring this function to make it shorter and more readable.

- Reduce the function length by extracting pieces of functionality out into
  their own functions. This is the most important thing you can do - ideally a
  function should be less than 10 lines.
- Reduce nesting, perhaps by introducing guard clauses to return early.
- Ensure that variables are tightly scoped, so that code using related concepts
  sits together within the function rather than being scattered.</details>
</issue_to_address>

### Comment 6
<location> `jtop/gui/pgpu_thor.py:416` </location>
<code_context>
    def update_chart_ram(self, jetson, name: str) -> Dict[str, object]:
        """
        GPU Shared RAM chart values.

        Order of preference:
          1) CUDA (thor_cuda_mem): FB total/used, shared=0 (no BAR1 visibility)
          2) NVML (if enabled): prefer BAR1 when available and show FB used as reference
          3) Legacy jetson.memory['RAM'] as last resort
        """
        # --- 1) CUDA driver first (Thor), with caching ---
        res = self._get_cuda_mem_cached(ttl=3.0, stale_ok=10.0)
        if res:
            used_b, total_b = res
            _dbg(f"RAM via CUDA: used={used_b} total={total_b}")
            return _to_gib_series(used_b, 0, total_b)  # shared=0 with CUDA path

        # --- 2) NVML BAR1/FB path (only if enabled) ---
        nv = _thor_nvml_mem_summary()
        if nv:
            fb_total = nv["fb_total"]
            fb_used = nv["fb_used"]
            bar1_total = nv["bar1_total"]
            bar1_used = nv["bar1_used"]

            if bar1_total and bar1_total > 0:
                total_bytes = bar1_total
                shared_bytes = bar1_used
                ref_bytes = fb_used
            else:
                total_bytes = fb_total
                shared_bytes = 0
                ref_bytes = fb_used

            _dbg(
                "RAM via NVML: "
                f"fb_used={fb_used} fb_total={fb_total} bar1_used={bar1_used} bar1_total={bar1_total}"
            )
            return _to_gib_series(ref_bytes, shared_bytes, total_bytes)

        # --- 3) Legacy absolute fallback (system RAM) ---
        parameter = jetson.memory.get("RAM", {})
        tot_kib = int(parameter.get("tot", 0))
        used_kib = int(parameter.get("used", 0))
        shared_kib = int(parameter.get("shared", 0))

        tot_b = tot_kib * 1024
        used_b = used_kib * 1024
        shared_b = shared_kib * 1024

        _dbg(f"RAM via HOST: used={used_b} total={tot_b}")
        return _to_gib_series(used_b, shared_b, tot_b)

</code_context>

<issue_to_address>
**issue (code-quality):** We've found these issues:

- Use named expression to simplify assignment and conditional [×2] ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
- Hoist repeated code outside conditional statement ([`hoist-statement-from-if`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/hoist-statement-from-if/))
- Extract code out into method ([`extract-method`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/extract-method/))
- Use previously assigned local variable ([`use-assigned-variable`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-assigned-variable/))
</issue_to_address>

### Comment 7
<location> `jtop/gui/pgpu_thor.py:498` </location>
<code_context>
    def draw(self, key, mouse) -> None:
        if self._handle_mouse(mouse) or self._handle_hotkeys(key):
            pass

        height, width, first = self.size_page()
        gpu_height = (height * 2 // 3 - 3) // max(1, len(self.jetson.gpu))

        # Temperatures header
        self.stdscr.addstr(first + 1, 1, "Temperatures:", curses.A_NORMAL)
        for name in self.jetson.temperature:
            if "gpu" in name.lower():
                color_temperature(
                    self.stdscr, first + 1, 15, name, self.jetson.temperature[name]
                )

        self._click_regions = {"scaling": [], "railgate": []}

        # Per-GPU blocks
        for idx, (gpu_name, gpu_data) in enumerate(self.jetson.gpu.items()):
            chart = self.draw_gpus[gpu_name]["chart"]
            chart_ram = self.draw_gpus[gpu_name]["ram"]
            gpu_status = gpu_data["status"]
            gpu_freq = gpu_data.get("freq", {})

            size_x = [1, width // 2 - 2]
            size_y = [
                first + 2 + idx * (gpu_height + 1),
                first + 2 + (idx + 1) * (gpu_height - 3),
            ]

            # --- draw podgov params directly under the gov line ---
            gp = gpu_status.get("gov_params")

            # If core didn't provide it, read directly from sysfs when governor is nvhost_podgov
            if not gp:
                gov_name = gpu_freq.get("governor", "")
                if gov_name == "nvhost_podgov":
                    gp = _read_podgov_params_gui()

            if isinstance(gp, dict) and gp:
                parts = []
                if "k" in gp:
                    parts.append(f"k={gp['k']}")
                if "load_target" in gp:
                    parts.append(f"load_target={gp['load_target']}")
                if "load_margin" in gp:
                    parts.append(f"load_margin={gp['load_margin']}")
                if parts:
                    try:
                        # draw one row below the chart title, indented slightly
                        self.stdscr.addstr(
                            size_y[0] + 1,
                            size_x[0] + 2,
                            "  " + "  ".join(parts),
                            curses.A_DIM,
                        )
                    except curses.error:
                        pass

            # Main GPU load chart (title + governor label)
            label_chart_gpu = (
                f"{gpu_status['load']:>3.0f}% - gov: {gpu_freq.get('governor', '')}"
            )
            chart.draw(self.stdscr, size_x, size_y, label=label_chart_gpu)

            # GPU Shared RAM chart + label (per GPU)
            if chart_ram:
                label = ""

                # NVML label (only if enabled)
                if _ENABLE_NVML:
                    nv = _thor_nvml_mem_summary()
                    if nv:
                        fb_total = nv["fb_total"]
                        fb_used = nv["fb_used"]
                        bar1_total = nv["bar1_total"]
                        bar1_used = nv["bar1_used"]
                        if bar1_total > 0:
                            used_k = bar1_used // 1024
                            tot_k = bar1_total // 1024
                            label = f"{size_to_string(used_k, 'k')}/{size_to_string(tot_k, 'k')} (BAR1)"
                        else:
                            used_k = fb_used // 1024
                            tot_k = fb_total // 1024
                            label = f"{size_to_string(used_k, 'k')}/{size_to_string(tot_k, 'k')} (FB)"

                # CUDA GPU memory on Thor
                if not label:
                    res = self._get_cuda_mem_cached(ttl=3.0, stale_ok=10.0)
                    if res:
                        used_b, total_b = res
                        used_k = used_b // 1024
                        tot_k = total_b // 1024
                        label = f"{size_to_string(used_k, 'k')}/{size_to_string(tot_k, 'k')}"

                # Last resort: legacy system RAM label (host)
                if not label:
                    try:
                        ram = self.jetson.memory.get("RAM", {})
                        used_k = int(ram.get("used", 0))  # use 'used' not 'shared'
                        tot_k = int(ram.get("tot", 0))
                        label = f"{size_to_string(used_k, 'k')}/{size_to_string(tot_k, 'k')} (host)"
                    except Exception:
                        label = "N/A"

                chart_ram.draw(
                    self.stdscr, [1 + width // 2, width - 2], size_y, label=label
                )

            # Clickable toggles row (per GPU area)
            y = first + 1 + (idx + 1) * gpu_height - 1
            x = 1

            try:
                gov = (current_governor() or "").strip()
                val3d = "Enabled" if gov != "performance" else "Disabled"
            except Exception:
                val3d = "Unknown"

            label = "3D scaling: "
            field = "{" + val3d + "}"
            color_3d = NColors.green() if val3d == "Enabled" else curses.A_NORMAL
            self.stdscr.addstr(y, x, label, curses.A_BOLD)
            self.stdscr.addstr(y, x + len(label), field, color_3d)
            self._click_regions["scaling"].append(
                (y, x + len(label), x + len(label) + len(field) - 1)
            )

            x += width // 4
            try:
                rs = rail_status()
                control = rs.get("control_value")
                valrg = (
                    "Enabled"
                    if control == "auto"
                    else ("Disabled" if control == "on" else "Unknown")
                )
            except Exception:
                valrg = "Unknown"

            label = "Railgate: "
            field = "{" + valrg + "}"
            color_rail = NColors.green() if valrg == "Enabled" else curses.A_NORMAL
            self.stdscr.addstr(y, x, label, curses.A_BOLD)
            self.stdscr.addstr(y, x + len(label), field, color_rail)
            self._click_regions["railgate"].append(
                (y, x + len(label), x + len(label) + len(field) - 1)
            )

            # Frequency meters (live row above the 3D/Railgate line)
            label_y = first + 1 + (idx + 1) * gpu_height - 1
            meter_y = label_y - 1

            frq_size = width - 3
            if "GPC" in gpu_freq:
                # allocate right half for GPC lane meters
                size_gpc_gauge = (width - 2) // (2 + len(gpu_freq["GPC"]))
                for gpc_idx, gpc in enumerate(gpu_freq["GPC"]):
                    freq_data = {
                        "name": f"GPC{gpc_idx}",
                        "cur": gpc,
                        "unit": "k",
                        "online": gpc > 0,
                    }
                    freq_gauge(
                        self.stdscr,
                        meter_y,
                        width // 2 + gpc_idx * size_gpc_gauge + 2,
                        size_gpc_gauge - 1,
                        freq_data,
                    )
                frq_size = width // 2

            # Overall frequency meter on the left — shift right 1 col and shrink 1 to avoid overlaps
            gpu_freq["name"] = "Frq"
            freq_gauge(self.stdscr, meter_y, 2, frq_size - 1, gpu_freq)

        height_table = height - first + 2 + gpu_height
        self.process_table.draw(
            first + 2 + gpu_height, 0, width, height_table, key, mouse
        )

</code_context>

<issue_to_address>
**issue (code-quality):** We've found these issues:

- Swap positions of nested conditionals [×2] ([`swap-nested-ifs`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/swap-nested-ifs/))
- Use named expression to simplify assignment and conditional [×2] ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))
- Move assignments closer to their usage ([`move-assign`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/move-assign/))
- Low code quality found in GPU.draw - 4% ([`low-code-quality`](https://docs.sourcery.ai/Reference/Default-Rules/comments/low-code-quality/))

<br/><details><summary>Explanation</summary>




The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.

How can you solve this?

It might be worth refactoring this function to make it shorter and more readable.

- Reduce the function length by extracting pieces of functionality out into
  their own functions. This is the most important thing you can do - ideally a
  function should be less than 10 lines.
- Reduce nesting, perhaps by introducing guard clauses to return early.
- Ensure that variables are tightly scoped, so that code using related concepts
  sits together within the function rather than being scattered.</details>
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread jtop/gui/pgpu_thor.py
# ---------------------------------------------------------------------------


class GPU(Page):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The draw_gpus dictionary is now typed, but its value type is Dict[str, object], which is very broad.

Consider specifying a more precise value type for draw_gpus to improve static analysis and code clarity.

Suggested implementation:

from typing import Dict, Iterable, List, Optional, Tuple
# draw_gpus: Dict[str, Dict[str, float]]

If the actual value type of draw_gpus is different (e.g., a custom class or another dictionary structure), you should replace Dict[str, float] with the correct type. You may also need to update any function signatures or usages of draw_gpus elsewhere in the file to match the new type annotation.

Comment thread jtop/gui/pgpu_thor.py
# ---------- main draw ----------

def draw(self, key, mouse):
def draw(self, key, mouse) -> None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The calculation of gpu_height may result in a value less than 1 if height is small.

Add a minimum value check for gpu_height to prevent zero or negative values and avoid rendering issues.

Comment thread jtop/gui/pgpu_thor.py
Return dict with {fb_total, fb_used, bar1_total, bar1_used} (bytes),
or None if NVML is disabled/unavailable.
"""
if not (_ENABLE_NVML and _HAS_NVML):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

  • Simplify logical expression using De Morgan identities (de-morgan)
  • Extract code out into function (extract-method)

Comment thread jtop/gui/pgpu_thor.py
Comment on lines 174 to 187
rows = []
for p in procs:
rows.append({
"pid": int(p.pid),
"name": getattr(p, "name", None),
"used_bytes": None if getattr(p, "usedGpuMemory", None) is None else int(p.usedGpuMemory),
})
rows.append(
{
"pid": int(p.pid),
"name": getattr(p, "name", None),
"used_bytes": (
None
if getattr(p, "usedGpuMemory", None) is None
else int(p.usedGpuMemory)
),
}
)
return rows
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

Suggested change
rows = []
for p in procs:
rows.append({
"pid": int(p.pid),
"name": getattr(p, "name", None),
"used_bytes": None if getattr(p, "usedGpuMemory", None) is None else int(p.usedGpuMemory),
})
rows.append(
{
"pid": int(p.pid),
"name": getattr(p, "name", None),
"used_bytes": (
None
if getattr(p, "usedGpuMemory", None) is None
else int(p.usedGpuMemory)
),
}
)
return rows
return [
{
"pid": int(p.pid),
"name": getattr(p, "name", None),
"used_bytes": (
None
if getattr(p, "usedGpuMemory", None) is None
else int(p.usedGpuMemory)
),
}
for p in procs
]

Comment thread jtop/gui/pgpu_thor.py
def compact_gpu(stdscr, pos_y, pos_x, width, jetson, mouse=None):
"""Compact GPU display for summary view."""
line_counter = 0
def compact_gpu(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Low code quality found in compact_gpu - 21% (low-code-quality)


ExplanationThe quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.

How can you solve this?

It might be worth refactoring this function to make it shorter and more readable.

  • Reduce the function length by extracting pieces of functionality out into
    their own functions. This is the most important thing you can do - ideally a
    function should be less than 10 lines.
  • Reduce nesting, perhaps by introducing guard clauses to return early.
  • Ensure that variables are tightly scoped, so that code using related concepts
    sits together within the function rather than being scattered.

Comment thread jtop/gui/pgpu_thor.py

def update_chart_ram(self, jetson, name):
def update_chart_ram(self, jetson, name: str) -> Dict[str, object]:
"""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:

Comment thread jtop/gui/pgpu_thor.py
# ---------- main draw ----------

def draw(self, key, mouse):
def draw(self, key, mouse) -> None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): We've found these issues:


Explanation

The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.

How can you solve this?

It might be worth refactoring this function to make it shorter and more readable.

  • Reduce the function length by extracting pieces of functionality out into
    their own functions. This is the most important thing you can do - ideally a
    function should be less than 10 lines.
  • Reduce nesting, perhaps by introducing guard clauses to return early.
  • Ensure that variables are tightly scoped, so that code using related concepts
    sits together within the function rather than being scattered.

@johnnynunez johnnynunez merged commit 0ed4d07 into rbonghi:master Nov 9, 2025
9 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants