fix(lab): seal RAV004 spatial replay transport
This commit is contained in:
@@ -75,7 +75,13 @@ def _session_times(batch: Any) -> Any | None:
|
||||
return batch.column("session_time")
|
||||
|
||||
|
||||
def _point_rows(chunks: list[Any], entity: str, component: str, *, nested: bool = False) -> _TimedPoints:
|
||||
def _point_rows(
|
||||
chunks: list[Any],
|
||||
entity: str,
|
||||
component: str,
|
||||
*,
|
||||
nested: bool = False,
|
||||
) -> _TimedPoints:
|
||||
rows: list[tuple[int, np.ndarray]] = []
|
||||
for chunk in chunks:
|
||||
if chunk.entity_path != entity:
|
||||
@@ -436,6 +442,8 @@ def _bounded_local_slam(
|
||||
def _canonical_lab_spatial_frame_from_index(
|
||||
index: _CanonicalSpatialIndex,
|
||||
target_time_ns: int,
|
||||
*,
|
||||
include_local_slam: bool = True,
|
||||
) -> dict[str, object]:
|
||||
point_index = _latest_index(index.points.times_ns, target_time_ns)
|
||||
pose_index = _latest_index(index.poses.times_ns, index.points.times_ns[point_index])
|
||||
@@ -462,12 +470,17 @@ def _canonical_lab_spatial_frame_from_index(
|
||||
ground_origin,
|
||||
basis_map_from_body,
|
||||
)
|
||||
local_slam, local_slam_source_frames, local_slam_source_points = _bounded_local_slam(
|
||||
index.points,
|
||||
index.points.times_ns[point_index],
|
||||
ground_origin,
|
||||
basis_map_from_body,
|
||||
)
|
||||
if include_local_slam:
|
||||
local_slam, local_slam_source_frames, local_slam_source_points = _bounded_local_slam(
|
||||
index.points,
|
||||
index.points.times_ns[point_index],
|
||||
ground_origin,
|
||||
basis_map_from_body,
|
||||
)
|
||||
else:
|
||||
local_slam = np.empty((0, 3), dtype=np.float32)
|
||||
local_slam_source_frames = 0
|
||||
local_slam_source_points = 0
|
||||
return {
|
||||
"schema_version": "missioncore.canonical-recorded-lab-spatial-frame/v3",
|
||||
"target_time_ns": target_time_ns,
|
||||
@@ -531,6 +544,8 @@ def canonical_lab_spatial_timeline_samples(
|
||||
frame_times_ns: tuple[int, ...],
|
||||
start_sequence: int,
|
||||
frame_count: int,
|
||||
*,
|
||||
include_local_slam: bool = True,
|
||||
) -> tuple[dict[str, object] | None, ...]:
|
||||
"""Project only new source increments onto a denser camera timeline.
|
||||
|
||||
@@ -545,7 +560,10 @@ def canonical_lab_spatial_timeline_samples(
|
||||
start_sequence < 0
|
||||
or frame_count < 1
|
||||
or start_sequence >= len(frame_times_ns)
|
||||
or any(current <= previous for previous, current in zip(frame_times_ns, frame_times_ns[1:]))
|
||||
or any(
|
||||
current <= previous
|
||||
for previous, current in zip(frame_times_ns, frame_times_ns[1:], strict=False)
|
||||
)
|
||||
):
|
||||
raise ValueError("Recorded LAB timeline sample request is invalid")
|
||||
stat = recording_path.stat()
|
||||
@@ -566,8 +584,75 @@ def canonical_lab_spatial_timeline_samples(
|
||||
else _latest_index(index.points.times_ns, frame_times_ns[sequence - 1])
|
||||
)
|
||||
samples.append(
|
||||
_canonical_lab_spatial_frame_from_index(index, target_time_ns)
|
||||
_canonical_lab_spatial_frame_from_index(
|
||||
index,
|
||||
target_time_ns,
|
||||
include_local_slam=include_local_slam,
|
||||
)
|
||||
if point_index != previous_point_index
|
||||
else None
|
||||
)
|
||||
return tuple(samples)
|
||||
|
||||
|
||||
@lru_cache(maxsize=2)
|
||||
def _canonical_lab_spatial_playback_points_cached(
|
||||
recording_path_text: str,
|
||||
recording_size: int,
|
||||
recording_mtime_ns: int,
|
||||
generation_sha256: str,
|
||||
frame_times_ns: tuple[int, ...],
|
||||
) -> tuple[np.ndarray, tuple[int, ...]]:
|
||||
del recording_size, recording_mtime_ns
|
||||
recording_path = Path(recording_path_text)
|
||||
stat = recording_path.stat()
|
||||
index = _load_index(
|
||||
str(recording_path),
|
||||
stat.st_size,
|
||||
stat.st_mtime_ns,
|
||||
generation_sha256,
|
||||
)
|
||||
increments: list[np.ndarray] = []
|
||||
offsets = [0]
|
||||
point_count = 0
|
||||
previous_point_index = -1
|
||||
for target_time_ns in frame_times_ns:
|
||||
point_index = _latest_index(index.points.times_ns, target_time_ns)
|
||||
if point_index != previous_point_index:
|
||||
increment = np.ascontiguousarray(index.points.values[point_index], dtype="<f4")
|
||||
increments.append(increment)
|
||||
point_count += int(increment.shape[0])
|
||||
offsets.append(point_count)
|
||||
previous_point_index = point_index
|
||||
points = (
|
||||
np.ascontiguousarray(np.concatenate(increments, axis=0), dtype="<f4")
|
||||
if increments
|
||||
else np.empty((0, 3), dtype="<f4")
|
||||
)
|
||||
points.setflags(write=False)
|
||||
return points, tuple(offsets)
|
||||
|
||||
|
||||
def canonical_lab_spatial_playback_points(
|
||||
recording_path: Path,
|
||||
generation_sha256: str,
|
||||
frame_times_ns: tuple[int, ...],
|
||||
) -> tuple[np.ndarray, tuple[int, ...]]:
|
||||
"""Return one retained map-coordinate point track for a camera timeline."""
|
||||
|
||||
if (
|
||||
not frame_times_ns
|
||||
or any(
|
||||
current <= previous
|
||||
for previous, current in zip(frame_times_ns, frame_times_ns[1:], strict=False)
|
||||
)
|
||||
):
|
||||
raise ValueError("Recorded LAB playback timeline is invalid")
|
||||
stat = recording_path.stat()
|
||||
return _canonical_lab_spatial_playback_points_cached(
|
||||
str(recording_path),
|
||||
stat.st_size,
|
||||
stat.st_mtime_ns,
|
||||
generation_sha256,
|
||||
frame_times_ns,
|
||||
)
|
||||
|
||||
@@ -26,13 +26,16 @@ from k1link.laboratory.evidence_report import (
|
||||
)
|
||||
from k1link.laboratory.vegetation_shadow_lab import LAB_SCHEMA
|
||||
from k1link.sessions import RecordedCameraFrame, SessionIntegrityError
|
||||
from k1link.sessions.canonical_lab_spatial import canonical_lab_spatial_timeline_samples
|
||||
from k1link.sessions.canonical_lab_spatial import (
|
||||
canonical_lab_spatial_playback_points,
|
||||
canonical_lab_spatial_timeline_samples,
|
||||
)
|
||||
|
||||
RootProvider = Callable[[], Path | None]
|
||||
CanonicalRecordingProvider = Callable[[str], tuple[Path, str] | None]
|
||||
CameraFrameProvider = Callable[[str, int], RecordedCameraFrame]
|
||||
_MAX_DOCUMENT_BYTES: Final = 1024 * 1024
|
||||
_CANONICAL_ROUTE_CHUNK_FRAMES: Final = 8
|
||||
_CANONICAL_ROUTE_CHUNK_FRAMES: Final = 24
|
||||
_DEFINITION: Final = LaboratoryEvidenceDefinition(
|
||||
work_id="lab-v1-vegetation-shadow",
|
||||
runtime_relative_root=PurePosixPath("lab-v1-vegetation/results"),
|
||||
@@ -167,19 +170,7 @@ def _build_vegetation_lab_router(
|
||||
archive_path = candidate.joinpath(*relative.parts)
|
||||
member = f"masks/frame-{sequence + 1:06d}.png"
|
||||
try:
|
||||
before = archive_path.stat()
|
||||
with zipfile.ZipFile(archive_path) as frozen:
|
||||
info = frozen.getinfo(member)
|
||||
if info.is_dir() or info.file_size < 8 or info.file_size > 1024 * 1024:
|
||||
raise ValueError("Vegetation video mask member is invalid")
|
||||
payload = frozen.read(info)
|
||||
after = archive_path.stat()
|
||||
if (
|
||||
before.st_size != after.st_size
|
||||
or before.st_mtime_ns != after.st_mtime_ns
|
||||
or len(payload) != info.file_size
|
||||
):
|
||||
raise ValueError("Vegetation video mask archive changed during read")
|
||||
payload = _read_cached_mask_member(archive_path, member)
|
||||
except (KeyError, OSError, ValueError, zipfile.BadZipFile):
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
@@ -286,7 +277,7 @@ def _build_vegetation_lab_router(
|
||||
route, frame_times_ns = _full_route_context(candidate, manifest)
|
||||
intervals = [
|
||||
(current - previous) / 1_000_000_000
|
||||
for previous, current in zip(frame_times_ns, frame_times_ns[1:])
|
||||
for previous, current in zip(frame_times_ns, frame_times_ns[1:], strict=False)
|
||||
]
|
||||
nominal_interval = statistics.median(intervals)
|
||||
if not math.isfinite(nominal_interval) or nominal_interval <= 0:
|
||||
@@ -350,7 +341,10 @@ def _build_vegetation_lab_router(
|
||||
if start >= len(frame_times_ns):
|
||||
raise HTTPException(status_code=404, detail="Full-route timeline chunk not found")
|
||||
if canonical_recording_provider is None:
|
||||
raise HTTPException(status_code=503, detail="Canonical spatial recording is unavailable")
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="Canonical spatial recording is unavailable",
|
||||
)
|
||||
recording = canonical_recording_provider(str(route["session_id"]))
|
||||
if recording is None:
|
||||
raise HTTPException(status_code=409, detail="Canonical spatial recording is not ready")
|
||||
@@ -362,6 +356,7 @@ def _build_vegetation_lab_router(
|
||||
frame_times_ns,
|
||||
start,
|
||||
count,
|
||||
include_local_slam=False,
|
||||
)
|
||||
except (OSError, ValueError):
|
||||
raise HTTPException(status_code=503, detail="Canonical spatial chunk failed") from None
|
||||
@@ -391,6 +386,98 @@ def _build_vegetation_lab_router(
|
||||
"access": "read-only-bounded-recorded-replay",
|
||||
}
|
||||
|
||||
@router.get("/{result_id}/timeline/playback")
|
||||
def get_canonical_route_timeline_playback(result_id: str) -> dict[str, object]:
|
||||
candidate = _resolve_candidate(root_provider, definition, result_id)
|
||||
manifest = _read_verified(candidate, definition)
|
||||
route, frame_times_ns = _full_route_context(candidate, manifest)
|
||||
points, offsets = _canonical_route_playback(
|
||||
canonical_recording_provider,
|
||||
route,
|
||||
frame_times_ns,
|
||||
)
|
||||
points_view = memoryview(points).cast("B")
|
||||
return {
|
||||
"schema_version": "missioncore.recorded-spatial-playback/v1",
|
||||
"result_id": result_id,
|
||||
"frame_count": len(frame_times_ns),
|
||||
"point_count": int(points.shape[0]),
|
||||
"point_offsets": list(offsets),
|
||||
"chunk_frame_count": _CANONICAL_ROUTE_CHUNK_FRAMES,
|
||||
"resident_chunk_count_max": 4,
|
||||
"forward_prefetch_chunk_count": 1,
|
||||
"chunks": _canonical_route_playback_chunk_catalog(
|
||||
prefix,
|
||||
result_id,
|
||||
points_view,
|
||||
offsets,
|
||||
),
|
||||
"track": {
|
||||
"id": "points-map-f32",
|
||||
"url": f"{prefix}/{result_id}/timeline/playback/tracks/points-map-f32",
|
||||
"media_type": "application/octet-stream",
|
||||
"dtype": "<f4",
|
||||
"shape": [int(points.shape[0]), 3],
|
||||
"bytes": int(points.nbytes),
|
||||
"sha256": hashlib.sha256(points_view).hexdigest(),
|
||||
},
|
||||
"coordinate_frame": "map",
|
||||
"ground_truth": False,
|
||||
"authority": "replay-simulated",
|
||||
"access": "read-only-sealed-binary-playback",
|
||||
}
|
||||
|
||||
@router.get("/{result_id}/timeline/playback/chunks/{chunk_index}")
|
||||
def get_canonical_route_timeline_playback_chunk(
|
||||
result_id: str,
|
||||
chunk_index: int,
|
||||
) -> Response:
|
||||
candidate = _resolve_candidate(root_provider, definition, result_id)
|
||||
manifest = _read_verified(candidate, definition)
|
||||
route, frame_times_ns = _full_route_context(candidate, manifest)
|
||||
points, offsets = _canonical_route_playback(
|
||||
canonical_recording_provider,
|
||||
route,
|
||||
frame_times_ns,
|
||||
)
|
||||
points_view = memoryview(points).cast("B")
|
||||
descriptor = _canonical_route_playback_chunk_descriptor(
|
||||
prefix,
|
||||
result_id,
|
||||
points_view,
|
||||
offsets,
|
||||
chunk_index,
|
||||
)
|
||||
if descriptor is None:
|
||||
raise HTTPException(status_code=404, detail="Full-route playback chunk not found")
|
||||
point_start = int(descriptor["point_start"])
|
||||
byte_length = int(descriptor["bytes"])
|
||||
byte_start = point_start * 3 * 4
|
||||
payload = bytes(points_view[byte_start : byte_start + byte_length])
|
||||
return Response(
|
||||
content=payload,
|
||||
media_type="application/octet-stream",
|
||||
headers=_immutable_binary_headers(byte_length, str(descriptor["sha256"])),
|
||||
)
|
||||
|
||||
@router.get("/{result_id}/timeline/playback/tracks/points-map-f32")
|
||||
def get_canonical_route_timeline_playback_track(result_id: str) -> Response:
|
||||
candidate = _resolve_candidate(root_provider, definition, result_id)
|
||||
manifest = _read_verified(candidate, definition)
|
||||
route, frame_times_ns = _full_route_context(candidate, manifest)
|
||||
points, _ = _canonical_route_playback(
|
||||
canonical_recording_provider,
|
||||
route,
|
||||
frame_times_ns,
|
||||
)
|
||||
payload = memoryview(points).cast("B")
|
||||
digest = hashlib.sha256(payload).hexdigest()
|
||||
return Response(
|
||||
content=bytes(payload),
|
||||
media_type="application/octet-stream",
|
||||
headers=_immutable_binary_headers(payload.nbytes, digest),
|
||||
)
|
||||
|
||||
@router.get("/{result_id}/timeline/frames/{sequence}/camera")
|
||||
def get_canonical_route_camera(result_id: str, sequence: int) -> Response:
|
||||
if camera_frame_provider is None:
|
||||
@@ -403,7 +490,10 @@ def _build_vegetation_lab_router(
|
||||
try:
|
||||
camera = camera_frame_provider(str(route["session_id"]), sequence)
|
||||
except (OSError, SessionIntegrityError, ValueError):
|
||||
raise HTTPException(status_code=503, detail="Full-route camera frame unavailable") from None
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="Full-route camera frame unavailable",
|
||||
) from None
|
||||
if camera.width != route["width"] or camera.height != route["height"]:
|
||||
raise HTTPException(status_code=503, detail="Full-route camera dimensions changed")
|
||||
return Response(
|
||||
@@ -495,15 +585,111 @@ def _full_route_context(
|
||||
values = np.frombuffer(payload, dtype="<u8")
|
||||
frame_times_ns = tuple(int(value) for value in values)
|
||||
except (OSError, ValueError):
|
||||
raise HTTPException(status_code=503, detail="Full-route timeline verification failed") from None
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="Full-route timeline verification failed",
|
||||
) from None
|
||||
if (
|
||||
len(frame_times_ns) != route["frame_count"]
|
||||
or any(current <= previous for previous, current in zip(frame_times_ns, frame_times_ns[1:]))
|
||||
or any(
|
||||
current <= previous
|
||||
for previous, current in zip(frame_times_ns, frame_times_ns[1:], strict=False)
|
||||
)
|
||||
):
|
||||
raise HTTPException(status_code=503, detail="Full-route timeline order changed")
|
||||
return route, frame_times_ns
|
||||
|
||||
|
||||
def _canonical_route_playback(
|
||||
provider: CanonicalRecordingProvider | None,
|
||||
route: dict[str, Any],
|
||||
frame_times_ns: tuple[int, ...],
|
||||
) -> tuple[np.ndarray, tuple[int, ...]]:
|
||||
if provider is None:
|
||||
raise HTTPException(status_code=503, detail="Canonical spatial recording is unavailable")
|
||||
recording = provider(str(route["session_id"]))
|
||||
if recording is None:
|
||||
raise HTTPException(status_code=409, detail="Canonical spatial recording is not ready")
|
||||
recording_path, generation_sha256 = recording
|
||||
try:
|
||||
return canonical_lab_spatial_playback_points(
|
||||
recording_path,
|
||||
generation_sha256,
|
||||
frame_times_ns,
|
||||
)
|
||||
except (OSError, ValueError):
|
||||
raise HTTPException(status_code=503, detail="Canonical spatial playback failed") from None
|
||||
|
||||
|
||||
def _canonical_route_playback_chunk_descriptor(
|
||||
endpoint_prefix: str,
|
||||
result_id: str,
|
||||
points_view: memoryview,
|
||||
offsets: tuple[int, ...],
|
||||
chunk_index: int,
|
||||
) -> dict[str, object] | None:
|
||||
frame_count = len(offsets) - 1
|
||||
start = chunk_index * _CANONICAL_ROUTE_CHUNK_FRAMES
|
||||
if chunk_index < 0 or start >= frame_count:
|
||||
return None
|
||||
count = min(_CANONICAL_ROUTE_CHUNK_FRAMES, frame_count - start)
|
||||
point_start = offsets[start]
|
||||
point_stop = offsets[start + count]
|
||||
byte_start = point_start * 3 * 4
|
||||
byte_stop = point_stop * 3 * 4
|
||||
payload = points_view[byte_start:byte_stop]
|
||||
return {
|
||||
"index": chunk_index,
|
||||
"start": start,
|
||||
"count": count,
|
||||
"point_start": point_start,
|
||||
"point_count": point_stop - point_start,
|
||||
"url": f"{endpoint_prefix}/{result_id}/timeline/playback/chunks/{chunk_index}",
|
||||
"media_type": "application/octet-stream",
|
||||
"dtype": "<f4",
|
||||
"shape": [point_stop - point_start, 3],
|
||||
"bytes": payload.nbytes,
|
||||
"sha256": hashlib.sha256(payload).hexdigest(),
|
||||
}
|
||||
|
||||
|
||||
def _canonical_route_playback_chunk_catalog(
|
||||
endpoint_prefix: str,
|
||||
result_id: str,
|
||||
points_view: memoryview,
|
||||
offsets: tuple[int, ...],
|
||||
) -> list[dict[str, object]]:
|
||||
frame_count = len(offsets) - 1
|
||||
chunk_count = (
|
||||
frame_count + _CANONICAL_ROUTE_CHUNK_FRAMES - 1
|
||||
) // _CANONICAL_ROUTE_CHUNK_FRAMES
|
||||
return [
|
||||
descriptor
|
||||
for chunk_index in range(chunk_count)
|
||||
if (
|
||||
descriptor := _canonical_route_playback_chunk_descriptor(
|
||||
endpoint_prefix,
|
||||
result_id,
|
||||
points_view,
|
||||
offsets,
|
||||
chunk_index,
|
||||
)
|
||||
)
|
||||
is not None
|
||||
]
|
||||
|
||||
|
||||
def _immutable_binary_headers(byte_length: int, sha256: str) -> dict[str, str]:
|
||||
return {
|
||||
"Cache-Control": "private, max-age=31536000, immutable",
|
||||
"Content-Encoding": "identity",
|
||||
"Content-Length": str(byte_length),
|
||||
"ETag": f'"{sha256}"',
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"X-Uncompressed-Content-Length": str(byte_length),
|
||||
}
|
||||
|
||||
|
||||
def _canonical_timeline_frame(
|
||||
*,
|
||||
result_id: str,
|
||||
@@ -518,7 +704,6 @@ def _canonical_timeline_frame(
|
||||
points = [] if spatial is None or not include_points else spatial["source_points_body_xyz_m"]
|
||||
point_count = 0 if spatial is None else int(spatial["source_point_count"])
|
||||
body_frame = None if spatial is None else spatial["body_frame"]
|
||||
local_slam = [] if spatial is None else spatial["local_slam_body_xyz_m"]
|
||||
return {
|
||||
"schema_version": "missioncore.recorded-spatial-evidence-frame/v1",
|
||||
"sequence": sequence,
|
||||
@@ -534,11 +719,6 @@ def _canonical_timeline_frame(
|
||||
"point_cloud_source_count": point_count,
|
||||
"point_cloud_sample_count": point_count if not include_points else len(points),
|
||||
"point_cloud_layer": "current-increment",
|
||||
"local_slam_body_xyz_m": local_slam,
|
||||
"local_slam_source_frame_count": 0
|
||||
if spatial is None else spatial["local_slam_source_frame_count"],
|
||||
"local_slam_source_point_count": 0
|
||||
if spatial is None else spatial["local_slam_source_point_count"],
|
||||
"rolling_map_component_count": 0,
|
||||
"metric_obstacles": [],
|
||||
"camera_proposals": _semantic_component_proposals(candidate, route, sequence),
|
||||
@@ -585,12 +765,14 @@ def _semantic_component_proposals_cached(
|
||||
archive_mtime_ns: int,
|
||||
sequence: int,
|
||||
) -> tuple[dict[str, object], ...]:
|
||||
del archive_size, archive_mtime_ns
|
||||
archive_path = Path(archive_path_text)
|
||||
member = f"masks/frame-{sequence + 1:06d}.png"
|
||||
try:
|
||||
with zipfile.ZipFile(archive_path) as frozen:
|
||||
payload = frozen.read(member)
|
||||
frozen = _cached_zip_archive(
|
||||
archive_path_text,
|
||||
archive_size,
|
||||
archive_mtime_ns,
|
||||
)
|
||||
payload = frozen.read(member)
|
||||
with Image.open(io.BytesIO(payload)) as image:
|
||||
mask = np.asarray(image.convert("L"), dtype=np.uint8)
|
||||
except (KeyError, OSError, ValueError, zipfile.BadZipFile):
|
||||
@@ -733,7 +915,10 @@ def _route_tgs_anchor_payload(path: Path, source_sequence: int) -> dict[str, obj
|
||||
selected_points = np.ascontiguousarray(points[start:end], dtype=np.float32)
|
||||
selected_states = np.ascontiguousarray(states[slot], dtype=np.uint8)
|
||||
selected_z_bounds = np.ascontiguousarray(z_bounds[slot], dtype=np.float32)
|
||||
if not np.isfinite(selected_points).all() or not np.isin(selected_states, [0, 1, 2, 3]).all():
|
||||
if (
|
||||
not np.isfinite(selected_points).all()
|
||||
or not np.isin(selected_states, [0, 1, 2, 3]).all()
|
||||
):
|
||||
raise ValueError("Route TGS payload changed")
|
||||
result = {
|
||||
"schema_version": "missioncore.lab-v1-route-tgs-anchor/v1",
|
||||
@@ -762,19 +947,7 @@ def _route_tgs_anchor_payload(path: Path, source_sequence: int) -> dict[str, obj
|
||||
def _zip_mask_response(archive_path: Path, sequence: int) -> Response:
|
||||
member = f"masks/frame-{sequence + 1:06d}.png"
|
||||
try:
|
||||
before = archive_path.stat()
|
||||
with zipfile.ZipFile(archive_path) as frozen:
|
||||
info = frozen.getinfo(member)
|
||||
if info.is_dir() or info.file_size < 8 or info.file_size > 1024 * 1024:
|
||||
raise ValueError("Semantic mask member is invalid")
|
||||
payload = frozen.read(info)
|
||||
after = archive_path.stat()
|
||||
if (
|
||||
before.st_size != after.st_size
|
||||
or before.st_mtime_ns != after.st_mtime_ns
|
||||
or len(payload) != info.file_size
|
||||
):
|
||||
raise ValueError("Semantic mask archive changed during read")
|
||||
payload = _read_cached_mask_member(archive_path, member)
|
||||
except (KeyError, OSError, ValueError, zipfile.BadZipFile):
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
@@ -792,6 +965,37 @@ def _zip_mask_response(archive_path: Path, sequence: int) -> Response:
|
||||
)
|
||||
|
||||
|
||||
@lru_cache(maxsize=8)
|
||||
def _cached_zip_archive(
|
||||
archive_path_text: str,
|
||||
archive_size: int,
|
||||
archive_mtime_ns: int,
|
||||
) -> zipfile.ZipFile:
|
||||
del archive_size, archive_mtime_ns
|
||||
return zipfile.ZipFile(archive_path_text)
|
||||
|
||||
|
||||
def _read_cached_mask_member(archive_path: Path, member: str) -> bytes:
|
||||
before = archive_path.stat()
|
||||
frozen = _cached_zip_archive(
|
||||
str(archive_path),
|
||||
before.st_size,
|
||||
before.st_mtime_ns,
|
||||
)
|
||||
info = frozen.getinfo(member)
|
||||
if info.is_dir() or info.file_size < 8 or info.file_size > 1024 * 1024:
|
||||
raise ValueError("Semantic mask member is invalid")
|
||||
payload = frozen.read(info)
|
||||
after = archive_path.stat()
|
||||
if (
|
||||
before.st_size != after.st_size
|
||||
or before.st_mtime_ns != after.st_mtime_ns
|
||||
or len(payload) != info.file_size
|
||||
):
|
||||
raise ValueError("Semantic mask archive changed during read")
|
||||
return payload
|
||||
|
||||
|
||||
def _configured_root(provider: RootProvider) -> Path | None:
|
||||
candidate = provider()
|
||||
if candidate is None:
|
||||
|
||||
Reference in New Issue
Block a user