xactfetch: Suppress asyncio InvalidStateError
dustin/xactfetch/pipeline/head This commit looks good
Details
dustin/xactfetch/pipeline/head This commit looks good
Details
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/2238master
parent
3ff18d1042
commit
bdcb8c93b6
41
xactfetch.py
41
xactfetch.py
|
@ -13,17 +13,21 @@ from types import TracebackType
|
||||||
from typing import Any, Optional, Type
|
from typing import Any, Optional, Type
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from playwright.async_api import Page
|
from playwright.async_api import Playwright, Page
|
||||||
from playwright.async_api import async_playwright
|
from playwright.async_api import async_playwright
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger('xactfetch')
|
log = logging.getLogger('xactfetch')
|
||||||
|
|
||||||
|
|
||||||
NTFY_URL = os.environ['NTFY_URL']
|
NTFY_URL = os.environ.get('NTFY_URL', 'https://ntfy.pyrocufflink.blue')
|
||||||
NTFY_TOPIC = os.environ['NTFY_TOPIC']
|
NTFY_TOPIC = os.environ.get('NTFY_TOPIC', 'dustin')
|
||||||
FIREFLY_III_URL = os.environ['FIREFLY_III_URL']
|
FIREFLY_III_URL = os.environ.get(
|
||||||
FIREFLY_III_IMPORTER_URL = os.environ['FIREFLY_IMPORT_URL']
|
'FIREFLY_III_URL', 'https://firefly.pyrocufflink.blue'
|
||||||
|
)
|
||||||
|
FIREFLY_III_IMPORTER_URL = os.environ.get(
|
||||||
|
'FIREFLY_IMPORT_URL', 'https://firefly-importer.pyrocufflink.blue'
|
||||||
|
)
|
||||||
SECRET_SOCKET_PATH = os.environ.get('SECRET_SOCKET_PATH')
|
SECRET_SOCKET_PATH = os.environ.get('SECRET_SOCKET_PATH')
|
||||||
XDG_RUNTIME_DIR = os.environ.get('XDG_RUNTIME_DIR')
|
XDG_RUNTIME_DIR = os.environ.get('XDG_RUNTIME_DIR')
|
||||||
|
|
||||||
|
@ -166,6 +170,7 @@ async def get_last_transaction_date(key: int, token: str) -> datetime.date:
|
||||||
'Authorization': f'Bearer {token}',
|
'Authorization': f'Bearer {token}',
|
||||||
'Accept': 'application/vnd.api+json',
|
'Accept': 'application/vnd.api+json',
|
||||||
},
|
},
|
||||||
|
timeout=10,
|
||||||
)
|
)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
last_date = datetime.datetime.min
|
last_date = datetime.datetime.min
|
||||||
|
@ -283,8 +288,12 @@ class ntfyerror:
|
||||||
)
|
)
|
||||||
if os.environ.get('DEBUG_NTFY', '1') == '0':
|
if os.environ.get('DEBUG_NTFY', '1') == '0':
|
||||||
return True
|
return True
|
||||||
|
try:
|
||||||
if ss := await self.page.screenshot():
|
if ss := await self.page.screenshot():
|
||||||
await asyncio.to_thread(save_screenshot, ss)
|
await asyncio.to_thread(save_screenshot, ss)
|
||||||
|
except Exception:
|
||||||
|
log.exception('Failed to get screenshot:')
|
||||||
|
ss = None
|
||||||
await ntfy(
|
await ntfy(
|
||||||
message=str(exc_value),
|
message=str(exc_value),
|
||||||
title=f'xactfetch failed for {self.bank}',
|
title=f'xactfetch failed for {self.bank}',
|
||||||
|
@ -702,10 +711,7 @@ class Chase:
|
||||||
return secret.decode()
|
return secret.decode()
|
||||||
|
|
||||||
|
|
||||||
async def amain() -> None:
|
async def fetch_transactions(pw: Playwright, secrets: SecretsClient) -> bool:
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
secrets = SecretsClient()
|
|
||||||
await secrets.connect()
|
|
||||||
log.debug('Getting Firefly III access token')
|
log.debug('Getting Firefly III access token')
|
||||||
token = (await secrets.get_secret('firefly.token')).decode()
|
token = (await secrets.get_secret('firefly.token')).decode()
|
||||||
import_secret = (
|
import_secret = (
|
||||||
|
@ -720,7 +726,6 @@ async def amain() -> None:
|
||||||
)
|
)
|
||||||
end_date = datetime.date.today() - datetime.timedelta(days=1)
|
end_date = datetime.date.today() - datetime.timedelta(days=1)
|
||||||
failed = False
|
failed = False
|
||||||
async with async_playwright() as pw, secrets:
|
|
||||||
browser = await pw.chromium.launch(headless=False)
|
browser = await pw.chromium.launch(headless=False)
|
||||||
context = await browser.new_context()
|
context = await browser.new_context()
|
||||||
await context.tracing.start(screenshots=True, snapshots=True)
|
await context.tracing.start(screenshots=True, snapshots=True)
|
||||||
|
@ -732,9 +737,7 @@ async def amain() -> None:
|
||||||
):
|
):
|
||||||
failed = True
|
failed = True
|
||||||
if 'chase' in banks:
|
if 'chase' in banks:
|
||||||
if not await download_chase(
|
if not await download_chase(page, secrets, end_date, token, importer):
|
||||||
page, secrets, end_date, token, importer
|
|
||||||
):
|
|
||||||
failed = True
|
failed = True
|
||||||
if failed:
|
if failed:
|
||||||
await context.tracing.stop(path='trace.zip')
|
await context.tracing.stop(path='trace.zip')
|
||||||
|
@ -744,7 +747,19 @@ async def amain() -> None:
|
||||||
attach=f.read(),
|
attach=f.read(),
|
||||||
filename='trace.zip',
|
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)
|
raise SystemExit(1 if failed else 0)
|
||||||
|
except asyncio.exceptions.InvalidStateError as e:
|
||||||
|
log.debug('Ignoring exception: %s', e, exc_info=sys.exc_info())
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
Loading…
Reference in New Issue