From 8688d1b949e20bd00dbd2d2c4eebd90b4b6855e6 Mon Sep 17 00:00:00 2001 From: jupyterjazz Date: Thu, 30 Mar 2023 16:05:54 +0200 Subject: [PATCH 1/9] refactor: docarray fastapi simple integration Signed-off-by: jupyterjazz --- docarray/array/array/io.py | 15 +++++++--- docarray/base_doc/doc_response.py | 10 +++++++ tests/integrations/externals/test_fastapi.py | 29 ++++++++++++++++++-- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/docarray/array/array/io.py b/docarray/array/array/io.py index 251afc65a4a..fdeef71fd73 100644 --- a/docarray/array/array/io.py +++ b/docarray/array/array/io.py @@ -94,7 +94,6 @@ def __getitem__(self, item: slice): class IOMixinArray(Iterable[BaseDoc]): - document_type: Type[BaseDoc] @abstractmethod @@ -251,7 +250,7 @@ def to_bytes( :return: the binary serialization in bytes or None if file_ctx is passed where to store """ - with (file_ctx or io.BytesIO()) as bf: + with file_ctx or io.BytesIO() as bf: self._write_bytes( bf=bf, protocol=protocol, @@ -319,13 +318,21 @@ def from_json( :return: the deserialized DocArray """ json_docs = json.loads(file) - return cls([cls.document_type.parse_raw(v) for v in json_docs]) + return cls( + [ + cls.document_type(**v) + if isinstance(v, dict) + else cls.document_type.parse_raw(v) + for v in json_docs + ] + ) def to_json(self) -> str: """Convert the object into a JSON string. Can be loaded via :meth:`.from_json`. :return: JSON serialization of DocArray """ - return json.dumps([doc.json() for doc in self]) + doc_jsons = ', '.join([doc.json() for doc in self]) + return f'[{doc_jsons}]' @classmethod def from_csv( diff --git a/docarray/base_doc/doc_response.py b/docarray/base_doc/doc_response.py index 7d2b7b76ef6..e2c9ec6f7e0 100644 --- a/docarray/base_doc/doc_response.py +++ b/docarray/base_doc/doc_response.py @@ -1,5 +1,6 @@ from typing import TYPE_CHECKING, Any +from docarray.base_doc.io.json import orjson_dumps from docarray.utils._internal.misc import import_library if TYPE_CHECKING: @@ -28,5 +29,14 @@ async def create_item(doc: Text) -> Text: def render(self, content: Any) -> bytes: if isinstance(content, bytes): return content + elif ( + isinstance(content, list) + and len(content) > 0 + and isinstance(content[0], bytes) + ): + content = [item.decode() for item in content] + return orjson_dumps(content) + elif isinstance(content, list) and len(content) == 0: + return bytes([]) 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 5c5ed0bba60..f8eed2bf6d2 100644 --- a/tests/integrations/externals/test_fastapi.py +++ b/tests/integrations/externals/test_fastapi.py @@ -1,9 +1,11 @@ +from typing import List + import numpy as np import pytest from fastapi import FastAPI from httpx import AsyncClient -from docarray import BaseDoc +from docarray import BaseDoc, DocArray from docarray.base_doc import DocResponse from docarray.documents import ImageDoc, TextDoc from docarray.typing import NdArray @@ -22,8 +24,8 @@ class Mmdoc(BaseDoc): app = FastAPI() - @app.post("/doc/", response_model=Mmdoc, response_class=DocResponse) - async def create_item(doc: Mmdoc): + @app.post("/doc/", response_class=DocResponse) + async def create_item(doc: Mmdoc) -> Mmdoc: return doc async with AsyncClient(app=app, base_url="http://test") as ac: @@ -107,3 +109,24 @@ async def create_item(doc: InputDoc) -> OutputDoc: assert isinstance(doc, OutputDoc) assert doc.embedding_clip.shape == (100, 1) assert doc.embedding_bert.shape == (100, 1) + + +@pytest.mark.asyncio +async def test_docarray(): + doc = ImageDoc(tensor=np.zeros((3, 224, 224))) + docs = DocArray[ImageDoc]([doc, doc]) + + app = FastAPI() + + @app.post("/doc/", response_class=DocResponse) + async def func(fastapi_docs: List[ImageDoc]) -> List[ImageDoc]: + docarray_docs = DocArray[ImageDoc].construct(fastapi_docs) + return list(docarray_docs) + + async with AsyncClient(app=app, base_url="http://test") as ac: + response = await ac.post("/doc/", data=docs.to_json()) + + assert response.status_code == 200 + docs = DocArray[ImageDoc].from_json(response.content.decode()) + assert len(docs) == 2 + assert docs[0].tensor.shape == (3, 224, 224) From 660ec314c79a22709b0c83ffe3916bd624dcf81d Mon Sep 17 00:00:00 2001 From: jupyterjazz Date: Thu, 30 Mar 2023 16:53:39 +0200 Subject: [PATCH 2/9] refactor: custom orjson response class Signed-off-by: jupyterjazz --- docarray/base_doc/doc.py | 5 +++-- docarray/base_doc/doc_response.py | 16 ++-------------- tests/integrations/externals/test_fastapi.py | 11 ++++++----- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/docarray/base_doc/doc.py b/docarray/base_doc/doc.py index c828a98f8b2..41a8194cac8 100644 --- a/docarray/base_doc/doc.py +++ b/docarray/base_doc/doc.py @@ -6,9 +6,10 @@ from rich.console import Console from docarray.base_doc.base_node import BaseNode -from docarray.base_doc.io.json import orjson_dumps, orjson_dumps_and_decode +from docarray.base_doc.io.json import orjson_dumps_and_decode from docarray.base_doc.mixins import IOMixin, UpdateMixin from docarray.typing import ID +from docarray.typing.tensor.abstract_tensor import AbstractTensor if TYPE_CHECKING: from docarray.array.stacked.column_storage import ColumnStorageView @@ -28,7 +29,7 @@ class BaseDoc(BaseModel, IOMixin, UpdateMixin, BaseNode): class Config: json_loads = orjson.loads json_dumps = orjson_dumps_and_decode - json_encoders = {dict: orjson_dumps} + json_encoders = {AbstractTensor: lambda x: x} validate_assignment = True diff --git a/docarray/base_doc/doc_response.py b/docarray/base_doc/doc_response.py index e2c9ec6f7e0..3e62cf64f9b 100644 --- a/docarray/base_doc/doc_response.py +++ b/docarray/base_doc/doc_response.py @@ -10,7 +10,7 @@ JSONResponse = fastapi.responses.JSONResponse -class DocResponse(JSONResponse): +class DocArrayResponse(JSONResponse): """ This is a custom Response class for FastAPI and starlette. This is needed to handle serialization of the Document types when using FastAPI @@ -27,16 +27,4 @@ async def create_item(doc: Text) -> Text: """ def render(self, content: Any) -> bytes: - if isinstance(content, bytes): - return content - elif ( - isinstance(content, list) - and len(content) > 0 - and isinstance(content[0], bytes) - ): - content = [item.decode() for item in content] - return orjson_dumps(content) - elif isinstance(content, list) and len(content) == 0: - return bytes([]) - else: - raise ValueError(f'{self.__class__} only work with json bytes content') + return orjson_dumps(content) diff --git a/tests/integrations/externals/test_fastapi.py b/tests/integrations/externals/test_fastapi.py index f8eed2bf6d2..2f9f177b0d8 100644 --- a/tests/integrations/externals/test_fastapi.py +++ b/tests/integrations/externals/test_fastapi.py @@ -6,7 +6,7 @@ from httpx import AsyncClient from docarray import BaseDoc, DocArray -from docarray.base_doc import DocResponse +from docarray.base_doc.doc_response import DocArrayResponse from docarray.documents import ImageDoc, TextDoc from docarray.typing import NdArray @@ -24,7 +24,7 @@ class Mmdoc(BaseDoc): app = FastAPI() - @app.post("/doc/", response_class=DocResponse) + @app.post("/doc/", response_class=DocArrayResponse) async def create_item(doc: Mmdoc) -> Mmdoc: return doc @@ -51,7 +51,7 @@ class OutputDoc(BaseDoc): app = FastAPI() - @app.post("/doc/", response_model=OutputDoc, response_class=DocResponse) + @app.post("/doc/", response_model=OutputDoc, response_class=DocArrayResponse) async def create_item(doc: InputDoc) -> OutputDoc: ## call my fancy model to generate the embeddings doc = OutputDoc( @@ -88,7 +88,7 @@ class OutputDoc(BaseDoc): app = FastAPI() - @app.post("/doc/", response_model=OutputDoc, response_class=DocResponse) + @app.post("/doc/", response_model=OutputDoc, response_class=DocArrayResponse) async def create_item(doc: InputDoc) -> OutputDoc: ## call my fancy model to generate the embeddings return OutputDoc( @@ -118,7 +118,7 @@ async def test_docarray(): app = FastAPI() - @app.post("/doc/", response_class=DocResponse) + @app.post("/doc/", response_class=DocArrayResponse) async def func(fastapi_docs: List[ImageDoc]) -> List[ImageDoc]: docarray_docs = DocArray[ImageDoc].construct(fastapi_docs) return list(docarray_docs) @@ -127,6 +127,7 @@ async def func(fastapi_docs: List[ImageDoc]) -> List[ImageDoc]: response = await ac.post("/doc/", data=docs.to_json()) assert response.status_code == 200 + docs = DocArray[ImageDoc].from_json(response.content.decode()) assert len(docs) == 2 assert docs[0].tensor.shape == (3, 224, 224) From bc5e56c466c2782e5b26116d633a9cad3608c62f Mon Sep 17 00:00:00 2001 From: jupyterjazz Date: Thu, 30 Mar 2023 17:01:25 +0200 Subject: [PATCH 3/9] refactor: docarray response name Signed-off-by: jupyterjazz --- docarray/base_doc/__init__.py | 4 ++-- docarray/base_doc/{doc_response.py => docarray_response.py} | 0 tests/integrations/externals/test_fastapi.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename docarray/base_doc/{doc_response.py => docarray_response.py} (100%) diff --git a/docarray/base_doc/__init__.py b/docarray/base_doc/__init__.py index 941e76c542b..0a0dd5dffb9 100644 --- a/docarray/base_doc/__init__.py +++ b/docarray/base_doc/__init__.py @@ -12,12 +12,12 @@ def __getattr__(name: str): if name == 'DocResponse': import_library('fastapi', raise_error=True) - from docarray.base_doc.doc_response import DocResponse + from docarray.base_doc.docarray_response import DocArrayResponse if name not in __all__: __all__.append(name) - return DocResponse + return DocArrayResponse else: raise ImportError( f'cannot import name \'{name}\' from \'{_get_path_from_docarray_root_level(__file__)}\'' diff --git a/docarray/base_doc/doc_response.py b/docarray/base_doc/docarray_response.py similarity index 100% rename from docarray/base_doc/doc_response.py rename to docarray/base_doc/docarray_response.py diff --git a/tests/integrations/externals/test_fastapi.py b/tests/integrations/externals/test_fastapi.py index 2f9f177b0d8..abe5ec1a182 100644 --- a/tests/integrations/externals/test_fastapi.py +++ b/tests/integrations/externals/test_fastapi.py @@ -6,7 +6,7 @@ from httpx import AsyncClient from docarray import BaseDoc, DocArray -from docarray.base_doc.doc_response import DocArrayResponse +from docarray.base_doc.docarray_response import DocArrayResponse from docarray.documents import ImageDoc, TextDoc from docarray.typing import NdArray From 8e9c7c11825c73223d67f5c874bac60dabb08f3a Mon Sep 17 00:00:00 2001 From: jupyterjazz Date: Thu, 30 Mar 2023 17:14:11 +0200 Subject: [PATCH 4/9] refactor: simplify from json Signed-off-by: jupyterjazz --- docarray/array/array/io.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/docarray/array/array/io.py b/docarray/array/array/io.py index fdeef71fd73..47b984ca398 100644 --- a/docarray/array/array/io.py +++ b/docarray/array/array/io.py @@ -318,14 +318,7 @@ def from_json( :return: the deserialized DocArray """ json_docs = json.loads(file) - return cls( - [ - cls.document_type(**v) - if isinstance(v, dict) - else cls.document_type.parse_raw(v) - for v in json_docs - ] - ) + return cls([cls.document_type(**v) for v in json_docs]) def to_json(self) -> str: """Convert the object into a JSON string. Can be loaded via :meth:`.from_json`. From 2e421764dda007f9106d803c0153f806f46e6c2c Mon Sep 17 00:00:00 2001 From: jupyterjazz Date: Fri, 31 Mar 2023 08:43:39 +0200 Subject: [PATCH 5/9] test: refactor tests Signed-off-by: jupyterjazz --- docarray/base_doc/__init__.py | 2 +- tests/integrations/externals/test_fastapi.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docarray/base_doc/__init__.py b/docarray/base_doc/__init__.py index 0a0dd5dffb9..47e01c1c662 100644 --- a/docarray/base_doc/__init__.py +++ b/docarray/base_doc/__init__.py @@ -10,7 +10,7 @@ def __getattr__(name: str): - if name == 'DocResponse': + if name == 'DocArrayResponse': import_library('fastapi', raise_error=True) from docarray.base_doc.docarray_response import DocArrayResponse diff --git a/tests/integrations/externals/test_fastapi.py b/tests/integrations/externals/test_fastapi.py index abe5ec1a182..438d2a86402 100644 --- a/tests/integrations/externals/test_fastapi.py +++ b/tests/integrations/externals/test_fastapi.py @@ -6,7 +6,7 @@ from httpx import AsyncClient from docarray import BaseDoc, DocArray -from docarray.base_doc.docarray_response import DocArrayResponse +from docarray.base_doc import DocArrayResponse from docarray.documents import ImageDoc, TextDoc from docarray.typing import NdArray @@ -24,7 +24,7 @@ class Mmdoc(BaseDoc): app = FastAPI() - @app.post("/doc/", response_class=DocArrayResponse) + @app.post("/doc/", response_model=Mmdoc, response_class=DocArrayResponse) async def create_item(doc: Mmdoc) -> Mmdoc: return doc @@ -125,8 +125,12 @@ async def func(fastapi_docs: List[ImageDoc]) -> List[ImageDoc]: async with AsyncClient(app=app, base_url="http://test") as ac: response = await ac.post("/doc/", data=docs.to_json()) + resp_doc = await ac.get("/docs") + resp_redoc = await ac.get("/redoc") assert response.status_code == 200 + assert resp_doc.status_code == 200 + assert resp_redoc.status_code == 200 docs = DocArray[ImageDoc].from_json(response.content.decode()) assert len(docs) == 2 From 4c0ab759204a01349fe6dcc25045e9ffba538a58 Mon Sep 17 00:00:00 2001 From: jupyterjazz Date: Fri, 31 Mar 2023 09:44:38 +0200 Subject: [PATCH 6/9] refactor: adjust type hint Signed-off-by: jupyterjazz --- docarray/array/array/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docarray/array/array/io.py b/docarray/array/array/io.py index 47b984ca398..84f9df3ce13 100644 --- a/docarray/array/array/io.py +++ b/docarray/array/array/io.py @@ -310,7 +310,7 @@ def to_base64( @classmethod def from_json( cls: Type[T], - file: Union[str, bytes, bytearray], + file: str, ) -> T: """Deserialize JSON strings or bytes into a DocArray. From 15183d5ba1e55b66b29ebf731d15aa63e9c8779a Mon Sep 17 00:00:00 2001 From: jupyterjazz Date: Fri, 31 Mar 2023 13:29:00 +0200 Subject: [PATCH 7/9] refactor: use orjson Signed-off-by: jupyterjazz --- docarray/array/array/io.py | 28 ++++++++++++++++++++++------ docarray/base_doc/base_node.py | 18 +++++++++++++++++- docarray/base_doc/doc.py | 9 ++++++++- docarray/base_doc/io/json.py | 5 ++--- docarray/typing/abstract_type.py | 23 +---------------------- 5 files changed, 50 insertions(+), 33 deletions(-) diff --git a/docarray/array/array/io.py b/docarray/array/array/io.py index 84f9df3ce13..3400d3b9255 100644 --- a/docarray/array/array/io.py +++ b/docarray/array/array/io.py @@ -25,7 +25,10 @@ Union, ) +import orjson + from docarray.base_doc import AnyDoc, BaseDoc +from docarray.base_doc.io.json import orjson_dumps from docarray.helper import ( _access_path_dict_to_nested_dict, _all_access_paths_valid, @@ -93,8 +96,15 @@ def __getitem__(self, item: slice): return self.content[item] +class ComplexEncoder(json.JSONEncoder): + def default(self, val): + if isinstance(val, BaseDoc): + return dict(val) + + class IOMixinArray(Iterable[BaseDoc]): document_type: Type[BaseDoc] + _data: List[BaseDoc] @abstractmethod def __len__(self): @@ -310,22 +320,28 @@ def to_base64( @classmethod def from_json( cls: Type[T], - file: str, + file: Union[str, bytes, bytearray], ) -> T: """Deserialize JSON strings or bytes into a DocArray. :param file: JSON object from where to deserialize a DocArray :return: the deserialized DocArray """ - json_docs = json.loads(file) + json_docs = orjson.loads(file) return cls([cls.document_type(**v) for v in json_docs]) - def to_json(self) -> str: - """Convert the object into a JSON string. Can be loaded via :meth:`.from_json`. + def to_json(self) -> bytes: + """Convert the object into a JSON bytes. Can be loaded via :meth:`.from_json`. :return: JSON serialization of DocArray """ - doc_jsons = ', '.join([doc.json() for doc in self]) - return f'[{doc_jsons}]' + return orjson_dumps(self._data) + + def _docarray_to_json_compatible(self) -> List[BaseDoc]: + """ + Convert itself into a json compatible object + :return: A list of documents + """ + return self._data @classmethod def from_csv( diff --git a/docarray/base_doc/base_node.py b/docarray/base_doc/base_node.py index 726403313aa..a0f934427ac 100644 --- a/docarray/base_doc/base_node.py +++ b/docarray/base_doc/base_node.py @@ -1,9 +1,11 @@ from abc import ABC, abstractmethod -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, TypeVar, Optional, Type if TYPE_CHECKING: from docarray.proto import NodeProto +T = TypeVar('T') + class BaseNode(ABC): """ @@ -11,6 +13,8 @@ class BaseNode(ABC): A Document itself is a DocumentNode as well as prebuilt type """ + _proto_type_name: Optional[str] = None + @abstractmethod def _to_node_protobuf(self) -> 'NodeProto': """Convert itself into a NodeProto message. This function should @@ -20,3 +24,15 @@ def _to_node_protobuf(self) -> 'NodeProto': :return: the nested item protobuf message """ ... + + @classmethod + @abstractmethod + def from_protobuf(cls: Type[T], pb_msg: T) -> T: + ... + + def _docarray_to_json_compatible(self): + """ + Convert itself into a json compatible object + :return: a representation of the tensor compatible with orjson + """ + return self diff --git a/docarray/base_doc/doc.py b/docarray/base_doc/doc.py index 41a8194cac8..da69838c4eb 100644 --- a/docarray/base_doc/doc.py +++ b/docarray/base_doc/doc.py @@ -1,5 +1,5 @@ import os -from typing import TYPE_CHECKING, Any, Optional, Type, TypeVar +from typing import TYPE_CHECKING, Any, Optional, Type, TypeVar, Dict import orjson from pydantic import BaseModel, Field @@ -98,3 +98,10 @@ def __setattr__(self, field, value) -> None: for key, val in self.__dict__.items(): dict_ref[key] = val object.__setattr__(self, '__dict__', dict_ref) + + def _docarray_to_json_compatible(self) -> Dict: + """ + Convert itself into a json compatible object + :return: A dictionary of the BaseDoc object + """ + return self.dict() diff --git a/docarray/base_doc/io/json.py b/docarray/base_doc/io/json.py index 1558659f061..27468b2b61c 100644 --- a/docarray/base_doc/io/json.py +++ b/docarray/base_doc/io/json.py @@ -1,8 +1,6 @@ import orjson from pydantic.json import ENCODERS_BY_TYPE -from docarray.typing.abstract_type import AbstractType - def _default_orjson(obj): """ @@ -10,8 +8,9 @@ def _default_orjson(obj): :param obj: :return: return a json compatible object """ + from docarray.base_doc import BaseNode - if isinstance(obj, AbstractType): + if isinstance(obj, BaseNode): return obj._docarray_to_json_compatible() else: for cls_, encoder in ENCODERS_BY_TYPE.items(): diff --git a/docarray/typing/abstract_type.py b/docarray/typing/abstract_type.py index fd73c93452e..3193116db08 100644 --- a/docarray/typing/abstract_type.py +++ b/docarray/typing/abstract_type.py @@ -1,20 +1,15 @@ from abc import abstractmethod -from typing import TYPE_CHECKING, Any, Optional, Type, TypeVar +from typing import Any, Type, TypeVar from pydantic import BaseConfig from pydantic.fields import ModelField from docarray.base_doc.base_node import BaseNode -if TYPE_CHECKING: - from docarray.proto import NodeProto - T = TypeVar('T') class AbstractType(BaseNode): - _proto_type_name: Optional[str] = None - @classmethod def __get_validators__(cls): yield cls.validate @@ -28,19 +23,3 @@ def validate( config: 'BaseConfig', ) -> T: ... - - @classmethod - @abstractmethod - def from_protobuf(cls: Type[T], pb_msg: T) -> T: - ... - - @abstractmethod - def _to_node_protobuf(self: T) -> 'NodeProto': - ... - - def _docarray_to_json_compatible(self): - """ - Convert itself into a json compatible object - :return: a representation of the tensor compatible with orjson - """ - return self From 8154a0430b92dd3f69c6c335a94fd8f3bfea82fe Mon Sep 17 00:00:00 2001 From: jupyterjazz Date: Fri, 31 Mar 2023 13:37:02 +0200 Subject: [PATCH 8/9] style: mypy errors Signed-off-by: jupyterjazz --- docarray/array/array/io.py | 20 +++++++------------- docarray/base_doc/doc.py | 3 +++ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/docarray/array/array/io.py b/docarray/array/array/io.py index 3400d3b9255..4b26a34c853 100644 --- a/docarray/array/array/io.py +++ b/docarray/array/array/io.py @@ -1,7 +1,6 @@ import base64 import csv import io -import json import os import pathlib import pickle @@ -44,6 +43,7 @@ from docarray.proto import DocumentArrayProto T = TypeVar('T', bound='IOMixinArray') +T_doc = TypeVar('T_doc', bound=BaseDoc) ARRAY_PROTOCOLS = {'protobuf-array', 'pickle-array'} SINGLE_PROTOCOLS = {'pickle', 'protobuf'} @@ -96,15 +96,9 @@ def __getitem__(self, item: slice): return self.content[item] -class ComplexEncoder(json.JSONEncoder): - def default(self, val): - if isinstance(val, BaseDoc): - return dict(val) - - -class IOMixinArray(Iterable[BaseDoc]): - document_type: Type[BaseDoc] - _data: List[BaseDoc] +class IOMixinArray(Iterable[T_doc]): + document_type: Type[T_doc] + _data: List[T_doc] @abstractmethod def __len__(self): @@ -336,7 +330,7 @@ def to_json(self) -> bytes: """ return orjson_dumps(self._data) - def _docarray_to_json_compatible(self) -> List[BaseDoc]: + def _docarray_to_json_compatible(self) -> List[T_doc]: """ Convert itself into a json compatible object :return: A list of documents @@ -631,7 +625,7 @@ def _load_binary_stream( protocol: str = 'protobuf', compress: Optional[str] = None, show_progress: bool = False, - ) -> Generator['BaseDoc', None, None]: + ) -> Generator['T_doc', None, None]: """Yield `Document` objects from a binary file :param protocol: protocol to use. It can be 'pickle' or 'protobuf' @@ -688,7 +682,7 @@ def load_binary( compress: Optional[str] = None, show_progress: bool = False, streaming: bool = False, - ) -> Union[T, Generator['BaseDoc', None, None]]: + ) -> Union[T, Generator['T_doc', None, None]]: """Load array elements from a compressed binary file. :param file: File or filename or serialized bytes where the data is stored. diff --git a/docarray/base_doc/doc.py b/docarray/base_doc/doc.py index da69838c4eb..50abc6722a7 100644 --- a/docarray/base_doc/doc.py +++ b/docarray/base_doc/doc.py @@ -29,6 +29,9 @@ class BaseDoc(BaseModel, IOMixin, UpdateMixin, BaseNode): class Config: json_loads = orjson.loads json_dumps = orjson_dumps_and_decode + # `DocArrayResponse` is able to handle tensors by itself. + # Therefore, we stop FastAPI from doing any transformations + # on tensors by setting an identity function as a custom encoder. json_encoders = {AbstractTensor: lambda x: x} validate_assignment = True From 59086130fbedb77be980b4719e8293ce4a9b51b1 Mon Sep 17 00:00:00 2001 From: jupyterjazz Date: Fri, 31 Mar 2023 13:43:33 +0200 Subject: [PATCH 9/9] refactor: abstract method Signed-off-by: jupyterjazz --- docarray/array/array/io.py | 2 +- docarray/base_doc/base_node.py | 3 +-- docarray/typing/tensor/abstract_tensor.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docarray/array/array/io.py b/docarray/array/array/io.py index 4b26a34c853..02b250fad4e 100644 --- a/docarray/array/array/io.py +++ b/docarray/array/array/io.py @@ -325,7 +325,7 @@ def from_json( return cls([cls.document_type(**v) for v in json_docs]) def to_json(self) -> bytes: - """Convert the object into a JSON bytes. Can be loaded via :meth:`.from_json`. + """Convert the object into JSON bytes. Can be loaded via :meth:`.from_json`. :return: JSON serialization of DocArray """ return orjson_dumps(self._data) diff --git a/docarray/base_doc/base_node.py b/docarray/base_doc/base_node.py index a0f934427ac..7cbb76c9e98 100644 --- a/docarray/base_doc/base_node.py +++ b/docarray/base_doc/base_node.py @@ -33,6 +33,5 @@ def from_protobuf(cls: Type[T], pb_msg: T) -> T: def _docarray_to_json_compatible(self): """ Convert itself into a json compatible object - :return: a representation of the tensor compatible with orjson """ - return self + ... diff --git a/docarray/typing/tensor/abstract_tensor.py b/docarray/typing/tensor/abstract_tensor.py index 08aa0d014ae..f9814b429e4 100644 --- a/docarray/typing/tensor/abstract_tensor.py +++ b/docarray/typing/tensor/abstract_tensor.py @@ -305,4 +305,4 @@ def _docarray_to_json_compatible(self): Convert tensor into a json compatible object :return: a representation of the tensor compatible with orjson """ - ... + return self