Skip to content

Commit 4b1dfcc

Browse files
authored
Merge pull request #131 from NickCrews/account-json
feat: adjust the ser/deser logic of AppleAccount
2 parents f2b65de + 5142350 commit 4b1dfcc

File tree

2 files changed

+35
-38
lines changed

2 files changed

+35
-38
lines changed

examples/_login.py

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
# ruff: noqa: ASYNC230
22

3-
import json
4-
from pathlib import Path
5-
63
from findmy.reports import (
74
AppleAccount,
85
AsyncAppleAccount,
@@ -71,33 +68,25 @@ async def _login_async(account: AsyncAppleAccount) -> None:
7168

7269
def get_account_sync(anisette: BaseAnisetteProvider) -> AppleAccount:
7370
"""Tries to restore a saved Apple account, or prompts the user for login otherwise. (sync)"""
74-
acc = AppleAccount(anisette)
75-
76-
# Save / restore account logic
77-
acc_store = Path("account.json")
71+
acc = AppleAccount(anisette=anisette)
72+
acc_store = "account.json"
7873
try:
79-
with acc_store.open() as f:
80-
acc.restore(json.load(f))
74+
acc.from_json(acc_store)
8175
except FileNotFoundError:
8276
_login_sync(acc)
83-
with acc_store.open("w+") as f:
84-
json.dump(acc.export(), f)
77+
acc.to_json(acc_store)
8578

8679
return acc
8780

8881

8982
async def get_account_async(anisette: BaseAnisetteProvider) -> AsyncAppleAccount:
9083
"""Tries to restore a saved Apple account, or prompts the user for login otherwise. (async)"""
91-
acc = AsyncAppleAccount(anisette)
92-
93-
# Save / restore account logic
94-
acc_store = Path("account.json")
84+
acc = AsyncAppleAccount(anisette=anisette)
85+
acc_store = "account.json"
9586
try:
96-
with acc_store.open() as f:
97-
acc.restore(json.load(f))
87+
acc.from_json(acc_store)
9888
except FileNotFoundError:
9989
await _login_async(acc)
100-
with acc_store.open("w+") as f:
101-
json.dump(acc.export(), f)
90+
acc.to_json(acc_store)
10291

10392
return acc

findmy/reports/account.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from abc import ABC, abstractmethod
1212
from datetime import datetime, timedelta, timezone
1313
from functools import wraps
14+
from pathlib import Path
1415
from typing import (
1516
TYPE_CHECKING,
1617
Any,
@@ -48,7 +49,7 @@
4849
)
4950

5051
if TYPE_CHECKING:
51-
from collections.abc import Sequence
52+
from collections.abc import Mapping, Sequence
5253

5354
from findmy.accessory import RollingKeyPairSource
5455
from findmy.keys import HasHashedPublicKey
@@ -151,24 +152,29 @@ def last_name(self) -> str | None:
151152
raise NotImplementedError
152153

153154
@abstractmethod
154-
def export(self) -> dict:
155+
def to_json(self, path: str | Path | None = None) -> dict:
155156
"""
156-
Export a representation of the current state of the account as a dictionary.
157+
Export the current state of the account as a JSON-serializable dictionary.
158+
159+
If `path` is provided, the output will also be written to that file.
157160
158161
The output of this method is guaranteed to be JSON-serializable, and passing
159-
the return value of this function as an argument to `BaseAppleAccount.restore`
162+
the return value of this function as an argument to `BaseAppleAccount.from_json`
160163
will always result in an exact copy of the internal state as it was when exported.
161164
162165
This method is especially useful to avoid having to keep going through the login flow.
163166
"""
164167
raise NotImplementedError
165168

166169
@abstractmethod
167-
def restore(self, data: dict) -> None:
170+
def from_json(self, json_: str | Path | Mapping, /) -> None:
168171
"""
169-
Restore a previous export of the internal state of the account.
172+
Restore the state from a previous `BaseAppleAccount.to_json` export.
173+
174+
If given a str or Path, it must point to a json file from `BaseAppleAccount.to_json`.
175+
Otherwise it should be the Mapping itself.
170176
171-
See `BaseAppleAccount.export` for more information.
177+
See `BaseAppleAccount.to_json` for more information.
172178
"""
173179
raise NotImplementedError
174180

@@ -363,6 +369,7 @@ class AsyncAppleAccount(BaseAppleAccount):
363369

364370
def __init__(
365371
self,
372+
*,
366373
anisette: BaseAnisetteProvider,
367374
user_id: str | None = None,
368375
device_id: str | None = None,
@@ -447,9 +454,8 @@ def last_name(self) -> str | None:
447454
return self._account_info["last_name"] if self._account_info else None
448455

449456
@override
450-
def export(self) -> dict:
451-
"""See `BaseAppleAccount.export`."""
452-
return {
457+
def to_json(self, path: str | Path | None = None) -> dict:
458+
result = {
453459
"ids": {"uid": self._uid, "devid": self._devid},
454460
"account": {
455461
"username": self._username,
@@ -461,10 +467,13 @@ def export(self) -> dict:
461467
"data": self._login_state_data,
462468
},
463469
}
470+
if path is not None:
471+
Path(path).write_text(json.dumps(result, indent=4))
472+
return result
464473

465474
@override
466-
def restore(self, data: dict) -> None:
467-
"""See `BaseAppleAccount.restore`."""
475+
def from_json(self, json_: str | Path | Mapping, /) -> None:
476+
data = json.loads(Path(json_).read_text()) if isinstance(json_, (str, Path)) else json_
468477
try:
469478
self._uid = data["ids"]["uid"]
470479
self._devid = data["ids"]["devid"]
@@ -972,12 +981,13 @@ class AppleAccount(BaseAppleAccount):
972981

973982
def __init__(
974983
self,
984+
*,
975985
anisette: BaseAnisetteProvider,
976986
user_id: str | None = None,
977987
device_id: str | None = None,
978988
) -> None:
979989
"""See `AsyncAppleAccount.__init__`."""
980-
self._asyncacc = AsyncAppleAccount(anisette, user_id, device_id)
990+
self._asyncacc = AsyncAppleAccount(anisette=anisette, user_id=user_id, device_id=device_id)
981991

982992
try:
983993
self._evt_loop = asyncio.get_running_loop()
@@ -1017,14 +1027,12 @@ def last_name(self) -> str | None:
10171027
return self._asyncacc.last_name
10181028

10191029
@override
1020-
def export(self) -> dict:
1021-
"""See `AsyncAppleAccount.export`."""
1022-
return self._asyncacc.export()
1030+
def to_json(self, path: str | Path | None = None) -> dict:
1031+
return self._asyncacc.to_json(path)
10231032

10241033
@override
1025-
def restore(self, data: dict) -> None:
1026-
"""See `AsyncAppleAccount.restore`."""
1027-
return self._asyncacc.restore(data)
1034+
def from_json(self, json_: str | Path | Mapping, /) -> None:
1035+
return self._asyncacc.from_json(json_)
10281036

10291037
@override
10301038
def login(self, username: str, password: str) -> LoginState:

0 commit comments

Comments
 (0)