Source code for jupyterlite_pyodide_lock.lockers._base

import asyncio
import json
import os
from pathlib import Path
from typing import TYPE_CHECKING

from jupyterlite_pyodide_kernel.constants import PYODIDE_LOCK, PYODIDE_VERSION
from traitlets import Dict, Instance, Int, List, Unicode, default
from traitlets.config import LoggingConfigurable

from jupyterlite_pyodide_lock.constants import ENV_VAR_TIMEOUT, FILES_PYTHON_HOSTED

if TYPE_CHECKING:  # pragma: no cover
    from jupyterlite_pyodide_lock.addons.lock import PyodideLockAddon


[docs] class BaseLocker(LoggingConfigurable): """Common traits and methods for 'pyodide-lock.json' resolving strategies.""" # configurables extra_micropip_args = Dict(help="options for 'micropip.install'").tag(config=True) pyodide_cdn_url = Unicode( f"https://cdn.jsdelivr.net/pyodide/v{PYODIDE_VERSION}/full", help="remote URL for the version of a full pyodide distribution", ).tag(config=True) pypi_api_url = Unicode( "https://pypi.org/pypi", help="remote URL for a Warehouse-compatible JSON API", ).tag(config=True) pythonhosted_cdn_url = Unicode( FILES_PYTHON_HOSTED, help="remote URL for python packages (third-party not supported)", ) timeout = Int(help="seconds to wait for a solve").tag(config=True) # from parent specs = List(Unicode()) packages = List(Instance(Path)) lockfile = Instance(Path) # runtime parent: "PyodideLockAddon" = Instance( "jupyterlite_pyodide_lock.addons.lock.PyodideLockAddon", ) micropip_args = Dict() # API methods
[docs] def resolve_sync(self) -> bool | None: """Provide a sync facade for doing async solves, called by ``PyodideLockAddon``. If a locker is entirely synchronous, it can overload this. """ loop = asyncio.get_event_loop() future = self.resolve_async() return loop.run_until_complete(future)
[docs] async def resolve_async(self) -> bool | None: """Asynchronous solve that handles timeout. An async locker should _not_ overload this unless it has some other means of timing out. """ self.log.info("Resolving with %s second deadline", self.timeout) try: await asyncio.wait_for(self.resolve(), self.timeout) except TimeoutError: # pragma: no cover self.log.error("Failed to lock within %s seconds", self.timeout)
[docs] async def resolve(self) -> bool | None: # pragma: no cover """Asynchronous solve. An async locker should overload this. """ msg = f"{self} cannot solve a ``{PYODIDE_LOCK}``." raise NotImplementedError(msg)
@default("timeout") def _default_timeout(self) -> int: return int(json.loads(os.environ.get(ENV_VAR_TIMEOUT, "").strip() or "120"))