Improve exception hierarchy with structured method/path/status attributes#2085
Improve exception hierarchy with structured method/path/status attributes#2085mik-laj wants to merge 5 commits into
Conversation
WLEDEmptyResponseError and WLEDInvalidResponseError now expose `method` and `path` attributes, allowing consumers like Home Assistant to distinguish which endpoint failed (e.g. /presets.json vs /json) without fragile string matching on the exception message. Also fixes WLEDEmptyResponseError inheriting from bare Exception instead of WLEDError, and corrects accidental tuple construction in error message strings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 5 minutes and 14 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. 📝 WalkthroughWalkthroughIntroduce WLEDResponseError and WLEDStatusError; make WLEDEmptyResponseError and WLEDInvalidResponseError inherit and store request metadata; propagate method/path/status/body through WLED.request(), update/listen empty-response branches, export the new exception types, and update tests to assert the metadata. ChangesRequest context enrichment for WLED exceptions
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2085 +/- ##
===========================================
+ Coverage 58.61% 97.54% +38.93%
===========================================
Files 6 8 +2
Lines 662 1141 +479
Branches 143 113 -30
===========================================
+ Hits 388 1113 +725
+ Misses 270 18 -252
- Partials 4 10 +6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR improves debuggability and downstream error handling by enriching response-related exceptions with the HTTP method and request path, allowing consumers to identify the failing endpoint without parsing exception strings.
Changes:
- Add keyword-only
.methodand.pathattributes toWLEDEmptyResponseErrorandWLEDInvalidResponseError. - Fix accidental tuple construction in empty-response error message composition and attach method/path when raising these exceptions.
- Extend preset-related tests to assert
.method/.pathvalues and updatelisten()/update()docstrings to include the additional raised exception type.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/wled/exceptions.py |
Adds method/path attributes and adjusts exception inheritance. |
src/wled/wled.py |
Raises enriched exceptions (and fixes tuple-message bug) in preset and request error paths; updates docstrings. |
tests/test_wled.py |
Adds assertions validating .method/.path on preset-related error cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Raised in code review: requiring `message` breaks `raise WLEDEmptyResponseError` and no-arg construction, which worked before this change. Default to "" to keep backward compatibility. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces a cleaner separation between HTTP-level and response-body errors: - WLEDResponseError: new base for 2xx responses with unusable bodies - WLEDEmptyResponseError (was a direct WLEDError subclass) - WLEDInvalidResponseError (was a direct WLEDError subclass) - WLEDStatusError: HTTP 4xx/5xx errors, direct child of WLEDError - WLEDResponseError/WLEDStatusError expose .method and .path attributes using *args to preserve full backward compatibility with Exception contract Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/wled/wled.py (1)
245-247: ⚡ Quick win
WLEDStatusErrorraised without a message may hinder debugging.When raising
WLEDStatusErroron lines 245-247 and 258-263, no message is passed. While the exception storesmethod,path,status, andbody, callingstr(exception)returns an empty string, making logs and tracebacks less informative.♻️ Suggested improvement
- raise WLEDStatusError( - method=method, path=uri, status=response.status, body=error_body - ) + msg = f"HTTP {response.status} error from request: {method} {uri}" + raise WLEDStatusError( + msg, method=method, path=uri, status=response.status, body=error_body + )And similarly for lines 258-263:
- raise WLEDStatusError( - method=method, - path=uri, - status=response.status, - body={"message": message}, - ) + msg = f"HTTP {response.status} error from request: {method} {uri}" + raise WLEDStatusError( + msg, + method=method, + path=uri, + status=response.status, + body={"message": message}, + )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/wled/wled.py` around lines 245 - 247, The WLEDStatusError is being raised without a descriptive message in the raise statements that build it with method, path (uri), status (response.status) and body (error_body); update those raise sites (the WLEDStatusError constructor calls) to include a formatted human-readable message (e.g., f"HTTP {response.status} {method} {uri}: {shortened/error summary from error_body}") so that str(exception) returns useful info while still preserving method, path, status and body on the exception object; apply the same change to both raise locations that currently pass only keyword fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/wled/wled.py`:
- Around line 245-247: The WLEDStatusError is being raised without a descriptive
message in the raise statements that build it with method, path (uri), status
(response.status) and body (error_body); update those raise sites (the
WLEDStatusError constructor calls) to include a formatted human-readable message
(e.g., f"HTTP {response.status} {method} {uri}: {shortened/error summary from
error_body}") so that str(exception) returns useful info while still preserving
method, path, status and body on the exception object; apply the same change to
both raise locations that currently pass only keyword fields.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0d717b15-3874-4204-8b41-178f9b8870af
📒 Files selected for processing (3)
src/wled/__init__.pysrc/wled/exceptions.pysrc/wled/wled.py
Pass (status, body) as positional args when raising WLEDStatusError so that exc.args and str(exc) remain identical to the previous WLEDError(status, body) behavior. Add test asserting WLEDStatusError with .method, .path, .status, .body and .args to lock in the structured error contract. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
This PR improves the exception hierarchy in the WLED library to allow consumers (e.g. Home Assistant) to distinguish which endpoint failed without fragile string matching on exception messages.
Motivation
Home Assistant's WLED integration was doing (PR: home-assistant/core#171646 ):
A reviewer correctly noted that parsing exception message strings is fragile. This PR moves that distinction into the library.
Exception hierarchy
Before
After
Why this separation?
WLEDStatusErrorandWLEDResponseErrorrepresent fundamentally different failure modes:WLEDStatusError— the device explicitly said "no" (returned a 4xx/5xx HTTP status). This is an HTTP-level error. It carries.status(int) and.bodyso consumers can inspect what the device reported.WLEDResponseError— the device said "ok" (2xx), but the body is unusable. There are two sub-cases: empty body (WLEDEmptyResponseError) and unparseable body (WLEDInvalidResponseError). These can be caught together since HA treats them identically — both mean "response body can't be used."There is no practical use case for catching all three in one handler, so grouping them under a single base would be misleading. Keeping
WLEDStatusErroras a direct child ofWLEDErrormakes the distinction explicit.Changes in detail
src/wled/exceptions.pyWLEDEmptyResponseErrorfixed to inherit fromWLEDError(was incorrectly inheriting from bareException)WLEDResponseError(WLEDError)base class with.methodand.pathkeyword-only attributes;WLEDEmptyResponseErrorandWLEDInvalidResponseErrornow inherit from itWLEDStatusError(WLEDError)for HTTP 4xx/5xx responses, with.method,.path,.status,.bodyattributes__init__use*argsforwarded tosuper().__init__(*args)to preserve the full backward-compatible positional-argument contract ofExceptionsrc/wled/wled.pyrequest()now raisesWLEDStatusError(withmethod,path,status,body) instead of bareWLEDError(response.status, body)for 4xx/5xx responsesWLEDInvalidResponseErrorandWLEDEmptyResponseErrorraised withmethod=andpath=so callers can inspect which endpoint failedupdate()andlisten())update()andlisten()to document additional raised exceptionssrc/wled/__init__.pyWLEDResponseErrorandWLEDStatusErrortests/test_wled.py.methodand.pathadded to preset-related error testsAfter this PR, HA can replace
with:
Test plan
pytest tests/— all 222 tests passWLEDInvalidResponseError,WLEDEmptyResponseError, orWLEDErrorcontinues to workWLEDEmptyResponseErrorretry backoff inupdate()still works (inherits fromWLEDResponseError→WLEDError)🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
WLEDResponseErrorandWLEDStatusErrorexception types for more granular error handling.Tests