Skip to content

Commit

Permalink
Merge pull request #39 from elupus/remote_proto
Browse files Browse the repository at this point in the history
Add retry on protocol errors
  • Loading branch information
elupus authored May 28, 2024
2 parents 21caf6f + d8d4201 commit 47e89aa
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 30 deletions.
71 changes: 41 additions & 30 deletions haphilipsjs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from urllib.parse import quote
from secrets import token_bytes, token_hex
from base64 import b64decode, b64encode
from functools import wraps


from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.padding import PKCS7
Expand Down Expand Up @@ -200,6 +202,27 @@ def __init__(self, data):
T = TypeVar("T")


def handle_httpx_exceptions(f):
"""Wrap up httpx exceptions in our wanted variants."""
@wraps(f)
async def wrapper(*args, **kwds):
try:
try:
return await f(*args, **kwds)
except httpx.RemoteProtocolError as err:
LOG.warning("%r. We retry once, could be a reused session that was closed", err)
return await f(*args, **kwds)

except (httpx.ConnectTimeout, httpx.ConnectError) as err:
raise ConnectionFailure(err) from err
except (httpx.ProtocolError, httpx.ReadError) as err:
raise ProtocolFailure(err) from err
except httpx.HTTPError as err:
raise GeneralFailure(err) from err

return wrapper


class PhilipsTV(object):

channels: ChannelsType
Expand Down Expand Up @@ -569,38 +592,32 @@ def _url(self, path, protocol = None):

return f"{protocol}://{self._host}:{port}/{self.api_version}/{path}"

@handle_httpx_exceptions
async def getReq(self, path, protocol = None) -> Optional[Dict]:
try:
resp = await self.session.get(self._url(path, protocol = protocol))
if resp.status_code == 401:
raise AutenticationFailure("Authenticaion failed to device")
resp = await self.session.get(self._url(path, protocol = protocol))

if resp.status_code != 200:
LOG.debug("Get failed: %s -> %d %s", path, resp.status_code, resp.text)
return None
if resp.status_code == 401:
raise AutenticationFailure("Authenticaion failed to device")

LOG.debug("Get succeded: %s -> %s", path, resp.text)
return decode_xtv_response(resp)
except (httpx.ConnectTimeout, httpx.ConnectError) as err:
raise ConnectionFailure(err) from err
except httpx.HTTPError as err:
raise GeneralFailure(err) from err
if resp.status_code != 200:
LOG.debug("Get failed: %s -> %d %s", path, resp.status_code, resp.text)
return None

LOG.debug("Get succeded: %s -> %s", path, resp.text)
return decode_xtv_response(resp)

@handle_httpx_exceptions
async def _getBinary(self, path: str) -> Tuple[Optional[bytes], Optional[str]]:

try:
resp = await self.session.get(self._url(path))
if resp.status_code == 401:
raise AutenticationFailure("Authenticaion failed to device")
resp = await self.session.get(self._url(path))
if resp.status_code == 401:
raise AutenticationFailure("Authenticaion failed to device")

if resp.status_code != 200:
return None, None
return resp.content, resp.headers.get("content-type")
except (httpx.ConnectTimeout, httpx.ConnectError) as err:
raise ConnectionFailure(err) from err
except httpx.HTTPError as err:
raise GeneralFailure(err) from err
if resp.status_code != 200:
return None, None
return resp.content, resp.headers.get("content-type")

@handle_httpx_exceptions
async def postReq(self, path: str, data: Any, timeout=None, protocol=None) -> Optional[Dict]:
try:
resp = await self.session.post(self._url(path, protocol), json=data, timeout=timeout)
Expand All @@ -616,12 +633,6 @@ async def postReq(self, path: str, data: Any, timeout=None, protocol=None) -> Op
except httpx.ReadTimeout:
LOG.debug("Read time out on postReq", exc_info=True)
return None
except (httpx.ConnectTimeout, httpx.ConnectError) as err:
raise ConnectionFailure(err) from err
except (httpx.ProtocolError, httpx.ReadError) as err:
raise ProtocolFailure(err) from err
except httpx.HTTPError as err:
raise GeneralFailure(err) from err

async def pairRequest(
self,
Expand Down
9 changes: 9 additions & 0 deletions tests/test_v6.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,15 @@ async def test_send_key_off(client_mock, param: Param):
await client_mock.sendKey("Standby")


async def test_send_key_retry(client_mock, param: Param):
route = respx.post(f"{param.base}/input/key").mock(side_effect=httpx.RemoteProtocolError)

with pytest.raises(haphilipsjs.ProtocolFailure):
await client_mock.sendKey("Standby")

assert route.call_count == 2


async def test_ambilight_mode(client_mock, param):
await client_mock.getSystem()

Expand Down

0 comments on commit 47e89aa

Please sign in to comment.