From 59139cff29bf45e38637aa8a10b9e471d5a9298a Mon Sep 17 00:00:00 2001 From: Benjamin Kane Date: Thu, 9 May 2024 11:30:12 -0400 Subject: [PATCH] Fix compute visualization results from operators (#4324) * add np casting to json encoder * isinstance * add test * add await --- fiftyone/server/decorators.py | 25 +++++++++++++++-- tests/unittests/server_decorators_tests.py | 32 ++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 tests/unittests/server_decorators_tests.py diff --git a/fiftyone/server/decorators.py b/fiftyone/server/decorators.py index 1d7e434e422..6f013b28944 100644 --- a/fiftyone/server/decorators.py +++ b/fiftyone/server/decorators.py @@ -5,11 +5,14 @@ | `voxel51.com `_ | """ + +from json import JSONEncoder import traceback import typing as t import logging from bson import json_util +import numpy as np from fiftyone.core.utils import run_sync_task @@ -18,6 +21,23 @@ from starlette.requests import Request +class Encoder(JSONEncoder): + def default(self, o): + if isinstance(o, np.floating): + return float(o) + + if isinstance(o, np.integer): + return int(o) + + return JSONEncoder.default(self, o) + + +async def create_response(response: dict): + return Response( + await run_sync_task(lambda: json_util.dumps(response, cls=Encoder)) + ) + + def route(func): async def wrapper( endpoint: HTTPEndpoint, request: Request, *args @@ -30,9 +50,8 @@ async def wrapper( if isinstance(response, Response): return response - return Response( - await run_sync_task(lambda: json_util.dumps(response)) - ) + return await create_response(response) + except Exception as e: logging.exception(e) return JSONResponse( diff --git a/tests/unittests/server_decorators_tests.py b/tests/unittests/server_decorators_tests.py new file mode 100644 index 00000000000..a281ad1a2c3 --- /dev/null +++ b/tests/unittests/server_decorators_tests.py @@ -0,0 +1,32 @@ +""" +FiftyOne Server decorators. + +| Copyright 2017-2024, Voxel51, Inc. +| `voxel51.com `_ +| +""" + +import unittest + +import numpy as np + +from fiftyone.server.decorators import create_response + + +class TestNumPyResponse(unittest.IsolatedAsyncioTestCase): + async def test_numpy_response(self): + await create_response( + { + "float16": np.array([16.0], dtype=np.float16), + "float32": np.array([32.0], dtype=np.float32), + "float64": np.array([64.0], dtype=np.float64), + "int8": np.array([8], dtype=np.int8), + "int16": np.array([8], dtype=np.int16), + "int32": np.array([8], dtype=np.int32), + "int64": np.array([8], dtype=np.int64), + "uint8": np.array([8], dtype=np.uint8), + "uint6": np.array([8], dtype=np.uint16), + "uint32": np.array([8], dtype=np.uint32), + "uint64": np.array([8], dtype=np.uint64), + } + )