Skip to content

Commit 8c4bd0f

Browse files
committed
🚑 Fix AsyncClient initialization to avoid blocking event loop
Prevent event loop blocking caused by lazy httpx client initialization The AsyncSignalRGBClient was previously initializing the httpx.AsyncClient lazily during async operations, which could block the event loop when the SSL certificates were loaded. This change ensures: - Initialize httpx.AsyncClient immediately during constructor call - Add client() property accessor to safely access the client instance - Properly handle client existence checks before closing in __aexit__ - Fix code style issues and linting warnings in async implementation - Add consistent null checks before aclose() calls in cleanup methods This fix is particularly important when using the library in async frameworks like Home Assistant where blocking the event loop can cause performance issues.
1 parent 12aa188 commit 8c4bd0f

File tree

2 files changed

+14
-6
lines changed

2 files changed

+14
-6
lines changed

‎signalrgb/async_client.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,21 @@ def __init__(self, host: str = DEFAULT_HOST, port: int = DEFAULT_PORT, timeout:
5757
self._base_url = f"http://{host}:{port}"
5858
self._timeout = timeout
5959
self._effects_cache: list[Effect] | None = None
60-
self._client = httpx.AsyncClient(timeout=timeout)
60+
self._client = httpx.AsyncClient(timeout=self._timeout)
6161

6262
async def __aenter__(self) -> AsyncSignalRGBClient:
6363
"""Async context manager entry."""
6464
return self
6565

6666
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
6767
"""Async context manager exit."""
68-
await self._client.aclose()
68+
if self._client:
69+
await self._client.aclose()
70+
71+
@property
72+
def client(self) -> httpx.AsyncClient:
73+
"""Get the HTTPX client instance."""
74+
return self._client
6975

7076
@asynccontextmanager
7177
async def _request_context(self, method: str, endpoint: str, **kwargs: Any) -> AsyncIterator[dict[str, Any]]:
@@ -93,7 +99,7 @@ async def _request_context(self, method: str, endpoint: str, **kwargs: Any) -> A
9399
pass
94100

95101
try:
96-
response = await self._client.request(method, url, **kwargs)
102+
response = await self.client.request(method, url, **kwargs)
97103
response.raise_for_status()
98104

99105
if debug:
@@ -114,7 +120,7 @@ async def _request_context(self, method: str, endpoint: str, **kwargs: Any) -> A
114120
error = Error.from_dict(json_data["errors"][0])
115121
else:
116122
error = Error(title=str(e))
117-
except Exception: # noqa: BLE001
123+
except Exception: # noqa: BLE001
118124
error = Error(title=str(e))
119125
else:
120126
error = Error(title=str(e))
@@ -679,7 +685,8 @@ async def aclose(self) -> None:
679685
680686
This method ensures resources are properly released when the client is no longer needed.
681687
"""
682-
await self._client.aclose()
688+
if self._client:
689+
await self._client.aclose()
683690

684691
async def get_effects_cached(self) -> list[Effect]:
685692
"""Get effects with caching.

‎signalrgb/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
)
2323

2424
# Define a TypeVar for the return type
25-
T = TypeVar('T')
25+
T = TypeVar("T")
26+
2627

2728
class SignalRGBClient:
2829
"""Client for interacting with the SignalRGB API.

0 commit comments

Comments
 (0)