Skip to content

Commit

Permalink
Introduce Closable ABC
Browse files Browse the repository at this point in the history
  • Loading branch information
malmeloo committed Feb 10, 2024
1 parent 128ce84 commit 223d421
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 30 deletions.
16 changes: 10 additions & 6 deletions findmy/reports/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

from findmy.util import HttpSession, decode_plist
from findmy.util.closable import Closable
from findmy.util.errors import InvalidCredentialsError, UnhandledProtocolError
from findmy.util.http import HttpSession, decode_plist

from .reports import LocationReport, LocationReportsFetcher
from .state import LoginState, require_login_state
Expand Down Expand Up @@ -91,7 +92,7 @@ def _extract_phone_numbers(html: str) -> list[dict]:
return data.get("direct", {}).get("phoneNumberVerification", {}).get("trustedPhoneNumbers", [])


class BaseAppleAccount(ABC):
class BaseAppleAccount(Closable, ABC):
"""Base class for an Apple account."""

@property
Expand Down Expand Up @@ -238,6 +239,8 @@ def __init__(
:param user_id: An optional user ID to use. Will be auto-generated if missing.
:param device_id: An optional device ID to use. Will be auto-generated if missing.
"""
super().__init__()

self._anisette: BaseAnisetteProvider = anisette
self._uid: str = user_id or str(uuid.uuid4())
self._devid: str = device_id or str(uuid.uuid4())
Expand Down Expand Up @@ -686,10 +689,11 @@ def __init__(
self._loop = asyncio.new_event_loop()
asyncio.set_event_loop(self._loop)

def __del__(self) -> None:
"""Gracefully close the async instance's session when garbage collected."""
coro = self._asyncacc.close()
return self._loop.run_until_complete(coro)
super().__init__(self._loop)

async def close(self) -> None:
"""See `AsyncAppleAccount.close`."""
await self._asyncacc.close()

@property
def login_state(self) -> LoginState:
Expand Down
17 changes: 10 additions & 7 deletions findmy/reports/anisette.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
from abc import ABC, abstractmethod
from datetime import datetime, timezone

from findmy.util import HttpSession
from typing_extensions import override

from findmy.util.closable import Closable
from findmy.util.http import HttpSession


def _gen_meta_headers(
Expand All @@ -30,18 +33,13 @@ def _gen_meta_headers(
}


class BaseAnisetteProvider(ABC):
class BaseAnisetteProvider(Closable, ABC):
"""Abstract base class for Anisette providers."""

@abstractmethod
async def _get_base_headers(self) -> dict[str, str]:
raise NotImplementedError

@abstractmethod
async def close(self) -> None:
"""Close any underlying sessions. Call when the provider will no longer be used."""
raise NotImplementedError

async def get_headers(
self,
user_id: str,
Expand All @@ -64,6 +62,8 @@ class RemoteAnisetteProvider(BaseAnisetteProvider):

def __init__(self, server_url: str) -> None:
"""Initialize the provider with URL to te remote server."""
super().__init__()

self._server_url = server_url

self._http = HttpSession()
Expand All @@ -79,6 +79,7 @@ async def _get_base_headers(self) -> dict[str, str]:
"X-Apple-I-MD-M": headers["X-Apple-I-MD-M"],
}

@override
async def close(self) -> None:
"""See `AnisetteProvider.close`."""
await self._http.close()
Expand All @@ -91,9 +92,11 @@ class LocalAnisetteProvider(BaseAnisetteProvider):

def __init__(self) -> None:
"""Initialize the provider."""
super().__init__()

async def _get_base_headers(self) -> dict[str, str]:
return NotImplemented

@override
async def close(self) -> None:
"""See `AnisetteProvider.close`."""
34 changes: 34 additions & 0 deletions findmy/util/closable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""ABC for async classes that need to be cleaned up before exiting."""
from __future__ import annotations

import asyncio
import logging
from abc import ABC, abstractmethod

logging.getLogger(__name__)


class Closable(ABC):
"""ABC for async classes that need to be cleaned up before exiting."""

def __init__(self, loop: asyncio.AbstractEventLoop | None = None) -> None:
"""
Initialize the ``Closable``.
If an event loop is given, the ``Closable`` will attempt to close itself
using the loop when it is garbage collected.
"""
self._loop: asyncio.AbstractEventLoop | None = loop

@abstractmethod
async def close(self) -> None:
"""Clean up."""
raise NotImplementedError

def __del__(self) -> None:
"""Attempt to automatically clean up when garbage collected."""
try:
loop = self._loop or asyncio.get_running_loop()
loop.call_soon_threadsafe(loop.create_task, self.close())
except RuntimeError:
pass
21 changes: 4 additions & 17 deletions findmy/util/http.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"""Module to simplify asynchronous HTTP calls."""
from __future__ import annotations

import asyncio
import json
import logging
from typing import Any, ParamSpec

from aiohttp import BasicAuth, ClientSession, ClientTimeout

from .closable import Closable
from .parsers import decode_plist

logging.getLogger(__name__)
Expand Down Expand Up @@ -52,10 +52,12 @@ def plist(self) -> dict[Any, Any]:
P = ParamSpec("P")


class HttpSession:
class HttpSession(Closable):
"""Asynchronous HTTP session manager. For internal use only."""

def __init__(self) -> None: # noqa: D107
super().__init__()

self._session: ClientSession | None = None

async def _ensure_session(self) -> None:
Expand All @@ -70,21 +72,6 @@ async def close(self) -> None:
await self._session.close()
self._session = None

def __del__(self) -> None:
"""
Attempt to gracefully close the session.
Ideally this should be done by manually calling close().
"""
if self._session is None:
return

try:
loop = asyncio.get_running_loop()
loop.call_soon_threadsafe(loop.create_task, self.close())
except RuntimeError: # cannot await closure
pass

async def request(
self,
method: str,
Expand Down

0 comments on commit 223d421

Please # to comment.