Source code for AFL.automation.shared.tiled

"""Shared helpers for resolving and connecting to an AFL Tiled service."""

import datetime
import json
import os
from pathlib import Path


[docs] class TiledConfigurationError(RuntimeError): """Raised when an AFL global configuration lacks a usable Tiled server."""
[docs] def get_afl_home(afl_home=None) -> Path: """Return the AFL configuration directory, honoring ``AFL_HOME``.""" if afl_home is not None: return Path(afl_home).expanduser() configured_home = os.environ.get("AFL_HOME", "").strip() if configured_home: return Path(configured_home).expanduser() return Path.home() / ".afl"
[docs] def resolve_tiled_config(afl_home=None) -> tuple[str, str]: """Resolve the newest Tiled server and API key from AFL global config. AFL's :class:`PersistentConfig` stores a history keyed by timestamps in ``YY/DD/MM HH:MM:SS.ffffff`` format. The newest entry that defines a non-empty ``tiled_server`` is selected. An empty API key is valid for anonymously accessible Tiled deployments. """ config_path = get_afl_home(afl_home) / "config.json" if not config_path.exists(): raise TiledConfigurationError( f"AFL global config was not found at {config_path}" ) try: with config_path.open() as config_file: config_data = json.load(config_file) except (OSError, json.JSONDecodeError) as exc: raise TiledConfigurationError( f"Unable to read AFL global config at {config_path}: {exc}" ) from exc if not isinstance(config_data, dict) or not config_data: raise TiledConfigurationError( f"AFL global config at {config_path} contains no configuration entries" ) timestamp_format = "%y/%d/%m %H:%M:%S.%f" try: keys = sorted( config_data, key=lambda key: datetime.datetime.strptime(key, timestamp_format), reverse=True, ) except (TypeError, ValueError): keys = sorted(config_data, reverse=True) for key in keys: entry = config_data.get(key) if not isinstance(entry, dict): continue server = str(entry.get("tiled_server") or "").strip() api_key = str(entry.get("tiled_api_key") or "").strip() if server: return server, api_key raise TiledConfigurationError( f"No tiled_server found in AFL global config at {config_path}" )
[docs] def get_tiled_client(afl_home=None, *, structure_clients="dask"): """Create a Tiled client using the configured AFL Tiled credentials.""" try: from tiled.client import from_uri except ImportError as exc: raise RuntimeError( "Tiled is required to create a Tiled client; install the tiled client extra." ) from exc tiled_server, tiled_api_key = resolve_tiled_config(afl_home) return from_uri( tiled_server, api_key=tiled_api_key, structure_clients=structure_clients, )