Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 25 additions & 17 deletions pyicloud/services/findmyiphone.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,29 +103,37 @@ def _refresh_client_with_reauth(self, locate: bool, retry: bool = False) -> None
def _initialize_devices(self, locate: bool) -> None:
"""Initializes the devices for the FindMyiPhoneServiceManager."""

# If family sharing is enabled, we may need to poll until all devices are ready
# This is indicated by the deviceFetchStatus being "LOADING"
# If family sharing is enabled, we may need to poll until all devices are ready.
# This is indicated by the deviceFetchStatus being "LOADING".
# Some members (e.g. Macs with location sharing disabled) remain permanently
# LOADING and will never resolve. Track which members are LOADING between
# retries and stop as soon as there is no progress, rather than always
# exhausting _MAX_REFRESH_RETRIES for stuck members.
retries: int = 0
prev_loading_keys: set[str] = set()
while (
self._with_family
and self._user_info
and self._user_info.get("hasMembers", False)
and retries < _MAX_REFRESH_RETRIES
):
needs_refresh: bool = False
for user in self._user_info.get("membersInfo", {}).values():
if user.get("deviceFetchStatus") == "LOADING":
needs_refresh = True
break

if needs_refresh:
time.sleep(0.1)
self._refresh_client(locate=locate)
retries += 1
if retries >= _MAX_REFRESH_RETRIES:
_LOGGER.debug("Max retries reached when fetching family devices")
break
else:
break
loading_keys = {
k
for k, v in self._user_info.get("membersInfo", {}).items()
if v.get("deviceFetchStatus") == "LOADING"
}
if not loading_keys:
break # all family members ready
if loading_keys == prev_loading_keys:
_LOGGER.debug(
"No progress on LOADING family members after retry %d, stopping",
retries,
)
break # no change since last retry — give up on permanently stuck members
prev_loading_keys = loading_keys
time.sleep(0.1)
self._refresh_client(locate=locate)
retries += 1
Comment thread
TeroPihlaja marked this conversation as resolved.

if not self._devices:
raise PyiCloudNoDevicesException()
Expand Down
69 changes: 7 additions & 62 deletions tests/services/test_findmyiphone.py
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,12 @@ def test_refresh_client_with_reauth_with_loading_to_done(
def test_refresh_client_with_reauth_with_loading_no_complete(
pyicloud_service_working: PyiCloudService,
) -> None:
"""Test refresh_client_with_reauth calls _refresh_client if the members are loading."""
"""Test that the retry loop stops as soon as no LOADING members make progress.

member2 resolves after the first retry (progress made → continue).
member1 remains LOADING after the second retry (no change → stop early).
Total refresh calls: 1 initial + 2 loop iterations = 3.
"""
with patch(
"pyicloud.services.findmyiphone.FindMyiPhoneServiceManager._refresh_client_with_reauth",
return_value=None,
Expand Down Expand Up @@ -753,69 +758,9 @@ def test_refresh_client_with_reauth_with_loading_no_complete(
"deviceFetchStatus": "DONE",
},
},
True,
{
"member1": {
"firstName": "Member1",
"lastName": "One",
"appleId": "member1@example.com",
"deviceFetchStatus": "LOADING",
},
"member2": {
"firstName": "Member2",
"lastName": "Two",
"appleId": "member2@example.com",
"deviceFetchStatus": "DONE",
},
},
True,
{
"member1": {
"firstName": "Member1",
"lastName": "One",
"appleId": "member1@example.com",
"deviceFetchStatus": "LOADING",
},
"member2": {
"firstName": "Member2",
"lastName": "Two",
"appleId": "member2@example.com",
"deviceFetchStatus": "DONE",
},
},
True,
{
"member1": {
"firstName": "Member1",
"lastName": "One",
"appleId": "member1@example.com",
"deviceFetchStatus": "DONE",
},
"member2": {
"firstName": "Member2",
"lastName": "Two",
"appleId": "member2@example.com",
"deviceFetchStatus": "DONE",
},
},
True,
{
"member1": {
"firstName": "Member1",
"lastName": "One",
"appleId": "member1@example.com",
"deviceFetchStatus": "LOADING",
},
"member2": {
"firstName": "Member2",
"lastName": "Two",
"appleId": "member2@example.com",
"deviceFetchStatus": "DONE",
},
},
]
manager._refresh_client_with_reauth(locate=True)
assert mock_refresh.call_count == 6
assert mock_refresh.call_count == 3
mock_refresh.assert_called_with(locate=True)


Expand Down