Skip to content

Conversation

@mulkieran
Copy link
Member

@mulkieran mulkieran commented Sep 5, 2025

Related #1207

Summary by CodeRabbit

  • Bug Fixes
    • Error reports now follow only explicitly linked causes, reducing noise from unrelated context.
    • Broader exception handling improves detection and reporting of edge-case failures.
    • DBus service errors (including unknown-service cases) now yield a clear message indicating the management service isn’t running, aiding troubleshooting.

@mulkieran mulkieran self-assigned this Sep 5, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 5, 2025

Walkthrough

Updated error traversal in get_errors to yield only the explicit cause chain, broadened the parameter type from Exception to BaseException, and extended DBus error handling to treat org.freedesktop.DBus.Error.ServiceUnknown like NameHasNoOwner, reporting that stratisd is not running.

Changes

Cohort / File(s) Summary
Error Reporting Logic
src/stratis_cli/_error_reporting.py
Changed get_errors signature to accept BaseException. Modified traversal to iterate solely over exc.__cause__ after yielding the initial exception (removed following __context__). Updated _interpret_errors_0 signature to accept dbus.exceptions.DBusException and added handling for org.freedesktop.DBus.Error.ServiceUnknown, treating it like NameHasNoOwner and returning messages indicating stratisd is not running.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant get_errors as get_errors(exc: BaseException)

  Note over Caller,get_errors #F0F7FF: Now yields initial exc, then follows only the explicit __cause__ chain.\nDBus ServiceUnknown is handled like NameHasNoOwner.

  Caller->>get_errors: call with exc
  activate get_errors
  get_errors-->>Caller: yield initial exc
  alt initial is DBus.ServiceUnknown
    get_errors-->>Caller: interpret as "stratisd not running" message
  else
    loop for each explicit __cause__
      get_errors->>get_errors: exc = exc.__cause__
      get_errors-->>Caller: yield exc
    end
  end
  deactivate get_errors
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I hop through causes, one by one,
No tangled contexts on the run.
When DBus says the service's gone,
I thump and tell you stratisd's not on.
A tidy trail beneath the sun.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • 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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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)
src/stratis_cli/_error_reporting.py (1)

82-92: Tiny polish: add return type and simplify loop.

  • Add a return type for clarity.
  • Slightly simpler loop reads cleaner.
  • Optional: update nearby docstrings that say “list of Exception” to “list[BaseException]”.

Apply within this hunk:

-def get_errors(exc: BaseException):
+def get_errors(exc: BaseException) -> "Iterator[BaseException]":
@@
-    while True:
-        yield exc
-        if exc.__cause__ is None:
-            return
-        exc = exc.__cause__
+    while exc is not None:
+        yield exc
+        exc = exc.__cause__

Add near imports:

from typing import Iterator
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7c057ca and 6a11158.

📒 Files selected for processing (1)
  • src/stratis_cli/_error_reporting.py (1 hunks)
⏰ 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). (2)
  • GitHub Check: test-runs (3.13)
  • GitHub Check: test-runs (3.12)
🔇 Additional comments (1)
src/stratis_cli/_error_reporting.py (1)

82-92: LGTM: cause-only traversal and widened type are correct.

Following only __cause__ fixes noisy chains and BaseException is appropriate.

@mulkieran mulkieran moved this to In Review in 2025September Sep 5, 2025
Copy link

@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 (3)
src/stratis_cli/_error_reporting.py (3)

82-90: Good fix; consider annotating return type and simplifying the loop

The behavioral change (follow only explicit cause) and widening to BaseException look right. Minor refinements:

  • Add a return type to help callers and static tools.
  • Simplify the loop to avoid repeating attribute access and to make intent clearer.

Apply:

-def get_errors(exc: BaseException):
-    """
-    Generates a sequence of exceptions starting with exc and following the chain
-    of causes.
-    """
-    yield exc
-    while exc.__cause__ is not None:
-        yield exc.__cause__
-        exc = exc.__cause__
+def get_errors(exc: BaseException) -> "Iterator[BaseException]":
+    """
+    Generates a sequence starting with exc and then following only the explicit
+    __cause__ chain (ignores __context__).
+    """
+    while exc is not None:
+        yield exc
+        exc = exc.__cause__

Add near other stdlib imports:

from typing import Iterator

(Optional safety if you ever accept user-crafted exceptions that could form cycles):

seen = set()
while exc is not None and id(exc) not in seen:
    seen.add(id(exc))
    yield exc
    exc = exc.__cause__

169-172: Docstring types still say “list of Exception”; update to BaseException

For consistency with get_errors(), update these docstrings to reflect BaseException.

-    :type errors: list of Exception
+    :type errors: list[BaseException]

Apply to:

  • _interpret_errors_1 (Lines 169–172)
  • _interpret_errors_2 (Lines 256–258)
  • _interpret_errors (Lines 325–327)

Also applies to: 256-258, 325-327


346-347: Align handle_error param type with BaseException

Docstring still specifies Exception. Optionally annotate the function, too.

-    :param Exception err: an exception
+    :param BaseException err: an exception

(Optional signature annotation)

-def handle_error(err):
+def handle_error(err: BaseException):
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6a11158 and 81df270.

📒 Files selected for processing (1)
  • src/stratis_cli/_error_reporting.py (1 hunks)
⏰ 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). (2)
  • GitHub Check: test-runs (3.13)
  • GitHub Check: test-runs (3.12)
🔇 Additional comments (1)
src/stratis_cli/_error_reporting.py (1)

82-90: No implicit context traversal detected—change is safe
Verified via ripgrep that neither production code nor tests reference __context__, and all get_errors consumers (including tests) operate solely on __cause__ chains.

@mulkieran
Copy link
Member Author

test failures must be infrastructure related

@mulkieran
Copy link
Member Author

Actually, fixing the get_errors method exposed a bug in the failing test.

Copy link

@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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 81df270 and 1917075.

📒 Files selected for processing (2)
  • src/stratis_cli/_error_reporting.py (1 hunks)
  • tests/whitebox/integration/test_stratis.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/stratis_cli/_error_reporting.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-18T15:25:05.145Z
Learnt from: mulkieran
PR: stratis-storage/stratis-cli#0
File: :0-0
Timestamp: 2025-08-18T15:25:05.145Z
Learning: For the stratis-cli project, use Python's standard unittest framework instead of pytest. The codebase follows unittest.TestCase patterns with standard unittest assertions and test discovery.

Applied to files:

  • tests/whitebox/integration/test_stratis.py
🧬 Code graph analysis (1)
tests/whitebox/integration/test_stratis.py (1)
tests/whitebox/integration/_misc.py (1)
  • SimTestCase (217-242)
⏰ 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). (2)
  • GitHub Check: test-runs (3.13)
  • GitHub Check: test-runs (3.12)

from stratis_cli._errors import StratisCliStratisdVersionError

from ._misc import RUNNER, TEST_RUNNER, RunTestCase, SimTestCase
from ._misc import RUNNER, TEST_RUNNER, SimTestCase
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Propagate tests need RunTestCase; re-add import.

SimTestCase always starts the simulator (see tests/whitebox/integration/_misc.py Lines 216-241), so using it for propagation/error tests prevents the expected D-Bus failure. You’ll need RunTestCase imported again for the next change.

-from ._misc import RUNNER, TEST_RUNNER, SimTestCase
+from ._misc import RUNNER, TEST_RUNNER, RunTestCase, SimTestCase
📝 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
from ._misc import RUNNER, TEST_RUNNER, SimTestCase
from ._misc import RUNNER, TEST_RUNNER, RunTestCase, SimTestCase
🤖 Prompt for AI Agents
In tests/whitebox/integration/test_stratis.py around line 28, the RunTestCase
import was removed causing propagation tests to use SimTestCase (which starts
the simulator) and thus mask expected D-Bus failures; re-add RunTestCase to the
import list (e.g. import RunTestCase alongside RUNNER, TEST_RUNNER, SimTestCase)
so propagation/error tests use RunTestCase instead of SimTestCase.



class PropagateTestCase(RunTestCase):
class PropagateTestCase(SimTestCase):
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Revert base class to RunTestCase to actually exercise propagation-on-error.

With SimTestCase, daemon version succeeds and no dbus.exceptions.DBusException is raised, invalidating the test’s premise. Using RunTestCase restores the “no daemon running” scenario that triggers the error and validates --propagate.

As an alternative, keep SimTestCase but patch the D-Bus call to raise DBusException, decoupling the test from daemon presence.

-class PropagateTestCase(SimTestCase):
+class PropagateTestCase(RunTestCase):
📝 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
class PropagateTestCase(SimTestCase):
class PropagateTestCase(RunTestCase):
🤖 Prompt for AI Agents
In tests/whitebox/integration/test_stratis.py around line 48, the test class
currently inherits SimTestCase which causes the daemon call to succeed and
prevents the expected dbus.exceptions.DBusException; change the base class to
RunTestCase so the test runs with no daemon and the propagation-on-error path is
exercised (also add or update the import for RunTestCase if missing).
Alternatively, if you prefer to keep SimTestCase, patch/mock the specific D-Bus
call used in the test to raise dbus.exceptions.DBusException so the --propagate
behavior is validated.

@mulkieran mulkieran force-pushed the fix-get-errors-bug branch 2 times, most recently from e9cae3d to 4d5ebe7 Compare September 5, 2025 14:08
@mulkieran
Copy link
Member Author

So, the error in get_errors() was not quite benign, it was hiding an omission in _interpret_errors_0 by following the context chain when the cause was missing has happens because NameHasNoOwner error is sometimes the context for the ServiceUnknown error.

Signed-off-by: mulhern <amulhern@redhat.com>
There was a lot wrong with it, even though it has never, to my
knowledge, failed.

It used getattr incorrectly, without providing a default value of false, so
that if the attribute was not present on the object, an AttributeError would
be raised regardless.

It would fall back on the context ancestor if there wasn't a cause,
although I suspect it never did. This also was wrong, because the
context would not give any help in determining the error message.

Neither of these code defects is ever known to have caused a bug, but
the ruff linter run by CodeRabbit objected to the construction, and it
was correct.

Signed-off-by: mulhern <amulhern@redhat.com>
Copy link

@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 (3)
src/stratis_cli/_error_reporting.py (3)

93-96: Annotate return type for clarity

The function returns Optional[str]; add an annotation to align code and docstring.

Apply:

-def _interpret_errors_0(
-    error: dbus.exceptions.DBusException,
-):
+from typing import Optional
+
+def _interpret_errors_0(
+    error: dbus.exceptions.DBusException,
+) -> Optional[str]:

125-128: Treating ServiceUnknown like NameHasNoOwner: LGTM; small readability tweak

The new mapping is appropriate. Minor readability/perf nit: cache the name and use a set; also narrow psutil iteration to name-only.

Apply:

-    if error.get_dbus_name() in (
-        "org.freedesktop.DBus.Error.NameHasNoOwner",
-        "org.freedesktop.DBus.Error.ServiceUnknown",
-    ):
+    name = error.get_dbus_name()
+    if name in {
+        "org.freedesktop.DBus.Error.NameHasNoOwner",
+        "org.freedesktop.DBus.Error.ServiceUnknown",
+    }:
         try:
             # pylint: disable=import-outside-toplevel
             # isort: THIRDPARTY
             import psutil
 
-            for proc in psutil.process_iter():
+            for proc in psutil.process_iter(attrs=["name"]):
                 try:
-                    if proc.name() == "stratisd":  # pragma: no cover
+                    if proc.info.get("name") == "stratisd":  # pragma: no cover
                         return (
                             "Most likely stratis is unable to connect to the "
                             "stratisd D-Bus service."
                         )

82-90: Add return type annotation to get_errors

get_errors correctly follows the __cause__ chain, and no raise statements without explicit from were found. Apply:

+from typing import Iterator

-def get_errors(exc: BaseException):
+def get_errors(exc: BaseException) -> Iterator[BaseException]:
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4d5ebe7 and 1589d1a.

📒 Files selected for processing (1)
  • src/stratis_cli/_error_reporting.py (2 hunks)
⏰ 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). (2)
  • GitHub Check: test-runs (3.13)
  • GitHub Check: test-runs (3.12)

@mulkieran mulkieran merged commit 24d7fd2 into stratis-storage:master Sep 5, 2025
10 checks passed
@github-project-automation github-project-automation bot moved this from In Review to Done in 2025September Sep 5, 2025
@mulkieran mulkieran deleted the fix-get-errors-bug branch September 5, 2025 16:04
@mulkieran mulkieran moved this from Done to Done(1) in 2025September Sep 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

No open projects
Status: Done(1)

Development

Successfully merging this pull request may close these issues.

1 participant