refactor(commands): replace hand-rolled fetch-with-timeout blocks with helpers - #166
Merged
Conversation
…h helpers Five near-identical blocks in Commands.py (WolframAlpha, UrbanDictionary, OpenWeather and WUnderground twice) each opened their own ClientSession and wrapped both the request and the body read in asyncio.wait_for. aiohttp already provides that natively via ClientTimeout, so the wait_for wrappers were redundant scaffolding repeated five times. Added Utils.FetchJson/FetchText, which own the session and the timeout budget, and switched all five call sites to them. URLInfo.get_url_data needs response headers plus a bounded 64K read, so it does not fit the same shape and keeps its own session -- but its redundant wait_for pair is replaced with a ClientTimeout too. The helpers let exceptions propagate rather than swallowing them, so the two WUnderground call sites keep their existing try/except and their user-facing failure message. A timeout still surfaces as asyncio.TimeoutError, exactly as asyncio.wait_for did. Also drops the `if not resp` guards, which were dead: aiohttp's ClientResponse defines neither __bool__ nor __len__, so it is always truthy and those branches could never run. Bumps VERSION to 1.8.19. Closes #152 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Dolly132 <109222243+Dolly132@users.noreply.github.com>
Rushaway
commented
Sep 5, 2026
Rushaway
left a comment
Member
Author
There was a problem hiding this comment.
Self-review: the thing I was most careful about is that the helper does NOT swallow exceptions -- if it did, WUnderground's try/except and its user-facing error message would silently stop firing. Verified against a live server that a timeout still raises asyncio.TimeoutError, the same type wait_for raised, so those handlers are unaffected. Also note I left URLInfo out of the shared helper on purpose (it needs headers + a capped read) and that FlareSolverr had already dropped out of this pattern in #144.
12 tasks
The previous commit used Utils.FetchJson/FetchText in Commands.py without importing Utils, so all five converted commands would have raised NameError at runtime. Caught by CI's ruff check (F821) -- my local ast.parse check only validated syntax, not name resolution. Also applies ruff format, which CI runs as `ruff format . --diff` in the same step and which was failing on the wunderground call site. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Dolly132 <109222243+Dolly132@users.noreply.github.com>
Dolly132
added a commit
to Dolly132/torchlight
that referenced
this pull request
Sep 6, 2026
Dolly132
approved these changes
Sep 6, 2026
Dolly132
left a comment
Collaborator
There was a problem hiding this comment.
Tested, works fine as intended.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Commands.py(WolframAlpha, UrbanDictionary, OpenWeather, WUnderground ×2) each opened their ownClientSessionand wrapped both the request and the body read inasyncio.wait_for. aiohttp provides that natively viaClientTimeout, so thewait_forwrappers were redundant scaffolding repeated five times.Utils.FetchJson/Utils.FetchText, which own the session and the timeout budget, and switched all five sites over.Notes on scope and behavior
URLInfo.get_url_datadeliberately keeps its own session. It needs response headers plus a bounded 64 KB read, so forcing it into the shared helper would mean a third helper shape used by exactly one caller. Its redundantwait_forpair is replaced with aClientTimeoutthough, since that's the same underlying point.FlareSolverris no longer part of this pattern — the issue listed it, but fix(flaresolverr): reuses a pooled session instead of opening one per call #144 already moved it to a pooled session with its own timeout. Outdated by the time I got here.try/exceptand their user-facing "Failed to retrieve data" message. Verified a timeout still surfaces asasyncio.TimeoutError— the exact typeasyncio.wait_forraised — so those handlers behave identically.if not resp: return Nguards, which were dead code:aiohttp.ClientResponsedefines neither__bool__nor__len__, so it's always truthy. Each site keeps its reachableif not datacode.Closes #152
Test plan
Ran the helpers against a live local aiohttp server:
FetchJsonreturns parsed JSON.FetchJson(params=...)forwards query params correctly.FetchTextreturns the body text.timeout=0.5raisesasyncio.TimeoutError(same exception type as before, so existing handlers still catch it).aiohttpimport fromCommands.pyandasynciofromURLInfo.py(ruff F401 would otherwise fail CI).🤖 Generated with Claude Code