home

Menu
  • ripgrep search

datasette-remote-metadata/datasette_remote_metadata/__init__.py

import asyncio
from datasette import hookimpl
from datasette.utils import parse_metadata
import httpx
import time
from functools import wraps
import random
 
 
async def update_remote_with_time_limit(datasette, timelimit=None):
    # Update datasette._remote_metadata from URL - returns when done OR
    # when it hits time limit. If the time limit is hit it will still
    # perform the update once the request has completed
    config = datasette.plugin_config("datasette-remote-metadata") or {}
    url = config.get("url")
    if not url:
        return
    headers = config.get("headers") or {}
    cachebust = bool(config.get("cachebust"))
 
    async def update_remote():
        fetch_url = url
        if cachebust:
            fetch_url += "&" if "?" in url else "?"
            fetch_url += str(random.random())
        async with httpx.AsyncClient() as client:
            response = await client.get(
                fetch_url,
                headers=dict(headers, **{"Cache-Control": "no-cache"}),
            )
            response.raise_for_status()
        metadata = parse_metadata(response.content)
        datasette._remote_metadata = metadata
        datasette._remote_metadata_last_updated = time.monotonic()
 
    if timelimit is not None:
        try:
            await asyncio.wait_for(asyncio.shield(update_remote()), timeout=timelimit)
        except asyncio.exceptions.TimeoutError:
            pass
    else:
        await update_remote()
 
 
@hookimpl
def startup(datasette):
    async def inner():
        await update_remote_with_time_limit(datasette)
 
    return inner
 
 
@hookimpl
def get_metadata(datasette):
    return getattr(datasette, "_remote_metadata", None) or {}
 
 
@hookimpl
def asgi_wrapper(datasette):
    # Refresh stale (over X seconds old) remote metadata on every request
    config = datasette.plugin_config("datasette-remote-metadata") or {}
    ttl = config.get("ttl") or 30
 
    def wrap_with_refresh(app):
        @wraps(app)
        async def add_refresh(scope, recieve, send):
            last_update = getattr(datasette, "_remote_metadata_last_updated", None)
            if last_update is None or (time.monotonic() - last_update) > ttl:
                await update_remote_with_time_limit(datasette, timelimit=0.2)
            await app(scope, recieve, send)
 
        return add_refresh
 
    return wrap_with_refresh
 
Powered by Datasette