Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

add info if package is missing#741

Merged
mangelajo merged 3 commits intojumpstarter-dev:mainfrom
bennyz:missing-package-info
Nov 11, 2025
Merged

add info if package is missing#741
mangelajo merged 3 commits intojumpstarter-dev:mainfrom
bennyz:missing-package-info

Conversation

@bennyz
Copy link
Copy Markdown
Member

@bennyz bennyz commented Nov 9, 2025

Error: Missing Jumpstarter driver module 'jumpstarter_driver_ssh'.

This usually indicates a version mismatch between your client and the exporter.
Please try the following try to update your client to the latest version
 (Caused by: No module named 'jumpstarter_driver_ssh')

address #733

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling to re-raise connection errors cleanly and provide clearer diagnostics for version mismatches between components.
    • Clearer messages guiding installation when required Python modules are missing.
  • Style / Minor UX

    • Small logging and status-display formatting tweaks for more consistent, readable output.

Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
@bennyz bennyz requested a review from mangelajo November 9, 2025 12:23
@netlify
Copy link
Copy Markdown

netlify Bot commented Nov 9, 2025

Deploy Preview for jumpstarter-docs ready!

Name Link
🔨 Latest commit 1bb2f8e
🔍 Latest deploy log https://app.netlify.com/projects/jumpstarter-docs/deploys/691397c5fc86dc0008d07057
😎 Deploy Preview https://deploy-preview-741--jumpstarter-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 9, 2025

Walkthrough

Adds explicit ConnectionError propagation in lease connection wait and introduces ModuleNotFoundError handling in dynamic class import to surface descriptive ConnectionError messages for missing Jumpstarter drivers or Python modules; plus minor formatting and logging cleanups.

Changes

Cohort / File(s) Summary
Lease connection handling
packages/jumpstarter/jumpstarter/client/lease.py
Re-raises ConnectionError from _wait_for_ready_connection instead of wrapping it; adjusts exception handler ordering and minor formatting/logging (elapsed-time string conversion, terminal capability checks, spinner construction, inline comments). No public API changes.
Dynamic import error handling
packages/jumpstarter/jumpstarter/common/importlib.py
Adds logging and catches ModuleNotFoundError from cached imports; distinguishes Jumpstarter driver modules (prefixed like jumpstarter_) to raise descriptive ConnectionError about exporter version mismatch, and raises descriptive ConnectionError instructing installation for non-driver missing modules. Preserves existing AttributeError handling.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Lease as Lease._wait_for_ready_connection
  participant Remote as RemoteService

  Client->>Lease: call _wait_for_ready_connection()
  Lease->>Remote: attempt connect
  alt ConnectionError from Remote
    Remote-->>Lease: ConnectionError
    Lease-->>Client: re-raise ConnectionError (propagate)
  else Other Exception
    Remote-->>Lease: Error
    Lease-->>Client: wrap/handle generic Exception
  end
Loading
sequenceDiagram
  autonumber
  participant Importer as import_class
  participant Cache as cached_import

  Importer->>Cache: request module/class
  Cache-->>Importer: throws ModuleNotFoundError
  alt module name starts with "jumpstarter_"
    Importer->>Importer: log exporter-version mismatch
    Importer-->>Caller: raise ConnectionError (exporter mismatch)
  else non-driver module
    Importer->>Importer: log missing python dependency
    Importer-->>Caller: raise ConnectionError (install dependency)
  end
  Cache-->>Importer: returns module/class (normal path)
  Importer-->>Caller: return imported class
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Two files modified with behavioral changes in error handling — requires reading both flows.
  • Review focus:
    • Correctness of re-raising ConnectionError vs other exceptions in lease.py.
    • Accurate detection of driver module names and appropriateness of ConnectionError messages in importlib.py.
    • Log messages and test coverage for the new branches.

Poem

🐰 I hopped through code with nimble paws,

Found missing modules and tightened laws.
Now connections cry their reason clear,
And imports tell why they won’t appear.
A small fix, a joyful nibble — cheers! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'add info if package is missing' directly corresponds to the main objective: adding descriptive error handling and logging for missing packages, specifically distinguishing between Jumpstarter drivers and regular Python modules.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/jumpstarter/jumpstarter/client/lease.py (1)

244-270: Prefer using ModuleNotFoundError.name.

Parsing the human-readable message is fragile; the exception already exposes the missing module via e.name, which handles alias cases and avoids splitting assumptions.

Apply this diff to rely on the structured attribute with a safe fallback:

-            except ModuleNotFoundError as e:
-                module_name = str(e).split("'")[1] if "'" in str(e) else str(e).split()[-1]
+            except ModuleNotFoundError as e:
+                message = str(e)
+                module_name = getattr(e, "name", None)
+                if not module_name:
+                    module_name = message.split("'")[1] if "'" in message else message.split()[-1]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4402736 and 5a743aa.

📒 Files selected for processing (1)
  • packages/jumpstarter/jumpstarter/client/lease.py (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: michalskrivanek
Repo: jumpstarter-dev/jumpstarter PR: 589
File: packages/jumpstarter/jumpstarter/exporter/exporter.py:147-155
Timestamp: 2025-08-14T13:11:35.034Z
Learning: In the jumpstarter fork-based architecture, when the exporter's serve() method exits (e.g., due to lease changes), the child process terminates and the parent process automatically restarts it, eliminating concerns about orphaned tasks since the entire process is restarted.
📚 Learning: 2025-10-14T17:43:07.788Z
Learnt from: michalskrivanek
Repo: jumpstarter-dev/jumpstarter PR: 704
File: packages/jumpstarter/jumpstarter/client/grpc.py:100-107
Timestamp: 2025-10-14T17:43:07.788Z
Learning: In the Jumpstarter client lease model (packages/jumpstarter/jumpstarter/client/grpc.py), `effective_duration` represents the elapsed time for an active lease so far, not the total duration. To calculate expected release time, use `effective_begin_time + duration` (where `duration` is the configured/requested duration), not `effective_begin_time + effective_duration`.

Applied to files:

  • packages/jumpstarter/jumpstarter/client/lease.py
🧬 Code graph analysis (1)
packages/jumpstarter/jumpstarter/client/lease.py (2)
packages/jumpstarter/jumpstarter/common/exceptions.py (1)
  • ConnectionError (42-45)
packages/jumpstarter/jumpstarter/config/client.py (1)
  • lease (135-143)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Redirect rules - jumpstarter-docs
  • GitHub Check: Header rules - jumpstarter-docs
  • GitHub Check: Pages changed - jumpstarter-docs
  • GitHub Check: e2e
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.13)
  • GitHub Check: build
  • GitHub Check: pytest-matrix (macos-15, 3.11)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.11)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)

else:
logger.warning("Waiting for ready connection to %s: %s", path, e)
await sleep(5)
except ModuleNotFoundError as e:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would do this on the import place in the code, because it could be that the driver is not from out jumpstarter_xxx namespace

Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/jumpstarter/jumpstarter/common/importlib.py (1)

67-73: Consider clarifying that module and package names may differ.

The error message assumes the module name matches the pip package name, which isn't always true (e.g., cv2 requires opencv-python, PIL requires Pillow). While this guidance is helpful in most cases, consider adding a note about potential name differences.

Apply this diff to clarify:

             raise ConnectionError(
                 f"Missing Python module '{module_name}'.\n\n"
-                "Please install the missing module:\n"
+                "Please install the missing module (note: package name may differ from module name):\n"
                 f"  pip install {module_name}\n\n"
                 "or if using uv:\n"
                 f"  uv pip install {module_name}"
             ) from e
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a743aa and ee45dc5.

📒 Files selected for processing (2)
  • packages/jumpstarter/jumpstarter/client/lease.py (5 hunks)
  • packages/jumpstarter/jumpstarter/common/importlib.py (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-14T17:43:07.788Z
Learnt from: michalskrivanek
Repo: jumpstarter-dev/jumpstarter PR: 704
File: packages/jumpstarter/jumpstarter/client/grpc.py:100-107
Timestamp: 2025-10-14T17:43:07.788Z
Learning: In the Jumpstarter client lease model (packages/jumpstarter/jumpstarter/client/grpc.py), `effective_duration` represents the elapsed time for an active lease so far, not the total duration. To calculate expected release time, use `effective_begin_time + duration` (where `duration` is the configured/requested duration), not `effective_begin_time + effective_duration`.

Applied to files:

  • packages/jumpstarter/jumpstarter/client/lease.py
🧬 Code graph analysis (2)
packages/jumpstarter/jumpstarter/client/lease.py (1)
packages/jumpstarter/jumpstarter/common/exceptions.py (1)
  • ConnectionError (42-45)
packages/jumpstarter/jumpstarter/common/importlib.py (1)
packages/jumpstarter/jumpstarter/common/exceptions.py (1)
  • ConnectionError (42-45)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: pytest-matrix (macos-15, 3.12)
  • GitHub Check: pytest-matrix (ubuntu-24.04, 3.12)
  • GitHub Check: pytest-matrix (macos-15, 3.11)
  • GitHub Check: pytest-matrix (macos-15, 3.13)
  • GitHub Check: e2e
🔇 Additional comments (6)
packages/jumpstarter/jumpstarter/common/importlib.py (1)

3-8: LGTM!

Adding logging support enables structured error reporting for dynamic imports.

packages/jumpstarter/jumpstarter/client/lease.py (5)

244-245: LGTM! Proper ConnectionError propagation.

Explicitly re-raising ConnectionError ensures that the descriptive error messages from the import layer (e.g., missing jumpstarter drivers or Python modules) are preserved and not wrapped in a generic connection error.


257-272: LGTM! Formatting improvements.

The reformatting improves readability without changing functionality.


325-329: LGTM! Readability improvement.

Multi-line formatting makes the compound boolean condition easier to read.


335-335: LGTM! Consolidated formatting.

Single-line formatting for the short argument list is appropriate.


349-361: LGTM! Consistent elapsed time formatting.

Using split(".")[0] to remove microseconds is consistently applied across all locations (lines 349, 354, 361), improving code uniformity.

Comment on lines +42 to +43
except ModuleNotFoundError as e:
module_name = str(e).split("'")[1] if "'" in str(e) else str(e).split()[-1]
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.

⚠️ Potential issue | 🟠 Major

Use ModuleNotFoundError's .name attribute instead of string parsing.

The current string parsing approach is fragile and can fail if the error message format changes. ModuleNotFoundError provides a .name attribute that directly contains the module name.

Apply this diff to use the more reliable approach:

     except ModuleNotFoundError as e:
-        module_name = str(e).split("'")[1] if "'" in str(e) else str(e).split()[-1]
+        module_name = e.name if e.name else "unknown"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
except ModuleNotFoundError as e:
module_name = str(e).split("'")[1] if "'" in str(e) else str(e).split()[-1]
except ModuleNotFoundError as e:
module_name = e.name if e.name else "unknown"
🤖 Prompt for AI Agents
In packages/jumpstarter/jumpstarter/common/importlib.py around lines 42-43,
replace the fragile string-parsing that extracts the missing module name from
ModuleNotFoundError with the built-in attribute: use the exception's .name
property (e.name) to obtain the module name and assign that to module_name; keep
behavior the same if .name is None by falling back to str(e) only if necessary.

@mangelajo mangelajo enabled auto-merge November 11, 2025 20:08
@mangelajo mangelajo merged commit 4f67f42 into jumpstarter-dev:main Nov 11, 2025
18 checks passed
@bennyz bennyz deleted the missing-package-info branch January 8, 2026 13:38
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants