From 48708af68e8e784e659637b5bbc520a7eb0a603f Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Thu, 11 Jul 2024 21:16:40 -0500 Subject: [PATCH] xactfetch: Suppress asyncio InvalidStateError There is currently a [bug][0] in the Python Playwright API that causes _asyncio_ to raise an `InvalidStateError` occasionally when the `PlaywrightContextManager` exits. This causes the program to exit with a nonzero return code, even though it actually completed successfully, which will cause the Job to be retried. To avoid this, we can catch and ignore the spurious exception. I've reorganized the code a bit here because we have to wrap the whole `with` block in the `try`/`except`; moving the contents of the block into a function keeps the indentation level from getting out of control. [0]: https://github.com/microsoft/playwright-python/issues/2238 --- xactfetch.py | 66 ++++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/xactfetch.py b/xactfetch.py index 67a019e..62882c9 100644 --- a/xactfetch.py +++ b/xactfetch.py @@ -13,7 +13,7 @@ from types import TracebackType from typing import Any, Optional, Type import httpx -from playwright.async_api import Page +from playwright.async_api import Playwright, Page from playwright.async_api import async_playwright @@ -702,10 +702,7 @@ class Chase: return secret.decode() -async def amain() -> None: - logging.basicConfig(level=logging.DEBUG) - secrets = SecretsClient() - await secrets.connect() +async def fetch_transactions(pw: Playwright, secrets: SecretsClient) -> bool: log.debug('Getting Firefly III access token') token = (await secrets.get_secret('firefly.token')).decode() import_secret = ( @@ -720,31 +717,40 @@ async def amain() -> None: ) end_date = datetime.date.today() - datetime.timedelta(days=1) failed = False - async with async_playwright() as pw, secrets: - browser = await pw.chromium.launch(headless=False) - context = await browser.new_context() - await context.tracing.start(screenshots=True, snapshots=True) - page = await context.new_page() - banks = sys.argv[1:] or list(ACCOUNTS.keys()) - if 'commerce' in banks: - if not await download_commerce( - page, secrets, end_date, token, importer - ): - failed = True - if 'chase' in banks: - if not await download_chase( - page, secrets, end_date, token, importer - ): - failed = True - if failed: - await context.tracing.stop(path='trace.zip') - with open('trace.zip', 'rb') as f: - await ntfy( - 'Downloading one or more transaction lists failed.', - attach=f.read(), - filename='trace.zip', - ) - raise SystemExit(1 if failed else 0) + browser = await pw.chromium.launch(headless=False) + context = await browser.new_context() + await context.tracing.start(screenshots=True, snapshots=True) + page = await context.new_page() + banks = sys.argv[1:] or list(ACCOUNTS.keys()) + if 'commerce' in banks: + if not await download_commerce( + page, secrets, end_date, token, importer + ): + failed = True + if 'chase' in banks: + if not await download_chase(page, secrets, end_date, token, importer): + failed = True + if failed: + await context.tracing.stop(path='trace.zip') + with open('trace.zip', 'rb') as f: + await ntfy( + 'Downloading one or more transaction lists failed.', + attach=f.read(), + filename='trace.zip', + ) + return failed + + +async def amain() -> None: + logging.basicConfig(level=logging.DEBUG) + + async with SecretsClient() as secrets: + try: + async with async_playwright() as pw: + failed = await fetch_transactions(pw, secrets) + raise SystemExit(1 if failed else 0) + except asyncio.exceptions.InvalidStateError: + log.debug('Ignoring exception: %s', exc_info=sys.exc_info()) def main():