Skip to content

fix: create httpx.AsyncClient off event loop (Python 3.13 / HA blocking SSL detection)#46

Open
danielbrunt57 wants to merge 19 commits intoalandtse:mainfrom
danielbrunt57:fix-Detected-blocking-call-to-load_verify_locations
Open

fix: create httpx.AsyncClient off event loop (Python 3.13 / HA blocking SSL detection)#46
danielbrunt57 wants to merge 19 commits intoalandtse:mainfrom
danielbrunt57:fix-Detected-blocking-call-to-load_verify_locations

Conversation

@danielbrunt57
Copy link
Contributor

@danielbrunt57 danielbrunt57 commented Feb 19, 2026

  • Home Assistant on Python 3.13 flags blocking SSL initialization (SSLContext.load_verify_locations) when httpx.AsyncClient() is created on the event loop thread.
  • This change lazily instantiates the session using asyncio.to_thread() and ensures it exists before request handling.
  • Prevents Detected blocking call to load_verify_locations warnings while preserving existing session_factory behavior.

Fixes: #47

Summary by CodeRabbit

  • Bug Fixes

    • Safer handling when an HTTP session is absent; CLI success output now shows empty cookies if no session.
    • More robust URL parsing with type checks and exception handling to avoid crashes on malformed values.
  • Refactor

    • Session initialization made event-loop aware and lazy to avoid blocking and improve responsiveness; reset/stop paths now handle session state safely.
  • Style

    • Cleaned up import ordering and minor formatting across tests.
  • Chores

    • Updated ignore patterns and tooling config; improved test randomness generation.

…ng SSL detec

- Home Assistant on Python 3.13 flags blocking SSL initialization -(SSLContext.load_verify_locations) when httpx.AsyncClient() is created on the event loop thread.
- This change lazily instantiates the session using asyncio.to_thread() and ensures it exists before request handling.
- Prevents Detected blocking call to load_verify_locations warnings while preserving existing session_factory behavior.
@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive Most changes are directly related to the core objective. However, unrelated formatting changes in test files (import reordering, string formatting, blank lines) and tool configuration (.gitignore additions, isort profile) are present without clear justification. Clarify whether test file formatting and .gitignore/pyproject.toml changes are necessary for this fix, or consider moving them to a separate refactoring PR.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: creating httpx.AsyncClient off the event loop to resolve Python 3.13 blocking SSL detection in Home Assistant.
Linked Issues check ✅ Passed The PR fully addresses issue #47 requirements: AsyncClient creation is deferred to asyncio.to_thread() to avoid event-loop blocking, lazy initialization ensures session exists before requests, and session_factory compatibility is preserved.
Docstring Coverage ✅ Passed Docstring coverage is 95.83% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

CodeRabbit can suggest fixes for GitHub Check annotations.

Configure the reviews.tools.github-checks setting to adjust the time to wait for GitHub Checks to complete.

@danielbrunt57 danielbrunt57 changed the title Fix: create httpx.AsyncClient off event loop (Python 3.13 / HA blocki… fix: create httpx.AsyncClient off event loop (Python 3.13 / HA blocking SSL detection) Feb 19, 2026
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
authcaptureproxy/auth_capture_proxy.py (1)

192-204: ⚠️ Potential issue | 🟠 Major

Return type mismatch and dead local variable.

  1. reset_data is typed -> None but line 203 returns a web.Response. Callers (e.g., change_host_url at line 190) discard the return value, so the error response is silently lost.
  2. The session local on line 201 is assigned but never used after the None-guard — it's dead code.

If the intent is purely defensive logging, raise or log instead of returning a response nobody will see:

Proposed fix
         self.session = None
         await self._ensure_session()
-        session = self.session
-        if session is None:  # pragma: no cover
-            return await self._build_response(text="Internal error: HTTP session not initialized")
+        if self.session is None:  # pragma: no cover
+            raise RuntimeError("Failed to initialize HTTP session")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@authcaptureproxy/auth_capture_proxy.py` around lines 192 - 204, The
reset_data method currently declares -> None but returns a web.Response and
assigns a dead local variable `session`; fix it by removing the unused local
`session = self.session` assignment and replacing the defensive `if session is
None` return with an explicit error handling path (either log the error and
raise a RuntimeError or just raise RuntimeError("Internal error: HTTP session
not initialized")) so the error is surfaced instead of silently discarded by
callers like change_host_url; ensure you keep the method signature `async def
reset_data(self) -> None` and use the class logger (or raise) rather than
returning a web.Response.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@authcaptureproxy/auth_capture_proxy.py`:
- Around line 362-364: In all_handler, after awaiting self._ensure_session(),
assign the created session to a local variable (e.g., session = self.session) so
the subsequent uses of session (the bare name referenced later in all_handler)
refer to the instance session; update the all_handler function to read from
self.session after _ensure_session() and ensure all downstream references (the
places in all_handler that currently use session) use that local variable or
self.session consistently so the NameError is resolved.
- Around line 213-221: The _ensure_session method can race during concurrent
calls and create duplicate clients; add an asyncio.Lock instance (e.g.,
self._session_lock = asyncio.Lock()) in __init__ and use it to guard session
creation in _ensure_session: acquire the lock before calling await
asyncio.to_thread(self.session_factory), re-check self.session after acquiring
to avoid duplicate creation, assign the newly created client to self.session
only if still None, and release the lock; ensure you reference
self._session_lock, self.session_factory, and _ensure_session when implementing
this change.

---

Outside diff comments:
In `@authcaptureproxy/auth_capture_proxy.py`:
- Around line 192-204: The reset_data method currently declares -> None but
returns a web.Response and assigns a dead local variable `session`; fix it by
removing the unused local `session = self.session` assignment and replacing the
defensive `if session is None` return with an explicit error handling path
(either log the error and raise a RuntimeError or just raise
RuntimeError("Internal error: HTTP session not initialized")) so the error is
surfaced instead of silently discarded by callers like change_host_url; ensure
you keep the method signature `async def reset_data(self) -> None` and use the
class logger (or raise) rather than returning a web.Response.

---

Duplicate comments:
In `@authcaptureproxy/auth_capture_proxy.py`:
- Around line 554-588: The six uses of session in the request-sending branch are
failing because session was not assigned; after calling _ensure_session() assign
the instance session to the local variable by adding session = self.session so
subsequent calls like getattr(session, method)(...) and other session usages
resolve; locate the function that calls _ensure_session() (the block containing
the multipart/raw/json/data branches) and insert the assignment immediately
after _ensure_session() is invoked.

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
authcaptureproxy/auth_capture_proxy.py (1)

207-218: ⚠️ Potential issue | 🟡 Minor

reset_data() return type is None but the guard returns web.Response.

The type annotation -> None conflicts with return await self._build_response(...) on line 218, which produces web.Response. Mypy/pyright will flag this. Additionally, change_host_url (line 205) calls await self.reset_data() and silently discards the return value, so any caller relying on reset_data completing successfully has no signal when the guard is hit.

The guard is marked # pragma: no cover because _ensure_session() re-raises on failure (no silent None path), making it effectively dead code. Either remove it or fix the type:

🛠️ Option A – remove the unreachable guard (preferred if _ensure_session always raises on failure)
-        self.session = None
-        await self._ensure_session()
-        session = self.session
-        if session is None:  # pragma: no cover
-            return await self._build_response(text="Internal error: HTTP session not initialized")
+        self.session = None
+        await self._ensure_session()
🛠️ Option B – fix the return type annotation if the guard is intentionally kept
-    async def reset_data(self) -> None:
+    async def reset_data(self) -> Optional[web.Response]:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@authcaptureproxy/auth_capture_proxy.py` around lines 207 - 218, reset_data()
is annotated to return None but contains a guard that returns await
self._build_response(...) (a web.Response), causing a type mismatch; either
remove the unreachable guard (preferred if _ensure_session() always raises on
failure) by deleting the "if session is None" block so reset_data remains ->
None, or explicitly make the function return Optional[web.Response] and update
callers (e.g., change_host_url which currently awaits reset_data() and ignores
its return) to check/propagate the web.Response; reference symbols: reset_data,
_ensure_session, _build_response, change_host_url.
🧹 Nitpick comments (1)
authcaptureproxy/auth_capture_proxy.py (1)

74-79: Redundant in_event_loop = False in the except block.

in_event_loop is already False at line 74; the re-assignment in the except branch is a no-op.

♻️ Proposed cleanup
-        in_event_loop = False
         try:
             asyncio.get_running_loop()
             in_event_loop = True
         except RuntimeError:
-            in_event_loop = False
+            pass

Or more idiomatically:

-        in_event_loop = False
-        try:
-            asyncio.get_running_loop()
-            in_event_loop = True
-        except RuntimeError:
-            in_event_loop = False
+        try:
+            asyncio.get_running_loop()
+            in_event_loop = True
+        except RuntimeError:
+            in_event_loop = False

The simplest form:

-        in_event_loop = False
-        try:
-            asyncio.get_running_loop()
-            in_event_loop = True
-        except RuntimeError:
-            in_event_loop = False
+        try:
+            asyncio.get_running_loop()
+            in_event_loop = True
+        except RuntimeError:
+            in_event_loop = False

Even simpler one-liner:

-        in_event_loop = False
-        try:
-            asyncio.get_running_loop()
-            in_event_loop = True
-        except RuntimeError:
-            in_event_loop = False
+        try:
+            asyncio.get_running_loop()
+            in_event_loop = True
+        except RuntimeError:
+            in_event_loop = False

Cleanest version:

-        in_event_loop = False
         try:
             asyncio.get_running_loop()
             in_event_loop = True
         except RuntimeError:
             in_event_loop = False
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@authcaptureproxy/auth_capture_proxy.py` around lines 74 - 79, The variable
in_event_loop is initialized to False then set to True in the try branch but
redundantly reassigned to False in the except branch; remove the no-op
assignment from the except block so the logic becomes: initialize in_event_loop
= False, call asyncio.get_running_loop() inside try and set in_event_loop = True
on success, and leave the except RuntimeError block empty (or just pass). Update
the code around the asyncio.get_running_loop() usage and the in_event_loop
variable so only the try branch sets True and the except does not reassign
False.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@authcaptureproxy/auth_capture_proxy.py`:
- Around line 207-218: reset_data() is annotated to return None but contains a
guard that returns await self._build_response(...) (a web.Response), causing a
type mismatch; either remove the unreachable guard (preferred if
_ensure_session() always raises on failure) by deleting the "if session is None"
block so reset_data remains -> None, or explicitly make the function return
Optional[web.Response] and update callers (e.g., change_host_url which currently
awaits reset_data() and ignores its return) to check/propagate the web.Response;
reference symbols: reset_data, _ensure_session, _build_response,
change_host_url.

---

Duplicate comments:
In `@authcaptureproxy/auth_capture_proxy.py`:
- Around line 228-236: Add an asyncio.Lock to prevent concurrent creation of
duplicate httpx.AsyncClient instances: in __init__ create self._session_lock =
asyncio.Lock(), then update _ensure_session to acquire that lock before
checking/creating the session so only one coroutine runs
asyncio.to_thread(self.session_factory) and assigns self.session; after
acquiring the lock, re-check if self.session is still None, create the client
via asyncio.to_thread(self.session_factory) only if needed, assign to
self.session, and release the lock (using async with self._session_lock) to
avoid leaking the overwritten client.

---

Nitpick comments:
In `@authcaptureproxy/auth_capture_proxy.py`:
- Around line 74-79: The variable in_event_loop is initialized to False then set
to True in the try branch but redundantly reassigned to False in the except
branch; remove the no-op assignment from the except block so the logic becomes:
initialize in_event_loop = False, call asyncio.get_running_loop() inside try and
set in_event_loop = True on success, and leave the except RuntimeError block
empty (or just pass). Update the code around the asyncio.get_running_loop()
usage and the in_event_loop variable so only the try branch sets True and the
except does not reassign False.

@alandtse
Copy link
Owner

Why is your venv in git?

@danielbrunt57
Copy link
Contributor Author

Why is your venv in git?

I accidentally committed my virtual environment directory.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pyproject.toml (1)

72-72: ⚠️ Potential issue | 🟠 Major

mypy dev-dependency upper bound <1.0 excludes all modern mypy releases.

mypy 1.0 shipped in early 2023; the current release is 1.x. This constraint means the project can never install a mypy version released in the last two years, which may be the root cause of the CI mypy issues mentioned in the PR's commit messages.

🔧 Proposed fix
-mypy = ">=0.790, <1.0"
+mypy = ">=1.0, <2.0"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pyproject.toml` at line 72, The mypy dev-dependency constraint "mypy =
\">=0.790, <1.0\"" prevents installing modern 1.x releases; update the mypy
version specifier in pyproject.toml to allow current releases (for example
change to "mypy = \">=0.790, <2.0\"" or remove the upper bound) so CI can
install mypy 1.x, then run the type checks/CI to confirm compatibility; locate
the mypy dependency line in pyproject.toml to make this edit.
🧹 Nitpick comments (3)
pyproject.toml (1)

169-171: target-version in [tool.black] is inconsistent with the declared Python support.

target-version = ["py38"] while requires-python = ">=3.10" and classifiers list 3.10–3.13. Black won't use newer syntax features available from 3.10+ unless the target version reflects the actual minimum.

♻️ Proposed fix
 [tool.black]
 line-length = 100
-target-version = ["py38"]
+target-version = ["py310", "py311", "py312", "py313"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pyproject.toml` around lines 169 - 171, The Black config in pyproject.toml
uses target-version = ["py38"] which contradicts requires-python = ">=3.10";
update the [tool.black] setting so Black targets the actual supported Python
baseline (e.g. change target-version to ["py310"] or remove target-version
entirely so Black infers from the environment), ensuring the symbol
target-version in [tool.black] matches the package's requires-python and
classifier Python versions.
authcaptureproxy/auth_capture_proxy.py (1)

240-241: Missing blank line between _ensure_session and refresh_tests.

PEP 8 requires a blank line between method definitions in a class body. The current code elides it, which is at minimum a linter violation and visually merges two unrelated methods.

🔧 Proposed fix
-                self.session = await asyncio.to_thread(self.session_factory)
-    def refresh_tests(self) -> None:
+                self.session = await asyncio.to_thread(self.session_factory)
+
+    def refresh_tests(self) -> None:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@authcaptureproxy/auth_capture_proxy.py` around lines 240 - 241, Insert a
single blank line separating the end of the preceding async method (the method
that assigns self.session via "self.session = await
asyncio.to_thread(self.session_factory)", i.e., _ensure_session) and the start
of the next method "def refresh_tests(self) -> None:" so that there is one empty
line between these two method definitions to satisfy PEP 8 and improve
readability.
authcaptureproxy/examples/modifiers.py (1)

219-219: Narrow the bare except Exception catches to ValueError.

yarl.URL() raises ValueError for invalid input. Catching Exception masks programming errors (e.g., AttributeError, TypeError) and is flagged by Ruff BLE001.

♻️ Proposed fix (both sites)
-                except Exception:
+                except ValueError:
                     url = None

Apply identically at line 219 (style branch) and line 239 (else branch).

Also applies to: 239-239

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@authcaptureproxy/examples/modifiers.py` at line 219, Change the bare "except
Exception" handlers that wrap yarl.URL() validation to catch ValueError only:
locate the try/except blocks around the yarl.URL(...) calls (the handlers
currently using "except Exception" at the two sites shown) and replace them with
"except ValueError" so only invalid-URL errors are caught (apply the same change
to both occurrences mentioned).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@authcaptureproxy/auth_capture_proxy.py`:
- Around line 383-384: The guard that handles a missing HTTP session currently
returns self._build_response(text="Internal error: HTTP session not
initialized") which yields a 200 OK; update the call in auth_capture_proxy.py
(the branch checking session is None) to return an internal-server-error
response by either passing status=500 into _build_response or raising/returning
a web.HTTPInternalServerError; modify the invocation near the session check so
it uses _build_response(..., status=500) (or equivalent) to ensure clients
receive a 500 error.

In `@authcaptureproxy/examples/modifiers.py`:
- Around line 222-223: The code calls re.sub(pattern, replacement,
attribute_value) where replacement is obtained via replacement = await
run_func(modifier, name="", url=url) but modifiers (swap_url, prepend_url,
replace_empty_url) return URL objects, causing TypeError; fix by converting the
replacement to a string before calling re.sub (e.g., wrap replacement with
str(...) or ensure run_func returns a str), mirroring the approach used in
find_regex_urls; update the code around run_func/modifier (and variables
replacement, new_value, attribute_value, pattern) so re.sub always receives a
str or callable.

---

Outside diff comments:
In `@pyproject.toml`:
- Line 72: The mypy dev-dependency constraint "mypy = \">=0.790, <1.0\""
prevents installing modern 1.x releases; update the mypy version specifier in
pyproject.toml to allow current releases (for example change to "mypy =
\">=0.790, <2.0\"" or remove the upper bound) so CI can install mypy 1.x, then
run the type checks/CI to confirm compatibility; locate the mypy dependency line
in pyproject.toml to make this edit.

---

Nitpick comments:
In `@authcaptureproxy/auth_capture_proxy.py`:
- Around line 240-241: Insert a single blank line separating the end of the
preceding async method (the method that assigns self.session via "self.session =
await asyncio.to_thread(self.session_factory)", i.e., _ensure_session) and the
start of the next method "def refresh_tests(self) -> None:" so that there is one
empty line between these two method definitions to satisfy PEP 8 and improve
readability.

In `@authcaptureproxy/examples/modifiers.py`:
- Line 219: Change the bare "except Exception" handlers that wrap yarl.URL()
validation to catch ValueError only: locate the try/except blocks around the
yarl.URL(...) calls (the handlers currently using "except Exception" at the two
sites shown) and replace them with "except ValueError" so only invalid-URL
errors are caught (apply the same change to both occurrences mentioned).

In `@pyproject.toml`:
- Around line 169-171: The Black config in pyproject.toml uses target-version =
["py38"] which contradicts requires-python = ">=3.10"; update the [tool.black]
setting so Black targets the actual supported Python baseline (e.g. change
target-version to ["py310"] or remove target-version entirely so Black infers
from the environment), ensuring the symbol target-version in [tool.black]
matches the package's requires-python and classifier Python versions.

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.

🧹 Nitpick comments (1)
authcaptureproxy/auth_capture_proxy.py (1)

239-240: Missing blank line between method definitions.

PEP 8 expects at least one blank line between methods in a class body.

Proposed fix
                 self.session = await asyncio.to_thread(self.session_factory)
+
     def refresh_tests(self) -> None:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@authcaptureproxy/auth_capture_proxy.py` around lines 239 - 240, There is no
blank line separating the end of the previous async method and the next method
definition; insert a single blank line between the statement "self.session =
await asyncio.to_thread(self.session_factory)" and the following "def
refresh_tests(self) -> None:" so the class methods comply with PEP 8 (one blank
line between method definitions).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@authcaptureproxy/auth_capture_proxy.py`:
- Around line 382-383: The error guard for a missing session currently returns a
200 because _build_response defaults to status 200; update the check in the
method where "if session is None" occurs to call self._build_response with an
explicit server error status (e.g., await self._build_response(text="Internal
error: HTTP session not initialized", status=500)) so the response is a 500
instead of 200; ensure you modify the call to _build_response in that block
only.

---

Nitpick comments:
In `@authcaptureproxy/auth_capture_proxy.py`:
- Around line 239-240: There is no blank line separating the end of the previous
async method and the next method definition; insert a single blank line between
the statement "self.session = await asyncio.to_thread(self.session_factory)" and
the following "def refresh_tests(self) -> None:" so the class methods comply
with PEP 8 (one blank line between method definitions).

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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@authcaptureproxy/auth_capture_proxy.py`:
- Around line 212-218: In reset_data, currently session is nulled then
_ensure_session is awaited and an early return occurs if session is still None,
which leaves self.last_resp, self.query, self.data, self._active and
self._all_handler_active stale; fix by unconditionally resetting those fields
(set self.last_resp = None, self.query = None, clear self.data, set self._active
= False, self._all_handler_active = False) before the "if self.session is None"
guard (i.e., after setting self.session = None and before awaiting/after
awaiting _ensure_session as appropriate) so that any race with stop_proxy or a
re-created session in all_handler starts from a clean state; keep references to
reset_data, _ensure_session and all_handler to locate the change.

Copy link
Owner

@alandtse alandtse left a comment

Choose a reason for hiding this comment

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

There's a lot random files touched and also unresolved so comments.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@danielbrunt57
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 1

🧹 Nitpick comments (1)
authcaptureproxy/examples/modifiers.py (1)

217-220: Catch specific exceptions in URL parsing, not Exception.

Lines 219 and 240 use bare except Exception, which hides unrelated failures. For URL(...) with string input, catch only the documented exceptions: TypeError (invalid type) and ValueError (decode errors on SplitResult).

Proposed fix
-                except Exception:
+                except (TypeError, ValueError):
                     url = None
...
-                    except Exception:
+                    except (TypeError, ValueError):
                         url = None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@authcaptureproxy/examples/modifiers.py` around lines 217 - 220, The
try/except around URL(match.group(0)) is too broad—replace the bare except
Exception with a specific except catching TypeError and ValueError only; update
both occurrences (the try that assigns url: Optional[URL] = URL(match.group(0))
and the similar try later around URL(...) at the other occurrence) so they catch
(TypeError, ValueError) to avoid hiding unrelated errors while preserving
expected parse failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@authcaptureproxy/examples/modifiers.py`:
- Around line 210-216: The current regex stored in pattern expects the full
attribute (style="...") but html_tag.get("style") returns only the style value,
so re.search never matches; change the regex used in this block (the pattern
variable) to match the style content itself (e.g., match "background-image:
url(...)" and capture the inner URL) and update the subsequent re.search/re.sub
usage to use that capture (or replace using a regex that targets
background-image:url(...) inside attribute_value) so replacements operate on
attribute_value returned by html_tag.get and not on the full attribute syntax.

---

Nitpick comments:
In `@authcaptureproxy/examples/modifiers.py`:
- Around line 217-220: The try/except around URL(match.group(0)) is too
broad—replace the bare except Exception with a specific except catching
TypeError and ValueError only; update both occurrences (the try that assigns
url: Optional[URL] = URL(match.group(0)) and the similar try later around
URL(...) at the other occurrence) so they catch (TypeError, ValueError) to avoid
hiding unrelated errors while preserving expected parse failures.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e986902 and a493a1c.

📒 Files selected for processing (3)
  • .gitignore
  • authcaptureproxy/auth_capture_proxy.py
  • authcaptureproxy/examples/modifiers.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • .gitignore

-                except Exception:
+                except (TypeError, ValueError):
                     url = None
...
-                    except Exception:
+                    except (TypeError, ValueError):
                         url = None
Fixed `except (TypeError, ValueError)::`
@danielbrunt57
Copy link
Contributor Author

@coderabbitai resume

@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

✅ Actions performed

Reviews resumed.

Updated regex pattern to match background-image URLs more accurately.
@danielbrunt57 danielbrunt57 requested a review from alandtse March 18, 2026 16:33
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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
authcaptureproxy/examples/modifiers.py (1)

203-225: ⚠️ Potential issue | 🟠 Major

Inline-style URL rewriting still never runs.

Line 204 calls soup.find_all(tag) with tag == "style=True" (a string literal). BeautifulSoup treats this as a tag name filter searching for tags literally named "style=True", which don't exist in HTML, so the loop never executes and the inline CSS background-image rewriting logic (lines 206–230) is unreachable. Use the keyword argument soup.find_all(style=True) to find tags with a style attribute.

Suggested fix
     for tag, attribute in search.items():
-        for html_tag in soup.find_all(tag):
-            if tag == "style=True":
+        html_tags = soup.find_all(style=True) if tag == "style=True" else soup.find_all(tag)
+        for html_tag in html_tags:
+            if tag == "style=True":
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@authcaptureproxy/examples/modifiers.py` around lines 203 - 225, The loop
currently calls soup.find_all(tag) where tag can be the string literal
"style=True", which never matches; change the logic that iterates search.items()
to detect the special key "style=True" (or when tag == "style=True") and call
soup.find_all(style=True) instead, and set attribute to "style" so the existing
inline-CSS handling runs; keep the rest of the block (pattern, re.search,
URL(...) parsing, run_func(modifier,...), and html_tag[attribute] replacement)
unchanged so modifier is invoked for inline background-image URLs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@authcaptureproxy/examples/modifiers.py`:
- Around line 203-225: The loop currently calls soup.find_all(tag) where tag can
be the string literal "style=True", which never matches; change the logic that
iterates search.items() to detect the special key "style=True" (or when tag ==
"style=True") and call soup.find_all(style=True) instead, and set attribute to
"style" so the existing inline-CSS handling runs; keep the rest of the block
(pattern, re.search, URL(...) parsing, run_func(modifier,...), and
html_tag[attribute] replacement) unchanged so modifier is invoked for inline
background-image URLs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88377177-49b0-4c33-b04b-d7be966e8083

📥 Commits

Reviewing files that changed from the base of the PR and between d23b826 and bdec6a8.

📒 Files selected for processing (1)
  • authcaptureproxy/examples/modifiers.py

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.

Detected blocking call to load_verify_locations with args (<ssl.SSLContext object at 0x7fa53a8b13b0> #3380

2 participants