Pozify / tests /test_app_zero_gpu.py
tiena2cva's picture
fix(hf-spaces): avoid pickling analysis progress callbacks
2357a36
Raw
History Blame Contribute Delete
3.8 kB
from __future__ import annotations
import importlib
import inspect
import sys
import types
import unittest
from pathlib import Path
from unittest.mock import patch
ROOT_DIR = Path(__file__).resolve().parents[1]
SRC_DIR = ROOT_DIR / "src"
sys.path.insert(0, str(ROOT_DIR))
sys.path.insert(0, str(SRC_DIR))
def _decorator_factory(_path: str | None = None, **_kwargs: object):
def decorator(function):
return function
return decorator
class _ServerStub:
def __init__(self, **_kwargs: object) -> None:
pass
def get(self, *_args: object, **_kwargs: object):
return _decorator_factory()
def post(self, *_args: object, **_kwargs: object):
return _decorator_factory()
def mount(self, *_args: object, **_kwargs: object) -> None:
pass
def launch(self, **_kwargs: object) -> None:
pass
class _ObjectStub:
def __init__(self, *_args: object, **_kwargs: object) -> None:
pass
def _default_marker(*_args: object, default: object = None, **_kwargs: object) -> object:
return default
def _app_import_stubs() -> dict[str, types.ModuleType]:
gradio = types.ModuleType("gradio")
gradio.Server = _ServerStub
fastapi = types.ModuleType("fastapi")
fastapi.File = _default_marker
fastapi.Form = _default_marker
fastapi.UploadFile = _ObjectStub
fastapi.HTTPException = type(
"HTTPException",
(Exception,),
{"__init__": _ObjectStub.__init__},
)
responses = types.ModuleType("fastapi.responses")
responses.FileResponse = _ObjectStub
responses.HTMLResponse = _ObjectStub
responses.StreamingResponse = _ObjectStub
staticfiles = types.ModuleType("fastapi.staticfiles")
staticfiles.StaticFiles = _ObjectStub
exercise_catalog = types.ModuleType("pozify.exercise_catalog")
exercise_catalog.USER_SELECTABLE_EXERCISES = ["squat"]
pipeline = types.ModuleType("pozify.pipeline")
def run_pipeline(**_kwargs: object) -> dict[str, object]:
return {"source": "pipeline"}
pipeline.run_pipeline = run_pipeline
return {
"gradio": gradio,
"fastapi": fastapi,
"fastapi.responses": responses,
"fastapi.staticfiles": staticfiles,
"pozify.exercise_catalog": exercise_catalog,
"pozify.pipeline": pipeline,
}
def _import_app_module():
sys.modules.pop("app", None)
return importlib.import_module("app")
class AppZeroGpuProgressTests(unittest.TestCase):
def tearDown(self) -> None:
sys.modules.pop("app", None)
def test_analysis_pipeline_is_not_wrapped_at_api_layer(self) -> None:
with patch.dict(sys.modules, _app_import_stubs()):
app = _import_app_module()
signature = inspect.signature(app._run_analysis_pipeline)
self.assertIn("progress", signature.parameters)
self.assertEqual(app._run_analysis_pipeline.__name__, "_run_analysis_pipeline")
def test_analysis_pipeline_forwards_progress_callback_inside_api_process(self) -> None:
with patch.dict(sys.modules, _app_import_stubs()):
app = _import_app_module()
progress_events: list[dict[str, object]] = []
progress_callback = progress_events.append
def local_pipeline(**kwargs: object) -> dict[str, object]:
self.assertIs(kwargs["progress"], progress_callback)
return {"source": "local"}
with patch.object(app, "run_pipeline", side_effect=local_pipeline):
result = app._run_analysis_pipeline(
"video.mp4",
{"goal": "beginner_practice"},
False,
progress_callback,
)
self.assertEqual(result, {"source": "local"})
if __name__ == "__main__":
unittest.main()