Source code for uchrom_browser

"""The U-Chrom web browser: views of a ChromData linked through the selected cells.

A local FastAPI server exposes loaded stores (``.chromdata.zarr`` / ``.cdz``, on disk or over HTTP) over a small HTTP
API (``API.md``); a React + Three.js single-page app (``frontend/``, built into ``static/``) renders it.

    python -m uchrom_browser data.chromdata.zarr        # or: uchrom-browser data.chromdata.zarr

``connect()`` drives an open browser from Python or an agent (the page's actions, ``ui_tools.json``).  The browser
needs only the data model, ``chromdata``; the analysis library (``u-chrom``) is not required.
"""

from __future__ import annotations

import socket
import sys
import threading
import webbrowser
from typing import Iterable, Optional

from .client import BrowserError, Workspace, connect
from .data import DatasetStore, decode_geometry, encode_geometry
from .hosted import HostedConfig

__version__ = "0.1.0"

__all__ = ["BrowserError", "DatasetStore", "Workspace", "connect", "create_app", "decode_geometry",
           "encode_geometry", "serve", "__version__"]


[docs] def create_app(store: Optional[DatasetStore] = None, atlas: Optional[str] = None, hosted=None): """Build the FastAPI app (imported lazily so ``data`` works without FastAPI).""" from .server import create_app as _create_app return _create_app(store, atlas, hosted)
def _free_port(host: str, port: int, tries: int = 20) -> int: """The first port from ``port`` on that the server can bind. The test bind sets SO_REUSEADDR, as uvicorn does: a port left with connections of a stopped server (e.g. an open browser tab) is free for it, while a port with a live listener is still taken.""" for candidate in range(port, port + tries): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: if hasattr(socket, "SO_REUSEADDR") and sys.platform != "win32": s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind((host, candidate)) except OSError: continue return candidate raise OSError(f"no free port in {port}-{port + tries - 1}")
[docs] def serve(paths: Iterable[str] = (), *, host: str = "127.0.0.1", port: int = 8765, open_browser: bool = True, atlas: Optional[str] = None, hosted: bool = False) -> None: """Load ``paths`` and serve the browser until interrupted.""" try: import uvicorn except ImportError as exc: # pragma: no cover - depends on environment raise ImportError( "The web browser needs the 'web' extra: pip install \"u-chrom[web]\"" ) from exc store = DatasetStore() for path in paths: ds = store.load(path) print(f"[uchrom] loaded {ds.name}: {ds.cd.n_spots} spots, " f"{ds.cd.n_traces} traces") port = _free_port(host, port) url = f"http://{host}:{port}/" print(f"[uchrom] browser at {url} (Ctrl+C to stop)") if open_browser: threading.Timer(1.0, webbrowser.open, args=(url,)).start() from .client import clear_state, write_state write_state(url) # lets uchrom_browser.connect() find this server try: uvicorn.run(create_app(store, atlas, HostedConfig() if hosted else None), host=host, port=port, log_level="warning") finally: clear_state(url)