diff --git a/webapp/blueprints/meta.py b/webapp/blueprints/meta.py index cfc80f3..d43579d 100644 --- a/webapp/blueprints/meta.py +++ b/webapp/blueprints/meta.py @@ -2,7 +2,7 @@ from fastapi import APIRouter import h5py import os -from ..utils import NumpySafeJSONResponse +from ..utils import NumpySafeJSONResponse, LOCK router = APIRouter() @@ -16,20 +16,21 @@ def get_meta(path: str, subpath: str = "/"): Returns: template: A rendered Jinja2 HTML template """ + with LOCK: + try: - path = "/" + path + path = "/" + path - try: - with h5py.File(path, "r", swmr=SWMR_DEFAULT, libver="latest") as file: - if subpath: - meta = NumpySafeJSONResponse(metadata(file[subpath])) - else: - meta = NumpySafeJSONResponse(metadata(file["/"])) - return meta + with h5py.File(path, "r", swmr=SWMR_DEFAULT, libver="latest") as file: + if subpath: + meta = NumpySafeJSONResponse(metadata(file[subpath])) + else: + meta = NumpySafeJSONResponse(metadata(file["/"])) + return meta - except Exception as e: - print(e) - # print(f"File {file} can not be opened yet.") + except Exception as e: + print(e) + # print(f"File {file} can not be opened yet.") def metadata(node: h5py.HLObject) -> Mapping[str, Any]: diff --git a/webapp/blueprints/search.py b/webapp/blueprints/search.py index a267af4..514fcbd 100644 --- a/webapp/blueprints/search.py +++ b/webapp/blueprints/search.py @@ -1,8 +1,8 @@ -from ast import Num +import time from fastapi import APIRouter import h5py import os -from ..utils import NumpySafeJSONResponse +from ..utils import NumpySafeJSONResponse, LOCK router = APIRouter() @@ -16,19 +16,20 @@ def get_nodes(path: str, subpath: str = "/"): Returns: template: A rendered Jinja2 HTML template """ - path = "/" + path + with LOCK: - try: + path = "/" + path - with h5py.File(path, "r", swmr=SWMR_DEFAULT, libver="latest") as file: - if subpath: - nodes = NumpySafeJSONResponse(search(file[subpath])) - else: - nodes = NumpySafeJSONResponse(search(file["/"])) - return nodes + try: + with h5py.File(path, "r", swmr=SWMR_DEFAULT, libver="latest") as file: + if subpath: + nodes = NumpySafeJSONResponse(search(file[subpath])) + else: + nodes = NumpySafeJSONResponse(search(file["/"])) + return nodes - except: - print(f"File {file} can not be opened yet.") + except: + print(f"File {file} can not be opened yet.") def search(node): diff --git a/webapp/blueprints/slice.py b/webapp/blueprints/slice.py index 0216d47..bbdac9a 100644 --- a/webapp/blueprints/slice.py +++ b/webapp/blueprints/slice.py @@ -2,7 +2,7 @@ import os import re from fastapi import APIRouter -from ..utils import safe_json_dump +from ..utils import safe_json_dump, LOCK router = APIRouter() @@ -16,36 +16,36 @@ def get_slice(path: str, subpath: str = "/", slice: str = None): Returns: template: A rendered Jinja2 HTML template """ - - path = "/" + path - - if slice is not None: - slices = re.search("(\d+):(\d+):(\d+)", slice) - if slice: - slice_start = int(slices[1]) - slice_stop = int(slices[2]) - slice_step = int(slices[3]) + with LOCK: + path = "/" + path + + if slice is not None: + slices = re.search("(\d+):(\d+):(\d+)", slice) + if slice: + slice_start = int(slices[1]) + slice_stop = int(slices[2]) + slice_step = int(slices[3]) + else: + print("Defaulting to 'all'.") + slice_start = 0 + slice_stop = 0 + slice_step = 0 else: - print("Defaulting to 'all'.") - slice_start = 0 - slice_stop = 0 - slice_step = 0 - else: - return "Please enter a valid url, e.g. \ - '/slice/?subpath=?slice='" - - try: - with h5py.File(path, "r", swmr=SWMR_DEFAULT, libver="latest") as file: - try: - if subpath and isinstance(file[subpath], h5py.Dataset): - sliced = file[subpath][slice_start:slice_stop:slice_step] - return safe_json_dump(sliced) - else: - # meta = metadata(file["/"]) - print("Path not valid or not a dataset.") - # return meta - except Exception as e: - print(e) - - except: - print(f"File {file} can not be opened yet.") + return "Please enter a valid url, e.g. \ + '/slice/?subpath=?slice='" + + try: + with h5py.File(path, "r", swmr=SWMR_DEFAULT, libver="latest") as file: + try: + if subpath and isinstance(file[subpath], h5py.Dataset): + sliced = file[subpath][slice_start:slice_stop:slice_step] + return safe_json_dump(sliced) + else: + # meta = metadata(file["/"]) + print("Path not valid or not a dataset.") + # return meta + except Exception as e: + print(e) + + except: + print(f"File {file} can not be opened yet.") diff --git a/webapp/blueprints/tree.py b/webapp/blueprints/tree.py index 4fcd835..d75ef0e 100644 --- a/webapp/blueprints/tree.py +++ b/webapp/blueprints/tree.py @@ -1,12 +1,11 @@ from collections import defaultdict import os from typing import Any, Dict, List, Union -import numpy import h5py from fastapi import APIRouter from webapp.blueprints.meta import metadata -from webapp.utils import NumpySafeJSONResponse +from webapp.utils import NumpySafeJSONResponse, LOCK SWMR_DEFAULT = bool(int(os.getenv("HDF5_SWMR_DEFAULT", "1"))) @@ -20,25 +19,25 @@ def show_tree(path: str, subpath: str = "/"): Returns: template: A rendered Jinja2 HTML template """ - - path = "/" + path - - tr = defaultdict(dict) - - def visit_node(addr: Union[str, List[str]], node: h5py.HLObject, tree: Dict[str, Any] = tr) -> None: - if isinstance(addr, str): - return visit_node(addr.split("/"), node) - elif len(addr) > 1: - return visit_node(addr[1:], node, tree[addr[0]]["subnodes"]) - else: - tree[addr[0]] = { - "subnodes": defaultdict(dict), - "metadata": metadata(node) - } - - try: - with h5py.File(path, "r", swmr=SWMR_DEFAULT, libver="latest") as file: - file.visititems(visit_node) - return NumpySafeJSONResponse(tr) - except Exception as e: - return e + with LOCK: + path = "/" + path + + tr = defaultdict(dict) + + def visit_node(addr: Union[str, List[str]], node: h5py.HLObject, tree: Dict[str, Any] = tr) -> None: + if isinstance(addr, str): + return visit_node(addr.split("/"), node) + elif len(addr) > 1: + return visit_node(addr[1:], node, tree[addr[0]]["subnodes"]) + else: + tree[addr[0]] = { + "subnodes": defaultdict(dict), + "metadata": metadata(node) + } + + try: + with h5py.File(path, "r", swmr=SWMR_DEFAULT, libver="latest") as file: + file.visititems(visit_node) + return NumpySafeJSONResponse(tr) + except Exception as e: + return e diff --git a/webapp/main.py b/webapp/main.py index be324ec..767466d 100644 --- a/webapp/main.py +++ b/webapp/main.py @@ -1,7 +1,7 @@ import os -from markupsafe import escape +from .utils import LOCK -from fastapi import FastAPI +from fastapi import FastAPI, Response, status # Setup the app app = FastAPI(root_path=os.path.abspath(os.path.dirname(__file__))) @@ -20,3 +20,10 @@ @app.get("/") def index(): return {"INFO": "Please provide a path to the HDF5 file, e.g. '/file/'."} + + +@app.get("/busy") +def busy(response: Response): + if LOCK.locked(): + response.status_code = status.HTTP_423_LOCKED + return {"busy": LOCK.locked()} \ No newline at end of file diff --git a/webapp/utils.py b/webapp/utils.py index 02c9f5f..b5056a8 100644 --- a/webapp/utils.py +++ b/webapp/utils.py @@ -1,7 +1,10 @@ +import threading import sys from typing import Any from starlette.responses import JSONResponse +LOCK = threading.Lock() + def safe_json_dump(content): """ Try to use native orjson path; fall back to going through Python list.