From 111158120d6cdff705ff9f991c5e62b6a08c5b48 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Mon, 9 Jan 2023 15:07:42 +0100 Subject: [PATCH 01/17] test: add failing test for fastapi Signed-off-by: Sami Jaghouar --- tests/integrations/externals/test_fastapi.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/integrations/externals/test_fastapi.py b/tests/integrations/externals/test_fastapi.py index 2e19b39ec8b..9ce05873dca 100644 --- a/tests/integrations/externals/test_fastapi.py +++ b/tests/integrations/externals/test_fastapi.py @@ -63,6 +63,12 @@ async def create_item(doc: InputDoc) -> OutputDoc: assert resp_doc.status_code == 200 assert resp_redoc.status_code == 200 + doc = OutputDoc.parse_raw(response.content.decode()) + + assert isinstance(doc, OutputDoc) + assert doc.embedding_clip.shape == (100, 1) + assert doc.embedding_bert.shape == (100, 1) + @pytest.mark.asyncio async def test_sentence_to_embeddings(): From d45377f84f99315b4d51d380109eb7fb83f5c17c Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Mon, 9 Jan 2023 15:17:31 +0100 Subject: [PATCH 02/17] refactor: renamed to json compatible and put it at abstract class level Signed-off-by: Sami Jaghouar --- docarray/document/io/json.py | 9 +++++---- docarray/typing/tensor/abstract_tensor.py | 10 +++++++++- docarray/typing/tensor/ndarray.py | 4 ++-- docarray/typing/tensor/torch_tensor.py | 4 ++-- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/docarray/document/io/json.py b/docarray/document/io/json.py index 16e875fa359..874d6019e25 100644 --- a/docarray/document/io/json.py +++ b/docarray/document/io/json.py @@ -1,16 +1,17 @@ import orjson +from docarray.typing.tensor.abstract_tensor import AbstractTensor + def _default_orjson(obj): """ - default option for orjson dumps. It will call _to_json_compatible - from docarray typing object that expose such method. + default option for orjson dumps. :param obj: :return: return a json compatible object """ - if getattr(obj, '_to_json_compatible'): - return obj._to_json_compatible() + if isinstance(obj, AbstractTensor): + return obj.__docarray_to_json_compatible__() else: return obj diff --git a/docarray/typing/tensor/abstract_tensor.py b/docarray/typing/tensor/abstract_tensor.py index 29bd773a9fb..708f3f4df35 100644 --- a/docarray/typing/tensor/abstract_tensor.py +++ b/docarray/typing/tensor/abstract_tensor.py @@ -120,7 +120,7 @@ def get_comp_backend() -> Type[AbstractComputationalBackend]: """The computational backend compatible with this tensor type.""" ... - @abc.abstractmethod + def __getitem__(self, item): """Get a slice of this tensor.""" ... @@ -136,4 +136,12 @@ def to_protobuf(self) -> 'NdArrayProto': def unwrap(self): """Return the native tensor object that this DocArray tensor wraps.""" + + + @abc.abstractmethod + def __docarray_to_json_compatible__(self): + """ + Convert tensor into a json compatible object + :return: a representation of the tensor compatible with orjson + """ ... diff --git a/docarray/typing/tensor/ndarray.py b/docarray/typing/tensor/ndarray.py index d4dce8c707c..12a350484ca 100644 --- a/docarray/typing/tensor/ndarray.py +++ b/docarray/typing/tensor/ndarray.py @@ -130,10 +130,10 @@ def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: # this is needed to dump to json field_schema.update(type='string', format='tensor') - def _to_json_compatible(self) -> np.ndarray: + def __docarray_to_json_compatible__(self) -> np.ndarray: """ Convert tensor into a json compatible object - :return: a list representation of the tensor + :return: a representation of the tensor compatible with orjson """ return self.unwrap() diff --git a/docarray/typing/tensor/torch_tensor.py b/docarray/typing/tensor/torch_tensor.py index 8a2126299a2..6182030edf2 100644 --- a/docarray/typing/tensor/torch_tensor.py +++ b/docarray/typing/tensor/torch_tensor.py @@ -126,10 +126,10 @@ def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: # this is needed to dump to json field_schema.update(type='string', format='tensor') - def _to_json_compatible(self) -> np.ndarray: + def __docarray_to_json_compatible__(self) -> np.ndarray: """ Convert torchTensor into a json compatible object - :return: a list representation of the torch tensor + :return: a representation of the tensor compatible with orjson """ return self.numpy() ## might need to check device later From acddcd7320c29557c71f15e0c2ab4095f94af01b Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 10:21:10 +0100 Subject: [PATCH 03/17] refactor: use fixture in tests to json Signed-off-by: Sami Jaghouar --- tests/integrations/document/test_to_json.py | 24 +++++++++------------ 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/tests/integrations/document/test_to_json.py b/tests/integrations/document/test_to_json.py index 81bfeb2db32..a3da5c82002 100644 --- a/tests/integrations/document/test_to_json.py +++ b/tests/integrations/document/test_to_json.py @@ -1,11 +1,13 @@ import numpy as np +import pytest import torch from docarray.document import BaseDocument from docarray.typing import AnyUrl, NdArray, TorchTensor -def test_to_json(): +@pytest.fixture() +def doc_and_class(): class Mmdoc(BaseDocument): img: NdArray url: AnyUrl @@ -18,22 +20,16 @@ class Mmdoc(BaseDocument): txt='hello', torch_tensor=torch.zeros(3, 224, 224), ) - doc.json() + return doc, Mmdoc -def test_from_json(): - class Mmdoc(BaseDocument): - img: NdArray - url: AnyUrl - txt: str - torch_tensor: TorchTensor +def test_to_json(doc_and_class): + doc, _ = doc_and_class + doc.json() - doc = Mmdoc( - img=np.zeros((2, 2)), - url='http://doccaray.io', - txt='hello', - torch_tensor=torch.zeros(3, 224, 224), - ) + +def test_from_json(doc_and_class): + doc, Mmdoc = doc_and_class new_doc = Mmdoc.parse_raw(doc.json()) for (field, field2) in zip(doc.dict().keys(), new_doc.dict().keys()): From 2abc3d1c7baac9169a4e0ac7b01605214bee9006 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 14:47:21 +0100 Subject: [PATCH 04/17] fix(tests): uses orjson response Signed-off-by: Sami Jaghouar --- docarray/document/document.py | 4 ++-- docarray/document/io/json.py | 11 +++++++---- tests/integrations/document/test_to_json.py | 16 ++++++++++++++-- tests/integrations/externals/test_fastapi.py | 12 ++++++++++-- tests/units/typing/tensor/test_embedding.py | 4 ++-- tests/units/typing/tensor/test_tensor.py | 6 +++--- tests/units/typing/tensor/test_torch_tensor.py | 4 ++-- tests/units/typing/test_id.py | 4 ++-- tests/units/typing/url/test_any_url.py | 4 ++-- tests/units/typing/url/test_audio_url.py | 4 ++-- tests/units/typing/url/test_image_url.py | 4 ++-- tests/units/typing/url/test_mesh_url.py | 4 ++-- tests/units/typing/url/test_point_cloud_url.py | 4 ++-- tests/units/typing/url/test_text_url.py | 4 ++-- 14 files changed, 54 insertions(+), 31 deletions(-) diff --git a/docarray/document/document.py b/docarray/document/document.py index 34d4c3768d0..e6ad4e87cbd 100644 --- a/docarray/document/document.py +++ b/docarray/document/document.py @@ -6,7 +6,7 @@ from docarray.document.abstract_document import AbstractDocument from docarray.document.base_node import BaseNode -from docarray.document.io.json import orjson_dumps +from docarray.document.io.json import orjson_dumps_and_decode from docarray.document.mixins import ProtoMixin from docarray.typing import ID @@ -20,7 +20,7 @@ class BaseDocument(BaseModel, ProtoMixin, AbstractDocument, BaseNode): class Config: json_loads = orjson.loads - json_dumps = orjson_dumps + json_dumps = orjson_dumps_and_decode validate_assignment = True @classmethod diff --git a/docarray/document/io/json.py b/docarray/document/io/json.py index 874d6019e25..826223207a4 100644 --- a/docarray/document/io/json.py +++ b/docarray/document/io/json.py @@ -16,8 +16,11 @@ def _default_orjson(obj): return obj -def orjson_dumps(v, *, default=None): +def orjson_dumps(v, *, default=None) -> bytes: + # dumps to bytes using orjson + return orjson.dumps(v, default=_default_orjson, option=orjson.OPT_SERIALIZE_NUMPY) + + +def orjson_dumps_and_decode(v, *, default=None) -> str: # orjson.dumps returns bytes, to match standard json.dumps we need to decode - return orjson.dumps( - v, default=_default_orjson, option=orjson.OPT_SERIALIZE_NUMPY - ).decode() + return orjson_dumps(v).decode() diff --git a/tests/integrations/document/test_to_json.py b/tests/integrations/document/test_to_json.py index a3da5c82002..d20019b77a0 100644 --- a/tests/integrations/document/test_to_json.py +++ b/tests/integrations/document/test_to_json.py @@ -3,6 +3,7 @@ import torch from docarray.document import BaseDocument +from docarray.document.io.json import orjson_dumps_and_decode from docarray.typing import AnyUrl, NdArray, TorchTensor @@ -15,10 +16,10 @@ class Mmdoc(BaseDocument): torch_tensor: TorchTensor doc = Mmdoc( - img=np.zeros((3, 224, 224)), + img=np.zeros((10)), url='http://doccaray.io', txt='hello', - torch_tensor=torch.zeros(3, 224, 224), + torch_tensor=torch.zeros(10), ) return doc, Mmdoc @@ -37,3 +38,14 @@ def test_from_json(doc_and_class): assert (getattr(doc, field) == getattr(doc, field2)).all() else: assert getattr(doc, field) == getattr(doc, field2) + + +def test_to_dict_to_json(doc_and_class): + doc, Mmdoc = doc_and_class + new_doc = Mmdoc.parse_raw(orjson_dumps_and_decode(doc.dict())) + + for (field, field2) in zip(doc.dict().keys(), new_doc.dict().keys()): + if field in ['torch_tensor', 'img']: + assert (getattr(doc, field) == getattr(doc, field2)).all() + else: + assert getattr(doc, field) == getattr(doc, field2) diff --git a/tests/integrations/externals/test_fastapi.py b/tests/integrations/externals/test_fastapi.py index 9ce05873dca..cd9b2f3773b 100644 --- a/tests/integrations/externals/test_fastapi.py +++ b/tests/integrations/externals/test_fastapi.py @@ -1,9 +1,11 @@ import numpy as np import pytest from fastapi import FastAPI +from fastapi.responses import JSONResponse from httpx import AsyncClient from docarray import BaseDocument, Image, Text +from docarray.document.io.json import orjson_dumps from docarray.typing import NdArray @@ -47,12 +49,18 @@ class OutputDoc(BaseDocument): app = FastAPI() + class OrjsonResponse(JSONResponse): + def render(self, content: BaseDocument) -> bytes: + return orjson_dumps(content.dict()) + @app.post("/doc/", response_model=OutputDoc) - async def create_item(doc: InputDoc) -> OutputDoc: + async def create_item(doc: InputDoc) -> OrjsonResponse: ## call my fancy model to generate the embeddings - return OutputDoc( + doc = OutputDoc( embedding_clip=np.zeros((100, 1)), embedding_bert=np.zeros((100, 1)) ) + resp = OrjsonResponse(content=doc) + return resp async with AsyncClient(app=app, base_url="http://test") as ac: response = await ac.post("/doc/", data=input_doc.json()) diff --git a/tests/units/typing/tensor/test_embedding.py b/tests/units/typing/tensor/test_embedding.py index 2965cc1df1c..87a979dc80a 100644 --- a/tests/units/typing/tensor/test_embedding.py +++ b/tests/units/typing/tensor/test_embedding.py @@ -1,7 +1,7 @@ import numpy as np from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps +from docarray.document.io.json import orjson_dumps_and_decode from docarray.typing import AnyEmbedding @@ -18,4 +18,4 @@ def test_json_schema(): def test_dump_json(): tensor = parse_obj_as(AnyEmbedding, np.zeros((3, 224, 224))) - orjson_dumps(tensor) + orjson_dumps_and_decode(tensor) diff --git a/tests/units/typing/tensor/test_tensor.py b/tests/units/typing/tensor/test_tensor.py index dd8356ed84d..8b8e817116c 100644 --- a/tests/units/typing/tensor/test_tensor.py +++ b/tests/units/typing/tensor/test_tensor.py @@ -3,7 +3,7 @@ import pytest from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps +from docarray.document.io.json import orjson_dumps_and_decode from docarray.typing import NdArray from docarray.typing.tensor import NdArrayEmbedding @@ -27,13 +27,13 @@ def test_json_schema(): def test_dump_json(): tensor = parse_obj_as(NdArray, np.zeros((3, 224, 224))) - orjson_dumps(tensor) + orjson_dumps_and_decode(tensor) def test_load_json(): tensor = parse_obj_as(NdArray, np.zeros((2, 2))) - json = orjson_dumps(tensor) + json = orjson_dumps_and_decode(tensor) print(json) print(type(json)) new_tensor = orjson.loads(json) diff --git a/tests/units/typing/tensor/test_torch_tensor.py b/tests/units/typing/tensor/test_torch_tensor.py index cd2fa257c37..1bd552cf8f4 100644 --- a/tests/units/typing/tensor/test_torch_tensor.py +++ b/tests/units/typing/tensor/test_torch_tensor.py @@ -2,7 +2,7 @@ import torch from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps +from docarray.document.io.json import orjson_dumps_and_decode from docarray.typing import TorchEmbedding, TorchTensor @@ -19,7 +19,7 @@ def test_json_schema(): def test_dump_json(): tensor = parse_obj_as(TorchTensor, torch.zeros(3, 224, 224)) - orjson_dumps(tensor) + orjson_dumps_and_decode(tensor) def test_unwrap(): diff --git a/tests/units/typing/test_id.py b/tests/units/typing/test_id.py index 5919b351209..95d2bf25471 100644 --- a/tests/units/typing/test_id.py +++ b/tests/units/typing/test_id.py @@ -4,7 +4,7 @@ from pydantic import schema_json_of from pydantic.tools import parse_obj_as -from docarray.document.io.json import orjson_dumps +from docarray.document.io.json import orjson_dumps_and_decode from docarray.typing import ID @@ -24,4 +24,4 @@ def test_json_schema(): def test_dump_json(): id = parse_obj_as(ID, 1234) - orjson_dumps(id) + orjson_dumps_and_decode(id) diff --git a/tests/units/typing/url/test_any_url.py b/tests/units/typing/url/test_any_url.py index c608aa2f079..9fdc2ae2a94 100644 --- a/tests/units/typing/url/test_any_url.py +++ b/tests/units/typing/url/test_any_url.py @@ -1,6 +1,6 @@ from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps +from docarray.document.io.json import orjson_dumps_and_decode from docarray.typing import AnyUrl @@ -17,7 +17,7 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(AnyUrl, 'http://jina.ai/img.png') - orjson_dumps(url) + orjson_dumps_and_decode(url) def test_relative_path(): diff --git a/tests/units/typing/url/test_audio_url.py b/tests/units/typing/url/test_audio_url.py index 20dd5477717..f505ee6e2e6 100644 --- a/tests/units/typing/url/test_audio_url.py +++ b/tests/units/typing/url/test_audio_url.py @@ -6,7 +6,7 @@ from pydantic.tools import parse_obj_as, schema_json_of from docarray import BaseDocument -from docarray.document.io.json import orjson_dumps +from docarray.document.io.json import orjson_dumps_and_decode from docarray.typing import AudioNdArray, AudioTorchTensor, AudioUrl from tests import TOYDATA_DIR @@ -66,7 +66,7 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(AudioUrl, REMOTE_AUDIO_FILE) - orjson_dumps(url) + orjson_dumps_and_decode(url) @pytest.mark.parametrize( diff --git a/tests/units/typing/url/test_image_url.py b/tests/units/typing/url/test_image_url.py index d7208d7674c..e4fcce16e17 100644 --- a/tests/units/typing/url/test_image_url.py +++ b/tests/units/typing/url/test_image_url.py @@ -6,7 +6,7 @@ import pytest from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps +from docarray.document.io.json import orjson_dumps_and_decode from docarray.typing import ImageUrl CUR_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -45,7 +45,7 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(ImageUrl, 'http://jina.ai/img.png') - orjson_dumps(url) + orjson_dumps_and_decode(url) @pytest.mark.slow diff --git a/tests/units/typing/url/test_mesh_url.py b/tests/units/typing/url/test_mesh_url.py index a792a0b201a..5aae374db43 100644 --- a/tests/units/typing/url/test_mesh_url.py +++ b/tests/units/typing/url/test_mesh_url.py @@ -2,7 +2,7 @@ import pytest from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps +from docarray.document.io.json import orjson_dumps_and_decode from docarray.typing import Mesh3DUrl from tests import TOYDATA_DIR @@ -41,7 +41,7 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(Mesh3DUrl, REMOTE_OBJ_FILE) - orjson_dumps(url) + orjson_dumps_and_decode(url) @pytest.mark.parametrize( diff --git a/tests/units/typing/url/test_point_cloud_url.py b/tests/units/typing/url/test_point_cloud_url.py index fa33163764d..f0e4f22aa35 100644 --- a/tests/units/typing/url/test_point_cloud_url.py +++ b/tests/units/typing/url/test_point_cloud_url.py @@ -2,7 +2,7 @@ import pytest from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps +from docarray.document.io.json import orjson_dumps_and_decode from docarray.typing import PointCloud3DUrl from tests import TOYDATA_DIR @@ -61,7 +61,7 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(PointCloud3DUrl, REMOTE_OBJ_FILE) - orjson_dumps(url) + orjson_dumps_and_decode(url) @pytest.mark.parametrize( diff --git a/tests/units/typing/url/test_text_url.py b/tests/units/typing/url/test_text_url.py index a38f8ffed0e..4ca8a607153 100644 --- a/tests/units/typing/url/test_text_url.py +++ b/tests/units/typing/url/test_text_url.py @@ -4,7 +4,7 @@ import pytest from pydantic import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps +from docarray.document.io.json import orjson_dumps_and_decode from docarray.typing import TextUrl REMOTE_TXT = 'https://de.wikipedia.org/wiki/Brixen' @@ -57,4 +57,4 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(TextUrl, REMOTE_TXT) - orjson_dumps(url) + orjson_dumps_and_decode(url) From b62e2b8211baf669a82dff45519ace60e5b923e2 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 14:50:38 +0100 Subject: [PATCH 05/17] refactor: rename orjson dump Signed-off-by: Sami Jaghouar --- docarray/document/io/json.py | 4 ++-- tests/integrations/document/test_to_json.py | 4 ++-- tests/units/typing/tensor/test_embedding.py | 4 ++-- tests/units/typing/tensor/test_tensor.py | 6 +++--- tests/units/typing/tensor/test_torch_tensor.py | 4 ++-- tests/units/typing/test_id.py | 4 ++-- tests/units/typing/url/test_any_url.py | 4 ++-- tests/units/typing/url/test_audio_url.py | 4 ++-- tests/units/typing/url/test_image_url.py | 4 ++-- tests/units/typing/url/test_mesh_url.py | 4 ++-- tests/units/typing/url/test_point_cloud_url.py | 4 ++-- tests/units/typing/url/test_text_url.py | 4 ++-- 12 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docarray/document/io/json.py b/docarray/document/io/json.py index 826223207a4..c52798bec6d 100644 --- a/docarray/document/io/json.py +++ b/docarray/document/io/json.py @@ -22,5 +22,5 @@ def orjson_dumps(v, *, default=None) -> bytes: def orjson_dumps_and_decode(v, *, default=None) -> str: - # orjson.dumps returns bytes, to match standard json.dumps we need to decode - return orjson_dumps(v).decode() + # dumps to bytes using orjson + return orjson_dumps(v, default=default).decode() diff --git a/tests/integrations/document/test_to_json.py b/tests/integrations/document/test_to_json.py index d20019b77a0..08eee4e7991 100644 --- a/tests/integrations/document/test_to_json.py +++ b/tests/integrations/document/test_to_json.py @@ -3,7 +3,7 @@ import torch from docarray.document import BaseDocument -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps from docarray.typing import AnyUrl, NdArray, TorchTensor @@ -42,7 +42,7 @@ def test_from_json(doc_and_class): def test_to_dict_to_json(doc_and_class): doc, Mmdoc = doc_and_class - new_doc = Mmdoc.parse_raw(orjson_dumps_and_decode(doc.dict())) + new_doc = Mmdoc.parse_raw(orjson_dumps(doc.dict())) for (field, field2) in zip(doc.dict().keys(), new_doc.dict().keys()): if field in ['torch_tensor', 'img']: diff --git a/tests/units/typing/tensor/test_embedding.py b/tests/units/typing/tensor/test_embedding.py index 87a979dc80a..2965cc1df1c 100644 --- a/tests/units/typing/tensor/test_embedding.py +++ b/tests/units/typing/tensor/test_embedding.py @@ -1,7 +1,7 @@ import numpy as np from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps from docarray.typing import AnyEmbedding @@ -18,4 +18,4 @@ def test_json_schema(): def test_dump_json(): tensor = parse_obj_as(AnyEmbedding, np.zeros((3, 224, 224))) - orjson_dumps_and_decode(tensor) + orjson_dumps(tensor) diff --git a/tests/units/typing/tensor/test_tensor.py b/tests/units/typing/tensor/test_tensor.py index 8b8e817116c..dd8356ed84d 100644 --- a/tests/units/typing/tensor/test_tensor.py +++ b/tests/units/typing/tensor/test_tensor.py @@ -3,7 +3,7 @@ import pytest from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps from docarray.typing import NdArray from docarray.typing.tensor import NdArrayEmbedding @@ -27,13 +27,13 @@ def test_json_schema(): def test_dump_json(): tensor = parse_obj_as(NdArray, np.zeros((3, 224, 224))) - orjson_dumps_and_decode(tensor) + orjson_dumps(tensor) def test_load_json(): tensor = parse_obj_as(NdArray, np.zeros((2, 2))) - json = orjson_dumps_and_decode(tensor) + json = orjson_dumps(tensor) print(json) print(type(json)) new_tensor = orjson.loads(json) diff --git a/tests/units/typing/tensor/test_torch_tensor.py b/tests/units/typing/tensor/test_torch_tensor.py index 1bd552cf8f4..cd2fa257c37 100644 --- a/tests/units/typing/tensor/test_torch_tensor.py +++ b/tests/units/typing/tensor/test_torch_tensor.py @@ -2,7 +2,7 @@ import torch from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps from docarray.typing import TorchEmbedding, TorchTensor @@ -19,7 +19,7 @@ def test_json_schema(): def test_dump_json(): tensor = parse_obj_as(TorchTensor, torch.zeros(3, 224, 224)) - orjson_dumps_and_decode(tensor) + orjson_dumps(tensor) def test_unwrap(): diff --git a/tests/units/typing/test_id.py b/tests/units/typing/test_id.py index 95d2bf25471..5919b351209 100644 --- a/tests/units/typing/test_id.py +++ b/tests/units/typing/test_id.py @@ -4,7 +4,7 @@ from pydantic import schema_json_of from pydantic.tools import parse_obj_as -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps from docarray.typing import ID @@ -24,4 +24,4 @@ def test_json_schema(): def test_dump_json(): id = parse_obj_as(ID, 1234) - orjson_dumps_and_decode(id) + orjson_dumps(id) diff --git a/tests/units/typing/url/test_any_url.py b/tests/units/typing/url/test_any_url.py index 9fdc2ae2a94..c608aa2f079 100644 --- a/tests/units/typing/url/test_any_url.py +++ b/tests/units/typing/url/test_any_url.py @@ -1,6 +1,6 @@ from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps from docarray.typing import AnyUrl @@ -17,7 +17,7 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(AnyUrl, 'http://jina.ai/img.png') - orjson_dumps_and_decode(url) + orjson_dumps(url) def test_relative_path(): diff --git a/tests/units/typing/url/test_audio_url.py b/tests/units/typing/url/test_audio_url.py index f505ee6e2e6..20dd5477717 100644 --- a/tests/units/typing/url/test_audio_url.py +++ b/tests/units/typing/url/test_audio_url.py @@ -6,7 +6,7 @@ from pydantic.tools import parse_obj_as, schema_json_of from docarray import BaseDocument -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps from docarray.typing import AudioNdArray, AudioTorchTensor, AudioUrl from tests import TOYDATA_DIR @@ -66,7 +66,7 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(AudioUrl, REMOTE_AUDIO_FILE) - orjson_dumps_and_decode(url) + orjson_dumps(url) @pytest.mark.parametrize( diff --git a/tests/units/typing/url/test_image_url.py b/tests/units/typing/url/test_image_url.py index e4fcce16e17..d7208d7674c 100644 --- a/tests/units/typing/url/test_image_url.py +++ b/tests/units/typing/url/test_image_url.py @@ -6,7 +6,7 @@ import pytest from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps from docarray.typing import ImageUrl CUR_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -45,7 +45,7 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(ImageUrl, 'http://jina.ai/img.png') - orjson_dumps_and_decode(url) + orjson_dumps(url) @pytest.mark.slow diff --git a/tests/units/typing/url/test_mesh_url.py b/tests/units/typing/url/test_mesh_url.py index 5aae374db43..a792a0b201a 100644 --- a/tests/units/typing/url/test_mesh_url.py +++ b/tests/units/typing/url/test_mesh_url.py @@ -2,7 +2,7 @@ import pytest from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps from docarray.typing import Mesh3DUrl from tests import TOYDATA_DIR @@ -41,7 +41,7 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(Mesh3DUrl, REMOTE_OBJ_FILE) - orjson_dumps_and_decode(url) + orjson_dumps(url) @pytest.mark.parametrize( diff --git a/tests/units/typing/url/test_point_cloud_url.py b/tests/units/typing/url/test_point_cloud_url.py index f0e4f22aa35..fa33163764d 100644 --- a/tests/units/typing/url/test_point_cloud_url.py +++ b/tests/units/typing/url/test_point_cloud_url.py @@ -2,7 +2,7 @@ import pytest from pydantic.tools import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps from docarray.typing import PointCloud3DUrl from tests import TOYDATA_DIR @@ -61,7 +61,7 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(PointCloud3DUrl, REMOTE_OBJ_FILE) - orjson_dumps_and_decode(url) + orjson_dumps(url) @pytest.mark.parametrize( diff --git a/tests/units/typing/url/test_text_url.py b/tests/units/typing/url/test_text_url.py index 4ca8a607153..a38f8ffed0e 100644 --- a/tests/units/typing/url/test_text_url.py +++ b/tests/units/typing/url/test_text_url.py @@ -4,7 +4,7 @@ import pytest from pydantic import parse_obj_as, schema_json_of -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps from docarray.typing import TextUrl REMOTE_TXT = 'https://de.wikipedia.org/wiki/Brixen' @@ -57,4 +57,4 @@ def test_json_schema(): def test_dump_json(): url = parse_obj_as(TextUrl, REMOTE_TXT) - orjson_dumps_and_decode(url) + orjson_dumps(url) From 0642f589dccddb984df59b4f3713a8a34b261418 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 15:01:44 +0100 Subject: [PATCH 06/17] feat: add document response Signed-off-by: Sami Jaghouar --- docarray/document/document_response.py | 9 +++++++++ tests/integrations/externals/test_fastapi.py | 11 +++-------- 2 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 docarray/document/document_response.py diff --git a/docarray/document/document_response.py b/docarray/document/document_response.py new file mode 100644 index 00000000000..bd3a766ee60 --- /dev/null +++ b/docarray/document/document_response.py @@ -0,0 +1,9 @@ +from starlette.responses import JSONResponse + +from docarray import BaseDocument +from docarray.document.io.json import orjson_dumps + + +class DocumentResponse(JSONResponse): + def render(self, content: BaseDocument) -> bytes: + return orjson_dumps(content.dict()) diff --git a/tests/integrations/externals/test_fastapi.py b/tests/integrations/externals/test_fastapi.py index cd9b2f3773b..b1d9e6d7d8f 100644 --- a/tests/integrations/externals/test_fastapi.py +++ b/tests/integrations/externals/test_fastapi.py @@ -1,11 +1,10 @@ import numpy as np import pytest from fastapi import FastAPI -from fastapi.responses import JSONResponse from httpx import AsyncClient from docarray import BaseDocument, Image, Text -from docarray.document.io.json import orjson_dumps +from docarray.document.document_response import DocumentResponse from docarray.typing import NdArray @@ -49,17 +48,13 @@ class OutputDoc(BaseDocument): app = FastAPI() - class OrjsonResponse(JSONResponse): - def render(self, content: BaseDocument) -> bytes: - return orjson_dumps(content.dict()) - @app.post("/doc/", response_model=OutputDoc) - async def create_item(doc: InputDoc) -> OrjsonResponse: + async def create_item(doc: InputDoc) -> DocumentResponse: ## call my fancy model to generate the embeddings doc = OutputDoc( embedding_clip=np.zeros((100, 1)), embedding_bert=np.zeros((100, 1)) ) - resp = OrjsonResponse(content=doc) + resp = DocumentResponse(content=doc) return resp async with AsyncClient(app=app, base_url="http://test") as ac: From 91f2c0b3153919a9bcd1720c27b8befd2c0333f5 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 15:02:56 +0100 Subject: [PATCH 07/17] refactor: better test Signed-off-by: Sami Jaghouar --- tests/integrations/externals/test_fastapi.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/integrations/externals/test_fastapi.py b/tests/integrations/externals/test_fastapi.py index b1d9e6d7d8f..667c6e79775 100644 --- a/tests/integrations/externals/test_fastapi.py +++ b/tests/integrations/externals/test_fastapi.py @@ -87,10 +87,12 @@ class OutputDoc(BaseDocument): app = FastAPI() @app.post("/doc/", response_model=OutputDoc) - async def create_item(doc: InputDoc) -> OutputDoc: + async def create_item(doc: InputDoc) -> DocumentResponse: ## call my fancy model to generate the embeddings - return OutputDoc( - embedding_clip=np.zeros((100, 1)), embedding_bert=np.zeros((100, 1)) + return DocumentResponse( + content=OutputDoc( + embedding_clip=np.zeros((100, 1)), embedding_bert=np.zeros((100, 1)) + ) ) async with AsyncClient(app=app, base_url="http://test") as ac: @@ -101,3 +103,9 @@ async def create_item(doc: InputDoc) -> OutputDoc: assert response.status_code == 200 assert resp_doc.status_code == 200 assert resp_redoc.status_code == 200 + + doc = OutputDoc.parse_raw(response.content.decode()) + + assert isinstance(doc, OutputDoc) + assert doc.embedding_clip.shape == (100, 1) + assert doc.embedding_bert.shape == (100, 1) From 5ed0828efe6fe5953b76afb1fc97bde204cf9b27 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 15:20:11 +0100 Subject: [PATCH 08/17] fix: starlette not needed Signed-off-by: Sami Jaghouar --- docarray/document/document_response.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docarray/document/document_response.py b/docarray/document/document_response.py index bd3a766ee60..3578f4c310f 100644 --- a/docarray/document/document_response.py +++ b/docarray/document/document_response.py @@ -1,4 +1,10 @@ -from starlette.responses import JSONResponse +try: + from starlette.responses import JSONResponse +except ImportError: + + class JSONResponse: + raise ImportError("Starlette is required to use JSONResponse") + from docarray import BaseDocument from docarray.document.io.json import orjson_dumps From 274dd299e13aed3b8b3e000f601faf06a6af9d39 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 15:53:30 +0100 Subject: [PATCH 09/17] fix: better fastapi support Signed-off-by: Sami Jaghouar --- docarray/document/document.py | 5 ++++- docarray/document/document_response.py | 15 --------------- tests/integrations/externals/test_fastapi.py | 19 ++++++++----------- 3 files changed, 12 insertions(+), 27 deletions(-) delete mode 100644 docarray/document/document_response.py diff --git a/docarray/document/document.py b/docarray/document/document.py index e6ad4e87cbd..f41a1feb089 100644 --- a/docarray/document/document.py +++ b/docarray/document/document.py @@ -6,7 +6,7 @@ from docarray.document.abstract_document import AbstractDocument from docarray.document.base_node import BaseNode -from docarray.document.io.json import orjson_dumps_and_decode +from docarray.document.io.json import orjson_dumps, orjson_dumps_and_decode from docarray.document.mixins import ProtoMixin from docarray.typing import ID @@ -21,8 +21,11 @@ class BaseDocument(BaseModel, ProtoMixin, AbstractDocument, BaseNode): class Config: json_loads = orjson.loads json_dumps = orjson_dumps_and_decode + json_encoders = {dict: orjson_dumps} + validate_assignment = True + @classmethod def _get_field_type(cls, field: str) -> Type['BaseDocument']: """ diff --git a/docarray/document/document_response.py b/docarray/document/document_response.py deleted file mode 100644 index 3578f4c310f..00000000000 --- a/docarray/document/document_response.py +++ /dev/null @@ -1,15 +0,0 @@ -try: - from starlette.responses import JSONResponse -except ImportError: - - class JSONResponse: - raise ImportError("Starlette is required to use JSONResponse") - - -from docarray import BaseDocument -from docarray.document.io.json import orjson_dumps - - -class DocumentResponse(JSONResponse): - def render(self, content: BaseDocument) -> bytes: - return orjson_dumps(content.dict()) diff --git a/tests/integrations/externals/test_fastapi.py b/tests/integrations/externals/test_fastapi.py index 667c6e79775..343e944577c 100644 --- a/tests/integrations/externals/test_fastapi.py +++ b/tests/integrations/externals/test_fastapi.py @@ -1,10 +1,10 @@ import numpy as np import pytest from fastapi import FastAPI +from fastapi.responses import Response from httpx import AsyncClient from docarray import BaseDocument, Image, Text -from docarray.document.document_response import DocumentResponse from docarray.typing import NdArray @@ -48,14 +48,13 @@ class OutputDoc(BaseDocument): app = FastAPI() - @app.post("/doc/", response_model=OutputDoc) - async def create_item(doc: InputDoc) -> DocumentResponse: + @app.post("/doc/", response_model=OutputDoc, response_class=Response) + async def create_item(doc: InputDoc) -> OutputDoc: ## call my fancy model to generate the embeddings doc = OutputDoc( embedding_clip=np.zeros((100, 1)), embedding_bert=np.zeros((100, 1)) ) - resp = DocumentResponse(content=doc) - return resp + return doc async with AsyncClient(app=app, base_url="http://test") as ac: response = await ac.post("/doc/", data=input_doc.json()) @@ -86,13 +85,11 @@ class OutputDoc(BaseDocument): app = FastAPI() - @app.post("/doc/", response_model=OutputDoc) - async def create_item(doc: InputDoc) -> DocumentResponse: + @app.post("/doc/", response_model=OutputDoc, response_class=Response) + async def create_item(doc: InputDoc) -> OutputDoc: ## call my fancy model to generate the embeddings - return DocumentResponse( - content=OutputDoc( - embedding_clip=np.zeros((100, 1)), embedding_bert=np.zeros((100, 1)) - ) + return OutputDoc( + embedding_clip=np.zeros((100, 1)), embedding_bert=np.zeros((100, 1)) ) async with AsyncClient(app=app, base_url="http://test") as ac: From ae3902176ac23504234d3c97846ddfa0607970be Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 16:20:24 +0100 Subject: [PATCH 10/17] fix: tests fastapi Signed-off-by: Sami Jaghouar --- tests/integrations/externals/test_fastapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integrations/externals/test_fastapi.py b/tests/integrations/externals/test_fastapi.py index 343e944577c..97922c53a9b 100644 --- a/tests/integrations/externals/test_fastapi.py +++ b/tests/integrations/externals/test_fastapi.py @@ -21,7 +21,7 @@ class Mmdoc(BaseDocument): app = FastAPI() - @app.post("/doc/") + @app.post("/doc/", response_model=Mmdoc, response_class=Response) async def create_item(doc: Mmdoc): return doc From 3b84ddd0e6b9a20843b10d9773f2152f6ce304c3 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 16:22:15 +0100 Subject: [PATCH 11/17] fix: apply black Signed-off-by: Sami Jaghouar --- docarray/document/document.py | 1 - docarray/typing/tensor/abstract_tensor.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/docarray/document/document.py b/docarray/document/document.py index f41a1feb089..cb54f462705 100644 --- a/docarray/document/document.py +++ b/docarray/document/document.py @@ -25,7 +25,6 @@ class Config: validate_assignment = True - @classmethod def _get_field_type(cls, field: str) -> Type['BaseDocument']: """ diff --git a/docarray/typing/tensor/abstract_tensor.py b/docarray/typing/tensor/abstract_tensor.py index 708f3f4df35..a9a17283868 100644 --- a/docarray/typing/tensor/abstract_tensor.py +++ b/docarray/typing/tensor/abstract_tensor.py @@ -120,7 +120,6 @@ def get_comp_backend() -> Type[AbstractComputationalBackend]: """The computational backend compatible with this tensor type.""" ... - def __getitem__(self, item): """Get a slice of this tensor.""" ... @@ -137,7 +136,6 @@ def to_protobuf(self) -> 'NdArrayProto': def unwrap(self): """Return the native tensor object that this DocArray tensor wraps.""" - @abc.abstractmethod def __docarray_to_json_compatible__(self): """ From f512f272cdab7e135b95912f3fc5ea6a2c9eb19e Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 16:41:20 +0100 Subject: [PATCH 12/17] refactor: rename docarray to json comaptible Signed-off-by: Sami Jaghouar --- docarray/document/io/json.py | 2 +- docarray/typing/tensor/abstract_tensor.py | 2 +- docarray/typing/tensor/ndarray.py | 2 +- docarray/typing/tensor/torch_tensor.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docarray/document/io/json.py b/docarray/document/io/json.py index c52798bec6d..3d7809ae11a 100644 --- a/docarray/document/io/json.py +++ b/docarray/document/io/json.py @@ -11,7 +11,7 @@ def _default_orjson(obj): """ if isinstance(obj, AbstractTensor): - return obj.__docarray_to_json_compatible__() + return obj._docarray_to_json_compatible() else: return obj diff --git a/docarray/typing/tensor/abstract_tensor.py b/docarray/typing/tensor/abstract_tensor.py index a9a17283868..a877861aa4e 100644 --- a/docarray/typing/tensor/abstract_tensor.py +++ b/docarray/typing/tensor/abstract_tensor.py @@ -137,7 +137,7 @@ def unwrap(self): """Return the native tensor object that this DocArray tensor wraps.""" @abc.abstractmethod - def __docarray_to_json_compatible__(self): + def _docarray_to_json_compatible(self): """ Convert tensor into a json compatible object :return: a representation of the tensor compatible with orjson diff --git a/docarray/typing/tensor/ndarray.py b/docarray/typing/tensor/ndarray.py index 12a350484ca..00f7109f966 100644 --- a/docarray/typing/tensor/ndarray.py +++ b/docarray/typing/tensor/ndarray.py @@ -130,7 +130,7 @@ def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: # this is needed to dump to json field_schema.update(type='string', format='tensor') - def __docarray_to_json_compatible__(self) -> np.ndarray: + def _docarray_to_json_compatible(self) -> np.ndarray: """ Convert tensor into a json compatible object :return: a representation of the tensor compatible with orjson diff --git a/docarray/typing/tensor/torch_tensor.py b/docarray/typing/tensor/torch_tensor.py index 6182030edf2..edbcf8a9ffc 100644 --- a/docarray/typing/tensor/torch_tensor.py +++ b/docarray/typing/tensor/torch_tensor.py @@ -126,7 +126,7 @@ def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: # this is needed to dump to json field_schema.update(type='string', format='tensor') - def __docarray_to_json_compatible__(self) -> np.ndarray: + def _docarray_to_json_compatible(self) -> np.ndarray: """ Convert torchTensor into a json compatible object :return: a representation of the tensor compatible with orjson From 59da4f81f5fc26018c3b2267af7cd4c3a55fa81d Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 16:56:29 +0100 Subject: [PATCH 13/17] refactor: expose a document response Signed-off-by: Sami Jaghouar --- docarray/document/__init__.py | 3 +- docarray/document/document_response.py | 33 ++++++++++++++++++++ tests/integrations/externals/test_fastapi.py | 8 ++--- 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 docarray/document/document_response.py diff --git a/docarray/document/__init__.py b/docarray/document/__init__.py index 7b233fca4a7..176558d637a 100644 --- a/docarray/document/__init__.py +++ b/docarray/document/__init__.py @@ -1,5 +1,6 @@ from docarray.document.any_document import AnyDocument from docarray.document.base_node import BaseNode from docarray.document.document import BaseDocument +from docarray.document.document_response import DocumentResponse -__all__ = ['AnyDocument', 'BaseDocument', 'BaseNode'] +__all__ = ['AnyDocument', 'BaseDocument', 'BaseNode', 'DocumentResponse'] diff --git a/docarray/document/document_response.py b/docarray/document/document_response.py new file mode 100644 index 00000000000..a23752598e7 --- /dev/null +++ b/docarray/document/document_response.py @@ -0,0 +1,33 @@ +from typing import Any + +try: + from fastapi.responses import JSONResponse, Response +except ImportError: + + class NoImportResponse: + raise ImportError('fastapi is not installed') + + Response = JSONResponse = NoImportResponse # type: ignore + + +class DocumentResponse(JSONResponse): + """ + This is a custom Response class for FastAPI and starlette. This is needed + to handle serialization of the Document types when using FastAPI + + EXAMPLE USAGE + .. code-block:: python + from docarray import Text + from docarray.document import DocumentResponse + + + @app.post("/doc/", response_model=Text, response_class=DocumentResponse) + async def create_item(doc: Text) -> Text: + return doc + """ + + def render(self, content: Any) -> bytes: + if isinstance(content, bytes): + return content + else: + raise ValueError(f'{self.__class__} only work with json bytes content') diff --git a/tests/integrations/externals/test_fastapi.py b/tests/integrations/externals/test_fastapi.py index 97922c53a9b..08c161a8e3e 100644 --- a/tests/integrations/externals/test_fastapi.py +++ b/tests/integrations/externals/test_fastapi.py @@ -1,10 +1,10 @@ import numpy as np import pytest from fastapi import FastAPI -from fastapi.responses import Response from httpx import AsyncClient from docarray import BaseDocument, Image, Text +from docarray.document import DocumentResponse from docarray.typing import NdArray @@ -21,7 +21,7 @@ class Mmdoc(BaseDocument): app = FastAPI() - @app.post("/doc/", response_model=Mmdoc, response_class=Response) + @app.post("/doc/", response_model=Mmdoc, response_class=DocumentResponse) async def create_item(doc: Mmdoc): return doc @@ -48,7 +48,7 @@ class OutputDoc(BaseDocument): app = FastAPI() - @app.post("/doc/", response_model=OutputDoc, response_class=Response) + @app.post("/doc/", response_model=OutputDoc, response_class=DocumentResponse) async def create_item(doc: InputDoc) -> OutputDoc: ## call my fancy model to generate the embeddings doc = OutputDoc( @@ -85,7 +85,7 @@ class OutputDoc(BaseDocument): app = FastAPI() - @app.post("/doc/", response_model=OutputDoc, response_class=Response) + @app.post("/doc/", response_model=OutputDoc, response_class=DocumentResponse) async def create_item(doc: InputDoc) -> OutputDoc: ## call my fancy model to generate the embeddings return OutputDoc( From 80f95b0b983a8633730aab7c995831b3cfa8d295 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 17:00:22 +0100 Subject: [PATCH 14/17] refactor: update dependency Signed-off-by: Sami Jaghouar --- poetry.lock | 17 +++++++++-------- pyproject.toml | 3 ++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5996317fe67..bddaefa8750 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,7 +2,7 @@ name = "anyio" version = "3.6.2" description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "dev" +category = "main" optional = false python-versions = ">=3.6.2" @@ -274,8 +274,8 @@ tests = ["asttokens", "littleutils", "pytest", "rich"] name = "fastapi" version = "0.87.0" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -category = "dev" -optional = false +category = "main" +optional = true python-versions = ">=3.7" [package.dependencies] @@ -372,7 +372,7 @@ license = ["ukkonen"] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" +category = "main" optional = false python-versions = ">=3.5" @@ -1366,7 +1366,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -1398,8 +1398,8 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] name = "starlette" version = "0.21.0" description = "The little ASGI library that shines." -category = "dev" -optional = false +category = "main" +optional = true python-versions = ">=3.7" [package.dependencies] @@ -1668,11 +1668,12 @@ common = ["protobuf"] image = ["pillow", "types-pillow"] mesh = ["trimesh"] torch = ["torch"] +web = ["fastapi"] [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "b1aa40aea6ec7f56a8c3b511fd2ce96ed217c6fc81d6f8dd931e519cc0774154" +content-hash = "e9505149fb25b56e7cbccfa923e71070f783ec35fc6b43f00564c6974eab3eae" [metadata.files] anyio = [ diff --git a/pyproject.toml b/pyproject.toml index 1d29532b696..66e16f420a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,12 +17,14 @@ types-pillow = {version = "^9.3.0.1", optional = true } trimesh = {version = "^3.17.1", optional = true} typing-inspect = "^0.8.0" types-requests = "^2.28.11.6" +fastapi = {version = "^0.87.0", optional = true } [tool.poetry.extras] common = ["protobuf"] torch = ["torch"] image = ["pillow", "types-pillow"] mesh = ["trimesh"] +web = ["fastapi"] [tool.poetry.dev-dependencies] pytest = "^6.1" @@ -35,7 +37,6 @@ isort = "^5.10.1" ruff = "^0.0.165" [tool.poetry.group.dev.dependencies] -fastapi = "^0.87.0" uvicorn = "^0.19.0" httpx = "^0.23.0" pytest-asyncio = "^0.20.2" From facbee24880b91d0fbaabe4df4edb5e07c4c1c32 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 17:01:51 +0100 Subject: [PATCH 15/17] fix: fix import wo fast api Signed-off-by: Sami Jaghouar --- docarray/document/document_response.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docarray/document/document_response.py b/docarray/document/document_response.py index a23752598e7..03704694824 100644 --- a/docarray/document/document_response.py +++ b/docarray/document/document_response.py @@ -5,7 +5,8 @@ except ImportError: class NoImportResponse: - raise ImportError('fastapi is not installed') + def __init__(self, *args, **kwargs): + ImportError('fastapi is not installed') Response = JSONResponse = NoImportResponse # type: ignore From bc9f9e5c8c4732b28d0e18ad9ec07f16a99be6e1 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 17:28:20 +0100 Subject: [PATCH 16/17] merge: merge dev branch Signed-off-by: Sami Jaghouar --- docarray/document/__init__.py | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 docarray/document/__init__.py diff --git a/docarray/document/__init__.py b/docarray/document/__init__.py deleted file mode 100644 index 176558d637a..00000000000 --- a/docarray/document/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from docarray.document.any_document import AnyDocument -from docarray.document.base_node import BaseNode -from docarray.document.document import BaseDocument -from docarray.document.document_response import DocumentResponse - -__all__ = ['AnyDocument', 'BaseDocument', 'BaseNode', 'DocumentResponse'] From ecf2992c27a6e82eef70b28d5ce225481c6d70c8 Mon Sep 17 00:00:00 2001 From: Sami Jaghouar Date: Tue, 10 Jan 2023 17:32:35 +0100 Subject: [PATCH 17/17] fix: update docstring Signed-off-by: Sami Jaghouar --- docarray/base_document/document_response.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docarray/base_document/document_response.py b/docarray/base_document/document_response.py index 03704694824..cb756c32412 100644 --- a/docarray/base_document/document_response.py +++ b/docarray/base_document/document_response.py @@ -18,8 +18,8 @@ class DocumentResponse(JSONResponse): EXAMPLE USAGE .. code-block:: python - from docarray import Text - from docarray.document import DocumentResponse + from docarray.documets import Text + from docarray.base_document import DocumentResponse @app.post("/doc/", response_model=Text, response_class=DocumentResponse)