1111from abc import ABC , abstractmethod
1212from datetime import datetime , timedelta , timezone
1313from functools import wraps
14+ from pathlib import Path
1415from typing import (
1516 TYPE_CHECKING ,
1617 Any ,
4849)
4950
5051if 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