docarray__docarray METASEP

tests/units/typing/url/test_text_url.py METASEP
import os
import urllib

import pytest
from pydantic import parse_obj_as, schema_json_of

from docarray.document.io.json import orjson_dumps
from docarray.typing import TextUrl

REMOTE_TXT = 'https://de.wikipedia.org/wiki/Brixen'
CUR_DIR = os.path.dirname(os.path.abspath(__file__))
LOCAL_TXT = os.path.join(CUR_DIR, '..', '..', '..', 'toydata', 'penal_colony.txt')


@pytest.mark.slow
@pytest.mark.internet
@pytest.mark.parametrize(
    'url,expected_beginning',
    [(REMOTE_TXT, '<!DOCTYPE html>'), (LOCAL_TXT, '“It’s a peculiar apparatus,”')],
)
def test_load(url, expected_beginning):
    uri = parse_obj_as(TextUrl, url)

    txt = uri.load()
    assert txt.startswith(expected_beginning)


@pytest.mark.slow
@pytest.mark.internet
@pytest.mark.parametrize('url', [REMOTE_TXT, LOCAL_TXT])
def test_load_to_bytes(url):
    uri = parse_obj_as(TextUrl, url)

    txt_bytes = uri.load_to_bytes()
    assert isinstance(txt_bytes, bytes)


def test_proto_text_url():

    uri = parse_obj_as(TextUrl, LOCAL_TXT)

    uri._to_node_protobuf()


def test_load_timeout():
    url = parse_obj_as(TextUrl, REMOTE_TXT)
    with pytest.raises(urllib.error.URLError):
        _ = url.load(timeout=0.001)
    with pytest.raises(urllib.error.URLError):
        _ = url.load_to_bytes(timeout=0.001)


def test_json_schema():
    schema_json_of(TextUrl)


def test_dump_json():
    url = parse_obj_as(TextUrl, REMOTE_TXT)
    orjson_dumps(url)

tests/units/typing/url/test_point_cloud_url.py METASEP
import numpy as np
import pytest
from pydantic.tools import parse_obj_as, schema_json_of

from docarray.document.io.json import orjson_dumps
from docarray.typing import PointCloud3DUrl
from tests import TOYDATA_DIR

MESH_FILES = {
    'obj': str(TOYDATA_DIR / 'tetrahedron.obj'),
    'glb': str(TOYDATA_DIR / 'test.glb'),
    'ply': str(TOYDATA_DIR / 'cube.ply'),
}
REMOTE_OBJ_FILE = 'https://people.sc.fsu.edu/~jburkardt/data/obj/al.obj'


@pytest.mark.slow
@pytest.mark.internet
@pytest.mark.parametrize(
    'file_format, file_path',
    [
        ('obj', MESH_FILES['obj']),
        ('glb', MESH_FILES['glb']),
        ('ply', MESH_FILES['ply']),
        ('remote-obj', REMOTE_OBJ_FILE),
    ],
)
def test_load(file_format, file_path):
    n_samples = 100
    url = parse_obj_as(PointCloud3DUrl, file_path)
    point_cloud = url.load(samples=n_samples)

    assert isinstance(point_cloud, np.ndarray)
    assert point_cloud.shape == (n_samples, 3)


@pytest.mark.slow
@pytest.mark.internet
@pytest.mark.parametrize(
    'file_format, file_path',
    [
        ('obj', MESH_FILES['obj']),
        ('glb', MESH_FILES['glb']),
        ('ply', MESH_FILES['ply']),
        ('remote-obj', REMOTE_OBJ_FILE),
    ],
)
def test_load_with_multiple_geometries_true(file_format, file_path):
    n_samples = 100
    url = parse_obj_as(PointCloud3DUrl, file_path)
    point_cloud = url.load(samples=n_samples, multiple_geometries=True)

    assert isinstance(point_cloud, np.ndarray)
    assert len(point_cloud.shape) == 3
    assert point_cloud.shape[1:] == (100, 3)


def test_json_schema():
    schema_json_of(PointCloud3DUrl)


def test_dump_json():
    url = parse_obj_as(PointCloud3DUrl, REMOTE_OBJ_FILE)
    orjson_dumps(url)


@pytest.mark.parametrize(
    'file_format,path_to_file',
    [
        ('obj', MESH_FILES['obj']),
        ('glb', MESH_FILES['glb']),
        ('ply', MESH_FILES['ply']),
        ('obj', REMOTE_OBJ_FILE),
        ('illegal', 'illegal'),
        ('illegal', 'https://www.google.com'),
        ('illegal', 'my/local/text/file.txt'),
        ('illegal', 'my/local/text/file.png'),
    ],
)
def test_validation(file_format, path_to_file):
    if file_format == 'illegal':
        with pytest.raises(ValueError, match='PointCloud3DUrl'):
            parse_obj_as(PointCloud3DUrl, path_to_file)
    else:
        url = parse_obj_as(PointCloud3DUrl, path_to_file)
        assert isinstance(url, PointCloud3DUrl)
        assert isinstance(url, str)


def test_proto_point_cloud_url():
    uri = parse_obj_as(PointCloud3DUrl, REMOTE_OBJ_FILE)
    uri._to_node_protobuf()

tests/units/typing/url/test_mesh_url.py METASEP
import numpy as np
import pytest
from pydantic.tools import parse_obj_as, schema_json_of

from docarray.document.io.json import orjson_dumps
from docarray.typing import Mesh3DUrl
from tests import TOYDATA_DIR

MESH_FILES = {
    'obj': str(TOYDATA_DIR / 'tetrahedron.obj'),
    'glb': str(TOYDATA_DIR / 'test.glb'),
    'ply': str(TOYDATA_DIR / 'cube.ply'),
}
REMOTE_OBJ_FILE = 'https://people.sc.fsu.edu/~jburkardt/data/obj/al.obj'


@pytest.mark.slow
@pytest.mark.internet
@pytest.mark.parametrize(
    'file_format, file_path',
    [
        ('obj', MESH_FILES['obj']),
        ('glb', MESH_FILES['glb']),
        ('ply', MESH_FILES['ply']),
        ('remote-obj', REMOTE_OBJ_FILE),
    ],
)
def test_load(file_format, file_path):
    url = parse_obj_as(Mesh3DUrl, file_path)
    vertices, faces = url.load()

    assert isinstance(vertices, np.ndarray)
    assert isinstance(faces, np.ndarray)
    assert vertices.shape[1] == 3
    assert faces.shape[1] == 3


def test_json_schema():
    schema_json_of(Mesh3DUrl)


def test_dump_json():
    url = parse_obj_as(Mesh3DUrl, REMOTE_OBJ_FILE)
    orjson_dumps(url)


@pytest.mark.parametrize(
    'file_format,path_to_file',
    [
        ('obj', MESH_FILES['obj']),
        ('glb', MESH_FILES['glb']),
        ('ply', MESH_FILES['ply']),
        ('obj', REMOTE_OBJ_FILE),
        ('illegal', 'illegal'),
        ('illegal', 'https://www.google.com'),
        ('illegal', 'my/local/text/file.txt'),
        ('illegal', 'my/local/text/file.png'),
    ],
)
def test_validation(file_format, path_to_file):
    if file_format == 'illegal':
        with pytest.raises(ValueError, match='Mesh3DUrl'):
            parse_obj_as(Mesh3DUrl, path_to_file)
    else:
        url = parse_obj_as(Mesh3DUrl, path_to_file)
        assert isinstance(url, Mesh3DUrl)
        assert isinstance(url, str)


def test_proto_mesh_url():
    uri = parse_obj_as(Mesh3DUrl, REMOTE_OBJ_FILE)
    uri._to_node_protobuf()

tests/units/typing/url/test_image_url.py METASEP
import os
import urllib

import numpy as np
import PIL
import pytest
from pydantic.tools import parse_obj_as, schema_json_of

from docarray.document.io.json import orjson_dumps
from docarray.typing import ImageUrl

CUR_DIR = os.path.dirname(os.path.abspath(__file__))
PATH_TO_IMAGE_DATA = os.path.join(CUR_DIR, '..', '..', '..', 'toydata', 'image-data')
IMAGE_PATHS = {
    'png': os.path.join(PATH_TO_IMAGE_DATA, 'so_good.png'),
    'jpg': os.path.join(PATH_TO_IMAGE_DATA, '05984.jpg'),
    'jpeg': os.path.join(PATH_TO_IMAGE_DATA, '05984-2.jpeg'),
}
REMOTE_JPG = (
    'https://upload.wikimedia.org/wikipedia/commons/8/80/'
    'Dag_Sebastian_Ahlander_at_G%C3%B6teborg_Book_Fair_2012b.jpg'
)


@pytest.mark.slow
@pytest.mark.internet
def test_image_url():
    uri = parse_obj_as(ImageUrl, REMOTE_JPG)

    tensor = uri.load()

    assert isinstance(tensor, np.ndarray)


def test_proto_image_url():

    uri = parse_obj_as(ImageUrl, REMOTE_JPG)

    uri._to_node_protobuf()


def test_json_schema():
    schema_json_of(ImageUrl)


def test_dump_json():
    url = parse_obj_as(ImageUrl, 'http://jina.ai/img.png')
    orjson_dumps(url)


@pytest.mark.slow
@pytest.mark.internet
@pytest.mark.parametrize(
    'image_format,path_to_img',
    [
        ('png', IMAGE_PATHS['png']),
        ('jpg', IMAGE_PATHS['jpg']),
        ('jpeg', IMAGE_PATHS['jpeg']),
        ('remote-jpg', REMOTE_JPG),
    ],
)
def test_load(image_format, path_to_img):
    url = parse_obj_as(ImageUrl, path_to_img)
    tensor = url.load()
    assert isinstance(tensor, np.ndarray)


@pytest.mark.slow
@pytest.mark.internet
@pytest.mark.parametrize(
    'image_format,path_to_img',
    [
        ('png', IMAGE_PATHS['png']),
        ('jpg', IMAGE_PATHS['jpg']),
        ('jpeg', IMAGE_PATHS['jpeg']),
        ('remote-jpg', REMOTE_JPG),
    ],
)
@pytest.mark.parametrize('width,height', [(224, None), (None, 224), (224, 224)])
def test_load_width_height(image_format, path_to_img, width, height):
    url = parse_obj_as(ImageUrl, path_to_img)
    tensor = url.load(width=width, height=height)
    assert isinstance(tensor, np.ndarray)

    shape = tensor.shape
    if width:
        assert shape[1] == width
    if height:
        assert shape[0] == height


@pytest.mark.slow
@pytest.mark.internet
@pytest.mark.parametrize(
    'image_format,path_to_img',
    [
        ('png', IMAGE_PATHS['png']),
        ('jpg', IMAGE_PATHS['jpg']),
        ('jpeg', IMAGE_PATHS['jpeg']),
        ('remote-jpg', REMOTE_JPG),
    ],
)
@pytest.mark.parametrize(
    'axis_layout',
    [
        ('H', 'W', 'C'),
        ('H', 'C', 'W'),
        ('C', 'H', 'W'),
        ('C', 'W', 'H'),
        ('W', 'C', 'H'),
        ('W', 'H', 'C'),
    ],
)
def test_load_channel_axis(image_format, path_to_img, axis_layout):
    sizes = {'H': 100, 'W': 200, 'C': 3}
    url = parse_obj_as(ImageUrl, path_to_img)
    tensor = url.load(axis_layout=axis_layout, height=sizes['H'], width=sizes['W'])
    assert isinstance(tensor, np.ndarray)

    shape = tensor.shape
    for axis, axis_name in enumerate(axis_layout):
        assert shape[axis] == sizes[axis_name]


def test_load_timeout():
    url = parse_obj_as(ImageUrl, REMOTE_JPG)
    with pytest.raises(urllib.error.URLError):
        _ = url.load(timeout=0.001)


@pytest.mark.slow
@pytest.mark.internet
@pytest.mark.parametrize(
    'image_format,path_to_img',
    [
        ('png', IMAGE_PATHS['png']),
        ('jpg', IMAGE_PATHS['jpg']),
        ('jpeg', IMAGE_PATHS['jpeg']),
        ('jpg', REMOTE_JPG),
    ],
)
def test_load_to_bytes(image_format, path_to_img):
    w, h = 224, 224
    url = parse_obj_as(ImageUrl, path_to_img)
    _bytes = url.load_to_bytes(width=w, height=h)
    assert isinstance(_bytes, bytes)
    img = PIL.Image.frombytes(mode='1', size=(w, h), data=_bytes)
    assert isinstance(img, PIL.Image.Image)


@pytest.mark.parametrize(
    'image_format,path_to_img',
    [
        ('png', IMAGE_PATHS['png']),
        ('jpg', IMAGE_PATHS['jpg']),
        ('jpeg', IMAGE_PATHS['jpeg']),
        ('jpg', REMOTE_JPG),
        ('illegal', 'illegal'),
        ('illegal', 'https://www.google.com'),
        ('illegal', 'my/local/text/file.txt'),
    ],
)
def test_validation(image_format, path_to_img):
    if image_format == 'illegal':
        with pytest.raises(ValueError):
            parse_obj_as(ImageUrl, path_to_img)
    else:
        url = parse_obj_as(ImageUrl, path_to_img)
        assert isinstance(url, ImageUrl)
        assert isinstance(url, str)

tests/units/typing/url/test_any_url.py METASEP
from pydantic.tools import parse_obj_as, schema_json_of

from docarray.document.io.json import orjson_dumps
from docarray.typing import AnyUrl


def test_proto_any_url():

    uri = parse_obj_as(AnyUrl, 'http://jina.ai/img.png')

    uri._to_node_protobuf()


def test_json_schema():
    schema_json_of(AnyUrl)


def test_dump_json():
    url = parse_obj_as(AnyUrl, 'http://jina.ai/img.png')
    orjson_dumps(url)

tests/units/typing/url/__init__.py METASEP

tests/units/typing/tensor/test_torch_tensor.py METASEP
import pytest
import torch
from pydantic.tools import parse_obj_as, schema_json_of

from docarray.document.io.json import orjson_dumps
from docarray.typing import TorchEmbedding, TorchTensor


def test_proto_tensor():

    tensor = parse_obj_as(TorchTensor, torch.zeros(3, 224, 224))

    tensor._to_node_protobuf()


def test_json_schema():
    schema_json_of(TorchTensor)


def test_dump_json():
    tensor = parse_obj_as(TorchTensor, torch.zeros(3, 224, 224))
    orjson_dumps(tensor)


def test_unwrap():
    tensor = parse_obj_as(TorchTensor, torch.zeros(3, 224, 224))
    ndarray = tensor.unwrap()

    assert not isinstance(ndarray, TorchTensor)
    assert isinstance(tensor, TorchTensor)
    assert isinstance(ndarray, torch.Tensor)

    assert tensor.data_ptr() == ndarray.data_ptr()

    assert (ndarray == torch.zeros(3, 224, 224)).all()


def test_parametrized():
    # correct shape, single axis
    tensor = parse_obj_as(TorchTensor[128], torch.zeros(128))
    assert isinstance(tensor, TorchTensor)
    assert isinstance(tensor, torch.Tensor)
    assert tensor.shape == (128,)

    # correct shape, multiple axis
    tensor = parse_obj_as(TorchTensor[3, 224, 224], torch.zeros(3, 224, 224))
    assert isinstance(tensor, TorchTensor)
    assert isinstance(tensor, torch.Tensor)
    assert tensor.shape == (3, 224, 224)

    # wrong but reshapable shape
    tensor = parse_obj_as(TorchTensor[3, 224, 224], torch.zeros(224, 3, 224))
    assert isinstance(tensor, TorchTensor)
    assert isinstance(tensor, torch.Tensor)
    assert tensor.shape == (3, 224, 224)

    # wrong and not reshapable shape
    with pytest.raises(ValueError):
        parse_obj_as(TorchTensor[3, 224, 224], torch.zeros(224, 224))


@pytest.mark.parametrize('shape', [(3, 224, 224), (224, 224, 3)])
def test_parameterized_tensor_class_name(shape):
    tensor = parse_obj_as(TorchTensor[3, 224, 224], torch.zeros(shape))

    assert tensor.__class__.__name__ == 'TorchTensor[3, 224, 224]'
    assert tensor.__class__.__qualname__ == 'TorchTensor[3, 224, 224]'
    assert f'{tensor[0][0][0]}' == 'TorchTensor[3, 224, 224](0.)'


def test_torch_embedding():
    # correct shape
    tensor = parse_obj_as(TorchEmbedding[128], torch.zeros(128))
    assert isinstance(tensor, TorchEmbedding)
    assert isinstance(tensor, torch.Tensor)
    assert tensor.shape == (128,)

    # wrong shape at data setting time
    with pytest.raises(ValueError):
        parse_obj_as(TorchEmbedding[128], torch.zeros(256))

    # illegal shape at class creation time
    with pytest.raises(ValueError):
        parse_obj_as(TorchEmbedding[128, 128], torch.zeros(128, 128))

tests/units/typing/tensor/test_tensor.py METASEP
import numpy as np
import orjson
import pytest
from pydantic.tools import parse_obj_as, schema_json_of

from docarray.document.io.json import orjson_dumps
from docarray.typing import NdArray
from docarray.typing.tensor import NdArrayEmbedding


def test_proto_tensor():

    tensor = parse_obj_as(NdArray, np.zeros((3, 224, 224)))

    tensor._to_node_protobuf()


def test_from_list():
    tensor = parse_obj_as(NdArray, [[0.0, 0.0], [0.0, 0.0]])

    assert (tensor == np.zeros((2, 2))).all()


def test_json_schema():
    schema_json_of(NdArray)


def test_dump_json():
    tensor = parse_obj_as(NdArray, np.zeros((3, 224, 224)))
    orjson_dumps(tensor)


def test_load_json():
    tensor = parse_obj_as(NdArray, np.zeros((2, 2)))

    json = orjson_dumps(tensor)
    print(json)
    print(type(json))
    new_tensor = orjson.loads(json)

    assert (new_tensor == tensor).all()


def test_unwrap():
    tensor = parse_obj_as(NdArray, np.zeros((3, 224, 224)))
    ndarray = tensor.unwrap()

    assert not isinstance(ndarray, NdArray)
    assert isinstance(ndarray, np.ndarray)
    assert isinstance(tensor, NdArray)
    assert (ndarray == np.zeros((3, 224, 224))).all()


def test_parametrized():
    # correct shape, single axis
    tensor = parse_obj_as(NdArray[128], np.zeros(128))
    assert isinstance(tensor, NdArray)
    assert isinstance(tensor, np.ndarray)
    assert tensor.shape == (128,)

    # correct shape, multiple axis
    tensor = parse_obj_as(NdArray[3, 224, 224], np.zeros((3, 224, 224)))
    assert isinstance(tensor, NdArray)
    assert isinstance(tensor, np.ndarray)
    assert tensor.shape == (3, 224, 224)

    # wrong but reshapable shape
    tensor = parse_obj_as(NdArray[3, 224, 224], np.zeros((3, 224, 224)))
    assert isinstance(tensor, NdArray)
    assert isinstance(tensor, np.ndarray)
    assert tensor.shape == (3, 224, 224)

    # wrong and not reshapable shape
    with pytest.raises(ValueError):
        parse_obj_as(NdArray[3, 224, 224], np.zeros((224, 224)))


def test_np_embedding():
    # correct shape
    tensor = parse_obj_as(NdArrayEmbedding[128], np.zeros((128,)))
    assert isinstance(tensor, NdArrayEmbedding)
    assert isinstance(tensor, NdArray)
    assert isinstance(tensor, np.ndarray)
    assert tensor.shape == (128,)

    # wrong shape at data setting time
    with pytest.raises(ValueError):
        parse_obj_as(NdArrayEmbedding[128], np.zeros((256,)))

    # illegal shape at class creation time
    with pytest.raises(ValueError):
        parse_obj_as(NdArrayEmbedding[128, 128], np.zeros((128, 128)))

tests/units/typing/tensor/test_embedding.py METASEP
import numpy as np
from pydantic.tools import parse_obj_as, schema_json_of

from docarray.document.io.json import orjson_dumps
from docarray.typing import Embedding


def test_proto_embedding():

    embedding = parse_obj_as(Embedding, np.zeros((3, 224, 224)))

    embedding._to_node_protobuf()


def test_json_schema():
    schema_json_of(Embedding)


def test_dump_json():
    tensor = parse_obj_as(Embedding, np.zeros((3, 224, 224)))
    orjson_dumps(tensor)

tests/units/typing/tensor/__init__.py METASEP

tests/units/document/proto/test_proto_based_object.py METASEP
import numpy as np

from docarray.proto import DocumentProto, NodeProto
from docarray.typing import NdArray


def test_nested_item_proto():
    NodeProto(text='hello')
    NodeProto(nested=DocumentProto())


def test_nested_optional_item_proto():
    NodeProto()


def test_ndarray():

    original_ndarray = np.zeros((3, 224, 224))

    custom_ndarray = NdArray.__docarray_from_native__(original_ndarray)

    tensor = NdArray.from_protobuf(custom_ndarray.to_protobuf())

    assert (tensor == original_ndarray).all()


def test_document_proto_set():

    data = {}

    nested_item1 = NodeProto(text='hello')

    ndarray = NdArray.__docarray_from_native__(np.zeros((3, 224, 224)))
    nd_proto = ndarray.to_protobuf()

    nested_item2 = NodeProto(ndarray=nd_proto)

    data['a'] = nested_item1
    data['b'] = nested_item2

    DocumentProto(data=data)

tests/units/document/proto/test_document_proto.py METASEP
from typing import Optional

import numpy as np
import torch

from docarray import DocumentArray
from docarray.document import BaseDocument
from docarray.typing import NdArray, TorchTensor


def test_proto_simple():
    class CustomDoc(BaseDocument):
        text: str

    doc = CustomDoc(text='hello')

    CustomDoc.from_protobuf(doc.to_protobuf())


def test_proto_ndarray():
    class CustomDoc(BaseDocument):
        tensor: NdArray

    tensor = np.zeros((3, 224, 224))
    doc = CustomDoc(tensor=tensor)

    new_doc = CustomDoc.from_protobuf(doc.to_protobuf())

    assert (new_doc.tensor == tensor).all()


def test_proto_with_nested_doc():
    class CustomInnerDoc(BaseDocument):
        tensor: NdArray

    class CustomDoc(BaseDocument):
        text: str
        inner: CustomInnerDoc

    doc = CustomDoc(text='hello', inner=CustomInnerDoc(tensor=np.zeros((3, 224, 224))))

    CustomDoc.from_protobuf(doc.to_protobuf())


def test_proto_with_chunks_doc():
    class CustomInnerDoc(BaseDocument):
        tensor: NdArray

    class CustomDoc(BaseDocument):
        text: str
        chunks: DocumentArray[CustomInnerDoc]

    doc = CustomDoc(
        text='hello',
        chunks=DocumentArray[CustomInnerDoc](
            [CustomInnerDoc(tensor=np.zeros((3, 224, 224))) for _ in range(5)],
        ),
    )

    new_doc = CustomDoc.from_protobuf(doc.to_protobuf())

    for chunk1, chunk2 in zip(doc.chunks, new_doc.chunks):

        assert (chunk1.tensor == chunk2.tensor).all()


def test_proto_with_nested_doc_pytorch():
    class CustomInnerDoc(BaseDocument):
        tensor: TorchTensor

    class CustomDoc(BaseDocument):
        text: str
        inner: CustomInnerDoc

    doc = CustomDoc(
        text='hello', inner=CustomInnerDoc(tensor=torch.zeros((3, 224, 224)))
    )

    CustomDoc.from_protobuf(doc.to_protobuf())


def test_proto_with_chunks_doc_pytorch():
    class CustomInnerDoc(BaseDocument):
        tensor: TorchTensor

    class CustomDoc(BaseDocument):
        text: str
        chunks: DocumentArray[CustomInnerDoc]

    doc = CustomDoc(
        text='hello',
        chunks=DocumentArray[CustomInnerDoc](
            [CustomInnerDoc(tensor=torch.zeros((3, 224, 224))) for _ in range(5)],
        ),
    )

    new_doc = CustomDoc.from_protobuf(doc.to_protobuf())

    for chunk1, chunk2 in zip(doc.chunks, new_doc.chunks):

        assert (chunk1.tensor == chunk2.tensor).all()


def test_optional_field_in_doc():
    class CustomDoc(BaseDocument):
        text: Optional[str]

    CustomDoc.from_protobuf(CustomDoc().to_protobuf())


def test_optional_field_nested_in_doc():
    class InnerDoc(BaseDocument):
        title: str

    class CustomDoc(BaseDocument):
        text: Optional[InnerDoc]

    CustomDoc.from_protobuf(CustomDoc().to_protobuf())

tests/units/document/proto/__init__.py METASEP

tests/units/computation_backends/torch_backend/test_retrieval.py METASEP
import torch

from docarray.computation.torch_backend import TorchCompBackend


def test_topk():
    top_k = TorchCompBackend.Retrieval.top_k

    a = torch.tensor([1, 4, 2, 7, 4, 9, 2])
    vals, indices = top_k(a, 3)
    assert vals.shape == (1, 3)
    assert indices.shape == (1, 3)
    assert (vals.squeeze() == torch.tensor([1, 2, 2])).all()
    assert (indices.squeeze() == torch.tensor([0, 2, 6])).all() or (
        indices.squeeze() == torch.tensor([0, 6, 2])
    ).all()

    a = torch.tensor([[1, 4, 2, 7, 4, 9, 2], [11, 6, 2, 7, 3, 10, 4]])
    vals, indices = top_k(a, 3)
    assert vals.shape == (2, 3)
    assert indices.shape == (2, 3)
    assert (vals[0] == torch.tensor([1, 2, 2])).all()
    assert (indices[0] == torch.tensor([0, 2, 6])).all() or (
        indices[0] == torch.tensor([0, 6, 2])
    ).all()
    assert (vals[1] == torch.tensor([2, 3, 4])).all()
    assert (indices[1] == torch.tensor([2, 4, 6])).all()

tests/units/computation_backends/torch_backend/test_metrics.py METASEP
import torch

from docarray.computation.torch_backend import TorchCompBackend

metrics = TorchCompBackend.Metrics


def test_cosine_sim_torch():
    a = torch.rand(128)
    b = torch.rand(128)
    assert metrics.cosine_sim(a, b).shape == (1,)
    assert metrics.cosine_sim(a, b) == metrics.cosine_sim(b, a)
    torch.testing.assert_close(metrics.cosine_sim(a, a), torch.ones(1))

    a = torch.rand(10, 3)
    b = torch.rand(5, 3)
    assert metrics.cosine_sim(a, b).shape == (10, 5)
    assert metrics.cosine_sim(b, a).shape == (5, 10)
    diag_dists = torch.diagonal(metrics.cosine_sim(b, b))  # self-comparisons
    torch.testing.assert_allclose(diag_dists, torch.ones(5))


def test_euclidean_dist_torch():
    a = torch.rand(128)
    b = torch.rand(128)
    assert metrics.euclidean_dist(a, b).shape == (1,)
    assert metrics.euclidean_dist(a, b) == metrics.euclidean_dist(b, a)
    torch.testing.assert_close(metrics.euclidean_dist(a, a), torch.zeros(1))

    a = torch.rand(10, 3)
    b = torch.rand(5, 3)
    assert metrics.euclidean_dist(a, b).shape == (10, 5)
    assert metrics.euclidean_dist(b, a).shape == (5, 10)
    diag_dists = torch.diagonal(metrics.euclidean_dist(b, b))  # self-comparisons
    torch.testing.assert_allclose(diag_dists, torch.zeros(5))

    a = torch.tensor([0.0, 2.0, 0.0])
    b = torch.tensor([0.0, 0.0, 2.0])
    desired_output_singleton = torch.sqrt(torch.tensor([2.0**2.0 + 2.0**2.0]))
    torch.testing.assert_close(metrics.euclidean_dist(a, b), desired_output_singleton)

    a = torch.tensor([[0.0, 2.0, 0.0], [0.0, 0.0, 2.0]])
    b = torch.tensor([[0.0, 0.0, 2.0], [0.0, 2.0, 0.0]])
    desired_output_singleton = torch.tensor(
        [[desired_output_singleton.item(), 0.0], [0.0, desired_output_singleton.item()]]
    )
    torch.testing.assert_close(metrics.euclidean_dist(a, b), desired_output_singleton)


def test_sqeuclidean_dist_torch():
    a = torch.rand(128)
    b = torch.rand(128)
    assert metrics.sqeuclidean_dist(a, b).shape == (1,)
    torch.testing.assert_close(
        metrics.sqeuclidean_dist(a, b),
        metrics.euclidean_dist(a, b) ** 2,
    )

    a = torch.rand(10, 3)
    b = torch.rand(5, 3)
    assert metrics.sqeuclidean_dist(a, b).shape == (10, 5)
    torch.testing.assert_close(
        metrics.sqeuclidean_dist(a, b),
        metrics.euclidean_dist(a, b) ** 2,
    )

tests/units/computation_backends/torch_backend/__init__.py METASEP

tests/units/computation_backends/numpy_backend/test_retrieval.py METASEP
import numpy as np

from docarray.computation.numpy_backend import NumpyCompBackend


def test_topk_numpy():
    top_k = NumpyCompBackend.Retrieval.top_k

    a = np.array([1, 4, 2, 7, 4, 9, 2])
    vals, indices = top_k(a, 3)
    assert vals.shape == (1, 3)
    assert indices.shape == (1, 3)
    assert (vals.squeeze() == np.array([1, 2, 2])).all()
    assert (indices.squeeze() == np.array([0, 2, 6])).all() or (
        indices.squeeze() == np.array([0, 6, 2])
    ).all()

    a = np.array([[1, 4, 2, 7, 4, 9, 2], [11, 6, 2, 7, 3, 10, 4]])
    vals, indices = top_k(a, 3)
    assert vals.shape == (2, 3)
    assert indices.shape == (2, 3)
    assert (vals[0] == np.array([1, 2, 2])).all()
    assert (indices[0] == np.array([0, 2, 6])).all() or (
        indices[0] == np.array([0, 6, 2])
    ).all()
    assert (vals[1] == np.array([2, 3, 4])).all()
    assert (indices[1] == np.array([2, 4, 6])).all()

tests/units/computation_backends/numpy_backend/test_metrics.py METASEP
import numpy as np

from docarray.computation.numpy_backend import NumpyCompBackend

metrics = NumpyCompBackend.Metrics


def test_cosine_sim_np():
    a = np.random.rand(128)
    b = np.random.rand(128)
    assert metrics.cosine_sim(a, b).shape == (1,)
    assert metrics.cosine_sim(a, b) == metrics.cosine_sim(b, a)
    np.testing.assert_array_almost_equal(metrics.cosine_sim(a, a), np.ones((1,)))

    a = np.random.rand(10, 3)
    b = np.random.rand(5, 3)
    assert metrics.cosine_sim(a, b).shape == (10, 5)
    assert metrics.cosine_sim(b, a).shape == (5, 10)
    diag_dists = np.diagonal(metrics.cosine_sim(b, b))  # self-comparisons
    np.testing.assert_array_almost_equal(diag_dists, np.ones((5,)))


def test_euclidean_dist_np():
    a = np.random.rand(128)
    b = np.random.rand(128)
    assert metrics.euclidean_dist(a, b).shape == (1,)
    assert metrics.euclidean_dist(a, b) == metrics.euclidean_dist(b, a)
    np.testing.assert_array_almost_equal(metrics.euclidean_dist(a, a), np.zeros((1,)))

    a = np.random.rand(10, 3)
    b = np.random.rand(5, 3)
    assert metrics.euclidean_dist(a, b).shape == (10, 5)
    assert metrics.euclidean_dist(b, a).shape == (5, 10)
    diag_dists = np.diagonal(metrics.euclidean_dist(b, b))  # self-comparisons
    np.testing.assert_array_almost_equal(diag_dists, np.zeros((5,)))

    a = np.array([0.0, 2.0, 0.0])
    b = np.array([0.0, 0.0, 2.0])
    desired_output_singleton = np.sqrt(np.array([2.0**2.0 + 2.0**2.0]))
    np.testing.assert_array_almost_equal(
        metrics.euclidean_dist(a, b), desired_output_singleton
    )

    a = np.array([[0.0, 2.0, 0.0], [0.0, 0.0, 2.0]])
    b = np.array([[0.0, 0.0, 2.0], [0.0, 2.0, 0.0]])
    desired_output_singleton = np.array(
        [[desired_output_singleton.item(), 0.0], [0.0, desired_output_singleton.item()]]
    )
    np.testing.assert_array_almost_equal(
        metrics.euclidean_dist(a, b), desired_output_singleton
    )


def test_sqeuclidea_dist_np():
    a = np.random.rand(128)
    b = np.random.rand(128)
    assert metrics.sqeuclidean_dist(a, b).shape == (1,)
    np.testing.assert_array_almost_equal(
        metrics.sqeuclidean_dist(a, b), metrics.euclidean_dist(a, b) ** 2
    )

    a = np.random.rand(10, 3)
    b = np.random.rand(5, 3)
    assert metrics.sqeuclidean_dist(a, b).shape == (10, 5)
    np.testing.assert_array_almost_equal(
        metrics.sqeuclidean_dist(a, b), metrics.euclidean_dist(a, b) ** 2
    )

tests/units/computation_backends/numpy_backend/__init__.py METASEP

tests/units/computation_backends/backend_comparisons/test_metrics.py METASEP
import torch

from docarray.computation.numpy_backend import NumpyCompBackend
from docarray.computation.torch_backend import TorchCompBackend

np_metrics = NumpyCompBackend.Metrics
torch_metrics = TorchCompBackend.Metrics


def test_cosine_sim_compare():
    a = torch.rand(128)
    b = torch.rand(128)
    torch.testing.assert_close(
        torch_metrics.cosine_sim(a, b),
        torch.from_numpy(np_metrics.cosine_sim(a.numpy(), b.numpy())),
    )

    a = torch.rand(10, 3)
    b = torch.rand(5, 3)
    torch.testing.assert_close(
        torch_metrics.cosine_sim(a, b),
        torch.from_numpy(np_metrics.cosine_sim(a.numpy(), b.numpy())),
    )


def test_euclidean_dist_compare():
    a = torch.rand(128)
    b = torch.rand(128)
    torch.testing.assert_close(
        torch_metrics.euclidean_dist(a, b),
        torch.from_numpy(np_metrics.euclidean_dist(a.numpy(), b.numpy())).to(
            torch.float32
        ),
    )

    a = torch.rand(10, 3)
    b = torch.rand(5, 3)
    torch.testing.assert_close(
        torch_metrics.euclidean_dist(a, b),
        torch.from_numpy(np_metrics.euclidean_dist(a.numpy(), b.numpy())),
    )


def test_sqeuclidean_dist_compare():
    a = torch.rand(128)
    b = torch.rand(128)
    torch.testing.assert_close(
        torch_metrics.sqeuclidean_dist(a, b),
        torch.from_numpy(np_metrics.sqeuclidean_dist(a.numpy(), b.numpy())).to(
            torch.float32
        ),
    )

    a = torch.rand(10, 3)
    b = torch.rand(5, 3)
    torch.testing.assert_close(
        torch_metrics.sqeuclidean_dist(a, b),
        torch.from_numpy(np_metrics.sqeuclidean_dist(a.numpy(), b.numpy())),
    )

tests/units/computation_backends/backend_comparisons/__init__.py METASEP

tests/toydata/image-data/so_good.png METASEP

tests/toydata/image-data/05984.jpg METASEP

tests/toydata/image-data/05984-2.jpeg METASEP

tests/toydata/image-data/05983.jpg METASEP

tests/toydata/image-data/05982.jpg METASEP

tests/toydata/image-data/05981.jpg METASEP

tests/toydata/image-data/05980.jpg METASEP

tests/toydata/image-data/05979.jpg METASEP

tests/toydata/image-data/05978.jpg METASEP

docs/_templates/sidebar/brand.html METASEP
<a class="sidebar-brand{% if logo %} centered{% endif %}" href="{{ pathto(master_doc) }}">
  {% block brand_content %}
  {%- if logo_url %}
  <div class="sidebar-logo-container">
    <img class="sidebar-logo" src="{{ logo_url }}" alt="Logo" />
  </div>
  {%- endif %}
  {%- if theme_light_logo and theme_dark_logo %}
  <div class="sidebar-logo-container">
    <img class="sidebar-logo only-light" src="{{ pathto('_static/' + theme_light_logo, 1) }}" alt="Light Logo" />
    <img class="sidebar-logo only-dark" src="{{ pathto('_static/' + theme_dark_logo, 1) }}" alt="Dark Logo" />
  </div>
  {%- endif %}
  {% if not theme_sidebar_hide_name %}
  <span class="sidebar-brand-text">{{ docstitle if docstitle else project }}</span>
  {%- endif %}
  {% endblock brand_content %}
</a>
<script>
  function setPrefix(prefix){
    window.location.href = prefix
  }
</script>
<script defer>
  fetch(`https://${window.location.host}/_versions.json`)
  .then((resp) => resp.json())
  .then((data) => {
    var versionSelector = document.getElementsByClassName("version-select")[0];
    var currentPrefix = window.location.href.toString().split(window.location.host)[1].split('/')[1];

    for(var i = 0; i < data.length; i++){
      var option = document.createElement("option");
      option.innerHTML = data[i].version;
      option.value = "/" + data[i].version;
      versionSelector.appendChild(option);
      if(currentPrefix === data[i].version){
        versionSelector.selectedIndex = i + 1;
      }
    }
  })
  .catch((err) => console.log(err));
</script>
<div class="sd-d-flex-row sd-align-major-spaced">
  <a class="github-button" href="https://github.com/jina-ai/docarray" data-icon="octicon-star" data-show-count="true" aria-label="Star jina-ai/jina on GitHub" style="opacity: 0;">Star</a>
  <select onChange="setPrefix(this.value)" class="version-select">
    <option value="/">latest</option>
  </select>
</div>
tests/units/util/test_find.py METASEP
import numpy as np
import pytest
import torch

from docarray import Document, DocumentArray
from docarray.typing import NdArray, TorchTensor
from docarray.util import find, find_batched


class TorchDoc(Document):
    tensor: TorchTensor


class NdDoc(Document):
    tensor: NdArray


@pytest.fixture()
def random_torch_query():
    return TorchDoc(tensor=torch.rand(128))


@pytest.fixture()
def random_torch_batch_query():
    return DocumentArray[TorchDoc]([TorchDoc(tensor=torch.rand(128)) for _ in range(5)])


@pytest.fixture()
def random_nd_query():
    return NdDoc(tensor=np.random.rand(128))


@pytest.fixture()
def random_nd_batch_query():
    return DocumentArray[NdDoc]([NdDoc(tensor=np.random.rand(128)) for _ in range(5)])


@pytest.fixture()
def random_torch_index():
    return DocumentArray[TorchDoc](TorchDoc(tensor=torch.rand(128)) for _ in range(10))


@pytest.fixture()
def random_nd_index():
    return DocumentArray[NdDoc](NdDoc(tensor=np.random.rand(128)) for _ in range(10))


@pytest.mark.parametrize('metric', ['cosine_sim', 'euclidean_dist', 'sqeuclidean_dist'])
def test_find_torch(random_torch_query, random_torch_index, metric):
    top_k, scores = find(
        random_torch_index,
        random_torch_query,
        embedding_field='tensor',
        limit=7,
        metric=metric,
    )
    assert len(top_k) == 7
    assert len(scores) == 7
    if metric.endswith('_dist'):
        assert (torch.stack(sorted(scores)) == scores).all()
    else:
        assert (torch.stack(sorted(scores, reverse=True)) == scores).all()


def test_find_torch_tensor_query(random_torch_query, random_torch_index):
    query = random_torch_query.tensor
    top_k, scores = find(
        random_torch_index,
        query,
        embedding_field='tensor',
        limit=7,
        metric='cosine_sim',
    )
    assert len(top_k) == 7
    assert len(scores) == 7
    assert (torch.stack(sorted(scores, reverse=True)) == scores).all()


def test_find_torch_stacked(random_torch_query, random_torch_index):
    random_torch_index.stack()
    top_k, scores = find(
        random_torch_index,
        random_torch_query,
        embedding_field='tensor',
        limit=7,
        metric='cosine_sim',
    )
    assert len(top_k) == 7
    assert len(scores) == 7
    assert (torch.stack(sorted(scores, reverse=True)) == scores).all()


@pytest.mark.parametrize('metric', ['cosine_sim', 'euclidean_dist', 'sqeuclidean_dist'])
def test_find_np(random_nd_query, random_nd_index, metric):
    top_k, scores = find(
        random_nd_index,
        random_nd_query,
        embedding_field='tensor',
        limit=7,
        metric=metric,
    )
    assert len(top_k) == 7
    assert len(scores) == 7
    if metric.endswith('_dist'):
        assert (sorted(scores) == scores).all()
    else:
        assert (sorted(scores, reverse=True) == scores).all()


def test_find_np_tensor_query(random_nd_query, random_nd_index):
    query = random_nd_query.tensor
    top_k, scores = find(
        random_nd_index,
        query,
        embedding_field='tensor',
        limit=7,
        metric='cosine_sim',
    )
    assert len(top_k) == 7
    assert len(scores) == 7
    assert (sorted(scores, reverse=True) == scores).all()


def test_find_np_stacked(random_nd_query, random_nd_index):
    random_nd_index.stack()
    top_k, scores = find(
        random_nd_index,
        random_nd_query,
        embedding_field='tensor',
        limit=7,
        metric='cosine_sim',
    )
    assert len(top_k) == 7
    assert len(scores) == 7
    assert (sorted(scores, reverse=True) == scores).all()


@pytest.mark.parametrize('metric', ['cosine_sim', 'euclidean_dist', 'sqeuclidean_dist'])
def test_find_batched_torch(random_torch_batch_query, random_torch_index, metric):
    results = find_batched(
        random_torch_index,
        random_torch_batch_query,
        embedding_field='tensor',
        limit=7,
        metric=metric,
    )
    assert len(results) == len(random_torch_batch_query)
    for top_k, scores in results:
        assert len(top_k) == 7
        assert len(scores) == 7
    for sc in [scores for _, scores in results]:
        if metric.endswith('_dist'):
            assert (torch.stack(sorted(sc)) == sc).all()
        else:
            assert (torch.stack(sorted(sc, reverse=True)) == sc).all()


def test_find_batched_torch_tensor_query(random_torch_batch_query, random_torch_index):
    query = torch.stack(random_torch_batch_query.tensor)
    results = find_batched(
        random_torch_index,
        query,
        embedding_field='tensor',
        limit=7,
        metric='cosine_sim',
    )
    assert len(results) == len(random_torch_batch_query)
    for top_k, scores in results:
        assert len(top_k) == 7
        assert len(scores) == 7
    for sc in [scores for _, scores in results]:
        assert (torch.stack(sorted(sc, reverse=True)) == sc).all()


@pytest.mark.parametrize('stack_what', ['index', 'query', 'both'])
def test_find_batched_torch_stacked(
    random_torch_batch_query, random_torch_index, stack_what
):
    if stack_what in ('index', 'both'):
        random_torch_index.stack()
    if stack_what in ('query', 'both'):
        random_torch_batch_query.stack()

    results = find_batched(
        random_torch_index,
        random_torch_batch_query,
        embedding_field='tensor',
        limit=7,
        metric='cosine_sim',
    )
    assert len(results) == len(random_torch_batch_query)
    for top_k, scores in results:
        assert len(top_k) == 7
        assert len(scores) == 7
    for sc in [scores for _, scores in results]:
        assert (torch.stack(sorted(sc, reverse=True)) == sc).all()


@pytest.mark.parametrize('metric', ['cosine_sim', 'euclidean_dist', 'sqeuclidean_dist'])
def test_find_batched_np(random_nd_batch_query, random_nd_index, metric):
    results = find_batched(
        random_nd_index,
        random_nd_batch_query,
        embedding_field='tensor',
        limit=7,
        metric=metric,
    )
    assert len(results) == len(random_nd_batch_query)
    for top_k, scores in results:
        assert len(top_k) == 7
        assert len(scores) == 7
    for sc in [scores for _, scores in results]:
        if metric.endswith('_dist'):
            assert (sorted(sc) == sc).all()
        else:
            assert (sorted(sc, reverse=True) == sc).all()


def test_find_batched_np_tensor_query(random_nd_batch_query, random_nd_index):
    query = np.stack(random_nd_batch_query.tensor)
    results = find_batched(
        random_nd_index,
        query,
        embedding_field='tensor',
        limit=7,
        metric='cosine_sim',
    )
    assert len(results) == len(random_nd_batch_query)
    for top_k, scores in results:
        assert len(top_k) == 7
        assert len(scores) == 7
    for sc in [scores for _, scores in results]:
        assert (sorted(sc, reverse=True) == sc).all()


@pytest.mark.parametrize('stack_what', ['index', 'query', 'both'])
def test_find_batched_np_stacked(random_nd_batch_query, random_nd_index, stack_what):
    if stack_what in ('index', 'both'):
        random_nd_index.stack()
    if stack_what in ('query', 'both'):
        random_nd_batch_query.stack()
    results = find_batched(
        random_nd_index,
        random_nd_batch_query,
        embedding_field='tensor',
        limit=7,
        metric='cosine_sim',
    )
    assert len(results) == len(random_nd_batch_query)
    for top_k, scores in results:
        assert len(top_k) == 7
        assert len(scores) == 7
    for sc in [scores for _, scores in results]:
        assert (sorted(sc, reverse=True) == sc).all()

tests/units/util/__init__.py METASEP

tests/units/typing/test_id.py METASEP
from uuid import UUID

import pytest
from pydantic import schema_json_of
from pydantic.tools import parse_obj_as

from docarray.document.io.json import orjson_dumps
from docarray.typing import ID


@pytest.mark.parametrize(
    'id', ['1234', 1234, UUID('cf57432e-809e-4353-adbd-9d5c0d733868')]
)
def test_id_validation(id):

    parsed_id = parse_obj_as(ID, id)

    assert parsed_id == str(id)


def test_json_schema():
    schema_json_of(ID)


def test_dump_json():
    id = parse_obj_as(ID, 1234)
    orjson_dumps(id)

tests/units/typing/__init__.py METASEP

tests/units/document/test_base_document.py METASEP
from docarray.document.document import BaseDocument


def test_base_document_init():

    doc = BaseDocument()

    assert doc.id is not None

tests/units/document/test_any_document.py METASEP
import numpy as np

from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray


def test_any_doc():
    class InnerDocument(BaseDocument):
        text: str
        tensor: NdArray

    class CustomDoc(BaseDocument):
        inner: InnerDocument
        text: str

    doc = CustomDoc(
        text='bye', inner=InnerDocument(text='hello', tensor=np.zeros((3, 224, 224)))
    )

    any_doc = AnyDocument(**doc.__dict__)

    assert any_doc.text == doc.text
    assert any_doc.inner.text == doc.inner.text
    assert (any_doc.inner.tensor == doc.inner.tensor).all()

tests/units/document/__init__.py METASEP

tests/units/computation_backends/__init__.py METASEP

tests/units/array/test_stack_mod.py METASEP
from typing import Optional

import numpy as np
import pytest
import torch

from docarray import Document, DocumentArray
from docarray.typing import NdArray, TorchTensor


def test_stack():
    class Image(Document):
        tensor: TorchTensor[3, 224, 224]

    batch = DocumentArray[Image](
        [Image(tensor=torch.zeros(3, 224, 224)) for _ in range(10)]
    )

    batch.stack()

    assert (batch._columns['tensor'] == torch.zeros(10, 3, 224, 224)).all()
    assert (batch.tensor == torch.zeros(10, 3, 224, 224)).all()
    assert batch._columns['tensor'].data_ptr() == batch.tensor.data_ptr()

    for doc, tensor in zip(batch, batch.tensor):
        assert doc.tensor.data_ptr() == tensor.data_ptr()

    for i in range(len(batch)):
        assert batch[i].tensor.data_ptr() == batch.tensor[i].data_ptr()


def test_stack_mod_nested_document():
    class Image(Document):
        tensor: TorchTensor[3, 224, 224]

    class MMdoc(Document):
        img: Image

    batch = DocumentArray[MMdoc](
        [MMdoc(img=Image(tensor=torch.zeros(3, 224, 224))) for _ in range(10)]
    )

    batch.stack()

    assert (
        batch._columns['img']._columns['tensor'] == torch.zeros(10, 3, 224, 224)
    ).all()

    assert (batch.img.tensor == torch.zeros(10, 3, 224, 224)).all()

    assert (
        batch._columns['img']._columns['tensor'].data_ptr()
        == batch.img.tensor.data_ptr()
    )


def test_unstack():
    class Image(Document):
        tensor: TorchTensor[3, 224, 224]

    batch = DocumentArray[Image](
        [Image(tensor=torch.zeros(3, 224, 224)) for _ in range(10)]
    )

    batch.stack()

    batch.unstack()

    for doc in batch:
        assert (doc.tensor == torch.zeros(3, 224, 224)).all()


def test_unstack_nested_document():
    class Image(Document):
        tensor: TorchTensor[3, 224, 224]

    class MMdoc(Document):
        img: Image

    batch = DocumentArray[MMdoc](
        [MMdoc(img=Image(tensor=torch.zeros(3, 224, 224))) for _ in range(10)]
    )

    batch.stack()

    batch.unstack()


def test_stack_runtime_error():
    class Image(Document):
        tensor: TorchTensor[3, 224, 224]

    batch = DocumentArray[Image](
        [Image(tensor=torch.zeros(3, 224, 224)) for _ in range(10)]
    )

    batch.stack()

    with pytest.raises(RuntimeError):
        batch.append([])


def test_context_stack():
    class Image(Document):
        tensor: TorchTensor[3, 224, 224]

    batch = DocumentArray[Image](
        [Image(tensor=torch.zeros(3, 224, 224)) for _ in range(10)]
    )

    assert not (batch.is_stacked())
    with batch.stacked_mode():
        assert batch.is_stacked()

    assert not (batch.is_stacked())


def test_context_not_stack():
    class Image(Document):
        tensor: TorchTensor[3, 224, 224]

    batch = DocumentArray[Image](
        [Image(tensor=torch.zeros(3, 224, 224)) for _ in range(10)]
    )

    batch.stack()

    assert batch.is_stacked()
    with batch.unstacked_mode():
        assert not (batch.is_stacked())

    assert batch.is_stacked()


def test_stack_setter():
    class Image(Document):
        tensor: TorchTensor[3, 224, 224]

    batch = DocumentArray[Image](
        [Image(tensor=torch.zeros(3, 224, 224)) for _ in range(10)]
    )

    batch.stack()

    batch.tensor = torch.ones(10, 3, 224, 224)

    assert (batch.tensor == torch.ones(10, 3, 224, 224)).all()


def test_stack_optional():
    class Image(Document):
        tensor: Optional[TorchTensor[3, 224, 224]]

    batch = DocumentArray[Image](
        [Image(tensor=torch.zeros(3, 224, 224)) for _ in range(10)]
    )

    batch.stack()

    assert (batch._columns['tensor'] == torch.zeros(10, 3, 224, 224)).all()
    assert (batch.tensor == torch.zeros(10, 3, 224, 224)).all()


def test_stack_numpy():
    class Image(Document):
        tensor: NdArray[3, 224, 224]

    batch = DocumentArray[Image](
        [Image(tensor=np.zeros((3, 224, 224))) for _ in range(10)]
    )

    batch.stack()

    assert (batch._columns['tensor'] == np.zeros((10, 3, 224, 224))).all()
    assert (batch.tensor == np.zeros((10, 3, 224, 224))).all()
    assert batch.tensor.ctypes.data == batch._columns['tensor'].ctypes.data

    batch.unstack()

tests/units/array/test_generic_array.py METASEP
from docarray import DocumentArray, Document
from docarray.document import AnyDocument


def test_generic_init():
    class Text(Document):
        text: str

    da = DocumentArray[Text]([])
    da.document_type == Text

    assert isinstance(da, DocumentArray)


def test_normal_access_init():
    da = DocumentArray([])
    da.document_type == AnyDocument

    assert isinstance(da, DocumentArray)

tests/units/array/test_array.py METASEP
from docarray import DocumentArray, Document


def test_document_array():
    class Text(Document):
        text: str

    da = DocumentArray([Text(text='hello') for _ in range(10)])


def test_document_array_fixed_type():
    class Text(Document):
        text: str

    da = DocumentArray[Text]([Text(text='hello') for _ in range(10)])

tests/units/array/__init__.py METASEP

tests/integrations/typing/test_typing_proto.py METASEP
import numpy as np
import torch

from docarray import Document
from docarray.document import AnyDocument
from docarray.typing import (
    AnyUrl,
    Embedding,
    ImageUrl,
    Mesh3DUrl,
    NdArray,
    PointCloud3DUrl,
    TextUrl,
    TorchTensor,
)


def test_proto_all_types():
    class Mymmdoc(Document):
        tensor: NdArray
        torch_tensor: TorchTensor
        embedding: Embedding
        any_url: AnyUrl
        image_url: ImageUrl
        text_url: TextUrl
        mesh_url: Mesh3DUrl
        point_cloud_url: PointCloud3DUrl

    doc = Mymmdoc(
        tensor=np.zeros((3, 224, 224)),
        torch_tensor=torch.zeros((3, 224, 224)),
        embedding=np.zeros((100, 1)),
        any_url='http://jina.ai',
        image_url='http://jina.ai/bla.jpg',
        text_url='http://jina.ai',
        mesh_url='http://jina.ai/mesh.obj',
        point_cloud_url='http://jina.ai/mesh.obj',
    )

    new_doc = AnyDocument.from_protobuf(doc.to_protobuf())

    for field, value in new_doc:
        if field == 'embedding':
            # embedding is a Union type, not supported by isinstance
            assert isinstance(value, np.ndarray) or isinstance(value, torch.Tensor)
        else:
            assert isinstance(value, doc._get_nested_document_class(field))

tests/integrations/typing/test_torch_tensor.py METASEP
import torch

from docarray import Document
from docarray.typing import TorchEmbedding, TorchTensor


def test_set_torch_tensor():
    class MyDocument(Document):
        tensor: TorchTensor

    d = MyDocument(tensor=torch.zeros((3, 224, 224)))

    assert isinstance(d.tensor, TorchTensor)
    assert isinstance(d.tensor, torch.Tensor)
    assert (d.tensor == torch.zeros((3, 224, 224))).all()


def test_set_torch_embedding():
    class MyDocument(Document):
        embedding: TorchEmbedding

    d = MyDocument(embedding=torch.zeros((128,)))

    assert isinstance(d.embedding, TorchTensor)
    assert isinstance(d.embedding, TorchEmbedding)
    assert isinstance(d.embedding, torch.Tensor)
    assert (d.embedding == torch.zeros((128,))).all()

tests/integrations/typing/test_tensor.py METASEP
import numpy as np
import torch

from docarray import Document
from docarray.typing import NdArray, Tensor, TorchTensor


def test_set_tensor():
    class MyDocument(Document):
        tensor: Tensor

    d = MyDocument(tensor=np.zeros((3, 224, 224)))

    assert isinstance(d.tensor, NdArray)
    assert isinstance(d.tensor, np.ndarray)
    assert (d.tensor == np.zeros((3, 224, 224))).all()

    d = MyDocument(tensor=torch.zeros((3, 224, 224)))

    assert isinstance(d.tensor, TorchTensor)
    assert isinstance(d.tensor, torch.Tensor)
    assert (d.tensor == torch.zeros((3, 224, 224))).all()

tests/integrations/typing/test_point_cloud_url.py METASEP
from docarray import Document
from docarray.typing import PointCloud3DUrl


def test_set_point_cloud_url():
    class MyDocument(Document):
        point_cloud_url: PointCloud3DUrl

    d = MyDocument(point_cloud_url="https://jina.ai/mesh.obj")

    assert isinstance(d.point_cloud_url, PointCloud3DUrl)
    assert d.point_cloud_url == "https://jina.ai/mesh.obj"

tests/integrations/typing/test_ndarray.py METASEP
import numpy as np

from docarray import Document
from docarray.typing import NdArray


def test_set_tensor():
    class MyDocument(Document):
        tensor: NdArray

    d = MyDocument(tensor=np.zeros((3, 224, 224)))

    assert isinstance(d.tensor, NdArray)
    assert isinstance(d.tensor, np.ndarray)
    assert (d.tensor == np.zeros((3, 224, 224))).all()

tests/integrations/typing/test_mesh_url.py METASEP
from docarray import Document
from docarray.typing import Mesh3DUrl


def test_set_mesh_url():
    class MyDocument(Document):
        mesh_url: Mesh3DUrl

    d = MyDocument(mesh_url="https://jina.ai/mesh.obj")

    assert isinstance(d.mesh_url, Mesh3DUrl)
    assert d.mesh_url == "https://jina.ai/mesh.obj"

tests/integrations/typing/test_image_url.py METASEP
from docarray import Document
from docarray.typing import ImageUrl


def test_set_image_url():
    class MyDocument(Document):
        image_url: ImageUrl

    d = MyDocument(image_url="https://jina.ai/img.png")

    assert isinstance(d.image_url, ImageUrl)
    assert d.image_url == "https://jina.ai/img.png"

tests/integrations/typing/test_id.py METASEP
from docarray import Document
from docarray.typing import ID


def test_set_id():
    class MyDocument(Document):
        id: ID

    d = MyDocument(id="123")

    assert isinstance(d.id, ID)
    assert d.id == "123"

tests/integrations/typing/test_embedding.py METASEP
import numpy as np

from docarray import Document
from docarray.typing import Embedding


def test_set_embedding():
    class MyDocument(Document):
        embedding: Embedding

    d = MyDocument(embedding=np.zeros((3, 224, 224)))

    assert isinstance(d.embedding, np.ndarray)
    assert (d.embedding == np.zeros((3, 224, 224))).all()

tests/integrations/typing/test_anyurl.py METASEP
from docarray import Document
from docarray.typing import AnyUrl


def test_set_any_url():
    class MyDocument(Document):
        any_url: AnyUrl

    d = MyDocument(any_url="https://jina.ai")

    assert isinstance(d.any_url, AnyUrl)
    assert d.any_url == "https://jina.ai"

tests/integrations/typing/__init__.py METASEP

tests/integrations/predefined_document/test_point_cloud.py METASEP
import numpy as np
import pytest

from docarray import PointCloud3D
from tests import TOYDATA_DIR

LOCAL_OBJ_FILE = str(TOYDATA_DIR / 'tetrahedron.obj')
REMOTE_OBJ_FILE = 'https://people.sc.fsu.edu/~jburkardt/data/obj/al.obj'


@pytest.mark.slow
@pytest.mark.internet
@pytest.mark.parametrize('file_url', [LOCAL_OBJ_FILE, REMOTE_OBJ_FILE])
def test_point_cloud(file_url):
    print(f"file_url = {file_url}")
    point_cloud = PointCloud3D(url=file_url)

    point_cloud.tensor = point_cloud.url.load(samples=100)

    assert isinstance(point_cloud.tensor, np.ndarray)

tests/integrations/predefined_document/test_mesh.py METASEP
import numpy as np
import pytest

from docarray import Mesh3D
from tests import TOYDATA_DIR

LOCAL_OBJ_FILE = str(TOYDATA_DIR / 'tetrahedron.obj')
REMOTE_OBJ_FILE = 'https://people.sc.fsu.edu/~jburkardt/data/obj/al.obj'


@pytest.mark.slow
@pytest.mark.internet
@pytest.mark.parametrize('file_url', [LOCAL_OBJ_FILE, REMOTE_OBJ_FILE])
def test_mesh(file_url):

    mesh = Mesh3D(url=file_url)

    mesh.vertices, mesh.faces = mesh.url.load()

    assert isinstance(mesh.vertices, np.ndarray)
    assert isinstance(mesh.faces, np.ndarray)

tests/integrations/predefined_document/test_image.py METASEP
import numpy as np
import pytest

from docarray import Image

REMOTE_JPG = (
    'https://upload.wikimedia.org/wikipedia/commons/8/80/'
    'Dag_Sebastian_Ahlander_at_G%C3%B6teborg_Book_Fair_2012b.jpg'
)


@pytest.mark.slow
@pytest.mark.internet
def test_image():

    image = Image(url=REMOTE_JPG)

    image.tensor = image.url.load()

    assert isinstance(image.tensor, np.ndarray)

tests/integrations/externals/test_fastapi.py METASEP
import numpy as np
import pytest
from fastapi import FastAPI
from httpx import AsyncClient

from docarray import Document, Image, Text
from docarray.typing import NdArray


@pytest.mark.asyncio
async def test_fast_api():
    class Mmdoc(Document):
        img: Image
        text: Text
        title: str

    input_doc = Mmdoc(
        img=Image(tensor=np.zeros((3, 224, 224))), text=Text(), title='hello'
    )

    app = FastAPI()

    @app.post("/doc/")
    async def create_item(doc: Mmdoc):
        return doc

    async with AsyncClient(app=app, base_url="http://test") as ac:
        response = await ac.post("/doc/", data=input_doc.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


@pytest.mark.asyncio
async def test_image():
    class InputDoc(Document):
        img: Image

    class OutputDoc(Document):
        embedding_clip: NdArray
        embedding_bert: NdArray

    input_doc = InputDoc(img=Image(tensor=np.zeros((3, 224, 224))))

    app = FastAPI()

    @app.post("/doc/", response_model=OutputDoc)
    async def create_item(doc: InputDoc) -> OutputDoc:
        ## call my fancy model to generate the embeddings
        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:
        response = await ac.post("/doc/", data=input_doc.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


@pytest.mark.asyncio
async def test_sentence_to_embeddings():
    class InputDoc(Document):
        text: str

    class OutputDoc(Document):
        embedding_clip: NdArray
        embedding_bert: NdArray

    input_doc = InputDoc(text='hello')

    app = FastAPI()

    @app.post("/doc/", response_model=OutputDoc)
    async def create_item(doc: InputDoc) -> OutputDoc:
        ## call my fancy model to generate the embeddings
        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:
        response = await ac.post("/doc/", data=input_doc.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

tests/integrations/document/test_to_json.py METASEP
import numpy as np
import torch

from docarray.document import BaseDocument
from docarray.typing import AnyUrl, NdArray, TorchTensor


def test_to_json():
    class Mmdoc(BaseDocument):
        img: NdArray
        url: AnyUrl
        txt: str
        torch_tensor: TorchTensor

    doc = Mmdoc(
        img=np.zeros((3, 224, 224)),
        url='http://doccaray.io',
        txt='hello',
        torch_tensor=torch.zeros(3, 224, 224),
    )
    doc.json()


def test_from_json():
    class Mmdoc(BaseDocument):
        img: NdArray
        url: AnyUrl
        txt: str
        torch_tensor: TorchTensor

    doc = Mmdoc(
        img=np.zeros((2, 2)),
        url='http://doccaray.io',
        txt='hello',
        torch_tensor=torch.zeros(3, 224, 224),
    )
    new_doc = Mmdoc.parse_raw(doc.json())

    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)

tests/integrations/document/test_proto.py METASEP
import numpy as np
import torch

from docarray import Document, Image, Text
from docarray.typing import (
    AnyUrl,
    Embedding,
    ImageUrl,
    Mesh3DUrl,
    NdArray,
    PointCloud3DUrl,
    Tensor,
    TextUrl,
    TorchEmbedding,
    TorchTensor,
)
from docarray.typing.tensor import NdArrayEmbedding


def test_multi_modal_doc_proto():
    class MyMultiModalDoc(Document):
        image: Image
        text: Text

    class MySUperDoc(Document):
        doc: MyMultiModalDoc
        description: str

    doc = MyMultiModalDoc(
        image=Image(tensor=np.zeros((3, 224, 224))), text=Text(text='hello')
    )

    MyMultiModalDoc.from_protobuf(doc.to_protobuf())


def test_all_types():
    class MyDoc(Document):
        img_url: ImageUrl
        txt_url: TextUrl
        mesh_url: Mesh3DUrl
        point_cloud_url: PointCloud3DUrl
        any_url: AnyUrl
        torch_tensor: TorchTensor
        torch_tensor_param: TorchTensor[224, 224, 3]
        np_array: NdArray
        np_array_param: NdArray[224, 224, 3]
        generic_nd_array: Tensor
        generic_torch_tensor: Tensor
        embedding: Embedding
        torch_embedding: TorchEmbedding[128]
        np_embedding: NdArrayEmbedding[128]

    doc = MyDoc(
        img_url='test.png',
        txt_url='test.txt',
        mesh_url='test.obj',
        point_cloud_url='test.obj',
        any_url='www.jina.ai',
        torch_tensor=torch.zeros((3, 224, 224)),
        torch_tensor_param=torch.zeros((3, 224, 224)),
        np_array=np.zeros((3, 224, 224)),
        np_array_param=np.zeros((3, 224, 224)),
        generic_nd_array=np.zeros((3, 224, 224)),
        generic_torch_tensor=torch.zeros((3, 224, 224)),
        embedding=np.zeros((3, 224, 224)),
        torch_embedding=torch.zeros((128,)),
        np_embedding=np.zeros((128,)),
    )
    doc = MyDoc.from_protobuf(doc.to_protobuf())

    assert doc.img_url == 'test.png'
    assert doc.txt_url == 'test.txt'
    assert doc.mesh_url == 'test.obj'
    assert doc.point_cloud_url == 'test.obj'
    assert doc.any_url == 'www.jina.ai'

    assert (doc.torch_tensor == torch.zeros((3, 224, 224))).all()
    assert isinstance(doc.torch_tensor, torch.Tensor)

    assert (doc.torch_tensor_param == torch.zeros((224, 224, 3))).all()
    assert isinstance(doc.torch_tensor_param, torch.Tensor)

    assert (doc.np_array == np.zeros((3, 224, 224))).all()
    assert isinstance(doc.np_array, np.ndarray)
    assert doc.np_array.flags.writeable

    assert (doc.np_array_param == np.zeros((224, 224, 3))).all()
    assert isinstance(doc.np_array_param, np.ndarray)

    assert (doc.generic_nd_array == np.zeros((3, 224, 224))).all()
    assert isinstance(doc.generic_nd_array, np.ndarray)

    assert (doc.generic_torch_tensor == torch.zeros((3, 224, 224))).all()
    assert isinstance(doc.generic_torch_tensor, torch.Tensor)

    assert (doc.torch_embedding == torch.zeros((128,))).all()
    assert isinstance(doc.torch_embedding, torch.Tensor)

    assert (doc.np_embedding == np.zeros((128,))).all()
    assert isinstance(doc.np_embedding, np.ndarray)

    assert (doc.embedding == np.zeros((3, 224, 224))).all()

tests/integrations/document/test_document.py METASEP
import numpy as np

from docarray import DocumentArray, Document, Image, Text


def test_multi_modal_doc():
    class MyMultiModalDoc(Document):
        image: Image
        text: Text

    doc = MyMultiModalDoc(
        image=Image(tensor=np.zeros((3, 224, 224))), text=Text(text='hello')
    )

    assert isinstance(doc.image, Document)
    assert isinstance(doc.image, Image)
    assert isinstance(doc.text, Text)

    assert doc.text.text == 'hello'
    assert (doc.image.tensor == np.zeros((3, 224, 224))).all()


def test_nested_chunks_document():
    class ChunksDocument(Document):
        text: str
        images: DocumentArray  # should find a way to do DocumentArray[Images]

    doc = ChunksDocument(
        text='hello',
        images=DocumentArray[Image]([Image() for _ in range(10)]),
    )

    assert isinstance(doc.images, DocumentArray)

tests/integrations/document/__init__.py METASEP

tests/integrations/array/test_torch_train.py METASEP
from typing import Optional

import torch

from docarray import Document, DocumentArray
from docarray.typing import TorchTensor


def test_torch_train():
    class Mmdoc(Document):
        text: str
        tensor: Optional[TorchTensor[3, 224, 224]]

    N = 10

    batch = DocumentArray[Mmdoc](Mmdoc(text=f'hello{i}') for i in range(N))

    batch.tensor = torch.zeros(N, 3, 224, 224)
    batch.stack()

    class Model(torch.nn.Module):
        def __init__(self):
            super().__init__()
            self.conv = torch.nn.Conv2d(3, 16, 3)

        def forward(self, x):
            return self.conv(x)

    model = Model()
    opt = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

    for _ in range(2):
        loss = model(batch.tensor).sum()
        loss.backward()
        opt.step()

tests/integrations/array/__init__.py METASEP

tests/toydata/tetrahedron.obj METASEP
# tetrahedron.obj 
#
 
mtllib tetrahedron.mtl

g tetrahedron
 
v 1.00 1.00 1.00
v 2.00 1.00 1.00
v 1.00 2.00 1.00
v 1.00 1.00 2.00

usemtl lime
f 1 3 2
usemtl gold
f 1 4 3
usemtl lime
f 1 2 4
usemtl red
f 2 3 4

tests/toydata/tetrahedron.mtl METASEP

newmtl red
Ka 0.4449 0.0000 0.0000
Kd 0.7714 0.0000 0.0000
Ks 0.8857 0.0000 0.0000
illum 2
Ns 136.4300

newmtl lime
Ka 0.0000 0.5000 0.0000
Kd 0.0000 1.0000 0.0000
Ks 0.0000 0.5000 0.0000
illum 2
Ns 65.8900

newmtl gold
Ka 0.5265 0.2735 0.0122
Kd 1.0000 0.5184 0.0286
Ks 0.3000 0.3000 0.3000
illum 2
Ns 123.2600


tests/toydata/test.png METASEP

tests/toydata/test.glb METASEP

tests/toydata/penal_colony.txt METASEP
“It’s a peculiar apparatus,” said the Officer to the Traveler, gazing with a certain admiration at the device, with which he was, of course, thoroughly familiar. It appeared that the Traveler had responded to the invitation of the Commandant only out of politeness, when he had been invited to attend the execution of a soldier condemned for disobeying and insulting his superior. Of course, interest in the execution was not very high, not even in the penal colony itself. At least, here in the small, deep, sandy valley, closed in on all sides by barren slopes, apart from the Officer and the Traveler there were present only the Condemned, a vacant-looking man with a broad mouth and dilapidated hair and face, and the Soldier, who held the heavy chain to which were connected the small chains which bound the Condemned Man by his feet and wrist bones, as well as by his neck, and which were also linked to each other by connecting chains. The Condemned Man had an expression of such dog-like resignation that it looked as if one could set him free to roam around the slopes and would only have to whistle at the start of the execution for him to return.

The Traveler had little interest in the apparatus and walked back and forth behind the Condemned Man, almost visibly indifferent, while the Officer took care of the final preparations. Sometimes he crawled under the apparatus, which was built deep into the earth, and sometimes he climbed up a ladder to inspect the upper parts. These were really jobs which could have been left to a mechanic, but the Officer carried them out with great enthusiasm, maybe because he was particularly fond of this apparatus or maybe because there was some other reason why one could not trust the work to anyone else. “It’s all ready now!” he finally cried and climbed back down the ladder. He was unusually tired, breathing with his mouth wide open, and he had pushed two fine lady’s handkerchiefs under the collar of his uniform.

“These uniforms are really too heavy for the tropics,” the Traveler said, instead of asking some questions about the apparatus, as the Officer had expected. “That’s true,” said the Officer. He washed the oil and grease from his dirty hands in a bucket of water standing ready, “but they mean home, and we don’t want to lose our homeland.” “Now, have a look at this apparatus,” he added immediately, drying his hands with a towel and pointing to the device. “Up to this point I had to do some work by hand, but from now on the apparatus should work entirely on its own.” The Traveler nodded and followed the Officer. The latter tried to protect himself against all eventualities by saying, “Of course, breakdowns do happen. I really hope none will occur today, but we must be prepared for it. The apparatus is supposed to keep going for twelve hours without interruption. But if any breakdowns do occur, they’ll only be very minor, and we’ll deal with them right away.”

“Don’t you want to sit down?” he asked finally, as he pulled out a chair from a pile of cane chairs and offered it to the Traveler. The latter could not refuse. He sat on the edge of the pit, into which he cast a fleeting glance. It was not very deep. On one side of the hole the piled earth was heaped up into a wall; on the other side stood the apparatus. “I don’t know,” the officer said, “whether the Commandant has already explained the apparatus to you.” The Traveler made an vague gesture with his hand. That was good enough for the Officer, for now he could explain the apparatus himself.

“This apparatus,” he said, grasping a connecting rod and leaning against it, “is our previous Commandant’s invention. I also worked with him on the very first tests and took part in all the work right up to its completion. However, the credit for the invention belongs to him alone. Have you heard of our previous Commandant? No? Well, I’m not claiming too much when I say that the organization of the entire penal colony is his work. We, his friends, already knew at the time of his death that the administration of the colony was so self-contained that even if his successor had a thousand new plans in mind, he would not be able to alter anything of the old plan, at least not for several years. And our prediction has held. The New Commandant has had to recognize that. It’s a shame that you didn’t know the previous Commandant!”

“However,” the Officer said, interrupting himself, “I’m chattering, and his apparatus stands here in front of us. As you see, it consists of three parts. With the passage of time certain popular names have been developed for each of these parts. The one underneath is called the bed, the upper one is called the inscriber, and here in the middle, this moving part is called the harrow.” “The harrow?” the Traveler asked. He had not been listening with full attention. The sun was excessively strong, trapped in the shadowless valley, and one could hardly collect one’s thoughts. So the Officer appeared to him all the more admirable in his tight tunic weighed down with epaulettes and festooned with braid, ready to go on parade, as he explained the matter so eagerly and, while he was talking, adjusted screws here and there with a screwdriver.

The Soldier appeared to be in a state similar to the Traveler. He had wound the Condemned Man’s chain around both his wrists and was supporting himself with his hand on his weapon, letting his head hang backward, not bothering about anything. The Traveler was not surprised at that, for the Officer spoke French, and clearly neither the Soldier nor the Condemned Man understood the language. So it was all the more striking that the Condemned Man, in spite of that, did what he could to follow the Officer’s explanation. With a sort of sleepy persistence he kept directing his gaze to the place where the Officer had just pointed, and when the question from the Traveler interrupted the Officer, the Condemned Man looked at the Traveler, too, just as the Officer was doing.

“Yes, the harrow,” said the Officer. “The name fits. The needles are arranged as in a harrow, and the whole thing is driven like a harrow, although it stays in one place and is, in principle, much more artistic. You’ll understand in a moment. The condemned is laid out here on the bed. First, I’ll describe the apparatus and only then let the procedure go to work. That way you’ll be able to follow it better. Also a sprocket in the inscriber is excessively worn. It really squeaks. When it’s in motion one can hardly make oneself understood. Unfortunately replacement parts are difficult to come by in this place. So, here is the bed, as I said. The whole thing is completely covered with a layer of cotton wool, the purpose of which you’ll find out in a moment. The condemned man is laid out on his stomach on the cotton wool—naked, of course. There are straps for the hands here, for the feet here, and for the throat here, to tie him in securely. At the head of the bed here, where the man, as I have mentioned, first lies face down, is this small protruding lump of felt, which can easily be adjusted so that it presses right into the man’s mouth. Its purpose is to prevent him screaming and biting his tongue to pieces. Of course, the man has to let the felt in his mouth—otherwise the straps around his throat would break his neck.” “That’s cotton wool?” asked the Traveler and bent down. “Yes, it is,” said the Officer smiling, “feel it for yourself.”

He took the Traveler’s hand and led him over to the bed. “It’s a specially prepared cotton wool. That’s why it looks so unrecognizable. I’ll get around to mentioning its purpose in a moment.” The Traveler was already being won over a little to the apparatus. With his hand over his eyes to protect them from the sun, he looked at the apparatus in the hole. It was a massive construction. The bed and the inscriber were the same size and looked like two dark chests. The inscriber was set about two metres above the bed, and the two were joined together at the corners by four brass rods, which almost reflected the sun. The harrow hung between the chests on a band of steel.

The Officer had hardly noticed the earlier indifference of the Traveler, but he did have a sense now of how the latter’s interest was being aroused for the first time. So he paused in his explanation in order to allow the Traveler time to observe the apparatus undisturbed. The Condemned Man imitated the Traveler, but since he could not put his hand over his eyes, he blinked upward with his eyes uncovered.

“So now the man is lying down,” said the Traveler. He leaned back in his chair and crossed his legs.

“Yes,” said the Officer, pushing his cap back a little and running his hand over his hot face. “Now, listen. Both the bed and the inscriber have their own electric batteries. The bed needs them for itself, and the inscriber for the harrow. As soon as the man is strapped in securely, the bed is set in motion. It quivers with tiny, very rapid oscillations from side to side and up and down simultaneously. You will have seen similar devices in mental hospitals. Only with our bed all movements are precisely calibrated, for they must be meticulously coordinated with the movements of the harrow. But it’s the harrow which has the job of actually carrying out the sentence.”

“What is the sentence?” the Traveler asked. “You don’t even know that?” asked the Officer in astonishment and bit his lip. “Forgive me if my explanations are perhaps confused. I really do beg your pardon. Previously it was the Commandant’s habit to provide such explanations. But the New Commandant has excused himself from this honourable duty. The fact that with such an eminent visitor”—the traveler tried to deflect the honour with both hands, but the officer insisted on the expression—“that with such an eminent visitor he didn’t even once make him aware of the form of our sentencing is yet again something new, which . . .” He had a curse on his lips, but controlled himself and said merely: “I was not informed about it. It’s not my fault. In any case, I am certainly the person best able to explain our style of sentencing, for here I am carrying”—he patted his breast pocket—“the relevant diagrams drawn by the previous Commandant.”

“Diagrams made by the Commandant himself?” asked the Traveler. “Then was he in his own person a combination of everything? Was he soldier, judge, engineer, chemist, and draftsman?”

“He was indeed,” said the Officer, nodding his head with a fixed and thoughtful expression. Then he looked at his hands, examining them. They didn’t seem to him clean enough to handle the diagrams. So he went to the bucket and washed them again. Then he pulled out a small leather folder and said, “Our sentence does not sound severe. The law which a condemned man has violated is inscribed on his body with the harrow. This Condemned Man, for example,” and the Officer pointed to the man, “will have inscribed on his body, ‘Honour your superiors.’”

The Traveler had a quick look at the man. When the Officer was pointing at him, the man kept his head down and appeared to be directing all his energy into listening in order to learn something. But the movements of his thick pouting lips showed clearly that he was incapable of understanding anything. The Traveler wanted to raise various questions, but after looking at the Condemned Man he merely asked, “Does he know his sentence?” “No,” said the Officer. He wished to get on with his explanation right away, but the Traveler interrupted him: “He doesn’t know his own sentence?” “No,” said the Officer once more. He then paused for a moment, as if he was asking the Traveler for a more detailed reason for his question, and said, “It would be useless to give him that information. He experiences it on his own body.” The Traveler really wanted to keep quiet at this point, but he felt how the Condemned Man was gazing at him—he seemed to be asking whether he could approve of the process the Officer had described. So the Traveler, who had up to this point been leaning back, bent forward again and kept up his questions, “But does he nonetheless have some general idea that he’s been condemned?” “Not that either,” said the Officer, and he smiled at the traveler, as if he was still waiting for some strange revelations from him. “No?” said the Traveler, wiping his forehead, “then does the man also not yet know how his defence was received?” “He has had no opportunity to defend himself,” said the Officer and looked away, as if he was talking to himself and wished not to embarrass the Traveler with an explanation of matters so self-evident to him. “But he must have had a chance to defend himself,” said the Traveler and stood up from his chair.

The Officer recognized that he was in danger of having his explanation of the apparatus held up for a long time. So he went to the Traveler, took him by the arm, pointed with his hand at the Condemned Man, who stood there stiffly now that the attention was so clearly directed at him—the Soldier was also pulling on his chain—and said, “The matter stands like this. Here in the penal colony I have been appointed judge. In spite of my youth. For I stood at the side of our Old Commandant in all matters of punishment, and I also know the most about the apparatus. The basic principle I use for my decisions is this: Guilt is always beyond a doubt. Other courts could not follow this principle, for they are made up of many heads and, in addition, have even higher courts above them. But that is not the case here, or at least it was not that way with the previous Commandant. It’s true the New Commandant has already shown a desire to get mixed up in my court, but I’ve succeeded so far in fending him off. And I’ll continue to be successful. You want this case explained. It’s simple—just like all of them. This morning a captain laid a charge that this man, who is assigned to him as a servant and who sleeps before his door, had been sleeping on duty. For his task is to stand up every time the clock strikes the hour and salute in front of the captain’s door. That’s certainly not a difficult duty—and it’s necessary, since he is supposed to remain fresh both for guarding and for service. Yesterday night the captain wanted to check whether his servant was fulfilling his duty. He opened the door on the stroke of two and found him curled up asleep. He got his horsewhip and hit him across the face. Now, instead of standing up and begging for forgiveness, the man grabbed his master by the legs, shook him, and cried out, ‘Throw away that whip or I’ll eat you up.’ Those are the facts. The captain came to me an hour ago. I wrote up his statement and right after that the sentence. Then I had the man chained up. It was all very simple. If I had first summoned the man and interrogated him, the result would have been confusion. He would have lied, and if I had been successful in refuting his lies, he would have replaced them with new lies, and so forth. But now I have him, and I won’t release him again. Now, does that clarify everything? But time is passing. We should be starting the execution, and I haven’t finished explaining the apparatus yet.”

He urged the traveler to sit down in his chair, moved to the apparatus again, and started, “As you see, the shape of the harrow corresponds to the shape of a man. This is the harrow for the upper body, and here are the harrows for the legs. This small cutter is the only one designated for the head. Is that clear to you?” He leaned forward to the Traveler in a friendly way, ready to give the most comprehensive explanation.

The Traveler looked at the harrow with a wrinkled frown. The information about the judicial procedures had not satisfied him. However, he had to tell himself that here it was a matter of a penal colony, that in this place special regulations were necessary, and that one had to give precedence to military measures right down to the last detail. Beyond that, however, he had some hopes in the New Commandant, who obviously, although slowly, was intending to introduce a new procedure which the limited understanding of this Officer could not cope with.

Following this train of thought, the Traveler asked, “Will the Commandant be present at the execution?” “That is not certain,” said the Officer, embarrassingly affected by the sudden question, and his friendly expression made a grimace. “That’s why we need to hurry up. As much as I regret the fact, I’ll have to make my explanation even shorter. But tomorrow, once the apparatus is clean again—the fact that it gets so very dirty is its only fault—I could add a detailed explanation. So now, only the most important things. When the man is lying on the bed and it starts quivering, the harrow sinks onto the body. It positions itself automatically in such a way that it touches the body only lightly with the needle tips. Once the machine is set in this position, this steel cable tightens up into a rod. And now the performance begins. Someone who is not an initiate sees no external difference among the punishments. The harrow seems to do its work uniformly. As it quivers, it sticks the tips of its needles into the body, which is also vibrating from the movement of the bed. Now, to enable someone to check on how the sentence is being carried out, the harrow is made of glass. That gave rise to certain technical difficulties with fastening the needles securely, but after several attempts we were successful. We didn’t spare any efforts. And now, as the inscription is made on the body, everyone can see through the glass. Don’t you want to come closer and see the needles for yourself.”

The Traveler stood slowly, moved up, and bent over the harrow. “You see,” the Officer said, “two sorts of needles in a multiple arrangement. Each long needle has a short one next to it. The long one inscribes, and the short one squirts water out to wash away the blood and keep the inscription always clear. The bloody water is then channeled here in small grooves and finally flows into these main gutters, and the outlet pipe takes it to the pit.” The officer pointed with his finger to the exact path which the bloody water had to take. As he began to demonstrate with both hands at the mouth of the outlet pipe, in order to make his account as clear as possible, the Traveler raised his head and, feeling behind him with his hand, wanted to return to his chair. Then he saw to his horror that the Condemned Man had also, like him, accepted the Officer’s invitation to inspect the arrangement of the harrow up close. He had pulled the sleeping Soldier holding the chain a little forward and was also bending over the glass. One could see how with a confused gaze he also was looking for what the two gentlemen had just observed, but how he didn’t succeed because he lacked the explanation. He leaned forward this way and that. He kept running his eyes over the glass again and again. The Traveler wanted to push him back, for what he was doing was probably punishable. But the Officer held the Traveler firmly with one hand, and with the other he took a lump of earth from the wall and threw it at the Soldier. The latter opened his eyes with a start, saw what the Condemned Man had dared to do, let his weapon fall, braced his heels in the earth, and pulled the Condemned Man back, so that he immediately collapsed. The Soldier looked down at him, as he writhed around, making his chain clink. “Stand him up,” cried the Officer. Then he noticed that the Condemned Man was distracting the Traveler too much. The latter was even leaning out away from the harrow, without paying any attention to it, wanting to find out what was happening to the Condemned Man. “Handle him carefully,” the Officer yelled again. He ran around the apparatus, personally grabbed the Condemned Man under the armpits and, with the help of the Soldier, stood the man, whose feet kept slipping, upright.

“Now I know all about it,” said the Traveler, as the Officer turned back to him again. “Except the most important thing,” said the latter, grabbing the Traveler by the arm and pointing up high. “There in the inscriber is the mechanism which determines the movement of the harrow, and this mechanism is arranged according to the diagram on which the sentence is set down. I still use the diagrams of the previous Commandant. Here they are.” He pulled some pages out of the leather folder. “Unfortunately I can’t hand them to you. They are the most cherished thing I possess. Sit down, and I’ll show you them from this distance. Then you’ll be able to see it all well.” He showed the first sheet. The Traveler would have been happy to say something appreciative, but all he saw was a labyrinthine series of lines, criss-crossing each other in all sort of ways. These covered the paper so thickly that only with difficulty could one make out the white spaces in between. “Read it,” said the Officer. “I can’t,” said the Traveler. “But it’s clear,” said the Officer.” “It’s very elaborate,” said the Traveler evasively, “but I can’t decipher it.”

“Yes,” said the Officer, smiling and putting the folder back again, “it’s not calligraphy for school children. One has to read it a long time. You too will finally understand it clearly. Of course, it has to be a script that isn’t simple. You see, it’s not supposed to kill right away, but on average over a period of twelve hours. The turning point is set for the sixth hour. There must also be many, many embellishments surrounding the basic script. The essential script moves around the body only in a narrow belt. The rest of the body is reserved for decoration. Can you now appreciate the work of the harrow and the whole apparatus? Just look at it!” He jumped up the ladder, turned a wheel, and called down, “Watch out—move to the side!” Everything started moving. If the wheel had not squeaked, it would have been marvelous. The officer threatened the wheel with his fist, as if he was surprised by the disturbance it created. Then he spread his arms, apologizing to the traveler, and quickly clambered down, in order to observe the operation of the apparatus from below.

Something was still not working properly, something only he noticed. He clambered up again and reached with both hands into the inside of the inscriber. Then, in order to descend more quickly, instead of using the ladder, he slid down on one of the poles and, to make himself understandable through the noise, strained his voice to the limit as he yelled in the traveler’s ear, “Do you understand the process? The harrow is starting to write. When it’s finished with the first part of the script on the man’s back, the layer of cotton wool rolls and turns the body slowly onto its side to give the harrow a new area. Meanwhile those parts lacerated by the inscription are lying on the cotton wool which, because it has been specially treated, immediately stops the bleeding and prepares the script for a further deepening. Here, as the body continues to rotate, prongs on the edge of the harrow then pull the cotton wool from the wounds, throw it into the pit, and the harrow goes to work again. In this way it keeps making the inscription deeper for twelve hours. For the first six hours the condemned man goes on living almost as before. He suffers nothing but pain. After two hours, the felt is removed, for at that point the man has no more energy for screaming. Here at the head of the bed warm rice pudding is put in this electrically heated bowl. From this the man, if he feels like it, can help himself to what he can lap up with his tongue. No one passes up this opportunity. I don’t know of a single one, and I have had a lot of experience. He first loses his pleasure in eating around the sixth hour. I usually kneel down at this point and observe the phenomenon. The man rarely swallows the last bit. He turns it around in his mouth and spits it into the pit. When he does that, I have to lean aside or else he’ll get me in the face. But how quiet the man becomes around the sixth hour! The most stupid of them begin to understand. It starts around the eyes and spreads out from there. A look that could tempt one to lie down under the harrow. Nothing else happens. The man simply begins to decipher the inscription. He purses his lips, as if he is listening. You’ve seen that it’s not easy to figure out the inscription with your eyes, but our man deciphers it with his wounds. True, it takes a lot of work. It requires six hours to complete. But then the harrow spits him right out and throws him into the pit, where he splashes down into the bloody water and cotton wool. Then the judgment is over, and we, the soldier and I, quickly bury him.”

The Traveler had leaned his ear towards the Officer and, with his hands in his coat pockets, was observing the machine at work. The Condemned Man was also watching, but without understanding. He bent forward a little and followed the moving needles, as the Soldier, after a signal from the Officer, cut through his shirt and trousers with a knife from the back, so that they fell off the Condemned Man. He wanted to grab the falling garments to cover his bare flesh, but the Soldier held him up and shook the last rags from him. The Officer turned the machine off, and in the silence which then ensued the Condemned Man was laid out under the harrow. The chains were taken off and the straps fastened in their place. For the Condemned Man it seemed at first glance to signify almost a relief. And now the harrow sunk down a stage lower, for the Condemned was a thin man. As the needle tips touched him, a shudder went over his skin. While the Soldier was busy with the right hand, the Condemned Man stretched out his left, with no sense of its direction. But it was pointing to where the Traveler was standing. The Officer kept looking at the Traveler from the side, without taking his eyes off him, as if he was trying to read from his face the impression he was getting of the execution, which he had now explained to him, at least superficially.

The strap meant to hold the wrist ripped off. The Soldier probably had pulled on it too hard. The Soldier showed the Officer the torn-off piece of strap, wanting him to help. So the Officer went over to him and said, with his face turned towards the Traveler, “The machine is very complicated. Now and then something has to tear or break. One shouldn’t let that detract from one’s overall opinion. Anyway, we have an immediate replacement for the strap. I’ll use a chain—even though that will affect the sensitivity of the movements for the right arm.” And while he put the chain in place, he kept talking, “Our resources for maintaining the machine are very limited at the moment. Under the previous Commandant, I had free access to a cash box specially set aside for this purpose. There was a store room here in which all possible replacement parts were kept. I admit I made almost extravagant use of it. I mean earlier, not now, as the New Commandant claims. For him everything serves only as a pretext to fight against the old arrangements. Now he keeps the cash box for machinery under his own control, and if I ask him for a new strap, he demands the torn one as a piece of evidence, the new one doesn’t arrive for ten days, and it’s an inferior brand, of not much use to me. But how I am supposed to get the machine to work in the meantime without a strap—no one’s concerned about that.”

The Traveler was thinking: it’s always questionable to intervene decisively in strange circumstances. He was neither a citizen of the penal colony nor a citizen of the state to which it belonged. If he wanted to condemn the execution or even hinder it, people could say to him: You’re a foreigner—keep quiet. He would have nothing in response to that, but could only add that he did not understand what he was doing on this occasion, for the purpose of his traveling was merely to observe and not to alter other people’s judicial systems in any way. True, at this point the way things were turning out it was very tempting. The injustice of the process and the inhumanity of the execution were beyond doubt. No one could assume that the Traveler was acting out of any sense of his own self-interest, for the Condemned Man was a stranger to him, not a countryman and not someone who invited sympathy in any way. The Traveler himself had letters of reference from high officials and had been welcomed here with great courtesy. The fact that he had been invited to this execution even seemed to indicate that people were asking for his judgment of this trial. This was all the more likely since the Commandant, as he had now heard only too clearly, was no supporter of this process and maintained an almost hostile relationship with the Officer.

Then the Traveler heard a cry of rage from the Officer. He had just shoved the stub of felt in the Condemned Man’s mouth, not without difficulty, when the Condemned Man, overcome by an irresistible nausea, shut his eyes and threw up. The Officer quickly yanked him up off the stump and wanted to turn his head aside toward the pit. But it was too late. The vomit was already flowing down onto the machine. “This is all the Commandant’s fault!” cried the officer and mindlessly rattled the brass rods at the front. “My machine’s as filthy as a pigsty.” With trembling hands he showed the Traveler what had happened. “Haven’t I spent hours trying to make the Commandant understand that a day before the execution there should be no more food served. But the new lenient administration has a different opinion. Before the man is led away, the Commandant’s women cram sugary things down his throat. His whole life he’s fed himself on stinking fish, and now he has to eat sweets! But that would be all right—I’d have no objections—but why don’t they get a new felt, the way I’ve been asking him for three months now? How can anyone take this felt into his mouth without feeling disgusted—something that a hundred man have sucked and bitten on it as they were dying?”

The Condemned Man had laid his head down and appeared peaceful. The Soldier was busy cleaning up the machine with the Condemned Man’s shirt. The Officer went up to the Traveler, who, feeling some premonition, took a step backwards. But the Officer grasped him by the hand and pulled him aside. “I want to speak a few words to you in confidence,” he said. “May I do that?” “Of course,” said the Traveler and listened with his eyes lowered.

“This process and execution, which you now have an opportunity to admire, have no more open supporters in our colony. I am its only defender, just as I am the single advocate for the legacy of the Old Commandant. I can no longer think about a more extensive organization of the process—I’m using all my powers to maintain what there is at present. When the Old Commandant was alive, the colony was full of his supporters. I have something of the Old Commandant’s power of persuasion, but I completely lack his power, and as a result the supporters have gone into hiding. There are still a lot of them, but no one admits to it. If you go into a tea house today—that is to say, on a day of execution—and keep your ears open, perhaps you’ll hear nothing but ambiguous remarks. They are all supporters, but under the present Commandant, considering his present views, they are totally useless to me. And now I’m asking you: Should such a life’s work,” he pointed to the machine, “come to nothing because of this Commandant and the women influencing him? Should people let that happen? Even if one is a foreigner and only on our island for a couple of days? But there’s no time to lose. People are already preparing something against my judicial proceedings. Discussions are already taking place in the Commandant’s headquarters, to which I am not invited. Even your visit today seems to me typical of the whole situation. People are cowards and send you out—a foreigner. You should have seen the executions in earlier days! The entire valley was overflowing with people, even a day before the execution. They all came merely to watch. Early in the morning the Commandant appeared with his women. Fanfares woke up the entire campsite. I delivered the news that everything was ready. The whole society—and every high official had to attend—arranged itself around the machine. This pile of cane chairs is a sorry left over from that time. The machine was freshly cleaned and glowed. For almost every execution I had new replacement parts. In front of hundreds of eyes—all the spectators stood on tip toe right up to the hills there—the condemned man was laid down under the harrow by the Commandant himself. What nowadays is done by a common soldier was then my work as the senior judge, and it was a honour for me. And then the execution began! No discordant note disturbed the work of the machine. Many people did not look any more at all, but lay down with closed eyes in the sand. They all knew: now justice was being carried out. In silence people listened to nothing but the groans of the condemned man, muffled by the felt. These days the machine no longer manages to squeeze a strong groan out of the condemned man—something the felt is not capable of smothering. But back then the needles which made the inscription dripped a caustic liquid which we are not permitted to use any more today. Well, then came the sixth hour. It was impossible to grant all the requests people made to be allowed to watch from up close. The Commandant, in his wisdom, arranged that the children should be taken care of before all the rest. Naturally, I was always allowed to stand close by, because of my official position. Often I crouched down there with two small children in my arms, on my right and left. How we all took in the expression of transfiguration on the martyred face! How we held our cheeks in the glow of this justice, finally attained and already passing away! What times we had, my friend!”

The Officer had obviously forgotten who was standing in front of him. He had put his arm around the Traveler and laid his head on his shoulder. The Traveler was extremely embarrassed. Impatiently he looked away over the Officer’s head. The Soldier had ended his task of cleaning and had just shaken some rice pudding into the bowl from a tin. No sooner had the Condemned Man, who seemed to have fully recovered already, noticed this than his tongue began to lick at the pudding. The Soldier kept pushing him away, for the pudding was probably meant for a later time, but in any case it was not proper for the Soldier to reach in and grab some food with his dirty hands and eat it in front of the famished Condemned Man.

The Officer quickly collected himself. “I didn’t want to upset you in any way,” he said. “I know it is impossible to make someone understand those days now. Besides, the machine still works and operates on its own. It operates on its own even when it is standing alone in this valley. And at the end, the body still keeps falling in that incredibly soft flight into the pit, even if hundreds of people are not gathered like flies around the hole the way they used to be. Back then we had to erect a strong railing around the pit. It was pulled out long ago.”

The Traveler wanted to turn his face away from the Officer and looked aimlessly around him. The Officer thought he was looking at the wasteland of the valley. So he grabbed his hands, turned him around in order to catch his gaze, and asked, “Do you see the shame of it?”

But the Traveler said nothing. The Officer left him alone for a while. With his legs apart and his hands on his hips, the Officer stood still and looked at the ground. Then he smiled at the Traveler cheerfully and said, “Yesterday I was nearby when the Commandant invited you. I heard the invitation. I know the Commandant. I understood right away what he intended with his invitation. Although his power might be sufficiently great to take action against me, he doesn’t yet dare to. But my guess is that with you he is exposing me to the judgment of a respected foreigner. He calculates things with care. You are now in your second day on the island. You didn’t know the Old Commandant and his way of thinking. You are trapped in a European way of seeing things. Perhaps you are fundamentally opposed to the death penalty in general and to this kind of mechanical style of execution in particular. Moreover, you see how the execution is a sad procedure, without any public participation, using a partially damaged machine. Now, if we take all this together (so the Commandant thinks) surely one could easily imagine that that you would not consider my procedure proper? And if you didn’t consider it right, you wouldn’t keep quiet about it—I’m still speaking the mind of the Commandant—for you no doubt have faith that your tried-and-true convictions are correct. It’s true that you have seen many peculiar things among many peoples and have learned to respect them. Thus, you will probably not speak out against the procedure with your full power, as you would perhaps in your own homeland. But the Commandant doesn’t really need that. A casual word, merely a careless remark, is enough. It doesn’t have to match your convictions at all, so long as it corresponds to his wishes. I’m certain he will use all his shrewdness to interrogate you. And his women will sit around in a circle and perk up their ears. You will say something like, ‘Among us the judicial procedures are different,’ or ‘With us the accused is questioned before the verdict,’ or ‘We had torture only in the Middle Ages.’ For you these observations appear as correct as they are self-evident—innocent remarks which do not impugn my procedure. But how will the Commandant take them? I see him, our excellent Commandant—the way he immediately pushes his stool aside and hurries out to the balcony—I see his women, how they stream after him. I hear his voice—the women call it a thunder voice. And now he’s speaking: ‘A great Western explorer who has been commissioned to inspect judicial procedures in all countries has just said that our process based on old customs is inhuman. After the verdict of such a personality it is, of course, no longer possible for me to tolerate this procedure. So from this day on I am ordering . . . and so forth.’ You want to intervene—you didn’t say what he is reporting—you didn’t call my procedure inhuman; by contrast, in keeping with your deep insight, you consider it most humane and most worthy of human beings. You also admire this machinery. But it is too late. You don’t even go onto the balcony, which is already filled with women. You want to attract attention. You want to cry out. But a lady’s hand is covering your mouth, and I and the Old Commandant’s work are lost.”

The Traveler had to suppress a smile. So the work which he had considered so difficult was easy. He said evasively, “You’re exaggerating my influence. The Commandant has read my letters of recommendation. He knows that I am no expert in judicial processes. If I were to express an opinion, it would be that of a lay person, no more significant than the opinion of anyone else, and in any case far less significant than the opinion of the Commandant, who, as I understand it, has very extensive powers in this penal colony. If his views of this procedure are as definite as you think they are, then I’m afraid the time has come for this procedure to end, without any need for my humble opinion.”

Did the Officer understand by now? No, he did not yet get it. He shook his head vigorously, briefly looked back at the Condemned Man and the Soldier, who both flinched and stopped eating the rice, went up really close up to the Traveler, without looking into his face, but gazing at parts of his jacket, and said more gently than before: “You don’t know the Commandant. Where he and all of us are concerned you are—forgive the expression—to a certain extent innocent. Your influence, believe me, cannot be overestimated. In fact, I was blissfully happy when I heard that you were to be present at the execution by yourself. This order of the Commandant was aimed at me, but now I’ll turn it to my advantage. Without being distracted by false insinuations and disparaging looks—which could not have been avoided with a greater number of participants at the execution—you have listened to my explanation, looked at the machine, and are now about to view the execution. Your verdict is no doubt already fixed. If some small uncertainties remain, witnessing the execution will remove them. And now I’m asking you—help me with the Commandant!”

The Traveler did not let him go on talking. “How can I do that,” he cried. “It’s totally impossible. I can help you as little as I can harm you.”

“You could do it,” said the Officer. With some apprehension the Traveler observed that the Officer was clenching his fists. “You could do it,” repeated the Officer, even more emphatically. “I have a plan which must succeed. You think your influence is insufficient. I know it will be enough. But assuming you’re right, doesn’t saving this whole procedure require one to try even those methods which may be inadequate? So listen to my plan. To carry it out, it’s necessary, above all, for you to keep as quiet as possible today in the colony about your verdict on this procedure. Unless someone asks you directly, you should not express any view whatsoever. But what you do say must be short and vague. People should notice that it’s difficult for you to speak about the subject, that you feel bitter, that, if you were to speak openly, you’d have to burst out cursing on the spot. I’m not asking you to lie, not at all. You should only give brief answers—something like, ‘Yes, I’ve seen the execution’ or ‘Yes, I’ve heard the full explanation.’ That’s all—nothing further. For that will be enough of an indication for people to observe in you a certain bitterness, even if that’s not what the Commandant will think. Naturally, he will completely misunderstand the issue and interpret it in his own way. My plan is based on that. Tomorrow a large meeting of all the higher administrative officials takes place at headquarters under the chairmanship of the Commandant. He, of course, understands how to turn such a meeting into a spectacle. A gallery has been built, which is always full of spectators. I’m compelled to take part in the discussions, though they fill me with disgust. In any case, you will certainly be invited to the meeting. If you follow my plan today and behave accordingly, the invitation will become an emphatic request. But should you for some inexplicable reason still not be invited, you must make sure you request an invitation. Then you’ll receive one without question. Now, tomorrow you are sitting with the women in the commandant’s box. With frequent upward glances he reassures himself that you are there. After various trivial and ridiculous agenda items designed for the spectators—mostly harbour construction—always harbour construction—the judicial process comes up for discussion. If it’s not raised by the Commandant himself or does not occur soon enough, I’ll make sure that it comes up. I’ll stand up and report on today’s execution. Really briefly—just the report. Such a report is not really customary; however, I’ll do it, nonetheless. The Commandant thanks me, as always, with a friendly smile. And now he cannot restrain himself. He seizes this excellent opportunity. ‘The report of the execution,’ he’ll say, or something like that, ‘has just been given. I would like to add to this report only the fact that this particular execution was attended by the great explorer whose visit confers such extraordinary honour on our colony, as you all know. Even the significance of our meeting today has been increased by his presence. Should we not now ask this great explorer for his appraisal of the execution based on old customs and of the process which preceded it?’ Of course, there is the noise of applause everywhere, universal agreement. And I’m louder than anyone. The Commandant bows before you and says, ‘Then in everyone’s name, I’m putting the question to you.’ And now you step up to the railing. Place your hands where everyone can see them. Otherwise the ladies will grab them and play with your fingers. And now finally come your remarks. I don’t know how I’ll bear the tension up to then. In your speech you mustn’t hold back. Let truth resound. Lean over the railing and shout it out—yes, yes, roar your opinion at the Commandant, your unshakeable opinion. But perhaps you don’t want to do that. It doesn’t suit your character. Perhaps in your country people behave differently in such situations. That’s all right. That’s perfectly satisfactory. Don’t stand up at all. Just say a couple of words. Whisper them so that only the officials underneath you can just hear them. That’s enough. You don’t even have to say anything at all about the lack of attendance at the execution or about the squeaky wheel, the torn strap, the disgusting felt. No. I’ll take over all further details, and, believe me, if my speech doesn’t chase him out of the room, it will force him to his knees, so he’ll have to admit it: ‘Old Commandant, I bow down before you.’ That’s my plan. Do you want to help me carry it out? But, of course, you want to. More than that—you have to.”

And the officer gripped the traveler by both arms and looked at him, breathing heavily into his face. He had yelled the last sentences so loudly that even the Soldier and the Condemned Man were paying attention. Although they couldn’t understand a thing, they stopped eating and looked over at the Traveler, still chewing.

From the start the Traveler had had no doubts about the answer he must give. He had experienced too much in his life to be able to waver here. Basically he was honest and unafraid. Still, with the Soldier and the Condemned Man looking at him, he hesitated a moment. But finally he said, as he had to, “No.” The Officer’s eyes blinked several times, but he did not take his eyes off the Traveler. “Would you like an explanation,” asked the Traveler. The Officer nodded dumbly. “I am opposed to this procedure,” said the Traveler. “Even before you took me into your confidence—and, of course, I will never abuse your confidence under any circumstances—I was already thinking about whether I was entitled to intervene against this procedure and whether my intervention could have the smallest chance of success. And if that was the case, it was clear to me whom I had to turn to first of all—naturally, to the Commandant. You clarified the issue for me even more, but without reinforcing my decision in any way—quite the reverse. I find your conviction genuinely moving, even if it cannot deter me.”

The Officer remained quiet, turned toward the machine, grabbed one of the brass rods, and then, leaning back a little, looked up at the inscriber, as if he was checking that everything was in order. The Soldier and the Condemned Man seemed to have made friends with each other. The Condemned Man was making signs to the Soldier, although, given the tight straps on him, this was difficult for him to do. The Soldier was leaning into him. The Condemned Man whispered something to him, and the Soldier nodded. The Traveler went over to the Officer and said, “You don’t yet know what I’ll do. Yes, I will tell the Commandant my opinion of the procedure—not in a meeting, but in private. In addition, I won’t stay here long enough to be able to get called in to some meeting or other. Early tomorrow morning I leave, or at least I go on board ship.” It didn’t look as if the Officer had been listening. “So the process has not convinced you,” he said to himself, smiling the way an old man smiles over the silliness of a child, concealing his own true thoughts behind that smile.

“Well then, it’s time,” he said finally and suddenly looked at the Traveler with bright eyes which contained some sort of demand, some appeal for participation. “Time for what?” asked the Traveler uneasily. But there was no answer.

“You are free,” the Officer told the Condemned Man in his own language. At first the man did not believe him. “You are free now,” said the Officer. For the first time the face of the Condemned Man showed signs of real life. Was it the truth? Was it only the Officer’s mood, which could change? Had the foreign Traveler brought him a reprieve? What was it? That’s what the man’s face seemed to be asking. But not for long. Whatever the case might be, if he could he wanted to be truly free, and he began to shake back and forth, as much as the harrow permitted.

“You’re tearing my straps,” cried the Officer. “Be still! We’ll undo them right away.” And, giving a signal to the Soldier, he set to work with him. The Condemned Man said nothing and smiled slightly to himself. He turned his face to the Officer and then to the Soldier and then back again, without ignoring the Traveler.

“Pull him out,” the Officer ordered the Soldier. This process required a certain amount of care because of the harrow. The Condemned Man already had a few small wounds on his back, thanks to his own impatience.

From this point on, however, the Officer paid him hardly any attention. He went up to the Traveler, pulled out the small leather folder once more, leafed through it, finally found the sheet he was looking for, and showed it to the Traveler. “Read that,” he said. “I can’t,” said the Traveler. “I’ve already told you I can’t read these pages.” “But take a close look at the page,” said the Officer, and moved up right next to the Traveler in order to read with him. When that didn’t help, he raised his little finger high up over the paper, as if the page must not be touched under any circumstances, so that using this he might make the task of reading easier for the Traveler. The Traveler also made an effort so that at least he could satisfy the Officer, but it was impossible for him. Then the Officer began to spell out the inscription and then read out once again the joined up letters. “‘Be just!’ it states,” he said. “Now you can read it.” The Traveler bent so low over the paper that the Officer, afraid that he might touch it, moved it further away. The Traveler didn’t say anything more, but it was clear that he was still unable to read anything. “ ‘Be just!’ it says,” the Officer remarked once again.

“That could be,” said the Traveler. “I do believe that’s written there.” “Good,” said the Officer, at least partially satisfied. He climbed up the ladder, holding the paper. With great care he set the page in the inscriber and appeared to rotate the gear mechanism completely around. This was very tiring work. It must have required him to deal with extremely small wheels. He had to inspect the gears so closely that sometimes his head disappeared completely into the inscriber.

The Traveler followed this work from below without looking away. His neck grew stiff, and his eyes found the sunlight pouring down from the sky painful. The Soldier and the Condemned Man were keeping each other busy. With the tip of his bayonet the Soldier pulled out the Condemned Man’s shirt and trousers which were lying in the hole. The shirt was horribly dirty, and the Condemned Man washed it in the bucket of water. When he was putting on his shirt and trousers, the Soldier and the Condemned Man had to laugh out loud, for the pieces of clothing were cut in two up the back. Perhaps the Condemned Man thought that it was his duty to amuse the Soldier. In his ripped-up clothes he circled around the Soldier, who crouched down on the ground, laughed, and slapped his knees. But they restrained themselves out of consideration for the two gentlemen present.

When the Officer was finally finished up on the machine, with a smile he looked over the whole thing and all its parts one more time, and this time closed the cover of the inscriber, which had been open up to this point. He climbed down, looked into the hole and then at the Condemned Man, observed with satisfaction that he had pulled out his clothes, then went to the bucket of water to wash his hands, recognized too late that it was disgustingly dirty, and was upset that now he couldn’t wash his hands. Finally he pushed them into the sand. This option didn’t satisfy him, but he had to do what he could in the circumstances. Then he stood up and began to unbutton the coat of his uniform. As he did this, the two lady’s handkerchiefs, which he had pushed into the back of his collar, fell into his hands. “Here you have your handkerchiefs,” he said and threw them over to the Condemned Man. And to the Traveler he said by way of an explanation, “Presents from the ladies.”

In spite of the obvious speed with which he took off the coat of his uniform and then undressed himself completely, he handled each piece of clothing very carefully, even running his fingers over the silver braids on his tunic with special care and shaking a tassel into place. But in great contrast to this care, as soon he was finished handling an article of clothing, he immediately flung it angrily into the hole. The last items he had left were his short sword and its harness. He pulled the sword out of its scabbard, broke it in pieces, gathered up everything—the pieces of the sword, the scabbard, and the harness—and threw them away so forcefully that they rattled against each other down in the pit.

Now he stood there naked. The Traveler bit his lip and said nothing. For he was aware what would happen, but he had no right to hinder the Officer in any way. If the judicial process to which the officer clung was really so close to the point of being cancelled—perhaps as a result of the intervention of the Traveler, something to which he for his part felt duty-bound—then the Officer was now acting in a completely correct manner. In his place, the Traveler would not have acted any differently.

The Soldier and the Condemned Man at first didn’t understand a thing. To begin with they didn’t look, not even once. The Condemned Man was extremely happy to get the handkerchiefs back, but he couldn’t enjoy them very long, for the Soldier snatched them from him with a quick grab, which he had not anticipated. The Condemned Man then tried to pull the handkerchiefs out from the Soldier’s belt, where he had put them for safe keeping, but the Soldier was too wary. So they were fighting, half in jest. Only when the Officer was fully naked did they start to pay attention. The Condemned Man especially seemed to be struck by a premonition of some sort of significant transformation. What had happened to him was now taking place with the Officer. Perhaps this time the procedure would play itself out to its conclusion. The foreign Traveler had probably given the order. So that was revenge. Without having suffered all the way to the end himself, nonetheless he would be completely revenged. A wide, silent laugh now appeared on his face and did not go away.

The Officer, however, had turned towards the machine. If earlier on it had already become clear that he understood the machine thoroughly, one might well get alarmed now at the way he handled it and how it obeyed. He only had to bring his hand near the harrow for it to rise and sink several times, until it had reached the correct position to make room for him. He only had to grasp the bed by the edges, and it already began to quiver. The stump of felt moved up to his mouth. One could see how the Officer really didn’t want to accept it, but his hesitation was only momentary—he immediately submitted and took it in. Everything was ready, except that the straps still hung down on the sides. But they were clearly unnecessary. The Officer did not have to be strapped down. When the Condemned Man saw the loose straps, he thought the execution would be incomplete unless they were fastened. He waved eagerly to the Soldier, and they ran over to strap in the Officer. The latter had already stuck out his foot to kick the crank designed to set the inscriber in motion. Then he saw the two men coming. So he pulled his foot back and let himself be strapped in. But now he could no longer reach the crank. Neither the Soldier nor the Condemned Man would find it, and the Traveler was determined not to touch it. But that was unnecessary. Hardly were the straps attached when the machine already started working. The bed quivered, the needles danced on his skin, and the harrow swung up and down. The Traveler had already been staring for some time before he remembered that a wheel in the inscriber was supposed to squeak. But everything was quiet, without the slightest audible hum.

Because of its silent working, the machine did not really attract attention. The Traveler looked over at the Soldier and the Condemned Man. The Condemned Man was the livelier of the two. Everything in the machine interested him. At times he bent down—at other times he stretched up, all the time pointing with his forefinger in order to show something to the Soldier. For the Traveler it was embarrassing. He was determined to remain here until the end, but he could no longer endure the sight of the two men. “Go home,” he said. The Soldier might have been ready to do that, but the Condemned Man took the order as a direct punishment. With his hands folded he begged and pleaded to be allowed to stay there. And when the Traveler shook his head and was unwilling to give in, he even knelt down. Seeing that orders were of no help here, the Traveler wanted to go over and chase the two away.

Then he heard a noise from up in the inscriber. He looked up. So was the gear wheel going out of alignment? But it was something else. The lid on the inscriber was lifting up slowly. Then it fell completely open. The teeth of a cog wheel were exposed and lifted up. Soon the entire wheel appeared. It was as if some huge force was compressing the inscriber, so that there was no longer sufficient room for this wheel. The wheel rolled all the way to the edge of the inscriber, fell down, rolled upright a bit in the sand, and then fell over and lay still. But already up on the inscriber another gear wheel was moving upwards. Several others followed—large ones, small ones, ones hard to distinguish. With each of them the same thing happened. One kept thinking that now the inscriber must surely be empty, but then a new cluster with lots of parts would move up, fall down, roll in the sand, and lie still. With all this going on, the Condemned Man totally forgot the Traveler’s order. The gear wheels completely delighted him. He kept wanting to grab one, and at the same time he was urging the Soldier to help him. But he kept pulling his hand back startled, for immediately another wheel followed, which, at least in its initial rolling, surprised him.

The Traveler, by contrast, was very upset. Obviously the machine was breaking up. Its quiet operation had been an illusion. He felt as if he had to look after the Officer, now that the latter could no longer look after himself. But while the falling gear wheels were claiming all his attention, he had neglected to look at the rest of the machine. However, when he now bent over the harrow, once the last gear wheel had left the inscriber, he had a new, even more unpleasant surprise. The harrow was not writing but only stabbing, and the bed was not rolling the body, but lifting it, quivering, up into the needles. The Traveler wanted to reach in to stop the whole thing, if possible. This was not the torture the Officer wished to attain. It was murder, pure and simple. He stretched out his hands. But at that point the harrow was already moving upwards and to the side, with the skewered body—just as it did in other cases, but only in the twelfth hour. Blood flowed out in hundreds of streams, not mixed with water—the water tubes had also failed to work this time. Then one last thing went wrong: the body would not come loose from the needles. Its blood streamed out, but it hung over the pit without falling. The harrow wanted to move back to its original position, but, as if it realized that it could not free itself of its load, it remained over the hole.

“Help,” the Traveler yelled out to the Soldier and the Condemned Man and grabbed the Officer’s feet. He wanted to push against the feet himself and have the two others grab the Officer’s head from the other side, so he could be slowly taken off the needles. But now the two men could not make up their mind whether to come or not. The Condemned Man turned away at once. The Traveler had to go over to him and drag him to the Officer’s head by force. At this point, almost against his will, he looked at the face of the corpse. It was as it had been in his life. He could discover no sign of the promised transfiguration. What all the others had found in the machine, the Officer had not. His lips were pressed firmly together, his eyes were open and looked as they had when he was alive, his gaze was calm and convinced. The tip of a large iron needle had gone through his forehead.



As the Traveler, with the Soldier and the Condemned Man behind him, came to the first houses in the colony, the Soldier pointed to one and said, “That’s the tea house.”

On the ground floor of one of the houses was a deep, low room, like a cave, with smoke-covered walls and ceiling. On the street side it was open along its full width. Although there was little difference between the tea house and the rest of the houses in the colony, which were all very dilapidated, except for the Commandant’s palatial structure, the Traveler was struck by the impression of historical memory, and he felt the power of earlier times. Followed by his companions, he walked closer, going between the unoccupied tables, which stood in the street in front of the tea house, and took a breath of the cool, stuffy air which came from inside. “The old man is buried here,” said the soldier; “a place in the cemetery was denied him by the chaplain. For a long time people were undecided where they should bury him. Finally they buried him here. Of course, the Officer explained none of that to you, for naturally he was the one most ashamed about it. A few times he even tried to dig up the old man at night, but he was always chased off.” “Where is the grave?” asked the Traveler, who could not believe the Soldier. Instantly both men, the Soldier and the Condemned Man, ran in front of him and with hands outstretched pointed to the place where the grave was located. They led the Traveler to the back wall, where guests were sitting at a few tables. They were presumably dock workers, strong men with short, shiny, black beards. None of them wore coats, and their shirts were torn. They were poor, oppressed people. As the Traveler came closer, a few got up, leaned against the wall, and looked at him. A whisper went up around the Traveler—“It’s a foreigner. He wants to look at the grave.” They pushed one of the tables aside, under which there was a real grave stone. It was a simple stone, low enough for it to remain hidden under a table. It bore an inscription in very small letters. In order to read it the Traveler had to kneel down. It read, “Here rests the Old Commandant. His followers, who are now not permitted to have a name, buried him in this grave and erected this stone. There exists a prophecy that the Commandant will rise again after a certain number of years and from this house will lead his followers to a re-conquest of the colony. Have faith and wait!”

When the Traveler had read it and got up, he saw the men standing around him and smiling, as if they had read the inscription with him, found it ridiculous, and were asking him to share their opinion. The Traveler acted as if he hadn’t noticed, distributed some coins among them, waited until the table was pushed back over the grave, left the tea house, and went to the harbour.

In the tea house the Soldier and the Condemned Man had come across some people they knew who detained them. However, they must have broken free of them soon, because by the time the Traveler found himself in the middle of a long staircase which led to the boats, they were already running after him. They probably wanted to force the Traveler at the last minute to take them with him. While the Traveler was haggling at the bottom of the stairs with a sailor about his passage out to the steamer, the two men were racing down the steps in silence, for they didn’t dare cry out. But as they reached the bottom, the Traveler was already in the boat, and the sailor at once cast off from shore. They could still have jumped into the boat, but the Traveler picked up a heavy knotted rope from the boat bottom, threatened them with it, and thus prevented them from jumping in. 

tests/toydata/olleh.wav METASEP

tests/toydata/mov_bbb.mp4 METASEP

tests/toydata/hello.wav METASEP

tests/toydata/docs_groundtruth.jsonlines METASEP
{"document": {"text": "a"}, "groundtruth": {"text": "b"}}
{"document": {"text": "c"}, "groundtruth": {"text": "d"}}

tests/toydata/docs.jsonlines METASEP
{"text": "a"}
{"text": "b"}

tests/toydata/docs.csv METASEP
source,url,question,answer,wrong_answer
testsrc,https://jina.ai,What are the symptoms?,Symptoms ...,As cases ...
testsrc,https://jina.ai,When should I get tested?,Your doctor ...,If you ...

tests/toydata/cube.ply METASEP
ply
format ascii 1.0
comment created by platoply
element vertex 8
property float32 x
property float32 y
property float32 z
element face 6
property list uint8 int32 vertex_indices
end_header
-1 -1 -1 
1 -1 -1 
1 1 -1 
-1 1 -1 
-1 -1 1 
1 -1 1 
1 1 1 
-1 1 1 
4 0 1 2 3 
4 5 4 7 6 
4 6 2 1 5 
4 3 7 4 0 
4 7 3 2 6 
4 5 1 0 4 

docs/html_extra/robots.txt METASEP
User-agent: *
sitemap: https://docarray.jina.ai/sitemap.xml
docs/_templates/page.html METASEP
{% extends "base.html" %}

{% block body -%}
{{ super() }}
{% include "partials/icons.html" %}

<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
<label class="overlay sidebar-overlay" for="__navigation">
    <div class="visually-hidden">Hide navigation sidebar</div>
</label>
<label class="overlay toc-overlay" for="__toc">
    <div class="visually-hidden">Hide table of contents sidebar</div>
</label>

{% if theme_announcement -%}
<div class="announcement">
    <aside class="announcement-content">
        {% block announcement %} {{ theme_announcement }} {% endblock announcement %}
    </aside>
</div>
{%- endif %}

<div class="page">
    <header class="mobile-header">
        <div class="header-left">
            <label class="nav-overlay-icon" for="__navigation">
                <div class="visually-hidden">Toggle site navigation sidebar</div>
                <i class="icon">
                    <svg>
                        <use href="#svg-menu"></use>
                    </svg>
                </i>
            </label>
        </div>
        <div class="header-center">
            <a href="{{ pathto(master_doc) }}">
                <div class="brand">{{ docstitle if docstitle else project }}</div>
            </a>
        </div>
        <div class="header-right">
            <div class="theme-toggle-container theme-toggle-header">
                <button class="theme-toggle">
                    <div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
                    <svg class="theme-icon-when-auto">
                        <use href="#svg-sun-half"></use>
                    </svg>
                    <svg class="theme-icon-when-dark">
                        <use href="#svg-moon"></use>
                    </svg>
                    <svg class="theme-icon-when-light">
                        <use href="#svg-sun"></use>
                    </svg>
                </button>
            </div>
            <label class="toc-overlay-icon toc-header-icon{% if furo_hide_toc %} no-toc{% endif %}" for="__toc">
                <div class="visually-hidden">Toggle table of contents sidebar</div>
                <i class="icon">
                    <svg>
                        <use href="#svg-toc"></use>
                    </svg>
                </i>
            </label>
        </div>
    </header>
    <aside class="sidebar-drawer">
        <div class="sidebar-container">
            {% block left_sidebar %}
            <div class="sidebar-sticky">
                {%- for sidebar_section in sidebars %}
                {%- include sidebar_section %}
                {%- endfor %}
            </div>
            {% endblock left_sidebar %}
        </div>
    </aside>
    <div class="main">
        <div class="content">
            <div class="article-container">
                <a href="#" class="back-to-top muted-link">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                        <path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8v12z"></path>
                    </svg>
                    <span>{% trans %}Back to top{% endtrans %}</span>
                </a>
                <div class="content-icon-container">
                    {#- Edit this page, on GitHub -#}
                    {%- if READTHEDOCS and conf_py_path and page_source_suffix and github_user != "None" and github_repo
                    != "None" and github_version %}
                    <div class="edit-this-page">
                        <a class="muted-link"
                           href="https://github.com/{{ github_user }}/{{ github_repo }}/edit/{{ github_version }}{{ conf_py_path }}{{ pagename }}{{ page_source_suffix }}"
                           title="{{ _(" Edit this page") }}">
                        <svg aria-hidden="true" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
                             stroke-linecap="round" stroke-linejoin="round">
                            <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
                            <path d="M4 20h4l10.5 -10.5a1.5 1.5 0 0 0 -4 -4l-10.5 10.5v4"/>
                            <line x1="13.5" y1="6.5" x2="17.5" y2="10.5"/>
                        </svg>
                        <span class="visually-hidden">{{ _("Edit this page") }}</span>
                        </a>
                    </div>
                    {% endif %}
                    {#- Theme toggle -#}
                    <div class="theme-toggle-container theme-toggle-content">
                        <button class="theme-toggle">
                            <div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
                            <svg class="theme-icon-when-auto">
                                <use href="#svg-sun-half"></use>
                            </svg>
                            <svg class="theme-icon-when-dark">
                                <use href="#svg-moon"></use>
                            </svg>
                            <svg class="theme-icon-when-light">
                                <use href="#svg-sun"></use>
                            </svg>
                        </button>
                    </div>
                    <label class="toc-overlay-icon toc-content-icon{% if furo_hide_toc %} no-toc{% endif %}"
                           for="__toc">
                        <div class="visually-hidden">Toggle table of contents sidebar</div>
                        <i class="icon">
                            <svg>
                                <use href="#svg-toc"></use>
                            </svg>
                        </i>
                    </label>
                </div>
                <article role="main">
                    {% block content %}{{ body }}{% endblock %}
                </article>
            </div>
            <footer>
                {% block footer %}
                <div class="related-pages">
                    {% if next -%}
                    <a class="next-page" href="{{ next.link }}">
                        <div class="page-info">
                            <div class="context">
                                <span>{{ _("Next") }}</span>
                            </div>
                            <div class="title">{{ next.title }}</div>
                        </div>
                        <svg class="furo-related-icon">
                            <use href="#svg-arrow-right"></use>
                        </svg>
                    </a>
                    {%- endif %}
                    {% if prev -%}
                    <a class="prev-page" href="{{ prev.link }}">
                        <svg class="furo-related-icon">
                            <use href="#svg-arrow-right"></use>
                        </svg>
                        <div class="page-info">
                            <div class="context">
                                <span>{{ _("Previous") }}</span>
                            </div>
                            {% if prev.link == pathto(master_doc) %}
                            <div class="title">{{ _("Home") }}</div>
                            {% else %}
                            <div class="title">{{ prev.title }}</div>
                            {% endif %}
                        </div>
                    </a>
                    {%- endif %}
                </div>
                <div class="bottom-of-page">
                    <div class="left-details">
                        {%- if show_copyright %}
                        <div class="copyright">
                            {%- if hasdoc('copyright') %}
                            {% trans path=pathto('copyright'), copyright=copyright|e -%}
                            <a href="{{ path }}">Copyright</a> &#169; {{ copyright }}
                            {%- endtrans %}
                            {%- else %}
                            {% trans copyright=copyright|e -%}
                            Copyright &#169; {{ copyright }}
                            {%- endtrans %}
                            {%- endif %}
                        </div>
                        {%- endif %}
                        {%- if last_updated -%}
                        <div class="last-updated">
                            {% trans last_updated=last_updated|e -%}
                            Last updated on {{ last_updated }}
                            {%- endtrans -%}
                        </div>
                        {%- endif %}
                    </div>
                    <div class="right-details">
                        <div class="social-btns">
                            <a class='social-btn' href="https://github.com/jina-ai/docarray/" aria-label="GitHub"
                               target="_blank" rel="noreferrer"> <i class="fab fa-github"></i></a>
                            <a class='social-btn' href="https://slack.jina.ai" aria-label="Slack" target="_blank"
                               rel="noreferrer"> <i class="fab fa-slack"></i></a>
                            <a class='social-btn' href="https://youtube.com/c/jina-ai" aria-label="YouTube"
                               target="_blank" rel="noreferrer"> <i class="fab fa-youtube"></i></a>
                            <a class='social-btn' href="https://twitter.com/JinaAI_" aria-label="Twitter"
                               target="_blank" rel="noreferrer"> <i class="fab fa-twitter"></i></a>
                            <a class='social-btn' href="https://www.linkedin.com/company/jinaai/" aria-label="LinkedIn"
                               target="_blank" rel="noreferrer"> <i class="fab fa-linkedin"></i></a>
                        </div>
                    </div>
                </div>
                {% endblock footer %}
            </footer>
        </div>
        <aside class="toc-drawer{% if furo_hide_toc %} no-toc{% endif %}">
            {% block right_sidebar %}
            {% if not furo_hide_toc %}
            <div class="toc-sticky toc-scroll">
                <div class="toc-title-container">
          <span class="toc-title">
            {{ _("Contents") }}
          </span>
                </div>
                <div class="toc-tree-container">
                    <div class="toc-tree">
                        {{ toc }}
                    </div>
                </div>
            </div>
            {% endif %}
            {% endblock right_sidebar %}
        </aside>
    </div>
    <qa-bot
            title="DocArray Bot"
            description="The data structure for unstructured data"
    >
        <template>
            <dl>
                <dt>You can ask questions about our docs. Try:</dt>
                <dd>What is a DocumentArray?</dd>
                <dd>How to use docarray with Jina?</dd>
                <dd>How to find nearest neighbour of Documents?</dd>
            </dl>
        </template>
    </qa-bot>
</div>
<img referrerpolicy="no-referrer-when-downgrade"
     src="https://static.scarf.sh/a.png?x-pxid=2823e771-0e1e-4320-8fde-48bc48e53262"/>
{%- endblock %}
docs/_static/mov_bbb.mp4 METASEP

docs/_static/main.css METASEP
html.loaded-in-iframe #announcement,
html.loaded-in-iframe #sidebar-drawer,
html.loaded-in-iframe footer,
html.loaded-in-iframe #toc-drawer {
  display: none!important;
}

html.loaded-in-iframe .page .main {
    justify-content: center;
}

.sidebar-logo {
    max-width: 50%;
}


table.docutils {
    border: thin;
}

table.docutils td, table.docutils th {
    padding: 1rem 1rem;
}

.highlight {
    background: #f5f5f5;
}

h1, h2, h3 {
    margin-top: 3rem;
}

.highlight-console .highlight {
    background: #00232b !important;
    color: whitesmoke;
}

.highlight-text .highlight {
    background: #00232b !important;
    color: whitesmoke;
}

.highlight-json .highlight {
    background: #00232b !important;
    color: whitesmoke;
}

.highlight-shell .highlight {
    background: #00232b !important;
    color: whitesmoke;
}

.highlight-bash .highlight {
    background: #00232b !important;
    color: whitesmoke;
}

.tab-set > input:checked + label {
    border-color: var(--tabs--label-text--active);
}

.tab-set > input:checked + label:hover {
    border-color: var(--tabs--label-text--active);
}


table code {
    background: var(--color-inline-code-background);
    border: 1px solid var(--color-background-border);
    border-radius: .2em;
    font-size: var(--font-size--small--2);
    padding: .1em .2em;
}

.related-information {
    justify-content: space-between;
}

.social-btn {
    margin: 0 .3em;
}

.social-btn:hover {
    opacity: .5;
}

.social-btns {
    display: inline-block;
}

.announcement {
    background-color: var(--color-brand-primary);
    color: var(--color-background-primary) !important;
}

.announcement a {
    color: inherit;
    text-decoration: none;
}

.announcement a:hover {
    color: inherit;
    text-decoration: underline;
}

.sidebar-ecosys-logo {
    width: 1.2em;
    margin-right: .5em;
    vertical-align: middle
}


body[data-theme="dark"] .only-dark-line {
    display: inline-block !important;
}

body[data-theme="dark"] .only-light-line {
    display: none !important;
}

body[data-theme="light"] .only-light-line {
    display: inline-block !important;
}

body[data-theme="light"] .only-dark-line {
    display: none !important;
}

body[data-theme="auto"] .only-light-line {
    display: inline-block !important;
}

body[data-theme="auto"] .only-dark-line {
    display: none !important;
}

.color-gradient-card {
    background: linear-gradient(270deg, #22c1c3, #fdbb2d);
    background-size: 200% 200%;

    -webkit-animation: AnimationName 30s ease infinite;
    -moz-animation: AnimationName 30s ease infinite;
    animation: AnimationName 30s ease infinite;
}

@-webkit-keyframes AnimationName {
    0%{background-position:0% 50%}
    50%{background-position:100% 50%}
    100%{background-position:0% 50%}
}
@-moz-keyframes AnimationName {
    0%{background-position:0% 50%}
    50%{background-position:100% 50%}
    100%{background-position:0% 50%}
}
@keyframes AnimationName {
    0%{background-position:0% 50%}
    50%{background-position:100% 50%}
    100%{background-position:0% 50%}
}

.version-select {
    font-size: .7em;
    border-radius: 5px;
    cursor: pointer;
    background-color: #fff;
    background-image: linear-gradient(to top, #f9f9f9, #fff 33%);
    border-color: var(--color-background-border);
    height: 1.8em;
    line-height: 1.8em;
    outline: none;
    text-align: center;
    max-width: 7em;
    color: var(--color-foreground-muted);
}
docs/_static/logo-light.svg METASEP
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1400px" height="1400px" viewBox="0 0 1400 1400" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>docarray-logo_text-light</title>
    <g id="docarray-logo_text-light" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="docarray" transform="translate(48.000000, 938.000000)" fill-rule="nonzero">
            <path d="M75.4,217.21 C89.1266667,217.21 101.113333,214.165 111.36,208.075 C121.606667,201.985 129.34,194.203333 134.56,184.73 L134.56,184.73 L134.56,214.6 L161.24,214.6 L161.24,0 L134.56,0 L134.56,84.1 C128.953333,74.82 120.833333,67.3283333 110.2,61.625 C99.5666667,55.9216667 88.0633333,53.07 75.69,53.07 C61.3833333,53.07 48.4783333,56.4533333 36.975,63.22 C25.4716667,69.9866667 16.4333333,79.5083333 9.86,91.785 C3.28666667,104.061667 0,118.32 0,134.56 C0,150.606667 3.28666667,164.913333 9.86,177.48 C16.4333333,190.046667 25.4716667,199.81 36.975,206.77 C48.4783333,213.73 61.2866667,217.21 75.4,217.21 Z M80.62,194.01 C70.76,194.01 61.77,191.641667 53.65,186.905 C45.53,182.168333 39.0533333,175.305 34.22,166.315 C29.3866667,157.325 26.97,146.74 26.97,134.56 C26.97,122.573333 29.3866667,112.133333 34.22,103.24 C39.0533333,94.3466667 45.53,87.58 53.65,82.94 C61.77,78.3 70.76,75.98 80.62,75.98 C90.48,75.98 99.5183333,78.3483333 107.735,83.085 C115.951667,87.8216667 122.476667,94.6366667 127.31,103.53 C132.143333,112.423333 134.56,122.863333 134.56,134.85 C134.56,146.836667 132.143333,157.325 127.31,166.315 C122.476667,175.305 115.951667,182.168333 107.735,186.905 C99.5183333,191.641667 90.48,194.01 80.62,194.01 Z M269.21,217.21 C284.29,217.21 298.016667,213.826667 310.39,207.06 C322.763333,200.293333 332.575,190.675 339.825,178.205 C347.075,165.735 350.7,151.283333 350.7,134.85 C350.7,118.416667 347.171667,104.013333 340.115,91.64 C333.058333,79.2666667 323.44,69.745 311.26,63.075 C299.08,56.405 285.45,53.07 270.37,53.07 C255.29,53.07 241.66,56.405 229.48,63.075 C217.3,69.745 207.681667,79.315 200.625,91.785 C193.568333,104.255 190.04,118.61 190.04,134.85 C190.04,151.283333 193.471667,165.735 200.335,178.205 C207.198333,190.675 216.671667,200.293333 228.755,207.06 C240.838333,213.826667 254.323333,217.21 269.21,217.21 Z M269.21,194.01 C259.736667,194.01 251.036667,191.835 243.11,187.485 C235.183333,183.135 228.851667,176.513333 224.115,167.62 C219.378333,158.726667 217.01,147.803333 217.01,134.85 C217.01,122.09 219.426667,111.263333 224.26,102.37 C229.093333,93.4766667 235.521667,86.855 243.545,82.505 C251.568333,78.155 260.413333,75.98 270.08,75.98 C279.553333,75.98 288.35,78.155 296.47,82.505 C304.59,86.855 311.163333,93.4766667 316.19,102.37 C321.216667,111.263333 323.73,122.09 323.73,134.85 C323.73,147.61 321.168333,158.436667 316.045,167.33 C310.921667,176.223333 304.203333,182.893333 295.89,187.34 C287.576667,191.786667 278.683333,194.01 269.21,194.01 Z M447.07,217.21 C466.21,217.21 482.16,212.328333 494.92,202.565 C507.68,192.801667 516.186667,179.8 520.44,163.56 L520.44,163.56 L492.02,163.56 C489.12,173.613333 483.803333,181.346667 476.07,186.76 C468.336667,192.173333 458.67,194.88 447.07,194.88 C431.99,194.88 419.81,189.66 410.53,179.22 C401.25,168.78 396.61,153.99 396.61,134.85 C396.61,115.903333 401.25,101.258333 410.53,90.915 C419.81,80.5716667 431.99,75.4 447.07,75.4 C458.67,75.4 468.385,78.2033333 476.215,83.81 C484.045,89.4166667 489.313333,97.0533333 492.02,106.72 L492.02,106.72 L520.44,106.72 C516.38,89.9 508.018333,76.7533333 495.355,67.28 C482.691667,57.8066667 466.596667,53.07 447.07,53.07 C431.99,53.07 418.601667,56.4533333 406.905,63.22 C395.208333,69.9866667 386.073333,79.5083333 379.5,91.785 C372.926667,104.061667 369.64,118.416667 369.64,134.85 C369.64,151.476667 372.926667,165.976667 379.5,178.35 C386.073333,190.723333 395.208333,200.293333 406.905,207.06 C418.601667,213.826667 431.99,217.21 447.07,217.21 Z" id="doc" fill="#009191"></path>
            <path d="M609.85,217.21 C621.836667,217.21 632.421667,214.745 641.605,209.815 C650.788333,204.885 657.99,198.65 663.21,191.11 L663.21,191.11 L663.21,214.6 L704.1,214.6 L704.1,53.94 L663.21,53.94 L663.21,76.85 C657.796667,69.5033333 650.595,63.4133333 641.605,58.58 C632.615,53.7466667 622.126667,51.33 610.14,51.33 C596.413333,51.33 583.991667,54.7133333 572.875,61.48 C561.758333,68.2466667 552.961667,77.9133333 546.485,90.48 C540.008333,103.046667 536.77,117.45 536.77,133.69 C536.77,150.123333 540.008333,164.671667 546.485,177.335 C552.961667,189.998333 561.758333,199.81 572.875,206.77 C583.991667,213.73 596.316667,217.21 609.85,217.21 Z M620.58,181.54 C613.233333,181.54 606.321667,179.606667 599.845,175.74 C593.368333,171.873333 588.148333,166.315 584.185,159.065 C580.221667,151.815 578.24,143.356667 578.24,133.69 C578.24,124.023333 580.221667,115.661667 584.185,108.605 C588.148333,101.548333 593.32,96.1833333 599.7,92.51 C606.08,88.8366667 613.04,87 620.58,87 C628.12,87 635.176667,88.885 641.75,92.655 C648.323333,96.425 653.543333,101.838333 657.41,108.895 C661.276667,115.951667 663.21,124.41 663.21,134.27 C663.21,144.13 661.276667,152.588333 657.41,159.645 C653.543333,166.701667 648.323333,172.115 641.75,175.885 C635.176667,179.655 628.12,181.54 620.58,181.54 Z M778.43,214.6 L778.43,134.56 C778.43,119.673333 781.668333,109.233333 788.145,103.24 C794.621667,97.2466667 804.24,94.25 817,94.25 L817,94.25 L827.73,94.25 L827.73,51.62 C816.903333,51.62 807.285,54.0366667 798.875,58.87 C790.465,63.7033333 783.65,70.3733333 778.43,78.88 L778.43,78.88 L778.43,53.94 L737.83,53.94 L737.83,214.6 L778.43,214.6 Z M889.59,214.6 L889.59,134.56 C889.59,119.673333 892.828333,109.233333 899.305,103.24 C905.781667,97.2466667 915.4,94.25 928.16,94.25 L928.16,94.25 L938.89,94.25 L938.89,51.62 C928.063333,51.62 918.445,54.0366667 910.035,58.87 C901.625,63.7033333 894.81,70.3733333 889.59,78.88 L889.59,78.88 L889.59,53.94 L848.99,53.94 L848.99,214.6 L889.59,214.6 Z M1022.79,217.21 C1034.77667,217.21 1045.36167,214.745 1054.545,209.815 C1063.72833,204.885 1070.93,198.65 1076.15,191.11 L1076.15,191.11 L1076.15,214.6 L1117.04,214.6 L1117.04,53.94 L1076.15,53.94 L1076.15,76.85 C1070.73667,69.5033333 1063.535,63.4133333 1054.545,58.58 C1045.555,53.7466667 1035.06667,51.33 1023.08,51.33 C1009.35333,51.33 996.931667,54.7133333 985.815,61.48 C974.698333,68.2466667 965.901667,77.9133333 959.425,90.48 C952.948333,103.046667 949.71,117.45 949.71,133.69 C949.71,150.123333 952.948333,164.671667 959.425,177.335 C965.901667,189.998333 974.698333,199.81 985.815,206.77 C996.931667,213.73 1009.25667,217.21 1022.79,217.21 Z M1033.52,181.54 C1026.17333,181.54 1019.26167,179.606667 1012.785,175.74 C1006.30833,171.873333 1001.08833,166.315 997.125,159.065 C993.161667,151.815 991.18,143.356667 991.18,133.69 C991.18,124.023333 993.161667,115.661667 997.125,108.605 C1001.08833,101.548333 1006.26,96.1833333 1012.64,92.51 C1019.02,88.8366667 1025.98,87 1033.52,87 C1041.06,87 1048.11667,88.885 1054.69,92.655 C1061.26333,96.425 1066.48333,101.838333 1070.35,108.895 C1074.21667,115.951667 1076.15,124.41 1076.15,134.27 C1076.15,144.13 1074.21667,152.588333 1070.35,159.645 C1066.48333,166.701667 1061.26333,172.115 1054.69,175.885 C1048.11667,179.655 1041.06,181.54 1033.52,181.54 Z M1205,290.58 L1304.47,53.94 L1261.26,53.94 L1219.21,166.17 L1177.74,53.94 L1132.21,53.94 L1196.59,210.54 L1161.79,290.58 L1205,290.58 Z" id="array" fill="#FFC92A"></path>
        </g>
        <g id="编组-58" transform="translate(429.000000, 245.000000)">
            <g id="编组备份" fill="#009191" fill-rule="nonzero">
                <g id="编组-57" transform="translate(271.000000, 271.000000) scale(-1, 1) translate(-271.000000, -271.000000) ">
                    <path d="M346.627907,21.0077519 C346.627907,32.610013 337.222416,42.0155039 325.620155,42.0155039 L271,42.0155039 L271,42.0155039 C147.052001,42.0155039 46.1052763,140.495664 42.1366813,263.479127 L42.0461813,267.213323 L42.0155039,271 C42.0155039,396.199999 142.49521,497.931705 267.213323,499.953819 L325.620157,499.976381 C337.223729,499.980863 346.627907,509.388675 346.627907,520.992248 C346.627907,532.594509 337.222416,542 325.620155,542 L271,542 L271,542 C122.827524,542 2.42945049,423.083651 0.0363062573,275.481481 L0,271 C0,122.827524 118.916349,2.42945049 266.518519,0.0363062573 L325.620157,0.00952089129 C337.21716,0.00426503077 346.622649,9.40123258 346.627905,20.9982354 C346.627906,21.0014076 346.627907,21.0045797 346.627907,21.0077519 Z M504.186047,0 C524.838071,0 541.623292,16.5557541 541.993747,37.1193001 L542,37.8139535 L542,520.992248 C542,532.397861 532.910637,541.680539 521.580265,541.991929 L520.992248,542 L428.55814,542 C416.955879,542 407.550388,532.594509 407.550388,520.992248 C407.550388,509.389987 416.955879,499.984496 428.55814,499.984496 L499.984496,499.984496 L499.984496,499.984496 L499.984496,42.0155039 L428.55814,42.0155039 C416.955879,42.0155039 407.550388,32.610013 407.550388,21.0077519 C407.550388,9.40549091 416.955879,2.13130077e-15 428.55814,0 L504.186047,0 L504.186047,0 Z" id="形状"></path>
                </g>
            </g>
            <path d="M245.135911,136.550388 L245.50622,136.561086 C248.814536,136.752774 251.438236,139.496345 251.438236,142.852713 L251.439682,247.58686 L251.402033,247.578948 C250.565424,247.400142 249.742645,247.227502 248.933696,247.061028 L246.548336,246.580102 C246.157691,246.503031 245.770503,246.427501 245.386773,246.353512 L243.125879,245.928078 C242.755978,245.860255 242.389535,245.793973 242.026549,245.729233 L239.890121,245.35929 L239.890121,245.35929 L237.836671,245.026342 C237.501344,244.973933 237.169475,244.923066 236.841062,244.87374 L234.912078,244.596283 L234.912078,244.596283 L233.066072,244.35582 C229.457036,243.911888 226.345865,243.689922 223.732558,243.689922 L222.473147,243.697756 C196.478622,244.021218 172.153306,254.348605 153.994349,272.103134 L152.988953,273.098674 L121.19007,273.100775 C117.709496,273.100775 114.887849,270.279128 114.887849,266.79845 L114.887849,142.852713 C114.887849,139.372035 117.709496,136.550388 121.190174,136.550388 L245.135911,136.550388 Z" id="路径备份" fill="#FF455A" fill-rule="nonzero"></path>
            <path d="M223.732558,264.697674 C236.346689,264.697674 248.26838,267.662367 258.83885,272.932972 L254.108697,280.963356 C245.272664,295.956773 250.26421,315.274359 265.257627,324.110392 C270.105387,326.967311 275.629713,328.474037 281.25668,328.474037 L301.084825,328.47418 C302.021255,333.330958 302.511628,338.346585 302.511628,343.476744 C302.511628,386.985223 267.241037,422.255814 223.732558,422.255814 C180.224079,422.255814 144.953488,386.985223 144.953488,343.476744 C144.953488,299.968265 180.224079,264.697674 223.732558,264.697674 Z" id="路径备份-2" fill="#009191"></path>
            <path d="M353.44511,184.919079 L413.083113,291.84593 C413.955528,293.410109 414.413492,295.171386 414.413492,296.962409 C414.413492,302.76354 409.710747,307.466285 403.909616,307.466285 L281.25668,307.466285 C279.381024,307.466285 277.539582,306.964043 275.923662,306.011737 C270.925857,303.066393 269.262008,296.627197 272.207352,291.629392 L335.222286,184.702541 C336.169083,183.095968 337.526517,181.770416 339.155134,180.862062 C344.221516,178.036308 350.619357,179.852697 353.44511,184.919079 Z" id="路径-73备份" fill="#FFC92A" fill-rule="nonzero"></path>
        </g>
    </g>
</svg>
docs/_static/logo-dark.svg METASEP
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1400px" height="1400px" viewBox="0 0 1400 1400" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>docarray-logo_text-dark</title>
    <g id="docarray-logo_text-dark" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="docarray" transform="translate(48.000000, 245.000000)" fill="#FFFFFF" fill-rule="nonzero">
            <path d="M1177.74,746.94 L1219.21,859.17 L1261.26,746.94 L1304.47,746.94 L1205,983.58 L1161.79,983.58 L1196.59,903.54 L1132.21,746.94 L1177.74,746.94 Z M161.24,693 L161.24,907.6 L134.56,907.6 L134.56,877.73 L134.216002,878.345428 C129.00258,887.540867 121.383913,895.117391 111.36,901.075 C101.113333,907.165 89.1266667,910.21 75.4,910.21 C61.2866667,910.21 48.4783333,906.73 36.975,899.77 C25.4716667,892.81 16.4333333,883.046667 9.86,870.48 C3.28666667,857.913333 0,843.606667 0,827.56 C0,811.32 3.28666667,797.061667 9.86,784.785 C16.4333333,772.508333 25.4716667,762.986667 36.975,756.22 C48.4783333,749.453333 61.3833333,746.07 75.69,746.07 C88.0633333,746.07 99.5666667,748.921667 110.2,754.625 C120.602174,760.204348 128.599102,767.495121 134.190784,776.497318 L134.56,777.1 L134.56,693 L161.24,693 Z M270.37,746.07 C285.45,746.07 299.08,749.405 311.26,756.075 C323.44,762.745 333.058333,772.266667 340.115,784.64 C347.171667,797.013333 350.7,811.416667 350.7,827.85 C350.7,844.283333 347.075,858.735 339.825,871.205 C332.575,883.675 322.763333,893.293333 310.39,900.06 C298.016667,906.826667 284.29,910.21 269.21,910.21 C254.323333,910.21 240.838333,906.826667 228.755,900.06 C216.671667,893.293333 207.198333,883.675 200.335,871.205 C193.471667,858.735 190.04,844.283333 190.04,827.85 C190.04,811.61 193.568333,797.255 200.625,784.785 C207.681667,772.315 217.3,762.745 229.48,756.075 C241.66,749.405 255.29,746.07 270.37,746.07 Z M447.07,746.07 C466.596667,746.07 482.691667,750.806667 495.355,760.28 C507.823513,769.60759 516.1217,782.496357 520.249561,798.946301 L520.44,799.72 L492.02,799.72 L491.847498,799.118477 C489.092708,789.73602 483.881875,782.299861 476.215,776.81 C468.385,771.203333 458.67,768.4 447.07,768.4 C431.99,768.4 419.81,773.571667 410.53,783.915 C401.25,794.258333 396.61,808.903333 396.61,827.85 C396.61,846.99 401.25,861.78 410.53,872.22 C419.81,882.66 431.99,887.88 447.07,887.88 C458.67,887.88 468.336667,885.173333 476.07,879.76 C483.64551,874.457143 488.901987,866.928013 491.839429,857.172611 L492.02,856.56 L520.44,856.56 L520.23751,857.318878 C515.919795,873.205511 507.480625,885.954219 494.92,895.565 C482.16,905.328333 466.21,910.21 447.07,910.21 C431.99,910.21 418.601667,906.826667 406.905,900.06 C395.208333,893.293333 386.073333,883.723333 379.5,871.35 C372.926667,858.976667 369.64,844.476667 369.64,827.85 C369.64,811.416667 372.926667,797.061667 379.5,784.785 C386.073333,772.508333 395.208333,762.986667 406.905,756.22 C418.601667,749.453333 431.99,746.07 447.07,746.07 Z M610.14,744.33 C622.126667,744.33 632.615,746.746667 641.605,751.58 C650.345278,756.279074 657.395194,762.16597 662.754749,769.240687 L663.21,769.85 L663.21,746.94 L704.1,746.94 L704.1,907.6 L663.21,907.6 L663.21,884.11 L662.793778,884.702552 C657.609593,891.977254 650.546667,898.014737 641.605,902.815 C632.421667,907.745 621.836667,910.21 609.85,910.21 C596.316667,910.21 583.991667,906.73 572.875,899.77 C561.758333,892.81 552.961667,882.998333 546.485,870.335 C540.008333,857.671667 536.77,843.123333 536.77,826.69 C536.77,810.45 540.008333,796.046667 546.485,783.48 C552.961667,770.913333 561.758333,761.246667 572.875,754.48 C583.991667,747.713333 596.413333,744.33 610.14,744.33 Z M1023.08,744.33 C1035.06667,744.33 1045.555,746.746667 1054.545,751.58 C1063.28528,756.279074 1070.33519,762.16597 1075.69475,769.240687 L1076.15,769.85 L1076.15,746.94 L1117.04,746.94 L1117.04,907.6 L1076.15,907.6 L1076.15,884.11 L1075.73378,884.702552 C1070.54959,891.977254 1063.48667,898.014737 1054.545,902.815 C1045.36167,907.745 1034.77667,910.21 1022.79,910.21 C1009.25667,910.21 996.931667,906.73 985.815,899.77 C974.698333,892.81 965.901667,882.998333 959.425,870.335 C952.948333,857.671667 949.71,843.123333 949.71,826.69 C949.71,810.45 952.948333,796.046667 959.425,783.48 C965.901667,770.913333 974.698333,761.246667 985.815,754.48 C996.931667,747.713333 1009.35333,744.33 1023.08,744.33 Z M827.73,744.62 L827.73,787.25 L817,787.25 L816.331322,787.252767 C803.903482,787.356069 794.508041,790.351813 788.145,796.24 C781.767974,802.141128 778.530407,812.353155 778.432299,826.87608 L778.43,827.56 L778.43,907.6 L737.83,907.6 L737.83,746.94 L778.43,746.94 L778.43,771.88 L778.834684,771.229264 C784.000536,763.03249 790.680641,756.579402 798.875,751.87 C807.089419,747.14907 816.456622,744.73371 826.976612,744.623921 L827.73,744.62 Z M938.89,744.62 L938.89,787.25 L928.16,787.25 L927.491322,787.252767 C915.063482,787.356069 905.668041,790.351813 899.305,796.24 C892.927974,802.141128 889.690407,812.353155 889.592299,826.87608 L889.59,827.56 L889.59,907.6 L848.99,907.6 L848.99,746.94 L889.59,746.94 L889.59,771.88 L889.994684,771.229264 C895.160536,763.03249 901.840641,756.579402 910.035,751.87 C918.249419,747.14907 927.616622,744.73371 938.136612,744.623921 L938.89,744.62 Z M80.62,768.98 C70.76,768.98 61.77,771.3 53.65,775.94 C45.53,780.58 39.0533333,787.346667 34.22,796.24 C29.3866667,805.133333 26.97,815.573333 26.97,827.56 C26.97,839.74 29.3866667,850.325 34.22,859.315 C39.0533333,868.305 45.53,875.168333 53.65,879.905 C61.77,884.641667 70.76,887.01 80.62,887.01 C90.48,887.01 99.5183333,884.641667 107.735,879.905 C115.951667,875.168333 122.476667,868.305 127.31,859.315 C132.143333,850.325 134.56,839.836667 134.56,827.85 C134.56,815.863333 132.143333,805.423333 127.31,796.53 C122.476667,787.636667 115.951667,780.821667 107.735,776.085 C99.5183333,771.348333 90.48,768.98 80.62,768.98 Z M270.08,768.98 C260.413333,768.98 251.568333,771.155 243.545,775.505 C235.521667,779.855 229.093333,786.476667 224.26,795.37 C219.426667,804.263333 217.01,815.09 217.01,827.85 C217.01,840.803333 219.378333,851.726667 224.115,860.62 C228.851667,869.513333 235.183333,876.135 243.11,880.485 C251.036667,884.835 259.736667,887.01 269.21,887.01 C278.683333,887.01 287.576667,884.786667 295.89,880.34 C304.203333,875.893333 310.921667,869.223333 316.045,860.33 C321.168333,851.436667 323.73,840.61 323.73,827.85 C323.73,815.09 321.216667,804.263333 316.19,795.37 C311.163333,786.476667 304.59,779.855 296.47,775.505 C288.35,771.155 279.553333,768.98 270.08,768.98 Z M620.58,780 C613.04,780 606.08,781.836667 599.7,785.51 C593.32,789.183333 588.148333,794.548333 584.185,801.605 C580.221667,808.661667 578.24,817.023333 578.24,826.69 C578.24,836.356667 580.221667,844.815 584.185,852.065 C588.148333,859.315 593.368333,864.873333 599.845,868.74 C606.321667,872.606667 613.233333,874.54 620.58,874.54 C628.12,874.54 635.176667,872.655 641.75,868.885 C648.323333,865.115 653.543333,859.701667 657.41,852.645 C661.276667,845.588333 663.21,837.13 663.21,827.27 C663.21,817.41 661.276667,808.951667 657.41,801.895 C653.543333,794.838333 648.323333,789.425 641.75,785.655 C635.176667,781.885 628.12,780 620.58,780 Z M1033.52,780 C1025.98,780 1019.02,781.836667 1012.64,785.51 C1006.26,789.183333 1001.08833,794.548333 997.125,801.605 C993.161667,808.661667 991.18,817.023333 991.18,826.69 C991.18,836.356667 993.161667,844.815 997.125,852.065 C1001.08833,859.315 1006.30833,864.873333 1012.785,868.74 C1019.26167,872.606667 1026.17333,874.54 1033.52,874.54 C1041.06,874.54 1048.11667,872.655 1054.69,868.885 C1061.26333,865.115 1066.48333,859.701667 1070.35,852.645 C1074.21667,845.588333 1076.15,837.13 1076.15,827.27 C1076.15,817.41 1074.21667,808.951667 1070.35,801.895 C1066.48333,794.838333 1061.26333,789.425 1054.69,785.655 C1048.11667,781.885 1041.06,780 1033.52,780 Z M597.379843,0.00952089129 L656.481481,0.0363062573 C802.607629,2.40551905 920.618804,120.431363 922.964415,266.563248 L923,271 L922.963694,275.481481 C920.594481,421.607629 802.568637,539.618804 656.436752,541.964415 L652,542 L597.379845,542 C585.777584,542 576.372093,532.594509 576.372093,520.992248 C576.372093,509.505882 585.587247,500.171144 597.028901,499.979388 L597.379843,499.976381 L655.786677,499.953819 C779.257609,497.951926 878.972478,398.224662 880.954428,274.748883 L880.984496,271 L880.953819,267.213323 L880.863319,263.479127 C876.93441,141.725498 777.957235,43.9875574 655.711466,42.0449739 L652,42.0155039 L597.379845,42.0155039 C585.894778,42.0155039 576.562327,32.7990632 576.372093,21.3586558 L576.372093,21.0077519 C576.377298,9.51847015 585.593789,0.194328201 597.029097,0.00952089129 L597.379843,0.00952089129 Z M494.44186,0 C506.044121,0 515.449612,9.40549091 515.449612,21.0077519 C515.449612,32.4928184 506.233172,41.8252698 494.792764,42.0126324 L494.44186,42.0155039 L423.015504,42.0155039 L423.015504,499.984496 L494.44186,499.984496 C506.044121,499.984496 515.449612,509.389987 515.449612,520.992248 C515.449612,532.477315 506.233172,541.809766 494.792764,542 L494.44186,542 L402.007752,542 L401.419735,541.991929 C390.206171,541.683749 381.187648,532.588303 381.002891,521.344315 L381,520.992248 L381,37.8139535 L381.006253,37.1193001 C381.373003,16.7613896 397.827866,0.331515862 418.195565,0 L418.813953,0 L494.44186,0 Z M604.732558,264.697674 C617.066375,264.697674 628.738185,267.532067 639.132157,272.585022 L639.83885,272.932972 L635.108697,280.963356 L634.84776,281.414441 C626.373495,296.342077 631.414144,315.36272 646.257627,324.110392 C650.938223,326.868796 656.249529,328.368519 661.67497,328.468669 L662.25668,328.474037 L682.084825,328.47418 L682.26615,329.447641 C683.084458,334.000218 683.511628,338.688595 683.511628,343.476744 C683.511628,386.985223 648.241037,422.255814 604.732558,422.255814 C561.224079,422.255814 525.953488,386.985223 525.953488,343.476744 C525.953488,299.968265 561.224079,264.697674 604.732558,264.697674 Z M720.155134,180.862062 C725.135645,178.084202 731.402863,179.792458 734.298373,184.664274 L734.44511,184.919079 L794.083113,291.84593 L794.2637,292.184067 C795.018615,293.661872 795.413492,295.299316 795.413492,296.962409 C795.413492,302.668439 790.863671,307.311802 785.194016,307.46251 L784.909616,307.466285 L662.25668,307.466285 L661.882062,307.459604 C660.136501,307.397319 658.431854,306.900556 656.923662,306.011737 C652.012026,303.117174 650.320373,296.8482 653.059111,291.889159 L653.207352,291.629392 L716.222286,184.702541 L716.417079,184.385014 C717.351279,182.921082 718.635091,181.709859 720.155134,180.862062 Z M626.135911,136.550388 L626.50622,136.561086 L626.735898,136.578576 C629.858977,136.87352 632.318647,139.447485 632.434004,142.619567 L632.438236,142.852713 L632.439682,247.58686 L630.747255,247.229558 L630.747255,247.229558 L629.933696,247.061028 L627.548336,246.580102 L626.386773,246.353512 L624.125879,245.928078 L623.026549,245.729233 L620.890121,245.35929 L618.836671,245.026342 L617.841062,244.87374 L615.912078,244.596283 L614.066072,244.35582 L613.350903,244.269993 L612.051443,244.12562 C609.498783,243.857498 607.223688,243.713125 605.226158,243.692501 L604.732558,243.689922 L603.473147,243.697756 L602.693816,243.710458 C577.249743,244.223327 553.452305,254.321175 535.535478,271.578011 L534.994349,272.103134 L533.988953,273.098674 L502.19007,273.100775 L501.959031,273.096619 C498.662154,272.977806 496.010817,270.326467 495.892005,267.029495 L495.887849,266.79845 L495.887849,142.852713 L495.892005,142.621668 C496.010817,139.324696 498.662156,136.673357 501.959129,136.554544 L502.190174,136.550388 L626.135911,136.550388 Z" id="形状"></path>
        </g>
        <g id="编组-58" transform="translate(429.000000, 245.000000)">
            <g id="编组-57" transform="translate(271.000000, 271.000000) scale(-1, 1) translate(-271.000000, -271.000000) "></g>
        </g>
    </g>
</svg>
docs/_static/favicon.png METASEP

docs/_static/docarray-light.svg METASEP
<?xml version="1.0" encoding="UTF-8"?>
<svg width="320px" height="320px" viewBox="0 0 320 320" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>docarray-light</title>
    <g id="docarray-light" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="编组备份" transform="translate(31.000000, 31.000000)" fill="#009191" fill-rule="nonzero">
            <g id="编组-57" transform="translate(129.000000, 129.000000) scale(-1, 1) translate(-129.000000, -129.000000) ">
                <path d="M155,0.0045310358 C160.42678,0.0045310358 164.84543,4.32478085 164.996032,9.71569372 L165,10 C165,15.5228475 160.522847,20 155,20 L129,20 C69.9989231,20 21.9467921,66.8780096 20.0576823,125.419953 L20.0146029,127.197486 L20,129 C20,188.597047 67.8298231,237.022841 127.197486,237.985397 L155,237.996137 C160.523473,237.998271 165,242.476528 165,248 C165,253.522847 160.522847,258 155,258 L129,258 C58.4677146,258 1.15645429,201.394063 0.0172823144,131.133251 L0,129 C0,58.4677146 56.6059375,1.15645429 126.866749,0.0172823144 L155,0.0045310358 Z M240,0 C249.830669,0 257.820682,7.88078333 257.997024,17.6693348 L258,18 L258,248 C258,253.42924 253.673329,257.847932 248.279905,257.996158 L248,258 L204,258 C198.477153,258 194,253.522847 194,248 C194,242.477153 198.477153,238 204,238 L238,238 L238,20 L204,20 C198.477153,20 194,15.5228475 194,10 C194,4.4771525 198.477153,0 204,0 L240,0 Z M151.5,126 C172.210678,126 189,142.789322 189,163.5 C189,184.210678 172.210678,201 151.5,201 C130.789322,201 114,184.210678 114,163.5 C114,161.329305 114.184434,159.201688 114.538488,157.131962 L114.679179,156.358558 L124.117669,156.35849 C126.796188,156.35849 129.425849,155.641266 131.733454,154.281331 C138.772774,150.132866 141.180601,141.130787 137.209662,134.03715 L137.04051,133.742705 L134.788887,129.920123 C139.820587,127.411237 145.495487,126 151.5,126 Z M89.7549106,88.0242111 C91.100011,85.6125382 94.1454775,84.7479105 96.5571504,86.0930109 C97.2462577,86.4773576 97.8333702,87.0186169 98.2718658,87.6712617 L98.4292442,87.9211357 L128.425282,138.819895 C129.82731,141.198924 129.035293,144.264076 126.656264,145.666104 C125.972529,146.069047 125.203973,146.302717 124.414554,146.349669 L124.117669,146.35849 L65.7330609,146.35849 C62.9716371,146.35849 60.7330609,144.119914 60.7330609,141.35849 C60.7330609,140.600665 60.9053061,139.854027 61.2352488,139.174527 L61.3663409,138.92297 L89.7549106,88.0242111 Z M200.311688,65 C201.909369,65 203.215349,66.24892 203.306596,67.8237272 L203.311688,68 L203.311688,127 C203.311688,128.597681 202.062768,129.903661 200.488006,129.994907 L200.311738,130 L185.175,129.999 L184.696417,129.525108 C176.194189,121.212238 164.852133,116.319831 152.707395,116.015135 L152.099498,116.003729 L151.5,116 C150.380425,116 149.068887,116.085584 147.565386,116.256751 L147.05711,116.316977 L146.178383,116.431441 L145.260158,116.563515 L144.786234,116.636155 L143.808762,116.794644 L142.791791,116.970742 L142.268493,117.065395 L141.192274,117.267908 L140.639353,117.375768 L139.503887,117.604696 L138.311,117.855 L138.311688,68 C138.311688,66.4614925 139.469809,65.1934785 140.961825,65.0201832 L141.135416,65.0050927 L141.311688,65 L200.311688,65 Z" id="形状"></path>
            </g>
        </g>
    </g>
</svg>
docs/_static/docarray-dark.svg METASEP
<?xml version="1.0" encoding="UTF-8"?>
<svg width="320px" height="320px" viewBox="0 0 320 320" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>docarray-dark 2</title>
    <g id="docarray-dark-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="编组" transform="translate(31.000000, 31.000000)" fill="#FBCB67" fill-rule="nonzero">
            <g id="编组-57" transform="translate(129.000000, 129.000000) scale(-1, 1) translate(-129.000000, -129.000000) ">
                <path d="M155,0.0045310358 C160.42678,0.0045310358 164.84543,4.32478085 164.996032,9.71569372 L165,10 C165,15.5228475 160.522847,20 155,20 L129,20 C69.9989231,20 21.9467921,66.8780096 20.0576823,125.419953 L20.0146029,127.197486 L20,129 C20,188.597047 67.8298231,237.022841 127.197486,237.985397 L155,237.996137 C160.523473,237.998271 165,242.476528 165,248 C165,253.522847 160.522847,258 155,258 L129,258 C58.4677146,258 1.15645429,201.394063 0.0172823144,131.133251 L0,129 C0,58.4677146 56.6059375,1.15645429 126.866749,0.0172823144 L155,0.0045310358 Z M240,0 C249.830669,0 257.820682,7.88078333 257.997024,17.6693348 L258,18 L258,248 C258,253.42924 253.673329,257.847932 248.279905,257.996158 L248,258 L204,258 C198.477153,258 194,253.522847 194,248 C194,242.477153 198.477153,238 204,238 L238,238 L238,20 L204,20 C198.477153,20 194,15.5228475 194,10 C194,4.4771525 198.477153,0 204,0 L240,0 Z M151.5,126 C172.210678,126 189,142.789322 189,163.5 C189,184.210678 172.210678,201 151.5,201 C130.789322,201 114,184.210678 114,163.5 C114,161.329305 114.184434,159.201688 114.538488,157.131962 L114.679179,156.358558 L124.117669,156.35849 C126.796188,156.35849 129.425849,155.641266 131.733454,154.281331 C138.772774,150.132866 141.180601,141.130787 137.209662,134.03715 L137.04051,133.742705 L134.788887,129.920123 C139.820587,127.411237 145.495487,126 151.5,126 Z M89.7549106,88.0242111 C91.100011,85.6125382 94.1454775,84.7479105 96.5571504,86.0930109 C97.2462577,86.4773576 97.8333702,87.0186169 98.2718658,87.6712617 L98.4292442,87.9211357 L128.425282,138.819895 C129.82731,141.198924 129.035293,144.264076 126.656264,145.666104 C125.972529,146.069047 125.203973,146.302717 124.414554,146.349669 L124.117669,146.35849 L65.7330609,146.35849 C62.9716371,146.35849 60.7330609,144.119914 60.7330609,141.35849 C60.7330609,140.600665 60.9053061,139.854027 61.2352488,139.174527 L61.3663409,138.92297 L89.7549106,88.0242111 Z M200.311688,65 C201.909369,65 203.215349,66.24892 203.306596,67.8237272 L203.311688,68 L203.311688,127 C203.311688,128.597681 202.062768,129.903661 200.488006,129.994907 L200.311738,130 L185.175,129.999 L184.696417,129.525108 C176.194189,121.212238 164.852133,116.319831 152.707395,116.015135 L152.099498,116.003729 L151.5,116 C150.380425,116 149.068887,116.085584 147.565386,116.256751 L147.05711,116.316977 L146.178383,116.431441 L145.260158,116.563515 L144.786234,116.636155 L143.808762,116.794644 L142.791791,116.970742 L142.268493,117.065395 L141.192274,117.267908 L140.639353,117.375768 L139.503887,117.604696 L138.311,117.855 L138.311688,68 C138.311688,66.4614925 139.469809,65.1934785 140.961825,65.0201832 L141.135416,65.0050927 L141.311688,65 L200.311688,65 Z" id="形状"></path>
            </g>
        </g>
    </g>
</svg>
docs/_static/banner.png METASEP

docarray/typing/url/url_3d/url_3d.py METASEP
from abc import ABC
from typing import TYPE_CHECKING, Any, Optional, Type, TypeVar, Union

import numpy as np

from docarray.typing.url.any_url import AnyUrl

if TYPE_CHECKING:
    import trimesh
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

MESH_FILE_FORMATS = ('obj', 'glb', 'ply')

T = TypeVar('T', bound='Url3D')


class Url3D(AnyUrl, ABC):
    """
    URL to a .obj, .glb, or .ply file containing 3D mesh or point cloud information.
    Can be remote (web) URL, or a local file path.
    """

    @classmethod
    def validate(
        cls: Type[T],
        value: Union[T, np.ndarray, Any],
        field: 'ModelField',
        config: 'BaseConfig',
    ) -> T:
        url = super().validate(value, field, config)
        has_mesh_extension = any(url.endswith(ext) for ext in MESH_FILE_FORMATS)
        if not has_mesh_extension:
            raise ValueError(
                f'{cls.__name__} must have one of the following extensions:'
                f'{MESH_FILE_FORMATS}'
            )
        return cls(str(url), scheme=None)

    def _load_trimesh_instance(
        self: T, force: Optional[str] = None
    ) -> Union['trimesh.Trimesh', 'trimesh.Scene']:
        """
        Load the data from the url into a trimesh.Mesh or trimesh.Scene object.

        :param url: url to load data from
        :param force: str or None. For 'mesh' try to coerce scenes into a single mesh.
            For 'scene' try to coerce everything into a scene.
        :return: trimesh.Mesh or trimesh.Scene object
        """
        import urllib.parse

        import trimesh

        scheme = urllib.parse.urlparse(self).scheme
        loader = trimesh.load_remote if scheme in ['http', 'https'] else trimesh.load

        mesh = loader(self, force=force)

        return mesh

docarray/typing/url/url_3d/point_cloud_url.py METASEP
from typing import TYPE_CHECKING, TypeVar

import numpy as np

from docarray.typing.url.url_3d.url_3d import Url3D

if TYPE_CHECKING:
    from docarray.proto import NodeProto

T = TypeVar('T', bound='PointCloud3DUrl')


class PointCloud3DUrl(Url3D):
    """
    URL to a .obj, .glb, or .ply file containing point cloud information.
    Can be remote (web) URL, or a local file path.
    """

    def _to_node_protobuf(self: T) -> 'NodeProto':
        """Convert Document into a NodeProto protobuf message. This function should
        be called when the Document is nested into another Document that needs to
        be converted into a protobuf

        :return: the nested item protobuf message
        """
        from docarray.proto import NodeProto

        return NodeProto(point_cloud_url=str(self))

    def load(self: T, samples: int, multiple_geometries: bool = False) -> np.ndarray:
        """
        Load the data from the url into a numpy.ndarray containing point cloud
        information.

        EXAMPLE USAGE

        .. code-block:: python

            import numpy as np
            from docarray import Document

            from docarray.typing import PointCloud3DUrl


            class MyDoc(Document):
                point_cloud_url: PointCloud3DvUrl


            doc = MyDoc(point_cloud_url="toydata/tetrahedron.obj")

            point_cloud = doc.point_cloud_url.load(samples=100)

            assert isinstance(point_cloud, np.ndarray)
            assert point_cloud.shape == (100, 3)

        :param samples: number of points to sample from the mesh
        :param multiple_geometries: if False, store point cloud in 2D np.ndarray.
            If True, store point clouds from multiple geometries in 3D np.ndarray.

        :return: np.ndarray representing the point cloud
        """
        if multiple_geometries:
            # try to coerce everything into a scene
            scene = self._load_trimesh_instance(force='scene')
            point_cloud = np.stack(
                [np.array(geo.sample(samples)) for geo in scene.geometry.values()],
                axis=0,
            )
        else:
            # combine a scene into a single mesh
            mesh = self._load_trimesh_instance(force='mesh')
            point_cloud = np.array(mesh.sample(samples))

        return point_cloud

docarray/typing/url/url_3d/mesh_url.py METASEP
from typing import TYPE_CHECKING, Tuple, TypeVar

import numpy as np

from docarray.typing.url.url_3d.url_3d import Url3D

if TYPE_CHECKING:
    from docarray.proto import NodeProto

T = TypeVar('T', bound='Mesh3DUrl')


class Mesh3DUrl(Url3D):
    """
    URL to a .obj, .glb, or .ply file containing 3D mesh information.
    Can be remote (web) URL, or a local file path.
    """

    def _to_node_protobuf(self: T) -> 'NodeProto':
        """Convert Document into a NodeProto protobuf message. This function should
        be called when the Document is nested into another Document that needs to
        be converted into a protobuf

        :return: the nested item protobuf message
        """
        from docarray.proto import NodeProto

        return NodeProto(mesh_url=str(self))

    def load(self: T) -> Tuple[np.ndarray, np.ndarray]:
        """
        Load the data from the url into a tuple of two numpy.ndarrays containing
        vertices and faces information.

        EXAMPLE USAGE

        .. code-block:: python

            from docarray import Document
            import numpy as np

            from docarray.typing import Mesh3DUrl


            class MyDoc(Document):
                mesh_url: Mesh3DUrl


            doc = MyDoc(mesh_url="toydata/tetrahedron.obj")

            vertices, faces = doc.mesh_url.load()
            assert isinstance(vertices, np.ndarray)
            assert isinstance(faces, np.ndarray)

        :return: tuple of two np.ndarrays representing the mesh's vertices and faces
        """

        mesh = self._load_trimesh_instance(force='mesh')

        vertices = mesh.vertices.view(np.ndarray)
        faces = mesh.faces.view(np.ndarray)

        return vertices, faces

docarray/typing/url/url_3d/__init__.py METASEP
from docarray.typing.url.url_3d.mesh_url import Mesh3DUrl
from docarray.typing.url.url_3d.point_cloud_url import PointCloud3DUrl

__all__ = ['Mesh3DUrl', 'PointCloud3DUrl']

docarray/typing/tensor/embedding/torch.py METASEP
from typing import Any  # noqa: F401

from docarray.typing.tensor.embedding.embedding_mixin import EmbeddingMixin
from docarray.typing.tensor.torch_tensor import TorchTensor

torch_base = type(TorchTensor)  # type: Any
embedding_base = type(EmbeddingMixin)  # type: Any


class metaTorchAndEmbedding(torch_base, embedding_base):
    pass


class TorchEmbedding(TorchTensor, EmbeddingMixin, metaclass=metaTorchAndEmbedding):
    alternative_type = TorchTensor

docarray/typing/tensor/embedding/ndarray.py METASEP
from docarray.typing.tensor.embedding.embedding_mixin import EmbeddingMixin
from docarray.typing.tensor.ndarray import NdArray


class NdArrayEmbedding(NdArray, EmbeddingMixin):
    alternative_type = NdArray

docarray/typing/tensor/embedding/embedding_mixin.py METASEP
from abc import ABC
from typing import Any, Optional, Tuple, Type

from docarray.typing.tensor.abstract_tensor import AbstractTensor


class EmbeddingMixin(AbstractTensor, ABC):
    alternative_type: Optional[Type] = None

    @classmethod
    def __docarray_validate_getitem__(cls, item: Any) -> Tuple[int]:
        shape = super().__docarray_validate_getitem__(item)
        if len(shape) > 1:
            error_msg = f'`{cls}` can only have a single dimension/axis.'
            if cls.alternative_type:
                error_msg += f' Consider using {cls.alternative_type} instead.'
            raise ValueError(error_msg)
        return shape

docarray/typing/tensor/embedding/embedding.py METASEP
from typing import Union

from docarray.typing.tensor.embedding.ndarray import NdArrayEmbedding

try:
    import torch  # noqa: F401
except ImportError:
    Embedding = Union[NdArrayEmbedding]  # type: ignore

else:
    from docarray.typing.tensor.embedding.torch import TorchEmbedding  # noqa: F401

    Embedding = Union[NdArrayEmbedding, TorchEmbedding]  # type: ignore

__all__ = ['Embedding']

docarray/typing/tensor/embedding/__init__.py METASEP
from docarray.typing.tensor.embedding.embedding import Embedding
from docarray.typing.tensor.embedding.ndarray import NdArrayEmbedding

__all__ = ['NdArrayEmbedding', 'Embedding']

try:
    import torch  # noqa: F401
except ImportError:
    pass
else:
    from docarray.typing.tensor.embedding.torch import TorchEmbedding  # noqa F401

    __all__.append('TorchEmbedding')

tests/units/__init__.py METASEP

tests/integrations/__init__.py METASEP

docs/requirements.txt METASEP
# cf. https://github.com/ryanfox/sphinx-markdown-tables/issues/36
sphinx
sphinx-argparse==0.3.1
sphinxcontrib-apidoc==0.3.0
sphinx-autodoc-typehints==1.18.3
sphinx_copybutton
sphinx-notfound-page==0.7.1
gitpython==3.1.13
sphinx-sitemap
sphinxext-opengraph
furo
myst-parser
sphinx-design
sphinx-inline-tabs
# sphinx-multiversion
git+https://github.com/Holzhaus/sphinx-multiversion.git

docs/makedoc.sh METASEP
#!/usr/bin/env bash

set -ex

rm -rf api && make clean

docker run --rm \
  -v $(pwd)/proto:/out \
  -v $(pwd)/../docarray/proto:/protos \
  pseudomuto/protoc-gen-doc --doc_opt=markdown,docs.md

cp ../README.md .

cat README.md index_init.md > index.md
make dirhtml

docs/index_init.md METASEP

```{toctree}
:caption: Developer References
:hidden:
:maxdepth: 1

api_public
api/docarray
```


---
{ref}`genindex` | {ref}`modindex`


docs/api_public.rst METASEP
======================
:fab:`python` Python API
======================

This section includes the API documentation from the `docarray` codebase, as extracted from the `docstrings <https://peps.python.org/pep-0257/>`_ in the code.


:mod:`docarray` - Document
--------------------

.. currentmodule:: docarray

.. autosummary::
   :nosignatures:
   :template: class.rst

    BaseDocument
    document.AnyDocument

:mod:`docarray` - DocumentArray
--------------------

.. currentmodule:: docarray

.. autosummary::
   :nosignatures:
   :template: class.rst

   array.DocumentArray

:mod:`docarray` - Predefined Document
--------------------

.. currentmodule:: docarray

.. autosummary::
   :nosignatures:
   :template: class.rst

   predefined_document.Image
   predefined_document.Text

:mod:`docarray` - Typing
--------------------

.. currentmodule:: docarray

.. autosummary::
   :nosignatures:
   :template: class.rst

    typing.TorchTensor
    typing.NdArray
    typing.ImageUrl
    typing.TextUrl
    typing.AnyUrl
    typing.ID
    typing.Embedding
    typing.Tensor
    typing.tensor.Ndarray
    typing.tensor.TorchTensor




docs/_versions.json METASEP
[{"version": "v0.20.0"}]
docs/Makefile METASEP
# Minimal makefile for Sphinx documentation
# Used only for local building

# You can set these variables from the command line.
SPHINXOPTS    =
SPHINXBUILD   = sphinx-build
SOURCEDIR     = .
BUILDDIR      = _build

# Put it first so that "make" without argument is like "make help".
help:
	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
docs/.gitignore METASEP
api/*
proto/*

README.md
index.md
docarray/typing/url/text_url.py METASEP
from typing import TYPE_CHECKING, Optional

if TYPE_CHECKING:
    from docarray.proto import NodeProto

from docarray.typing.url.any_url import AnyUrl
from docarray.typing.url.helper import _uri_to_blob


class TextUrl(AnyUrl):
    """
    URL to a text file.
    Cane be remote (web) URL, or a local file path.
    """

    def _to_node_protobuf(self) -> 'NodeProto':
        """Convert Document into a NodeProto protobuf message. This function should
        be called when the Document is nested into another Document that need to
        be converted into a protobuf

        :return: the nested item protobuf message
        """
        from docarray.proto import NodeProto

        return NodeProto(text_url=str(self))

    def load_to_bytes(self, timeout: Optional[float] = None) -> bytes:
        """
        Load the text file into a bytes object.

        EXAMPLE USAGE

        .. code-block:: python

            from docarray import Document
            from docarray.typing import TextUrl


            class MyDoc(Document):
                remote_url: TextUrl
                local_url: TextUrl


            doc = MyDoc(
                remote_url='https://de.wikipedia.org/wiki/Brixen',
                local_url='home/username/my_file.txt',
            )

            remote_txt_bytes = doc.remote_url.load_to_bytes()
            local_txt_bytes = doc.local_url.load_to_bytes()

        :param timeout: timeout (sec) for urlopen network request.
            Only relevant if URL is not local
        :return: the text file content as bytes
        """
        return _uri_to_blob(self, timeout=timeout)

    def load(self, charset: str = 'utf-8', timeout: Optional[float] = None) -> str:
        """
        Load the text file into a string.

        EXAMPLE USAGE

        .. code-block:: python

            from docarray import Document
            from docarray.typing import TextUrl


            class MyDoc(Document):
                remote_url: TextUrl
                local_url: TextUrl


            doc = MyDoc(
                remote_url='https://de.wikipedia.org/wiki/Brixen',
                local_url='home/username/my_file.txt',
            )

            remote_txt = doc.remote_url.load()
            print(remote_txt)
            # prints: ```<!DOCTYPE html>\n<html class="client-nojs" ... > ...```

            local_txt = doc.local_url.load()
            print(local_txt)
            # prints content of my_file.txt


        :param timeout: timeout (sec) for urlopen network request.
            Only relevant if URL is not local
        :param charset: decoding charset; may be any character set registered with IANA
        :return: the text file content
        """
        _bytes = _uri_to_blob(self, timeout=timeout)
        return _bytes.decode(charset)

docarray/typing/url/image_url.py METASEP
import io
import struct
from typing import TYPE_CHECKING, Any, Optional, Tuple, Type, TypeVar, Union

import numpy as np

from docarray.typing.url.any_url import AnyUrl
from docarray.typing.url.helper import _uri_to_blob

if TYPE_CHECKING:
    import PIL
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import NodeProto

T = TypeVar('T', bound='ImageUrl')

IMAGE_FILE_FORMATS = ('png', 'jpeg', 'jpg')


class ImageUrl(AnyUrl):
    """
    URL to a .png, .jpeg, or .jpg file.
    Can be remote (web) URL, or a local file path.
    """

    def _to_node_protobuf(self) -> 'NodeProto':
        """Convert Document into a NodeProto protobuf message. This function should
        be called when the Document is nested into another Document that needs to
        be converted into a protobuf

        :return: the nested item protobuf message
        """
        from docarray.proto import NodeProto

        return NodeProto(image_url=str(self))

    @classmethod
    def validate(
        cls: Type[T],
        value: Union[T, np.ndarray, Any],
        field: 'ModelField',
        config: 'BaseConfig',
    ) -> T:
        url = super().validate(value, field, config)  # basic url validation
        has_image_extension = any(url.endswith(ext) for ext in IMAGE_FILE_FORMATS)
        if not has_image_extension:
            raise ValueError(
                f'Image URL must have one of the following extensions:'
                f'{IMAGE_FILE_FORMATS}'
            )
        return cls(str(url), scheme=None)

    def load(
        self,
        width: Optional[int] = None,
        height: Optional[int] = None,
        axis_layout: Tuple[str, str, str] = ('H', 'W', 'C'),
        timeout: Optional[float] = None,
    ) -> np.ndarray:
        """
        Load the data from the url into a numpy.ndarray image tensor

        EXAMPLE USAGE

        .. code-block:: python

            from docarray import Document
            from docarray.typing import ImageUrl
            import numpy as np


            class MyDoc(Document):
                img_url: ImageUrl


            doc = MyDoc(
                img_url="https://upload.wikimedia.org/wikipedia/commons/8/80/"
                "Dag_Sebastian_Ahlander_at_G%C3%B6teborg_Book_Fair_2012b.jpg"
            )

            img_tensor = doc.img_url.load()
            assert isinstance(img_tensor, np.ndarray)

            img_tensor = doc.img_url.load(height=224, width=224)
            assert img_tensor.shape == (224, 224, 3)

            layout = ('C', 'W', 'H')
            img_tensor = doc.img_url.load(height=100, width=200, axis_layout=layout)
            assert img_tensor.shape == (3, 200, 100)


        :param width: width of the image tensor.
        :param height: height of the image tensor.
        :param axis_layout: ordering of the different image axes.
            'H' = height, 'W' = width, 'C' = color channel
        :param timeout: timeout (sec) for urlopen network request.
            Only relevant if URL is not local
        :return: np.ndarray representing the image as RGB values
        """

        buffer = _uri_to_blob(self, timeout=timeout)
        tensor = _to_image_tensor(io.BytesIO(buffer), width=width, height=height)
        return _move_channel_axis(tensor, axis_layout=axis_layout)

    def load_to_bytes(
        self,
        image_format: str = 'png',
        width: Optional[int] = None,
        height: Optional[int] = None,
        timeout: Optional[float] = None,
    ) -> bytes:
        """Load image at URL to bytes (buffer).

        EXAMPLE USAGE

        .. code-block:: python

            from docarray import Document
            from docarray.typing import ImageUrl
            import numpy as np


            class MyDoc(Document):
                img_url: ImageUrl


            doc = MyDoc(
                img_url="https://upload.wikimedia.org/wikipedia/commons/8/80/"
                "Dag_Sebastian_Ahlander_at_G%C3%B6teborg_Book_Fair_2012b.jpg"
            )

            img_tensor = doc.img_url.load_to_bytes(image_format='jpg')
            assert isinstance(img_tensor, bytes)

        :param image_format: File format of the file located the the url.
            Supported formats are `png`, `jpg`, and `jpeg`.
        :param width: Before converting to bytes, resize the image to this width.
        :param height: Before converting to bytes, resize the image to this height.
        :param timeout: timeout (sec) for urlopen network request.
            Only relevant if URL is not local
        :return: The image as bytes (buffer).
        """
        image_tensor = self.load(width=width, height=height, timeout=timeout)
        return _image_tensor_to_bytes(image_tensor, image_format=image_format)


def _image_tensor_to_bytes(arr: np.ndarray, image_format: str) -> bytes:
    """
    Convert image-ndarray to buffer bytes.

    :param arr: Data representations of the png.
    :param image_format: `png` or `jpeg`
    :return: Png in buffer bytes.
    """

    if image_format not in IMAGE_FILE_FORMATS:
        raise ValueError(
            f'image_format must be one of {IMAGE_FILE_FORMATS},'
            f'receiving `{image_format}`'
        )
    if image_format == 'jpg':
        image_format = 'jpeg'  # unify it to ISO standard

    arr = arr.astype(np.uint8).squeeze()

    if arr.ndim == 1:
        # note this should be only used for MNIST/FashionMNIST dataset,
        # because of the nature of these two datasets
        # no other image data should flattened into 1-dim array.
        image_bytes = _png_to_buffer_1d(arr, 28, 28)
    elif arr.ndim == 2:
        from PIL import Image

        im = Image.fromarray(arr).convert('L')
        image_bytes = _pillow_image_to_buffer(im, image_format=image_format.upper())
    elif arr.ndim == 3:
        from PIL import Image

        im = Image.fromarray(arr).convert('RGB')
        image_bytes = _pillow_image_to_buffer(im, image_format=image_format.upper())
    else:
        raise ValueError(
            f'{arr.shape} ndarray can not be converted into an image buffer.'
        )

    return image_bytes


def _png_to_buffer_1d(arr: np.ndarray, width: int, height: int) -> bytes:
    import zlib

    pixels = []
    for p in arr[::-1]:
        pixels.extend([p, p, p, 255])
    buf = bytearray(pixels)

    # reverse the vertical line order and add null bytes at the start
    width_byte_4 = width * 4
    raw_data = b''.join(
        b'\x00' + buf[span : span + width_byte_4]
        for span in range((height - 1) * width_byte_4, -1, -width_byte_4)
    )

    def png_pack(png_tag, data):
        chunk_head = png_tag + data
        return (
            struct.pack('!I', len(data))
            + chunk_head
            + struct.pack('!I', 0xFFFFFFFF & zlib.crc32(chunk_head))
        )

    png_bytes = b''.join(
        [
            b'\x89PNG\r\n\x1a\n',
            png_pack(b'IHDR', struct.pack('!2I5B', width, height, 8, 6, 0, 0, 0)),
            png_pack(b'IDAT', zlib.compress(raw_data, 9)),
            png_pack(b'IEND', b''),
        ]
    )

    return png_bytes


def _pillow_image_to_buffer(image: 'PIL.Image.Image', image_format: str) -> bytes:
    img_byte_arr = io.BytesIO()
    image.save(img_byte_arr, format=image_format)
    img_bytes = img_byte_arr.getvalue()
    return img_bytes


def _move_channel_axis(
    tensor: np.ndarray, axis_layout: Tuple[str, str, str] = ('H', 'W', 'C')
) -> np.ndarray:
    """Moves channel axis around."""
    channel_to_offset = {'H': 0, 'W': 1, 'C': 2}
    permutation = tuple(channel_to_offset[axis] for axis in axis_layout)
    return np.transpose(tensor, permutation)


def _to_image_tensor(
    source: Union[str, bytes, io.BytesIO],
    width: Optional[int] = None,
    height: Optional[int] = None,
) -> 'np.ndarray':
    """
    Convert an image blob to tensor

    :param source: binary blob or file path
    :param width: the width of the image tensor.
    :param height: the height of the tensor.
    :return: image tensor
    """
    from PIL import Image as PILImage

    raw_img = PILImage.open(source)
    if width or height:
        new_width = width or raw_img.width
        new_height = height or raw_img.height
        raw_img = raw_img.resize((new_width, new_height))
    try:
        return np.array(raw_img.convert('RGB'))
    except Exception:
        return np.array(raw_img)

docarray/typing/url/helper.py METASEP
import os
import urllib.parse
import urllib.request
from contextlib import nullcontext


def _uri_to_blob(uri: str, timeout=None) -> bytes:
    """Convert uri to blob
    Internally it reads uri into blob.
    :param uri: the uri of Document
    :param timeout: timeout for urlopen. Only relevant if uri is not local
    :return: blob bytes.
    """
    if urllib.parse.urlparse(uri).scheme in {'http', 'https', 'data'}:
        req = urllib.request.Request(uri, headers={'User-Agent': 'Mozilla/5.0'})
        urlopen_kwargs = {'timeout': timeout} if timeout is not None else {}
        with urllib.request.urlopen(req, **urlopen_kwargs) as fp:
            return fp.read()
    elif os.path.exists(uri):
        with open(uri, 'rb') as fp:
            return fp.read()
    else:
        raise FileNotFoundError(f'`{uri}` is not a URL or a valid local path')


def _get_file_context(file):
    if hasattr(file, 'write'):
        file_ctx = nullcontext(file)
    else:
        file_ctx = open(file, 'wb')

    return file_ctx


def _is_uri(value: str) -> bool:
    scheme = urllib.parse.urlparse(value).scheme
    return (
        (scheme in {'http', 'https'})
        or (scheme in {'data'})
        or os.path.exists(value)
        or os.access(os.path.dirname(value), os.W_OK)
    )


def _is_datauri(value: str) -> bool:
    scheme = urllib.parse.urlparse(value).scheme
    return scheme in {'data'}

docarray/typing/url/any_url.py METASEP
from typing import TYPE_CHECKING, Type, TypeVar

from pydantic import AnyUrl as BaseAnyUrl
from pydantic import errors, parse_obj_as

from docarray.typing.abstract_type import AbstractType

if TYPE_CHECKING:
    from pydantic.networks import Parts

    from docarray.proto import NodeProto

T = TypeVar('T', bound='AnyUrl')


class AnyUrl(BaseAnyUrl, AbstractType):
    host_required = (
        False  # turn off host requirement to allow passing of local paths as URL
    )

    def _to_node_protobuf(self) -> 'NodeProto':
        """Convert Document into a NodeProto protobuf message. This function should
        be called when the Document is nested into another Document that need to
        be converted into a protobuf

        :return: the nested item protobuf message
        """
        from docarray.proto import NodeProto

        return NodeProto(any_url=str(self))

    @classmethod
    def validate_parts(cls, parts: 'Parts', validate_port: bool = True) -> 'Parts':
        """
        A method used to validate parts of a URL.
        Our URLs should be able to function both in local and remote settings.
        Therefore, we allow missing `scheme`, making it possible to pass a file path.
        """
        scheme = parts['scheme']
        if scheme is None:
            pass  # allow missing scheme, unlike pydantic

        elif cls.allowed_schemes and scheme.lower() not in cls.allowed_schemes:
            raise errors.UrlSchemePermittedError(set(cls.allowed_schemes))

        if validate_port:
            cls._validate_port(parts['port'])

        user = parts['user']
        if cls.user_required and user is None:
            raise errors.UrlUserInfoError()

        return parts

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'str') -> T:
        """
        read url from a proto msg
        :param pb_msg:
        :return: url
        """
        return parse_obj_as(cls, pb_msg)

docarray/typing/url/__init__.py METASEP
from docarray.typing.url.any_url import AnyUrl
from docarray.typing.url.image_url import ImageUrl
from docarray.typing.url.text_url import TextUrl
from docarray.typing.url.url_3d.mesh_url import Mesh3DUrl
from docarray.typing.url.url_3d.point_cloud_url import PointCloud3DUrl

__all__ = ['ImageUrl', 'AnyUrl', 'TextUrl', 'Mesh3DUrl', 'PointCloud3DUrl']

docarray/typing/tensor/torch_tensor.py METASEP
import warnings
from copy import copy
from typing import TYPE_CHECKING, Any, Dict, Generic, Tuple, Type, TypeVar, Union, cast

import numpy as np
import torch  # type: ignore

from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic.fields import ModelField
    from pydantic import BaseConfig
    import numpy as np
    from docarray.proto import NdArrayProto, NodeProto
    from docarray.computation.torch_backend import TorchCompBackend

from docarray.document.base_node import BaseNode

T = TypeVar('T', bound='TorchTensor')
ShapeT = TypeVar('ShapeT')

torch_base = type(torch.Tensor)  # type: Any
node_base = type(BaseNode)  # type: Any


class metaTorchAndNode(torch_base, node_base):
    pass


class TorchTensor(
    AbstractTensor, torch.Tensor, Generic[ShapeT], metaclass=metaTorchAndNode
):
    # Subclassing torch.Tensor following the advice from here:
    # https://pytorch.org/docs/stable/notes/extending.html#subclassing-torch-tensor
    """
    Subclass of torch.Tensor, intended for use in a Document.
    This enables (de)serialization from/to protobuf and json, data validation,
    and coersion from compatible types like numpy.ndarray.

    This type can also be used in a parametrized way,
    specifying the shape of the tensor.

    EXAMPLE USAGE

    .. code-block:: python

        from docarray import Document
        from docarray.typing import TorchTensor
        import torch


        class MyDoc(Document):
            tensor: TorchTensor
            image_tensor: TorchTensor[3, 224, 224]


        # create a document with tensors
        doc = MyDoc(
            tensor=torch.zeros(128),
            image_tensor=torch.zeros(3, 224, 224),
        )

        # automatic shape conversion
        doc = MyDoc(
            tensor=torch.zeros(128),
            image_tensor=torch.zeros(224, 224, 3),  # will reshape to (3, 224, 224)
        )

        # !! The following will raise an error due to shape mismatch !!
        doc = MyDoc(
            tensor=torch.zeros(128),
            image_tensor=torch.zeros(224, 224),  # this will fail validation
        )

    """

    __parametrized_meta__ = metaTorchAndNode

    @classmethod
    def __get_validators__(cls):
        # one or more validators may be yielded which will be called in the
        # order to validate the input, each validator will receive as an input
        # the value returned from the previous validator
        yield cls.validate

    @classmethod
    def __docarray_validate_shape__(cls, t: T, shape: Tuple[int]) -> T:  # type: ignore
        if t.shape == shape:
            return t
        else:
            warnings.warn(
                f'Tensor shape mismatch. Reshaping tensor '
                f'of shape {t.shape} to shape {shape}'
            )
            try:
                value = cls.__docarray_from_native__(t.view(shape))
                return cast(T, value)
            except RuntimeError:
                raise ValueError(
                    f'Cannot reshape tensor of ' f'shape {t.shape} to shape {shape}'
                )

    @classmethod
    def validate(
        cls: Type[T],
        value: Union[T, np.ndarray, Any],
        field: 'ModelField',
        config: 'BaseConfig',
    ) -> T:
        if isinstance(value, TorchTensor):
            return cast(T, value)
        elif isinstance(value, torch.Tensor):
            return cls.__docarray_from_native__(value)

        else:
            try:
                arr: torch.Tensor = torch.tensor(value)
                return cls.__docarray_from_native__(arr)
            except Exception:
                pass  # handled below
        raise ValueError(f'Expected a torch.Tensor compatible type, got {type(value)}')

    @classmethod
    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:
        """
        Convert torch Tensor into a json compatible object
        :return: a list representation of the torch tensor
        """
        return self.numpy()  ## might need to  check device later

    def unwrap(self) -> torch.Tensor:
        """
        Return the original torch.Tensor without any memory copy.

        The original view rest intact and is still a Document Tensor
        but the return object is a pure torch Tensor but both object share
        the same memory layout.

        EXAMPLE USAGE
        .. code-block:: python
            from docarray.typing import TorchTensor
            import torch

            t = Tensor.validate(torch.zeros(3, 224, 224), None, None)
            # here t is a docarray Tensor
            t2 = t.unwrap()
            # here t2 is a pure torch.Tensor but t1 is still a Docarray Tensor
            # But both share the same underlying memory


        :return: a torch Tensor
        """
        value = copy(self)  # as unintuitive as it sounds, this
        # does not do any relevant memory copying, just shallow
        # reference to the torch data
        value.__class__ = torch.Tensor  # type: ignore
        return value

    @classmethod
    def __docarray_from_native__(cls: Type[T], value: torch.Tensor) -> T:
        """Create a TorchTensor from a native torch.Tensor

        :param value: the native torch.Tensor
        :return: a TorchTensor
        """
        value.__class__ = cls
        return cast(T, value)

    @classmethod
    def from_ndarray(cls: Type[T], value: np.ndarray) -> T:
        """Create a TorchTensor from a numpy array

        :param value: the numpy array
        :return: a TorchTensor
        """
        return cls.__docarray_from_native__(torch.from_numpy(value))

    def _to_node_protobuf(self: T, field: str = 'torch_tensor') -> 'NodeProto':
        """Convert Document into a NodeProto protobuf message. This function should
        be called when the Document is nested into another Document that need to be
        converted into a protobuf
        :param field: field in which to store the content in the node proto
        :return: the nested item protobuf message
        """
        from docarray.proto import NodeProto

        nd_proto = self.to_protobuf()
        return NodeProto(**{field: nd_proto})

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'NdArrayProto') -> 'T':
        """
        read ndarray from a proto msg
        :param pb_msg:
        :return: a torch tensor
        """
        source = pb_msg.dense
        if source.buffer:
            x = np.frombuffer(bytearray(source.buffer), dtype=source.dtype)
            return cls.from_ndarray(x.reshape(source.shape))
        elif len(source.shape) > 0:
            return cls.from_ndarray(np.zeros(source.shape))
        else:
            raise ValueError(f'proto message {pb_msg} cannot be cast to a TorchTensor')

    def to_protobuf(self) -> 'NdArrayProto':
        """
        transform self into a NdArrayProto protobuf message
        """
        from docarray.proto import NdArrayProto

        nd_proto = NdArrayProto()

        value_np = self.detach().cpu().numpy()
        nd_proto.dense.buffer = value_np.tobytes()
        nd_proto.dense.ClearField('shape')
        nd_proto.dense.shape.extend(list(value_np.shape))
        nd_proto.dense.dtype = value_np.dtype.str

        return nd_proto

    @staticmethod
    def get_comp_backend() -> Type['TorchCompBackend']:
        """Return the computational backend of the tensor"""
        from docarray.computation.torch_backend import TorchCompBackend

        return TorchCompBackend

docarray/typing/tensor/tensor.py METASEP
from typing import Union

from docarray.typing.tensor.ndarray import NdArray

try:
    import torch  # noqa: F401
except ImportError:
    Tensor = Union[NdArray]  # type: ignore

else:
    from docarray.typing.tensor.torch_tensor import TorchTensor  # noqa: F401

    Tensor = Union[NdArray, TorchTensor]  # type: ignore

docarray/typing/tensor/ndarray.py METASEP
import warnings
from typing import (
    TYPE_CHECKING,
    Any,
    Dict,
    Generic,
    List,
    Tuple,
    Type,
    TypeVar,
    Union,
    cast,
)

import numpy as np

from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.computation.numpy_backend import NumpyCompBackend
    from docarray.proto import NdArrayProto, NodeProto

T = TypeVar('T', bound='NdArray')
ShapeT = TypeVar('ShapeT')


class NdArray(AbstractTensor, np.ndarray, Generic[ShapeT]):
    """
    Subclass of np.ndarray, intended for use in a Document.
    This enables (de)serialization from/to protobuf and json, data validation,
    and coersion from compatible types like torch.Tensor.

    This type can also be used in a parametrized way, specifying the shape of the array.

    EXAMPLE USAGE

    .. code-block:: python

        from docarray import Document
        from docarray.typing import NdArray
        import numpy as np


        class MyDoc(Document):
            arr: NdArray
            image_arr: NdArray[3, 224, 224]


        # create a document with tensors
        doc = MyDoc(
            arr=np.zeros((128,)),
            image_arr=np.zeros((3, 224, 224)),
        )
        assert doc.image_arr.shape == (3, 224, 224)

        # automatic shape conversion
        doc = MyDoc(
            arr=np.zeros((128,)),
            image_arr=np.zeros((224, 224, 3)),  # will reshape to (3, 224, 224)
        )
        assert doc.image_arr.shape == (3, 224, 224)

        # !! The following will raise an error due to shape mismatch !!
        doc = MyDoc(
            arr=np.zeros((128,)),
            image_arr=np.zeros((224, 224)),  # this will fail validation
        )
    """

    @classmethod
    def __get_validators__(cls):
        # one or more validators may be yielded which will be called in the
        # order to validate the input, each validator will receive as an input
        # the value returned from the previous validator
        yield cls.validate

    @classmethod
    def __docarray_validate_shape__(cls, t: T, shape: Tuple[int]) -> T:  # type: ignore
        if t.shape == shape:
            return t
        else:
            warnings.warn(
                f'Tensor shape mismatch. Reshaping array '
                f'of shape {t.shape} to shape {shape}'
            )
            try:
                value = cls.__docarray_from_native__(np.reshape(t, shape))
                return cast(T, value)
            except RuntimeError:
                raise ValueError(
                    f'Cannot reshape array of shape {t.shape} to shape {shape}'
                )

    @classmethod
    def validate(
        cls: Type[T],
        value: Union[T, np.ndarray, List[Any], Tuple[Any], Any],
        field: 'ModelField',
        config: 'BaseConfig',
    ) -> T:
        if isinstance(value, np.ndarray):
            return cls.__docarray_from_native__(value)
        elif isinstance(value, NdArray):
            return cast(T, value)
        elif isinstance(value, list) or isinstance(value, tuple):
            try:
                arr_from_list: np.ndarray = np.asarray(value)
                return cls.__docarray_from_native__(arr_from_list)
            except Exception:
                pass  # handled below
        else:
            try:
                arr: np.ndarray = np.ndarray(value)
                return cls.__docarray_from_native__(arr)
            except Exception:
                pass  # handled below
        raise ValueError(f'Expected a numpy.ndarray compatible type, got {type(value)}')

    @classmethod
    def __docarray_from_native__(cls: Type[T], value: np.ndarray) -> T:
        return value.view(cls)

    @classmethod
    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:
        """
        Convert tensor into a json compatible object
        :return: a list representation of the tensor
        """
        return self.unwrap()

    def unwrap(self) -> np.ndarray:
        """
        Return the original ndarray without any memory copy.

        The original view rest intact and is still a Document NdArray
        but the return object is a pure np.ndarray but both object share
        the same memory layout.

        EXAMPLE USAGE
        .. code-block:: python
            from docarray.typing import NdArray
            import numpy as np

            t1 = NdArray.validate(np.zeros((3, 224, 224)), None, None)
            # here t is a docarray TenNdArray
            t2 = t.unwrap()
            # here t2 is a pure np.ndarray but t1 is still a Docarray NdArray
            # But both share the same underlying memory


        :return: a numpy ndarray
        """
        return self.view(np.ndarray)

    def _to_node_protobuf(self: T, field: str = 'ndarray') -> 'NodeProto':
        """Convert itself into a NodeProto protobuf message. This function should
        be called when the Document is nested into another Document that need to be
        converted into a protobuf
        :param field: field in which to store the content in the node proto
        :return: the nested item protobuf message
        """
        from docarray.proto import NodeProto

        nd_proto = self.to_protobuf()
        return NodeProto(**{field: nd_proto})

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'NdArrayProto') -> 'T':
        """
        read ndarray from a proto msg
        :param pb_msg:
        :return: a numpy array
        """
        source = pb_msg.dense
        if source.buffer:
            x = np.frombuffer(bytearray(source.buffer), dtype=source.dtype)
            return cls.__docarray_from_native__(x.reshape(source.shape))
        elif len(source.shape) > 0:
            return cls.__docarray_from_native__(np.zeros(source.shape))
        else:
            raise ValueError(f'proto message {pb_msg} cannot be cast to a NdArray')

    def to_protobuf(self) -> 'NdArrayProto':
        """
        transform self into a NdArrayProto protobuf message
        """
        from docarray.proto import NdArrayProto

        nd_proto = NdArrayProto()

        nd_proto.dense.buffer = self.tobytes()
        nd_proto.dense.ClearField('shape')
        nd_proto.dense.shape.extend(list(self.shape))
        nd_proto.dense.dtype = self.dtype.str

        return nd_proto

    @staticmethod
    def get_comp_backend() -> Type['NumpyCompBackend']:
        """Return the computational backend of the tensor"""
        from docarray.computation.numpy_backend import NumpyCompBackend

        return NumpyCompBackend

docarray/typing/tensor/abstract_tensor.py METASEP
import abc
from abc import ABC
from typing import TYPE_CHECKING, Any, Generic, List, Tuple, Type, TypeVar, Union

from docarray.computation import AbstractComputationalBackend
from docarray.typing.abstract_type import AbstractType

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

T = TypeVar('T', bound='AbstractTensor')
ShapeT = TypeVar('ShapeT')


class AbstractTensor(AbstractType, Generic[ShapeT], ABC):

    __parametrized_meta__ = type

    @classmethod
    @abc.abstractmethod
    def __docarray_validate_shape__(cls, t: T, shape: Tuple[int]) -> T:
        """Every tensor has to implement this method in order to
        enable syntax of the form Tensor[shape].

        It is called when a tensor is assigned to a field of this type.
        i.e. when a tensor is passed to a Document field of type Tensor[shape].

        The intended behaviour is as follows:
        - If the shape of `t` is equal to `shape`, return `t`.
        - If the shape of `t` is not equal to `shape`,
            but can be reshaped to `shape`, return `t` reshaped to `shape`.
        - If the shape of `t` is not equal to `shape`
            and cannot be reshaped to `shape`, raise a ValueError.

        :param t: The tensor to validate.
        :param shape: The shape to validate against.
        :return: The validated tensor.
        """
        ...

    @classmethod
    def __docarray_validate_getitem__(cls, item: Any) -> Tuple[int]:
        """This method validates the input to __class_getitem__.

        It is called at "class creation time",
        i.e. when a class is created with syntax of the form Tensor[shape].

        The default implementation tries to cast any `item` to a tuple of ints.
        A subclass can override this method to implement custom validation logic.

        The output of this is eventually passed to
        {ref}`AbstractTensor.__validate_shape__` as its `shape` argument.

        Raises `ValueError` if the input `item` does not pass validation.

        :param item: The item to validate, passed to __class_getitem__ (`Tensor[item]`).
        :return: The validated item == the target shape of this tensor.
        """
        if isinstance(item, int):
            item = (item,)
        try:
            item = tuple(item)
        except TypeError:
            raise TypeError(f'{item} is not a valid tensor shape.')
        return item

    @classmethod
    def _docarray_create_parametrized_type(cls: Type[T], shape: Tuple[int]):
        shape_str = ', '.join([str(s) for s in shape])

        class _ParametrizedTensor(
            cls,  # type: ignore
            metaclass=cls.__parametrized_meta__,  # type: ignore
        ):
            _docarray_target_shape = shape

            @classmethod
            def validate(
                _cls,
                value: Any,
                field: 'ModelField',
                config: 'BaseConfig',
            ):
                t = super().validate(value, field, config)
                return _cls.__docarray_validate_shape__(t, _cls._docarray_target_shape)

        _ParametrizedTensor.__name__ = f'{cls.__name__}[{shape_str}]'
        _ParametrizedTensor.__qualname__ = f'{cls.__qualname__}[{shape_str}]'

        return _ParametrizedTensor

    def __class_getitem__(cls, item: Any):
        target_shape = cls.__docarray_validate_getitem__(item)
        return cls._docarray_create_parametrized_type(target_shape)

    @classmethod
    def __docarray_stack__(cls: Type[T], seq: Union[List[T], Tuple[T]]) -> T:
        """Stack a sequence of tensors into a single tensor."""
        comp_backend = cls.get_comp_backend()
        # at runtime, 'T' is always the correct input type for .stack()
        # but mypy doesn't know that, so we ignore it here
        return cls.__docarray_from_native__(comp_backend.stack(seq))  # type: ignore

    @classmethod
    @abc.abstractmethod
    def __docarray_from_native__(cls: Type[T], value: Any) -> T:
        """
        Create a DocArray tensor from a tensor that is native to the given framework,
        e.g. from numpy.ndarray or torch.Tensor.
        """
        ...

    @staticmethod
    @abc.abstractmethod
    def get_comp_backend() -> Type[AbstractComputationalBackend]:
        """The computational backend compatible with this tensor type."""
        ...

docarray/typing/tensor/__init__.py METASEP
from docarray.typing.tensor.embedding import Embedding, NdArrayEmbedding
from docarray.typing.tensor.ndarray import NdArray
from docarray.typing.tensor.tensor import Tensor

__all__ = [
    'NdArray',
    'Tensor',
    'Embedding',
    'NdArrayEmbedding',
]

try:
    import torch  # noqa: F401
except ImportError:
    pass
else:
    from docarray.typing.tensor.embedding import TorchEmbedding  # noqa: F401
    from docarray.typing.tensor.torch_tensor import TorchTensor  # noqa: F401

    __all__.extend(['TorchEmbedding', 'TorchTensor'])

docarray/proto/pb2/docarray_pb2.py METASEP
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: docarray.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder

# @@protoc_insertion_point(imports)

_sym_db = _symbol_database.Default()


from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2

DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
    b'\n\x0e\x64ocarray.proto\x12\x08\x64ocarray\x1a\x1cgoogle/protobuf/struct.proto\"A\n\x11\x44\x65nseNdArrayProto\x12\x0e\n\x06\x62uffer\x18\x01 \x01(\x0c\x12\r\n\x05shape\x18\x02 \x03(\r\x12\r\n\x05\x64type\x18\x03 \x01(\t\"g\n\x0cNdArrayProto\x12*\n\x05\x64\x65nse\x18\x01 \x01(\x0b\x32\x1b.docarray.DenseNdArrayProto\x12+\n\nparameters\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x92\x03\n\tNodeProto\x12\x0e\n\x04\x62lob\x18\x01 \x01(\x0cH\x00\x12)\n\x07ndarray\x18\x02 \x01(\x0b\x32\x16.docarray.NdArrayProtoH\x00\x12\x0e\n\x04text\x18\x03 \x01(\tH\x00\x12)\n\x06nested\x18\x04 \x01(\x0b\x32\x17.docarray.DocumentProtoH\x00\x12.\n\x06\x63hunks\x18\x05 \x01(\x0b\x32\x1c.docarray.DocumentArrayProtoH\x00\x12+\n\tembedding\x18\x06 \x01(\x0b\x32\x16.docarray.NdArrayProtoH\x00\x12\x11\n\x07\x61ny_url\x18\x07 \x01(\tH\x00\x12\x13\n\timage_url\x18\x08 \x01(\tH\x00\x12\x12\n\x08text_url\x18\t \x01(\tH\x00\x12\x0c\n\x02id\x18\n \x01(\tH\x00\x12.\n\x0ctorch_tensor\x18\x0b \x01(\x0b\x32\x16.docarray.NdArrayProtoH\x00\x12\x12\n\x08mesh_url\x18\x0c \x01(\tH\x00\x12\x19\n\x0fpoint_cloud_url\x18\r \x01(\tH\x00\x42\t\n\x07\x63ontent\"\x82\x01\n\rDocumentProto\x12/\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32!.docarray.DocumentProto.DataEntry\x1a@\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.docarray.NodeProto:\x02\x38\x01\"?\n\x16\x44ocumentArrayListProto\x12%\n\x04\x64ocs\x18\x01 \x03(\x0b\x32\x17.docarray.DocumentProto\"\x86\x01\n\x0fUnionArrayProto\x12=\n\x0e\x64ocument_array\x18\x01 \x01(\x0b\x32#.docarray.DocumentArrayStackedProtoH\x00\x12)\n\x07ndarray\x18\x02 \x01(\x0b\x32\x16.docarray.NdArrayProtoH\x00\x42\t\n\x07\x63ontent\"\xda\x01\n\x19\x44ocumentArrayStackedProto\x12/\n\x05list_\x18\x01 \x01(\x0b\x32 .docarray.DocumentArrayListProto\x12\x41\n\x07\x63olumns\x18\x02 \x03(\x0b\x32\x30.docarray.DocumentArrayStackedProto.ColumnsEntry\x1aI\n\x0c\x43olumnsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12(\n\x05value\x18\x02 \x01(\x0b\x32\x19.docarray.UnionArrayProto:\x02\x38\x01\"\x88\x01\n\x12\x44ocumentArrayProto\x12\x31\n\x05list_\x18\x01 \x01(\x0b\x32 .docarray.DocumentArrayListProtoH\x00\x12\x34\n\x05stack\x18\x02 \x01(\x0b\x32#.docarray.DocumentArrayStackedProtoH\x00\x42\t\n\x07\x63ontentb\x06proto3'
)

_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'docarray_pb2', globals())
if _descriptor._USE_C_DESCRIPTORS == False:

    DESCRIPTOR._options = None
    _DOCUMENTPROTO_DATAENTRY._options = None
    _DOCUMENTPROTO_DATAENTRY._serialized_options = b'8\001'
    _DOCUMENTARRAYSTACKEDPROTO_COLUMNSENTRY._options = None
    _DOCUMENTARRAYSTACKEDPROTO_COLUMNSENTRY._serialized_options = b'8\001'
    _DENSENDARRAYPROTO._serialized_start = 58
    _DENSENDARRAYPROTO._serialized_end = 123
    _NDARRAYPROTO._serialized_start = 125
    _NDARRAYPROTO._serialized_end = 228
    _NODEPROTO._serialized_start = 231
    _NODEPROTO._serialized_end = 633
    _DOCUMENTPROTO._serialized_start = 636
    _DOCUMENTPROTO._serialized_end = 766
    _DOCUMENTPROTO_DATAENTRY._serialized_start = 702
    _DOCUMENTPROTO_DATAENTRY._serialized_end = 766
    _DOCUMENTARRAYLISTPROTO._serialized_start = 768
    _DOCUMENTARRAYLISTPROTO._serialized_end = 831
    _UNIONARRAYPROTO._serialized_start = 834
    _UNIONARRAYPROTO._serialized_end = 968
    _DOCUMENTARRAYSTACKEDPROTO._serialized_start = 971
    _DOCUMENTARRAYSTACKEDPROTO._serialized_end = 1189
    _DOCUMENTARRAYSTACKEDPROTO_COLUMNSENTRY._serialized_start = 1116
    _DOCUMENTARRAYSTACKEDPROTO_COLUMNSENTRY._serialized_end = 1189
    _DOCUMENTARRAYPROTO._serialized_start = 1192
    _DOCUMENTARRAYPROTO._serialized_end = 1328
# @@protoc_insertion_point(module_scope)

docarray/document/mixins/proto.py METASEP
from typing import TYPE_CHECKING, Any, Dict, Type, TypeVar

from docarray.document.abstract_document import AbstractDocument
from docarray.document.base_node import BaseNode

if TYPE_CHECKING:
    from docarray.proto import DocumentProto, NodeProto


try:
    import torch  # noqa: F401
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True


T = TypeVar('T', bound='ProtoMixin')


class ProtoMixin(AbstractDocument, BaseNode):
    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentProto') -> T:
        """create a Document from a protobuf message"""
        from docarray.typing import (  # TorchTensor,
            ID,
            AnyUrl,
            Embedding,
            ImageUrl,
            Mesh3DUrl,
            NdArray,
            PointCloud3DUrl,
            TextUrl,
        )

        fields: Dict[str, Any] = {}

        for field in pb_msg.data:
            value = pb_msg.data[field]

            content_type = value.WhichOneof('content')

            # this if else statement need to be refactored it is too long
            # the check should be delegated to the type level
            content_type_dict = dict(
                ndarray=NdArray,
                embedding=Embedding,
                any_url=AnyUrl,
                text_url=TextUrl,
                image_url=ImageUrl,
                mesh_url=Mesh3DUrl,
                point_cloud_url=PointCloud3DUrl,
                id=ID,
            )

            if torch_imported:
                content_type_dict['torch_tensor'] = TorchTensor

            if content_type in content_type_dict:
                fields[field] = content_type_dict[content_type].from_protobuf(
                    getattr(value, content_type)
                )
            elif content_type == 'text':
                fields[field] = value.text
            elif content_type == 'nested':
                fields[field] = cls._get_nested_document_class(field).from_protobuf(
                    value.nested
                )  # we get to the parent class
            elif content_type == 'chunks':
                from docarray import DocumentArray

                fields[field] = DocumentArray.from_protobuf(
                    value.chunks
                )  # we get to the parent class
            elif content_type is None:
                fields[field] = None
            else:
                raise ValueError(
                    f'type {content_type} is not supported for deserialization'
                )

        return cls.construct(**fields)

    def to_protobuf(self) -> 'DocumentProto':
        """Convert Document into a Protobuf message.

        :return: the protobuf message
        """
        from docarray.proto import DocumentProto, NodeProto

        data = {}
        for field, value in self:
            try:
                if isinstance(value, BaseNode):
                    nested_item = value._to_node_protobuf()

                elif type(value) is str:
                    nested_item = NodeProto(text=value)

                elif type(value) is bytes:
                    nested_item = NodeProto(blob=value)
                elif value is None:
                    nested_item = NodeProto()
                else:
                    raise ValueError(f'field {field} with {value} is not supported')

                data[field] = nested_item

            except RecursionError as ex:
                if len(ex.args) >= 1:
                    ex.args = (
                        (
                            f'Field `{field}` contains cyclic reference in memory. '
                            'Could it be your Document is referring to itself?'
                        ),
                    )
                raise
            except Exception as ex:
                if len(ex.args) >= 1:
                    ex.args = (f'Field `{field}` is problematic',) + ex.args
                raise

        return DocumentProto(data=data)

    def _to_node_protobuf(self) -> 'NodeProto':
        from docarray.proto import NodeProto

        """Convert Document into a NodeProto protobuf message. This function should be
        called when the Document is nest into another Document that need to be
        converted into a protobuf

        :return: the nested item protobuf message
        """
        return NodeProto(nested=self.to_protobuf())

docarray/document/mixins/__init__.py METASEP
from docarray.document.mixins.proto import ProtoMixin

__all__ = ['ProtoMixin']

docarray/document/io/json.py METASEP
import orjson


def _default_orjson(obj):
    """
    default option for orjson dumps. It will call _to_json_compatible
    from docarray typing object that expose such method.
    :param obj:
    :return: return a json compatible object
    """

    if getattr(obj, '_to_json_compatible'):
        return obj._to_json_compatible()
    else:
        return obj


def orjson_dumps(v, *, default=None):
    # 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()

docarray/document/io/__init__.py METASEP

tests/__init__.py METASEP
from pathlib import Path

REPO_ROOT_DIR = Path(__file__).parent.parent.absolute()
TOYDATA_DIR = REPO_ROOT_DIR / 'tests' / 'toydata'

docs/conf.py METASEP
import os
import re
import sys
from os import path
from typing import Tuple, Any, Iterator

sys.path.insert(0, path.abspath('..'))

project = 'DocArray'
slug = re.sub(r'\W+', '-', project.lower())
author = 'Jina AI'
copyright = 'DocArray is a trademark of LF Projects, LLC.'
source_suffix = ['.rst', '.md']
master_doc = 'index'
language = 'en'
repo_dir = '../'
autodoc_default_options = {"members": True, "inherited-members": True}

try:
    if 'DOCARRAY_VERSION' not in os.environ:
        pkg_name = 'docarray'
        libinfo_py = path.join(repo_dir, pkg_name, '__init__.py')
        libinfo_content = open(libinfo_py, 'r').readlines()
        version_line = [
            l.strip() for l in libinfo_content if l.startswith('__version__')
        ][0]
        exec(version_line)
    else:
        __version__ = os.environ['DOCARRAY_VERSION']
except FileNotFoundError:
    __version__ = '0.0.0'

version = __version__
release = __version__

templates_path = ['_templates']
exclude_patterns = [
    '_build',
    'Thumbs.db',
    '.DS_Store',
    'tests',
    'page_templates',
    '.github',
]
pygments_style = 'rainbow_dash'
html_theme = 'furo'

base_url = '/'
html_baseurl = 'https://docarray.jina.ai'
sitemap_url_scheme = '{link}'
sitemap_locales = [None]
sitemap_filename = "sitemap.xml"

html_theme_options = {
    'light_logo': 'logo-light.svg',
    'dark_logo': 'logo-dark.svg',
    "sidebar_hide_name": True,
    "light_css_variables": {
        "color-brand-primary": "#009191",
        "color-brand-content": "#009191",
    },
    "dark_css_variables": {
        "color-brand-primary": "#FBCB67",
        "color-brand-content": "#FBCB67",
    },
    # PLEASE DO NOT DELETE the empty line between `start-announce` and `end-announce`
    # PLEASE DO NOT DELETE `start-announce`/ `end-announce` it is used for our dev bot to inject announcement from GH
    # start-announce
    # end-announce
}

html_static_path = ['_static']
html_extra_path = ['html_extra']
html_css_files = [
    'main.css',
    'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta2/css/all.min.css',
]
html_js_files = [
    'https://cdn.jsdelivr.net/npm/vue@2/dist/vue.min.js',
    'https://cdn.jsdelivr.net/npm/qabot@0.4',
]
htmlhelp_basename = slug
html_show_sourcelink = False
html_favicon = '_static/favicon.png'

latex_documents = [(master_doc, f'{slug}.tex', project, author, 'manual')]
man_pages = [(master_doc, slug, project, [author], 1)]
texinfo_documents = [
    (master_doc, slug, project, author, slug, project, 'Miscellaneous')
]
epub_title = project
epub_exclude_files = ['search.html']

# -- Extension configuration -------------------------------------------------

extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.autosummary',
    'sphinx_autodoc_typehints',
    'sphinx.ext.viewcode',
    'sphinx.ext.coverage',
    'sphinxcontrib.apidoc',
    'sphinxarg.ext',
    'sphinx_copybutton',
    'sphinx_sitemap',
    'sphinx.ext.intersphinx',
    'sphinxext.opengraph',
    'notfound.extension',
    'myst_parser',
    'sphinx_design',
    'sphinx_inline_tabs',
]

myst_enable_extensions = ['colon_fence', 'substitution']

# -- Custom 404 page

# sphinx-notfound-page
# https://github.com/readthedocs/sphinx-notfound-page
notfound_context = {
    'title': 'Page Not Found',
    'body': '''
<h1>Page Not Found</h1>
<p>Oops, we couldn't find that page. </p>
<p>You can try "asking our docs" on the right corner of the page to find answer.</p>
<p>Otherwise, <a href="https://github.com/jina-ai/docarray/">please create a Github issue</a> and one of our team will respond.</p>

''',
}
notfound_no_urls_prefix = True

apidoc_module_dir = repo_dir
apidoc_output_dir = 'api'
apidoc_excluded_paths = ['tests', 'legacy', 'hub', 'toy*', 'setup.py']
apidoc_separate_modules = True
apidoc_extra_args = ['-t', 'template/']
autodoc_member_order = 'bysource'
autodoc_mock_imports = ['argparse', 'numpy', 'np', 'tensorflow', 'scipy']
autoclass_content = 'both'
set_type_checking_flag = False
html_last_updated_fmt = ''
nitpicky = True
nitpick_ignore = [('py:class', 'type')]
linkcheck_ignore = [
    # Avoid link check on local uri
    'http://0.0.0.0:*',
    'pods/encode.yml',
    'https://github.com/jina-ai/docarray/commit/*',
    '.github/*',
    'extra-requirements.txt',
    'fastentrypoints.py' '../../101',
    '../../102',
    'http://www.twinsun.com/tz/tz-link.htm',  # Broken link from pytz library
    'https://urllib3.readthedocs.io/en/latest/contrib.html#google-app-engine',  # Broken link from urllib3 library
    'https://linuxize.com/post/how-to-add-swap-space-on-ubuntu-20-04/',
    # This link works but gets 403 error on linkcheck
]
linkcheck_timeout = 20
linkcheck_retries = 2
linkcheck_anchors = False

ogp_site_url = 'https://docarray.jina.ai/'
ogp_image = 'https://docarray.jina.ai/_static/banner.png'
ogp_use_first_image = True
ogp_description_length = 300
ogp_type = 'website'
ogp_site_name = (
    f'DocArray {os.environ.get("SPHINX_MULTIVERSION_VERSION", version)} Documentation'
)

ogp_custom_meta_tags = [
    '<meta name="twitter:card" content="summary_large_image">',
    '<meta name="twitter:site" content="@JinaAI_">',
    '<meta name="twitter:creator" content="@JinaAI_">',
    '<meta name="description" content="DocArray is a library for nested, unstructured data such as text, image, audio, video, 3D mesh.">',
    '<meta property="og:description" content="DocArray is a library for nested, unstructured data such as text, image, audio, video, 3D mesh. It allows deep learning engineers to easily preprocess, embed, search, recommend and transfer the data.">',
    '''
    <!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-48WE9V68SD"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-48WE9V68SD');
</script>

<script async defer src="https://buttons.github.io/buttons.js"></script>
    ''',
]


def add_server_address(app):
    # This makes variable `server_address` available to docbot.js
    server_address = app.config['server_address']
    js_text = "var server_address = '%s';" % server_address
    app.add_js_file(None, body=js_text)


def configure_qa_bot_ui(app):
    # This sets the server address to <qa-bot>
    server_address = app.config['server_address']
    js_text = (
        """
        document.addEventListener('DOMContentLoaded', function() { 
            document.querySelector('qa-bot').setAttribute('server', '%s');
        });
        """
        % server_address
    )
    app.add_js_file(None, body=js_text)


def setup(app):
    from sphinx.domains.python import PyField
    from sphinx.util.docfields import Field
    from sphinx.locale import _

    app.add_object_type(
        'confval',
        'confval',
        objname='configuration value',
        indextemplate='pair: %s; configuration value',
        doc_field_types=[
            PyField(
                'type',
                label=_('Type'),
                has_arg=False,
                names=('type',),
                bodyrolename='class',
            ),
            Field(
                'default',
                label=_('Default'),
                has_arg=False,
                names=('default',),
            ),
        ],
    )
    app.add_config_value(
        name='server_address',
        default=os.getenv(
            'JINA_DOCSBOT_SERVER', 'https://jina-ai-docarray.docsqa.jina.ai'
        ),
        rebuild='',
    )
    app.connect('builder-inited', configure_qa_bot_ui)


from sphinx.ext.autodoc.mock import _make_subclass


# This is a very ugly ugly hack but otherwise autodoc is failing with our meta class
# for TorchTensor
class _MockObjectTorchMeta:

    """Used by autodoc_mock_imports."""

    __display_name__ = '_MockObject'
    __name__ = ''
    __sphinx_mock__ = True
    __sphinx_decorator_args__: Tuple[Any, ...] = ()

    def __new__(cls, *args: Any, **kwargs: Any) -> Any:
        if len(args) == 3 and isinstance(args[1], tuple):
            superclass = args[1][-1].__class__
            if superclass is cls:
                # subclassing MockObject
                return _make_subclass(
                    args[0],
                    superclass.__display_name__,
                    superclass=superclass,
                    attributes=args[2],
                )
        try:
            return super().__new__(cls)
        except TypeError:
            return super().__new__(cls, name='torch', bases=(), namespace={})

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        self.__qualname__ = self.__name__

    def __len__(self) -> int:
        return 0

    def __contains__(self, key: str) -> bool:
        return False

    def __iter__(self) -> Iterator:
        return iter([])

    def __mro_entries__(self, bases: Tuple) -> Tuple:
        return (self.__class__,)

    def __getitem__(self, key: Any) -> "_MockObject":
        return _make_subclass(str(key), self.__display_name__, self.__class__)()

    def __getattr__(self, key: str) -> "_MockObject":
        return _make_subclass(key, self.__display_name__, self.__class__)()

    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        call = self.__class__()
        call.__sphinx_decorator_args__ = args
        return call

    def __repr__(self) -> str:
        return self.__display_name__


sys.modules['torch'] = _MockObjectTorchMeta()

docarray/proto/docarray.proto METASEP
syntax = "proto3";
import "google/protobuf/struct.proto";

package docarray;

/**
 * Represents a (quantized) dense n-dim array
 */
message DenseNdArrayProto {
  // the actual array data, in bytes
  bytes buffer = 1;

  // the shape (dimensions) of the array
  repeated uint32 shape = 2;

  // the data type of the array
  string dtype = 3;
}

/**
 * Represents a general n-dim array, can be either dense or sparse
 */
message NdArrayProto {
  DenseNdArrayProto dense = 1; // dense representation of the ndarray

  google.protobuf.Struct parameters = 2;
}


//
message NodeProto {



  oneof content {
    bytes blob = 1;

    // the ndarray of the image/audio/video document
    NdArrayProto ndarray = 2;

    // a text
    string text = 3;

    // a sub Document
    DocumentProto nested = 4;

    // a sub DocumentArray
    DocumentArrayProto chunks = 5;

    NdArrayProto embedding = 6;

    string any_url = 7;

    string image_url = 8;

    string text_url = 9;

    string id = 10;

    NdArrayProto torch_tensor = 11;

    string mesh_url = 12;

    string point_cloud_url = 13;


  }

}



/**
 * Represents a Document
 */
message DocumentProto {

  map<string, NodeProto> data = 1;

}

message DocumentArrayListProto {
    repeated DocumentProto docs = 1; // a list of Documents
}

message UnionArrayProto { // represent the column of the da stacked
    oneof content {
        DocumentArrayStackedProto document_array = 1;
        NdArrayProto ndarray = 2;
    }
}

message DocumentArrayStackedProto{
  DocumentArrayListProto list_ = 1; // a list of Documents
  map<string, UnionArrayProto> columns = 2; // a dict of columns
}

message DocumentArrayProto{
  oneof content {
    DocumentArrayListProto list_ = 1;
    DocumentArrayStackedProto stack = 2;
  }
}
docarray/proto/build-proto.sh METASEP
#!/usr/bin/env bash
set -ex

# Do NOT use this directly, use jinaai/protogen image
# use jinaai/protogen:3.21 in order to use compiler version == 21 (creates pb/docarray_pb2.py)
# and use jinaai/protogen:latest to use compiler version <= 20 (creates pb2/docarray_pb2.py)
# make sure to use jinaai/protogen:3.21 to avoid overriting the module
#
# current dir: docarray/docarray
# run the following in bash:
# docker run -v $(pwd)/proto:/jina/proto jinaai/protogen
# finally, set back owner of the generated files using: sudo chown -R $(id -u ${USER}):$(id -g ${USER}) .

SRC_DIR=./
MODULE=docarray
SRC_NAME="${MODULE}.proto"
COMP_OUT_NAME="${MODULE}_pb2.py"

OUT_FOLDER="${2:-pb2}/"

VER_FILE=../__init__.py

if [ "$#" -ne 1 ] && [ "$#" -ne 2 ]; then
    echo "Error: Please specify the [PATH_TO_GRPC_PYTHON_PLUGIN], refer more details at " \
      "https://docarray.jina.ai/"
    printf "\n"
    echo "USAGE:"
    printf "\t"
    echo "bash ./build-proto.sh [PATH_TO_GRPC_PYTHON_PLUGIN]"
    exit 1
fi

PLUGIN_PATH=${1}  # /Volumes/TOSHIBA-4T/Documents/grpc/bins/opt/grpc_python_plugin

printf "\e[1;33mgenerating protobuf and grpc python interface\e[0m\n"

protoc -I ${SRC_DIR} --python_out="${SRC_DIR}${OUT_FOLDER}" ${SRC_DIR}${SRC_NAME}

pyproject.toml METASEP
[tool.poetry]
name = "docarray"
version = '2022.11.29.alpha'
description='The data structure for unstructured data'
authors=['DocArray']
license='Apache 2.0'

[tool.poetry.dependencies]
python = "^3.8"
pydantic = "^1.10.2"
numpy = "^1.23.4"
protobuf = { version = "^4.21.9", optional = true }
torch = { version = "^1.0.0", optional = true }
orjson = "^3.8.2"
pillow = {version = "^9.3.0", optional = true }
types-pillow = {version = "^9.3.0.1", optional = true }
trimesh = {version = "^3.17.1", optional = true}

[tool.poetry.extras]
common = ["protobuf"]
torch = ["torch"]
image = ["pillow", "types-pillow"]
mesh = ["trimesh"]

[tool.poetry.dev-dependencies]
pytest = "^6.1"
pre-commit = "^2.20.0"
jupyterlab = "^3.5.0"
mypy = "^0.990"
types-protobuf = "^3.20.4"
black = "^22.10.0"
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"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.mypy]
exclude = ['docarray/proto']
plugins = "pydantic.mypy"

[[tool.mypy.overrides]]
module = "trimesh"
ignore_missing_imports = true

[tool.black]
skip-string-normalization = true # equivalent to black -S
exclude = 'docarray/proto/pb2/*'


[tool.isort]
skip_glob= ['docarray/proto/pb2/*']

[tool.ruff]
exclude = ['docarray/proto/pb2/*', 'docs/*']

[tool.pytest.ini_options]
markers = [
    "slow: marks tests as slow (deselect with '-m \"not slow\"')",
    "internet: marks tests as requiring internet (deselect with '-m \"not internet\"')",
    "asyncio: marks that run async tests",

]
poetry.lock METASEP
[[package]]
name = "anyio"
version = "3.6.2"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
category = "dev"
optional = false
python-versions = ">=3.6.2"

[package.dependencies]
idna = ">=2.8"
sniffio = ">=1.1"

[package.extras]
doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
trio = ["trio (>=0.16,<0.22)"]

[[package]]
name = "appnope"
version = "0.1.3"
description = "Disable App Nap on macOS >= 10.9"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "argon2-cffi"
version = "21.3.0"
description = "The secure Argon2 password hashing algorithm."
category = "dev"
optional = false
python-versions = ">=3.6"

[package.dependencies]
argon2-cffi-bindings = "*"

[package.extras]
dev = ["cogapp", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "pre-commit", "pytest", "sphinx", "sphinx-notfound-page", "tomli"]
docs = ["furo", "sphinx", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"]

[[package]]
name = "argon2-cffi-bindings"
version = "21.2.0"
description = "Low-level CFFI bindings for Argon2"
category = "dev"
optional = false
python-versions = ">=3.6"

[package.dependencies]
cffi = ">=1.0.1"

[package.extras]
dev = ["cogapp", "pre-commit", "pytest", "wheel"]
tests = ["pytest"]

[[package]]
name = "asttokens"
version = "2.1.0"
description = "Annotate AST trees with source code positions"
category = "dev"
optional = false
python-versions = "*"

[package.dependencies]
six = "*"

[package.extras]
test = ["astroid (<=2.5.3)", "pytest"]

[[package]]
name = "atomicwrites"
version = "1.4.1"
description = "Atomic file writes."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[[package]]
name = "attrs"
version = "22.1.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = ">=3.5"

[package.extras]
dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]

[[package]]
name = "babel"
version = "2.11.0"
description = "Internationalization utilities"
category = "dev"
optional = false
python-versions = ">=3.6"

[package.dependencies]
pytz = ">=2015.7"

[[package]]
name = "backcall"
version = "0.2.0"
description = "Specifications for callback functions passed in to an API"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "beautifulsoup4"
version = "4.11.1"
description = "Screen-scraping library"
category = "dev"
optional = false
python-versions = ">=3.6.0"

[package.dependencies]
soupsieve = ">1.2"

[package.extras]
html5lib = ["html5lib"]
lxml = ["lxml"]

[[package]]
name = "black"
version = "22.10.0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}

[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]

[[package]]
name = "bleach"
version = "5.0.1"
description = "An easy safelist-based HTML-sanitizing tool."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
six = ">=1.9.0"
webencodings = "*"

[package.extras]
css = ["tinycss2 (>=1.1.0,<1.2)"]
dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"]

[[package]]
name = "certifi"
version = "2022.9.24"
description = "Python package for providing Mozilla's CA Bundle."
category = "dev"
optional = false
python-versions = ">=3.6"

[[package]]
name = "cffi"
version = "1.15.1"
description = "Foreign Function Interface for Python calling C code."
category = "dev"
optional = false
python-versions = "*"

[package.dependencies]
pycparser = "*"

[[package]]
name = "cfgv"
version = "3.3.1"
description = "Validate configuration and produce human readable error messages."
category = "dev"
optional = false
python-versions = ">=3.6.1"

[[package]]
name = "charset-normalizer"
version = "2.1.1"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "dev"
optional = false
python-versions = ">=3.6.0"

[package.extras]
unicode-backport = ["unicodedata2"]

[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}

[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"

[[package]]
name = "debugpy"
version = "1.6.3"
description = "An implementation of the Debug Adapter Protocol for Python"
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "decorator"
version = "5.1.1"
description = "Decorators for Humans"
category = "dev"
optional = false
python-versions = ">=3.5"

[[package]]
name = "defusedxml"
version = "0.7.1"
description = "XML bomb protection for Python stdlib modules"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"

[[package]]
name = "distlib"
version = "0.3.6"
description = "Distribution utilities"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "entrypoints"
version = "0.4"
description = "Discover and load entry points from installed packages."
category = "dev"
optional = false
python-versions = ">=3.6"

[[package]]
name = "executing"
version = "1.2.0"
description = "Get the currently executing AST node of a frame, and other information"
category = "dev"
optional = false
python-versions = "*"

[package.extras]
tests = ["asttokens", "littleutils", "pytest", "rich"]

[[package]]
name = "fastapi"
version = "0.87.0"
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
starlette = "0.21.0"

[package.extras]
all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.114)", "uvicorn[standard] (>=0.12.0,<0.19.0)"]
doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer[all] (>=0.6.1,<0.7.0)"]
test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.8.0)", "coverage[toml] (>=6.5.0,<7.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.114)", "sqlalchemy (>=1.3.18,<=1.4.41)", "types-orjson (==3.6.2)", "types-ujson (==5.5.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"]

[[package]]
name = "fastjsonschema"
version = "2.16.2"
description = "Fastest Python implementation of JSON schema"
category = "dev"
optional = false
python-versions = "*"

[package.extras]
devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"]

[[package]]
name = "filelock"
version = "3.8.0"
description = "A platform independent file lock."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"]
testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"]

[[package]]
name = "h11"
version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "httpcore"
version = "0.16.1"
description = "A minimal low-level HTTP client."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
anyio = ">=3.0,<5.0"
certifi = "*"
h11 = ">=0.13,<0.15"
sniffio = ">=1.0.0,<2.0.0"

[package.extras]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (>=1.0.0,<2.0.0)"]

[[package]]
name = "httpx"
version = "0.23.1"
description = "The next generation HTTP client."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
certifi = "*"
httpcore = ">=0.15.0,<0.17.0"
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
sniffio = "*"

[package.extras]
brotli = ["brotli", "brotlicffi"]
cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (>=1.0.0,<2.0.0)"]

[[package]]
name = "identify"
version = "2.5.8"
description = "File identification library for Python"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.extras]
license = ["ukkonen"]

[[package]]
name = "idna"
version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "dev"
optional = false
python-versions = ">=3.5"

[[package]]
name = "importlib-metadata"
version = "5.0.0"
description = "Read metadata from Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
zipp = ">=0.5"

[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
perf = ["ipython"]
testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]

[[package]]
name = "importlib-resources"
version = "5.10.0"
description = "Read resources from Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}

[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]

[[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "ipykernel"
version = "6.17.1"
description = "IPython Kernel for Jupyter"
category = "dev"
optional = false
python-versions = ">=3.8"

[package.dependencies]
appnope = {version = "*", markers = "platform_system == \"Darwin\""}
debugpy = ">=1.0"
ipython = ">=7.23.1"
jupyter-client = ">=6.1.12"
matplotlib-inline = ">=0.1"
nest-asyncio = "*"
packaging = "*"
psutil = "*"
pyzmq = ">=17"
tornado = ">=6.1"
traitlets = ">=5.1.0"

[package.extras]
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt"]
test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-cov", "pytest-timeout"]

[[package]]
name = "ipython"
version = "8.6.0"
description = "IPython: Productive Interactive Computing"
category = "dev"
optional = false
python-versions = ">=3.8"

[package.dependencies]
appnope = {version = "*", markers = "sys_platform == \"darwin\""}
backcall = "*"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
decorator = "*"
jedi = ">=0.16"
matplotlib-inline = "*"
pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
pickleshare = "*"
prompt-toolkit = ">3.0.1,<3.1.0"
pygments = ">=2.4.0"
stack-data = "*"
traitlets = ">=5"

[package.extras]
all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.20)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"]
black = ["black"]
doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"]
kernel = ["ipykernel"]
nbconvert = ["nbconvert"]
nbformat = ["nbformat"]
notebook = ["ipywidgets", "notebook"]
parallel = ["ipyparallel"]
qtconsole = ["qtconsole"]
test = ["pytest (<7.1)", "pytest-asyncio", "testpath"]
test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.20)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"]

[[package]]
name = "ipython-genutils"
version = "0.2.0"
description = "Vestigial utilities from IPython"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "isort"
version = "5.10.1"
description = "A Python utility / library to sort Python imports."
category = "dev"
optional = false
python-versions = ">=3.6.1,<4.0"

[package.extras]
colors = ["colorama (>=0.4.3,<0.5.0)"]
pipfile-deprecated-finder = ["pipreqs", "requirementslib"]
plugins = ["setuptools"]
requirements-deprecated-finder = ["pip-api", "pipreqs"]

[[package]]
name = "jedi"
version = "0.18.1"
description = "An autocompletion tool for Python that can be used for text editors."
category = "dev"
optional = false
python-versions = ">=3.6"

[package.dependencies]
parso = ">=0.8.0,<0.9.0"

[package.extras]
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"]

[[package]]
name = "jinja2"
version = "3.1.2"
description = "A very fast and expressive template engine."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
MarkupSafe = ">=2.0"

[package.extras]
i18n = ["Babel (>=2.7)"]

[[package]]
name = "json5"
version = "0.9.10"
description = "A Python implementation of the JSON5 data format."
category = "dev"
optional = false
python-versions = "*"

[package.extras]
dev = ["hypothesis"]

[[package]]
name = "jsonschema"
version = "4.17.0"
description = "An implementation of JSON Schema validation for Python"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
attrs = ">=17.4.0"
importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""}
pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""}
pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2"

[package.extras]
format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"]

[[package]]
name = "jupyter-client"
version = "7.4.6"
description = "Jupyter protocol implementation and client libraries"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
entrypoints = "*"
jupyter-core = ">=4.9.2"
nest-asyncio = ">=1.5.4"
python-dateutil = ">=2.8.2"
pyzmq = ">=23.0"
tornado = ">=6.2"
traitlets = "*"

[package.extras]
doc = ["ipykernel", "myst-parser", "sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"]
test = ["codecov", "coverage", "ipykernel (>=6.12)", "ipython", "mypy", "pre-commit", "pytest", "pytest-asyncio (>=0.18)", "pytest-cov", "pytest-timeout"]

[[package]]
name = "jupyter-core"
version = "5.0.0"
description = "Jupyter core package. A base package on which Jupyter projects rely."
category = "dev"
optional = false
python-versions = ">=3.8"

[package.dependencies]
platformdirs = "*"
pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""}
traitlets = "*"

[package.extras]
test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"]

[[package]]
name = "jupyter-server"
version = "1.23.2"
description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
anyio = ">=3.1.0,<4"
argon2-cffi = "*"
jinja2 = "*"
jupyter-client = ">=6.1.12"
jupyter-core = ">=4.7.0"
nbconvert = ">=6.4.4"
nbformat = ">=5.2.0"
packaging = "*"
prometheus-client = "*"
pywinpty = {version = "*", markers = "os_name == \"nt\""}
pyzmq = ">=17"
Send2Trash = "*"
terminado = ">=0.8.3"
tornado = ">=6.1.0"
traitlets = ">=5.1"
websocket-client = "*"

[package.extras]
test = ["coverage", "ipykernel", "pre-commit", "pytest (>=7.0)", "pytest-console-scripts", "pytest-cov", "pytest-mock", "pytest-timeout", "pytest-tornasync", "requests"]

[[package]]
name = "jupyterlab"
version = "3.5.0"
description = "JupyterLab computational environment"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
ipython = "*"
jinja2 = ">=2.1"
jupyter-core = "*"
jupyter-server = ">=1.16.0,<3"
jupyterlab-server = ">=2.10,<3.0"
nbclassic = "*"
notebook = "<7"
packaging = "*"
tomli = "*"
tornado = ">=6.1.0"

[package.extras]
test = ["check-manifest", "coverage", "jupyterlab-server[test]", "pre-commit", "pytest (>=6.0)", "pytest-check-links (>=0.5)", "pytest-console-scripts", "pytest-cov", "requests", "requests-cache", "virtualenv"]
ui-tests = ["build"]

[[package]]
name = "jupyterlab-pygments"
version = "0.2.2"
description = "Pygments theme using JupyterLab CSS variables"
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "jupyterlab-server"
version = "2.16.3"
description = "A set of server components for JupyterLab and JupyterLab like applications."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
babel = "*"
importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""}
jinja2 = ">=3.0.3"
json5 = "*"
jsonschema = ">=3.0.1"
jupyter-server = ">=1.8,<3"
packaging = "*"
requests = "*"

[package.extras]
docs = ["autodoc-traits", "docutils (<0.19)", "jinja2 (<3.1.0)", "mistune (<1)", "myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-copybutton", "sphinxcontrib-openapi"]
openapi = ["openapi-core (>=0.14.2)", "ruamel-yaml"]
test = ["codecov", "ipykernel", "jupyter-server[test]", "openapi-core (>=0.14.2,<0.15.0)", "openapi-spec-validator (<0.5)", "pytest (>=7.0)", "pytest-console-scripts", "pytest-cov", "requests-mock", "ruamel-yaml", "strict-rfc3339"]

[[package]]
name = "markupsafe"
version = "2.1.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "matplotlib-inline"
version = "0.1.6"
description = "Inline Matplotlib backend for Jupyter"
category = "dev"
optional = false
python-versions = ">=3.5"

[package.dependencies]
traitlets = "*"

[[package]]
name = "mistune"
version = "2.0.4"
description = "A sane Markdown parser with useful plugins and renderers"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "mypy"
version = "0.990"
description = "Optional static typing for Python"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
mypy-extensions = ">=0.4.3"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
typing-extensions = ">=3.10"

[package.extras]
dmypy = ["psutil (>=4.0)"]
install-types = ["pip"]
python2 = ["typed-ast (>=1.4.0,<2)"]
reports = ["lxml"]

[[package]]
name = "mypy-extensions"
version = "0.4.3"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "nbclassic"
version = "0.4.8"
description = "A web-based notebook environment for interactive computing"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
argon2-cffi = "*"
ipykernel = "*"
ipython-genutils = "*"
jinja2 = "*"
jupyter-client = ">=6.1.1"
jupyter-core = ">=4.6.1"
jupyter-server = ">=1.8"
nbconvert = ">=5"
nbformat = "*"
nest-asyncio = ">=1.5"
notebook-shim = ">=0.1.0"
prometheus-client = "*"
pyzmq = ">=17"
Send2Trash = ">=1.8.0"
terminado = ">=0.8.3"
tornado = ">=6.1"
traitlets = ">=4.2.1"

[package.extras]
docs = ["myst-parser", "nbsphinx", "sphinx", "sphinx-rtd-theme", "sphinxcontrib-github-alt"]
json-logging = ["json-logging"]
test = ["coverage", "nbval", "pytest", "pytest-cov", "pytest-playwright", "pytest-tornasync", "requests", "requests-unixsocket", "testpath"]

[[package]]
name = "nbclient"
version = "0.7.0"
description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor."
category = "dev"
optional = false
python-versions = ">=3.7.0"

[package.dependencies]
jupyter-client = ">=6.1.5"
nbformat = ">=5.0"
nest-asyncio = "*"
traitlets = ">=5.2.2"

[package.extras]
sphinx = ["Sphinx (>=1.7)", "autodoc-traits", "mock", "moto", "myst-parser", "sphinx-book-theme"]
test = ["black", "check-manifest", "flake8", "ipykernel", "ipython", "ipywidgets", "mypy", "nbconvert", "pip (>=18.1)", "pre-commit", "pytest (>=4.1)", "pytest-asyncio", "pytest-cov (>=2.6.1)", "setuptools (>=60.0)", "testpath", "twine (>=1.11.0)", "xmltodict"]

[[package]]
name = "nbconvert"
version = "7.2.5"
description = "Converting Jupyter Notebooks"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
beautifulsoup4 = "*"
bleach = "*"
defusedxml = "*"
importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""}
jinja2 = ">=3.0"
jupyter-core = ">=4.7"
jupyterlab-pygments = "*"
markupsafe = ">=2.0"
mistune = ">=2.0.3,<3"
nbclient = ">=0.5.0"
nbformat = ">=5.1"
packaging = "*"
pandocfilters = ">=1.4.1"
pygments = ">=2.4.1"
tinycss2 = "*"
traitlets = ">=5.0"

[package.extras]
all = ["ipykernel", "ipython", "ipywidgets (>=7)", "myst-parser", "nbsphinx (>=0.2.12)", "pre-commit", "pyppeteer (>=1,<1.1)", "pyqtwebengine (>=5.15)", "pytest", "pytest-cov", "pytest-dependency", "sphinx (==5.0.2)", "sphinx-rtd-theme", "tornado (>=6.1)"]
docs = ["ipython", "myst-parser", "nbsphinx (>=0.2.12)", "sphinx (==5.0.2)", "sphinx-rtd-theme"]
qtpdf = ["pyqtwebengine (>=5.15)"]
qtpng = ["pyqtwebengine (>=5.15)"]
serve = ["tornado (>=6.1)"]
test = ["ipykernel", "ipywidgets (>=7)", "pre-commit", "pyppeteer (>=1,<1.1)", "pytest", "pytest-cov", "pytest-dependency"]
webpdf = ["pyppeteer (>=1,<1.1)"]

[[package]]
name = "nbformat"
version = "5.7.0"
description = "The Jupyter Notebook format"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
fastjsonschema = "*"
jsonschema = ">=2.6"
jupyter-core = "*"
traitlets = ">=5.1"

[package.extras]
test = ["check-manifest", "pep440", "pre-commit", "pytest", "testpath"]

[[package]]
name = "nest-asyncio"
version = "1.5.6"
description = "Patch asyncio to allow nested event loops"
category = "dev"
optional = false
python-versions = ">=3.5"

[[package]]
name = "nodeenv"
version = "1.7.0"
description = "Node.js virtual environment builder"
category = "dev"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"

[package.dependencies]
setuptools = "*"

[[package]]
name = "notebook"
version = "6.5.2"
description = "A web-based notebook environment for interactive computing"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
argon2-cffi = "*"
ipykernel = "*"
ipython-genutils = "*"
jinja2 = "*"
jupyter-client = ">=5.3.4"
jupyter-core = ">=4.6.1"
nbclassic = ">=0.4.7"
nbconvert = ">=5"
nbformat = "*"
nest-asyncio = ">=1.5"
prometheus-client = "*"
pyzmq = ">=17"
Send2Trash = ">=1.8.0"
terminado = ">=0.8.3"
tornado = ">=6.1"
traitlets = ">=4.2.1"

[package.extras]
docs = ["myst-parser", "nbsphinx", "sphinx", "sphinx-rtd-theme", "sphinxcontrib-github-alt"]
json-logging = ["json-logging"]
test = ["coverage", "nbval", "pytest", "pytest-cov", "requests", "requests-unixsocket", "selenium (==4.1.5)", "testpath"]

[[package]]
name = "notebook-shim"
version = "0.2.2"
description = "A shim layer for notebook traits and config"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
jupyter-server = ">=1.8,<3"

[package.extras]
test = ["pytest", "pytest-console-scripts", "pytest-tornasync"]

[[package]]
name = "numpy"
version = "1.23.4"
description = "NumPy is the fundamental package for array computing with Python."
category = "main"
optional = false
python-versions = ">=3.8"

[[package]]
name = "nvidia-cublas-cu11"
version = "11.10.3.66"
description = "CUBLAS native runtime libraries"
category = "main"
optional = true
python-versions = ">=3"

[package.dependencies]
setuptools = "*"
wheel = "*"

[[package]]
name = "nvidia-cuda-nvrtc-cu11"
version = "11.7.99"
description = "NVRTC native runtime libraries"
category = "main"
optional = true
python-versions = ">=3"

[package.dependencies]
setuptools = "*"
wheel = "*"

[[package]]
name = "nvidia-cuda-runtime-cu11"
version = "11.7.99"
description = "CUDA Runtime native Libraries"
category = "main"
optional = true
python-versions = ">=3"

[package.dependencies]
setuptools = "*"
wheel = "*"

[[package]]
name = "nvidia-cudnn-cu11"
version = "8.5.0.96"
description = "cuDNN runtime libraries"
category = "main"
optional = true
python-versions = ">=3"

[package.dependencies]
setuptools = "*"
wheel = "*"

[[package]]
name = "orjson"
version = "3.8.2"
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
category = "main"
optional = false
python-versions = ">=3.7"

[[package]]
name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"

[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"

[[package]]
name = "pandocfilters"
version = "1.5.0"
description = "Utilities for writing pandoc filters in python"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[[package]]
name = "parso"
version = "0.8.3"
description = "A Python Parser"
category = "dev"
optional = false
python-versions = ">=3.6"

[package.extras]
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["docopt", "pytest (<6.0.0)"]

[[package]]
name = "pathspec"
version = "0.10.2"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "pexpect"
version = "4.8.0"
description = "Pexpect allows easy control of interactive console applications."
category = "dev"
optional = false
python-versions = "*"

[package.dependencies]
ptyprocess = ">=0.5"

[[package]]
name = "pickleshare"
version = "0.7.5"
description = "Tiny 'shelve'-like database with concurrency support"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "pillow"
version = "9.3.0"
description = "Python Imaging Library (Fork)"
category = "main"
optional = true
python-versions = ">=3.7"

[package.extras]
docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]

[[package]]
name = "pkgutil-resolve-name"
version = "1.3.10"
description = "Resolve a name to an object."
category = "dev"
optional = false
python-versions = ">=3.6"

[[package]]
name = "platformdirs"
version = "2.5.4"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"]
test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]

[[package]]
name = "pluggy"
version = "0.13.1"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[package.extras]
dev = ["pre-commit", "tox"]

[[package]]
name = "pre-commit"
version = "2.20.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
cfgv = ">=2.0.0"
identify = ">=1.0.0"
nodeenv = ">=0.11.1"
pyyaml = ">=5.1"
toml = "*"
virtualenv = ">=20.0.8"

[[package]]
name = "prometheus-client"
version = "0.15.0"
description = "Python client for the Prometheus monitoring system."
category = "dev"
optional = false
python-versions = ">=3.6"

[package.extras]
twisted = ["twisted"]

[[package]]
name = "prompt-toolkit"
version = "3.0.32"
description = "Library for building powerful interactive command lines in Python"
category = "dev"
optional = false
python-versions = ">=3.6.2"

[package.dependencies]
wcwidth = "*"

[[package]]
name = "protobuf"
version = "4.21.9"
description = ""
category = "main"
optional = true
python-versions = ">=3.7"

[[package]]
name = "psutil"
version = "5.9.4"
description = "Cross-platform lib for process and system monitoring in Python."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[package.extras]
test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]

[[package]]
name = "ptyprocess"
version = "0.7.0"
description = "Run a subprocess in a pseudo terminal"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "pure-eval"
version = "0.2.2"
description = "Safely evaluate AST nodes without side effects"
category = "dev"
optional = false
python-versions = "*"

[package.extras]
tests = ["pytest"]

[[package]]
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"

[[package]]
name = "pycparser"
version = "2.21"
description = "C parser in Python"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"

[[package]]
name = "pydantic"
version = "1.10.2"
description = "Data validation and settings management using python type hints"
category = "main"
optional = false
python-versions = ">=3.7"

[package.dependencies]
typing-extensions = ">=4.1.0"

[package.extras]
dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"]

[[package]]
name = "pygments"
version = "2.13.0"
description = "Pygments is a syntax highlighting package written in Python."
category = "dev"
optional = false
python-versions = ">=3.6"

[package.extras]
plugins = ["importlib-metadata"]

[[package]]
name = "pyparsing"
version = "3.0.9"
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
category = "dev"
optional = false
python-versions = ">=3.6.8"

[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]

[[package]]
name = "pyrsistent"
version = "0.19.2"
description = "Persistent/Functional/Immutable data structures"
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "pytest"
version = "6.2.5"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.6"

[package.dependencies]
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
py = ">=1.8.2"
toml = "*"

[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]

[[package]]
name = "pytest-asyncio"
version = "0.20.2"
description = "Pytest support for asyncio"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
pytest = ">=6.1.0"

[package.extras]
testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]

[[package]]
name = "python-dateutil"
version = "2.8.2"
description = "Extensions to the standard Python datetime module"
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"

[package.dependencies]
six = ">=1.5"

[[package]]
name = "pytz"
version = "2022.6"
description = "World timezone definitions, modern and historical"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "pywin32"
version = "305"
description = "Python for Window Extensions"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "pywinpty"
version = "2.0.9"
description = "Pseudo terminal support for Windows from Python."
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "pyyaml"
version = "6.0"
description = "YAML parser and emitter for Python"
category = "dev"
optional = false
python-versions = ">=3.6"

[[package]]
name = "pyzmq"
version = "24.0.1"
description = "Python bindings for 0MQ"
category = "dev"
optional = false
python-versions = ">=3.6"

[package.dependencies]
cffi = {version = "*", markers = "implementation_name == \"pypy\""}
py = {version = "*", markers = "implementation_name == \"pypy\""}

[[package]]
name = "requests"
version = "2.28.1"
description = "Python HTTP for Humans."
category = "dev"
optional = false
python-versions = ">=3.7, <4"

[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<3"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<1.27"

[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]

[[package]]
name = "rfc3986"
version = "1.5.0"
description = "Validating URI References per RFC 3986"
category = "dev"
optional = false
python-versions = "*"

[package.dependencies]
idna = {version = "*", optional = true, markers = "extra == \"idna2008\""}

[package.extras]
idna2008 = ["idna"]

[[package]]
name = "ruff"
version = "0.0.165"
description = "An extremely fast Python linter, written in Rust."
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "send2trash"
version = "1.8.0"
description = "Send file to trash natively under Mac OS X, Windows and Linux."
category = "dev"
optional = false
python-versions = "*"

[package.extras]
nativelib = ["pyobjc-framework-Cocoa", "pywin32"]
objc = ["pyobjc-framework-Cocoa"]
win32 = ["pywin32"]

[[package]]
name = "setuptools"
version = "65.5.1"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]

[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"

[[package]]
name = "sniffio"
version = "1.3.0"
description = "Sniff out which async library your code is running under"
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "soupsieve"
version = "2.3.2.post1"
description = "A modern CSS selector implementation for Beautiful Soup."
category = "dev"
optional = false
python-versions = ">=3.6"

[[package]]
name = "stack-data"
version = "0.6.1"
description = "Extract data from python stack frames and tracebacks for informative displays"
category = "dev"
optional = false
python-versions = "*"

[package.dependencies]
asttokens = ">=2.1.0"
executing = ">=1.2.0"
pure-eval = "*"

[package.extras]
tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]

[[package]]
name = "starlette"
version = "0.21.0"
description = "The little ASGI library that shines."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
anyio = ">=3.4.0,<5"
typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}

[package.extras]
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"]

[[package]]
name = "terminado"
version = "0.17.0"
description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
ptyprocess = {version = "*", markers = "os_name != \"nt\""}
pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""}
tornado = ">=6.1.0"

[package.extras]
docs = ["pydata-sphinx-theme", "sphinx"]
test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"]

[[package]]
name = "tinycss2"
version = "1.2.1"
description = "A tiny CSS parser"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
webencodings = ">=0.4"

[package.extras]
doc = ["sphinx", "sphinx_rtd_theme"]
test = ["flake8", "isort", "pytest"]

[[package]]
name = "toml"
version = "0.10.2"
description = "Python Library for Tom's Obvious, Minimal Language"
category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"

[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
python-versions = ">=3.7"

[[package]]
name = "torch"
version = "1.13.0"
description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration"
category = "main"
optional = true
python-versions = ">=3.7.0"

[package.dependencies]
nvidia-cublas-cu11 = "11.10.3.66"
nvidia-cuda-nvrtc-cu11 = "11.7.99"
nvidia-cuda-runtime-cu11 = "11.7.99"
nvidia-cudnn-cu11 = "8.5.0.96"
typing-extensions = "*"

[package.extras]
opt-einsum = ["opt-einsum (>=3.3)"]

[[package]]
name = "tornado"
version = "6.2"
description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
category = "dev"
optional = false
python-versions = ">= 3.7"

[[package]]
name = "traitlets"
version = "5.5.0"
description = ""
category = "dev"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
test = ["pre-commit", "pytest"]

[[package]]
name = "trimesh"
version = "3.17.1"
description = "Import, export, process, analyze and view triangular meshes."
category = "main"
optional = true
python-versions = "*"

[package.dependencies]
numpy = "*"

[package.extras]
all = ["chardet", "colorlog", "glooey", "jsonschema", "lxml", "mapbox-earcut", "meshio", "msgpack", "networkx", "pillow", "psutil", "pycollada", "pyglet (<2)", "python-fcl", "requests", "rtree", "scikit-image", "scipy", "setuptools", "shapely", "svg.path", "sympy", "xatlas", "xxhash"]
easy = ["chardet", "colorlog", "jsonschema", "lxml", "mapbox-earcut", "msgpack", "networkx", "pillow", "pycollada", "pyglet (<2)", "requests", "rtree", "scipy", "setuptools", "shapely", "svg.path", "sympy", "xxhash"]
test = ["coveralls", "ezdxf", "pyinstrument", "pytest", "pytest-cov"]

[[package]]
name = "types-pillow"
version = "9.3.0.1"
description = "Typing stubs for Pillow"
category = "main"
optional = true
python-versions = "*"

[[package]]
name = "types-protobuf"
version = "3.20.4.5"
description = "Typing stubs for protobuf"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "typing-extensions"
version = "4.4.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main"
optional = false
python-versions = ">=3.7"

[[package]]
name = "urllib3"
version = "1.26.12"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"

[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]

[[package]]
name = "uvicorn"
version = "0.19.0"
description = "The lightning-fast ASGI server."
category = "dev"
optional = false
python-versions = ">=3.7"

[package.dependencies]
click = ">=7.0"
h11 = ">=0.8"

[package.extras]
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"]

[[package]]
name = "virtualenv"
version = "20.16.7"
description = "Virtual Python Environment builder"
category = "dev"
optional = false
python-versions = ">=3.6"

[package.dependencies]
distlib = ">=0.3.6,<1"
filelock = ">=3.4.1,<4"
platformdirs = ">=2.4,<3"

[package.extras]
docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"]
testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"]

[[package]]
name = "wcwidth"
version = "0.2.5"
description = "Measures the displayed width of unicode strings in a terminal"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "webencodings"
version = "0.5.1"
description = "Character encoding aliases for legacy web content"
category = "dev"
optional = false
python-versions = "*"

[[package]]
name = "websocket-client"
version = "1.4.2"
description = "WebSocket client for Python with low level API options"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"]
optional = ["python-socks", "wsaccel"]
test = ["websockets"]

[[package]]
name = "wheel"
version = "0.38.4"
description = "A built-package format for Python"
category = "main"
optional = true
python-versions = ">=3.7"

[package.extras]
test = ["pytest (>=3.0.0)"]

[[package]]
name = "zipp"
version = "3.10.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "dev"
optional = false
python-versions = ">=3.7"

[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]

[extras]
common = ["protobuf"]
image = ["pillow", "types-pillow"]
mesh = ["trimesh"]
torch = ["torch"]

[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "1b52fed9287336da07ee1fdb96a0c79a7ade0a9183871300b209094e66f9c8e4"

[metadata.files]
anyio = [
    {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"},
    {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"},
]
appnope = [
    {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"},
    {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"},
]
argon2-cffi = [
    {file = "argon2-cffi-21.3.0.tar.gz", hash = "sha256:d384164d944190a7dd7ef22c6aa3ff197da12962bd04b17f64d4e93d934dba5b"},
    {file = "argon2_cffi-21.3.0-py3-none-any.whl", hash = "sha256:8c976986f2c5c0e5000919e6de187906cfd81fb1c72bf9d88c01177e77da7f80"},
]
argon2-cffi-bindings = [
    {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"},
    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"},
    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"},
    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"},
    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"},
    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"},
    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"},
    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"},
    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"},
    {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"},
    {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"},
    {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"},
    {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"},
    {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"},
    {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"},
    {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"},
    {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"},
    {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"},
    {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"},
    {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"},
    {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"},
]
asttokens = [
    {file = "asttokens-2.1.0-py2.py3-none-any.whl", hash = "sha256:1b28ed85e254b724439afc783d4bee767f780b936c3fe8b3275332f42cf5f561"},
    {file = "asttokens-2.1.0.tar.gz", hash = "sha256:4aa76401a151c8cc572d906aad7aea2a841780834a19d780f4321c0fe1b54635"},
]
atomicwrites = [
    {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
]
attrs = [
    {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
    {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
]
babel = [
    {file = "Babel-2.11.0-py3-none-any.whl", hash = "sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe"},
    {file = "Babel-2.11.0.tar.gz", hash = "sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"},
]
backcall = [
    {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
    {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
]
beautifulsoup4 = [
    {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"},
    {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"},
]
black = [
    {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"},
    {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"},
    {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"},
    {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"},
    {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"},
    {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"},
    {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"},
    {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"},
    {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"},
    {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"},
    {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"},
    {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"},
    {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"},
    {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"},
    {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"},
    {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"},
    {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"},
    {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"},
    {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"},
    {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"},
    {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
]
bleach = [
    {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"},
    {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"},
]
certifi = [
    {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
    {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
]
cffi = [
    {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
    {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
    {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
    {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
    {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
    {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
    {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
    {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
    {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
    {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
    {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
    {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
    {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
    {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
    {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
    {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
    {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
    {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
    {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
    {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
    {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
    {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
    {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
    {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
    {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
    {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
    {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
    {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
    {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
    {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
    {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
    {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
    {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
    {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
    {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
    {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
    {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
    {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
    {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
    {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
    {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
]
cfgv = [
    {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
    {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
]
charset-normalizer = [
    {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
    {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
]
click = [
    {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
    {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
]
colorama = [
    {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
    {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
debugpy = [
    {file = "debugpy-1.6.3-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:c4b2bd5c245eeb49824bf7e539f95fb17f9a756186e51c3e513e32999d8846f3"},
    {file = "debugpy-1.6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b8deaeb779699350deeed835322730a3efec170b88927debc9ba07a1a38e2585"},
    {file = "debugpy-1.6.3-cp310-cp310-win32.whl", hash = "sha256:fc233a0160f3b117b20216f1169e7211b83235e3cd6749bcdd8dbb72177030c7"},
    {file = "debugpy-1.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:dda8652520eae3945833e061cbe2993ad94a0b545aebd62e4e6b80ee616c76b2"},
    {file = "debugpy-1.6.3-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5c814596a170a0a58fa6fad74947e30bfd7e192a5d2d7bd6a12156c2899e13a"},
    {file = "debugpy-1.6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c4cd6f37e3c168080d61d698390dfe2cd9e74ebf80b448069822a15dadcda57d"},
    {file = "debugpy-1.6.3-cp37-cp37m-win32.whl", hash = "sha256:3c9f985944a30cfc9ae4306ac6a27b9c31dba72ca943214dad4a0ab3840f6161"},
    {file = "debugpy-1.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:5ad571a36cec137ae6ed951d0ff75b5e092e9af6683da084753231150cbc5b25"},
    {file = "debugpy-1.6.3-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:adcfea5ea06d55d505375995e150c06445e2b20cd12885bcae566148c076636b"},
    {file = "debugpy-1.6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:daadab4403427abd090eccb38d8901afd8b393e01fd243048fab3f1d7132abb4"},
    {file = "debugpy-1.6.3-cp38-cp38-win32.whl", hash = "sha256:6efc30325b68e451118b795eff6fe8488253ca3958251d5158106d9c87581bc6"},
    {file = "debugpy-1.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:86d784b72c5411c833af1cd45b83d80c252b77c3bfdb43db17c441d772f4c734"},
    {file = "debugpy-1.6.3-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:4e255982552b0edfe3a6264438dbd62d404baa6556a81a88f9420d3ed79b06ae"},
    {file = "debugpy-1.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cca23cb6161ac89698d629d892520327dd1be9321c0960e610bbcb807232b45d"},
    {file = "debugpy-1.6.3-cp39-cp39-win32.whl", hash = "sha256:7c302095a81be0d5c19f6529b600bac971440db3e226dce85347cc27e6a61908"},
    {file = "debugpy-1.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:34d2cdd3a7c87302ba5322b86e79c32c2115be396f3f09ca13306d8a04fe0f16"},
    {file = "debugpy-1.6.3-py2.py3-none-any.whl", hash = "sha256:84c39940a0cac410bf6aa4db00ba174f973eef521fbe9dd058e26bcabad89c4f"},
    {file = "debugpy-1.6.3.zip", hash = "sha256:e8922090514a890eec99cfb991bab872dd2e353ebb793164d5f01c362b9a40bf"},
]
decorator = [
    {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
    {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
]
defusedxml = [
    {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
    {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
]
distlib = [
    {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"},
    {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"},
]
entrypoints = [
    {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"},
    {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"},
]
executing = [
    {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"},
    {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"},
]
fastapi = [
    {file = "fastapi-0.87.0-py3-none-any.whl", hash = "sha256:254453a2e22f64e2a1b4e1d8baf67d239e55b6c8165c079d25746a5220c81bb4"},
    {file = "fastapi-0.87.0.tar.gz", hash = "sha256:07032e53df9a57165047b4f38731c38bdcc3be5493220471015e2b4b51b486a4"},
]
fastjsonschema = [
    {file = "fastjsonschema-2.16.2-py3-none-any.whl", hash = "sha256:21f918e8d9a1a4ba9c22e09574ba72267a6762d47822db9add95f6454e51cc1c"},
    {file = "fastjsonschema-2.16.2.tar.gz", hash = "sha256:01e366f25d9047816fe3d288cbfc3e10541daf0af2044763f3d0ade42476da18"},
]
filelock = [
    {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"},
    {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"},
]
h11 = [
    {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
    {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
]
httpcore = [
    {file = "httpcore-0.16.1-py3-none-any.whl", hash = "sha256:8d393db683cc8e35cc6ecb02577c5e1abfedde52b38316d038932a84b4875ecb"},
    {file = "httpcore-0.16.1.tar.gz", hash = "sha256:3d3143ff5e1656a5740ea2f0c167e8e9d48c5a9bbd7f00ad1f8cff5711b08543"},
]
httpx = [
    {file = "httpx-0.23.1-py3-none-any.whl", hash = "sha256:0b9b1f0ee18b9978d637b0776bfd7f54e2ca278e063e3586d8f01cda89e042a8"},
    {file = "httpx-0.23.1.tar.gz", hash = "sha256:202ae15319be24efe9a8bd4ed4360e68fde7b38bcc2ce87088d416f026667d19"},
]
identify = [
    {file = "identify-2.5.8-py2.py3-none-any.whl", hash = "sha256:48b7925fe122720088aeb7a6c34f17b27e706b72c61070f27fe3789094233440"},
    {file = "identify-2.5.8.tar.gz", hash = "sha256:7a214a10313b9489a0d61467db2856ae8d0b8306fc923e03a9effa53d8aedc58"},
]
idna = [
    {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
    {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
importlib-metadata = [
    {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"},
    {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"},
]
importlib-resources = [
    {file = "importlib_resources-5.10.0-py3-none-any.whl", hash = "sha256:ee17ec648f85480d523596ce49eae8ead87d5631ae1551f913c0100b5edd3437"},
    {file = "importlib_resources-5.10.0.tar.gz", hash = "sha256:c01b1b94210d9849f286b86bb51bcea7cd56dde0600d8db721d7b81330711668"},
]
iniconfig = [
    {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
    {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
ipykernel = [
    {file = "ipykernel-6.17.1-py3-none-any.whl", hash = "sha256:3a9a1b2ad6dbbd5879855aabb4557f08e63fa2208bffed897f03070e2bb436f6"},
    {file = "ipykernel-6.17.1.tar.gz", hash = "sha256:e178c1788399f93a459c241fe07c3b810771c607b1fb064a99d2c5d40c90c5d4"},
]
ipython = [
    {file = "ipython-8.6.0-py3-none-any.whl", hash = "sha256:91ef03016bcf72dd17190f863476e7c799c6126ec7e8be97719d1bc9a78a59a4"},
    {file = "ipython-8.6.0.tar.gz", hash = "sha256:7c959e3dedbf7ed81f9b9d8833df252c430610e2a4a6464ec13cd20975ce20a5"},
]
ipython-genutils = [
    {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
    {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"},
]
isort = [
    {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
    {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
]
jedi = [
    {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"},
    {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"},
]
jinja2 = [
    {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
    {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
]
json5 = [
    {file = "json5-0.9.10-py2.py3-none-any.whl", hash = "sha256:993189671e7412e9cdd8be8dc61cf402e8e579b35f1d1bb20ae6b09baa78bbce"},
    {file = "json5-0.9.10.tar.gz", hash = "sha256:ad9f048c5b5a4c3802524474ce40a622fae789860a86f10cc4f7e5f9cf9b46ab"},
]
jsonschema = [
    {file = "jsonschema-4.17.0-py3-none-any.whl", hash = "sha256:f660066c3966db7d6daeaea8a75e0b68237a48e51cf49882087757bb59916248"},
    {file = "jsonschema-4.17.0.tar.gz", hash = "sha256:5bfcf2bca16a087ade17e02b282d34af7ccd749ef76241e7f9bd7c0cb8a9424d"},
]
jupyter-client = [
    {file = "jupyter_client-7.4.6-py3-none-any.whl", hash = "sha256:540b6a5c9c2dc481c5dd54fd5acb260f03dfaaa7c5325b2ffb1f676710f8c7c4"},
    {file = "jupyter_client-7.4.6.tar.gz", hash = "sha256:f7f9a9dc3a0ecd223ed6a5a00cf4140a5c252ec72e52d6de370748ed0aa083dd"},
]
jupyter-core = [
    {file = "jupyter_core-5.0.0-py3-none-any.whl", hash = "sha256:6da1fae48190da8551e1b5dbbb19d51d00b079d59a073c7030407ecaf96dbb1e"},
    {file = "jupyter_core-5.0.0.tar.gz", hash = "sha256:4ed68b7c606197c7e344a24b7195eef57898157075a69655a886074b6beb7043"},
]
jupyter-server = [
    {file = "jupyter_server-1.23.2-py3-none-any.whl", hash = "sha256:c01d0e84c22a14dd6b0e7d8ce4105b08a3426b46582668e28046a64c07311a4f"},
    {file = "jupyter_server-1.23.2.tar.gz", hash = "sha256:69cb954ef02c0ba1837787e34e4a1240c93c8eb590662fae1840778861957660"},
]
jupyterlab = [
    {file = "jupyterlab-3.5.0-py3-none-any.whl", hash = "sha256:f433059fe0e12d75ea90a81a0b6721113bb132857e3ec2197780b6fe84cbcbde"},
    {file = "jupyterlab-3.5.0.tar.gz", hash = "sha256:e02556c8ea1b386963c4b464e4618aee153c5416b07ab481425c817a033323a2"},
]
jupyterlab-pygments = [
    {file = "jupyterlab_pygments-0.2.2-py2.py3-none-any.whl", hash = "sha256:2405800db07c9f770863bcf8049a529c3dd4d3e28536638bd7c1c01d2748309f"},
    {file = "jupyterlab_pygments-0.2.2.tar.gz", hash = "sha256:7405d7fde60819d905a9fa8ce89e4cd830e318cdad22a0030f7a901da705585d"},
]
jupyterlab-server = [
    {file = "jupyterlab_server-2.16.3-py3-none-any.whl", hash = "sha256:d18eb623428b4ee732c2258afaa365eedd70f38b609981ea040027914df32bc6"},
    {file = "jupyterlab_server-2.16.3.tar.gz", hash = "sha256:635a0b176a901f19351c02221a124e59317c476f511200409b7d867e8b2905c3"},
]
markupsafe = [
    {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
    {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
    {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
    {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
    {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
    {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
]
matplotlib-inline = [
    {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"},
    {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"},
]
mistune = [
    {file = "mistune-2.0.4-py2.py3-none-any.whl", hash = "sha256:182cc5ee6f8ed1b807de6b7bb50155df7b66495412836b9a74c8fbdfc75fe36d"},
    {file = "mistune-2.0.4.tar.gz", hash = "sha256:9ee0a66053e2267aba772c71e06891fa8f1af6d4b01d5e84e267b4570d4d9808"},
]
mypy = [
    {file = "mypy-0.990-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aaf1be63e0207d7d17be942dcf9a6b641745581fe6c64df9a38deb562a7dbafa"},
    {file = "mypy-0.990-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d555aa7f44cecb7ea3c0ac69d58b1a5afb92caa017285a8e9c4efbf0518b61b4"},
    {file = "mypy-0.990-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f694d6d09a460b117dccb6857dda269188e3437c880d7b60fa0014fa872d1e9"},
    {file = "mypy-0.990-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:269f0dfb6463b8780333310ff4b5134425157ef0d2b1d614015adaf6d6a7eabd"},
    {file = "mypy-0.990-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8798c8ed83aa809f053abff08664bdca056038f5a02af3660de00b7290b64c47"},
    {file = "mypy-0.990-cp310-cp310-win_amd64.whl", hash = "sha256:47a9955214615108c3480a500cfda8513a0b1cd3c09a1ed42764ca0dd7b931dd"},
    {file = "mypy-0.990-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4a8a6c10f4c63fbf6ad6c03eba22c9331b3946a4cec97f008e9ffb4d3b31e8e2"},
    {file = "mypy-0.990-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cd2dd3730ba894ec2a2082cc703fbf3e95a08479f7be84912e3131fc68809d46"},
    {file = "mypy-0.990-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7da0005e47975287a92b43276e460ac1831af3d23032c34e67d003388a0ce8d0"},
    {file = "mypy-0.990-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:262c543ef24deb10470a3c1c254bb986714e2b6b1a67d66daf836a548a9f316c"},
    {file = "mypy-0.990-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3ff201a0c6d3ea029d73b1648943387d75aa052491365b101f6edd5570d018ea"},
    {file = "mypy-0.990-cp311-cp311-win_amd64.whl", hash = "sha256:1767830da2d1afa4e62b684647af0ff79b401f004d7fa08bc5b0ce2d45bcd5ec"},
    {file = "mypy-0.990-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6826d9c4d85bbf6d68cb279b561de6a4d8d778ca8e9ab2d00ee768ab501a9852"},
    {file = "mypy-0.990-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46897755f944176fbc504178422a5a2875bbf3f7436727374724842c0987b5af"},
    {file = "mypy-0.990-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0680389c34284287fe00e82fc8bccdea9aff318f7e7d55b90d967a13a9606013"},
    {file = "mypy-0.990-cp37-cp37m-win_amd64.whl", hash = "sha256:b08541a06eed35b543ae1a6b301590eb61826a1eb099417676ddc5a42aa151c5"},
    {file = "mypy-0.990-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:be88d665e76b452c26fb2bdc3d54555c01226fba062b004ede780b190a50f9db"},
    {file = "mypy-0.990-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b8f4a8213b1fd4b751e26b59ae0e0c12896568d7e805861035c7a15ed6dc9eb"},
    {file = "mypy-0.990-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2b6f85c2ad378e3224e017904a051b26660087b3b76490d533b7344f1546d3ff"},
    {file = "mypy-0.990-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ee5f99817ee70254e7eb5cf97c1b11dda29c6893d846c8b07bce449184e9466"},
    {file = "mypy-0.990-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49082382f571c3186ce9ea0bd627cb1345d4da8d44a8377870f4442401f0a706"},
    {file = "mypy-0.990-cp38-cp38-win_amd64.whl", hash = "sha256:aba38e3dd66bdbafbbfe9c6e79637841928ea4c79b32e334099463c17b0d90ef"},
    {file = "mypy-0.990-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9d851c09b981a65d9d283a8ccb5b1d0b698e580493416a10942ef1a04b19fd37"},
    {file = "mypy-0.990-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d847dd23540e2912d9667602271e5ebf25e5788e7da46da5ffd98e7872616e8e"},
    {file = "mypy-0.990-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc6019808580565040cd2a561b593d7c3c646badd7e580e07d875eb1bf35c695"},
    {file = "mypy-0.990-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a3150d409609a775c8cb65dbe305c4edd7fe576c22ea79d77d1454acd9aeda8"},
    {file = "mypy-0.990-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3227f14fe943524f5794679156488f18bf8d34bfecd4623cf76bc55958d229c5"},
    {file = "mypy-0.990-cp39-cp39-win_amd64.whl", hash = "sha256:c76c769c46a1e6062a84837badcb2a7b0cdb153d68601a61f60739c37d41cc74"},
    {file = "mypy-0.990-py3-none-any.whl", hash = "sha256:8f1940325a8ed460ba03d19ab83742260fa9534804c317224e5d4e5aa588e2d6"},
    {file = "mypy-0.990.tar.gz", hash = "sha256:72382cb609142dba3f04140d016c94b4092bc7b4d98ca718740dc989e5271b8d"},
]
mypy-extensions = [
    {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
    {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
nbclassic = [
    {file = "nbclassic-0.4.8-py3-none-any.whl", hash = "sha256:cbf05df5842b420d5cece0143462380ea9d308ff57c2dc0eb4d6e035b18fbfb3"},
    {file = "nbclassic-0.4.8.tar.gz", hash = "sha256:c74d8a500f8e058d46b576a41e5bc640711e1032cf7541dde5f73ea49497e283"},
]
nbclient = [
    {file = "nbclient-0.7.0-py3-none-any.whl", hash = "sha256:434c91385cf3e53084185334d675a0d33c615108b391e260915d1aa8e86661b8"},
    {file = "nbclient-0.7.0.tar.gz", hash = "sha256:a1d844efd6da9bc39d2209bf996dbd8e07bf0f36b796edfabaa8f8a9ab77c3aa"},
]
nbconvert = [
    {file = "nbconvert-7.2.5-py3-none-any.whl", hash = "sha256:3e90e108bb5637b5b8a1422af1156af1368b39dd25369ff7faa7dfdcdef18f81"},
    {file = "nbconvert-7.2.5.tar.gz", hash = "sha256:8fdc44fd7d9424db7fdc6e1e834a02f6b8620ffb653767388be2f9eb16f84184"},
]
nbformat = [
    {file = "nbformat-5.7.0-py3-none-any.whl", hash = "sha256:1b05ec2c552c2f1adc745f4eddce1eac8ca9ffd59bb9fd859e827eaa031319f9"},
    {file = "nbformat-5.7.0.tar.gz", hash = "sha256:1d4760c15c1a04269ef5caf375be8b98dd2f696e5eb9e603ec2bf091f9b0d3f3"},
]
nest-asyncio = [
    {file = "nest_asyncio-1.5.6-py3-none-any.whl", hash = "sha256:b9a953fb40dceaa587d109609098db21900182b16440652454a146cffb06e8b8"},
    {file = "nest_asyncio-1.5.6.tar.gz", hash = "sha256:d267cc1ff794403f7df692964d1d2a3fa9418ffea2a3f6859a439ff482fef290"},
]
nodeenv = [
    {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"},
    {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"},
]
notebook = [
    {file = "notebook-6.5.2-py3-none-any.whl", hash = "sha256:e04f9018ceb86e4fa841e92ea8fb214f8d23c1cedfde530cc96f92446924f0e4"},
    {file = "notebook-6.5.2.tar.gz", hash = "sha256:c1897e5317e225fc78b45549a6ab4b668e4c996fd03a04e938fe5e7af2bfffd0"},
]
notebook-shim = [
    {file = "notebook_shim-0.2.2-py3-none-any.whl", hash = "sha256:9c6c30f74c4fbea6fce55c1be58e7fd0409b1c681b075dcedceb005db5026949"},
    {file = "notebook_shim-0.2.2.tar.gz", hash = "sha256:090e0baf9a5582ff59b607af523ca2db68ff216da0c69956b62cab2ef4fc9c3f"},
]
numpy = [
    {file = "numpy-1.23.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:95d79ada05005f6f4f337d3bb9de8a7774f259341c70bc88047a1f7b96a4bcb2"},
    {file = "numpy-1.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:926db372bc4ac1edf81cfb6c59e2a881606b409ddc0d0920b988174b2e2a767f"},
    {file = "numpy-1.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c237129f0e732885c9a6076a537e974160482eab8f10db6292e92154d4c67d71"},
    {file = "numpy-1.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8365b942f9c1a7d0f0dc974747d99dd0a0cdfc5949a33119caf05cb314682d3"},
    {file = "numpy-1.23.4-cp310-cp310-win32.whl", hash = "sha256:2341f4ab6dba0834b685cce16dad5f9b6606ea8a00e6da154f5dbded70fdc4dd"},
    {file = "numpy-1.23.4-cp310-cp310-win_amd64.whl", hash = "sha256:d331afac87c92373826af83d2b2b435f57b17a5c74e6268b79355b970626e329"},
    {file = "numpy-1.23.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:488a66cb667359534bc70028d653ba1cf307bae88eab5929cd707c761ff037db"},
    {file = "numpy-1.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce03305dd694c4873b9429274fd41fc7eb4e0e4dea07e0af97a933b079a5814f"},
    {file = "numpy-1.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8981d9b5619569899666170c7c9748920f4a5005bf79c72c07d08c8a035757b0"},
    {file = "numpy-1.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a70a7d3ce4c0e9284e92285cba91a4a3f5214d87ee0e95928f3614a256a1488"},
    {file = "numpy-1.23.4-cp311-cp311-win32.whl", hash = "sha256:5e13030f8793e9ee42f9c7d5777465a560eb78fa7e11b1c053427f2ccab90c79"},
    {file = "numpy-1.23.4-cp311-cp311-win_amd64.whl", hash = "sha256:7607b598217745cc40f751da38ffd03512d33ec06f3523fb0b5f82e09f6f676d"},
    {file = "numpy-1.23.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7ab46e4e7ec63c8a5e6dbf5c1b9e1c92ba23a7ebecc86c336cb7bf3bd2fb10e5"},
    {file = "numpy-1.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8aae2fb3180940011b4862b2dd3756616841c53db9734b27bb93813cd79fce6"},
    {file = "numpy-1.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c053d7557a8f022ec823196d242464b6955a7e7e5015b719e76003f63f82d0f"},
    {file = "numpy-1.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0882323e0ca4245eb0a3d0a74f88ce581cc33aedcfa396e415e5bba7bf05f68"},
    {file = "numpy-1.23.4-cp38-cp38-win32.whl", hash = "sha256:dada341ebb79619fe00a291185bba370c9803b1e1d7051610e01ed809ef3a4ba"},
    {file = "numpy-1.23.4-cp38-cp38-win_amd64.whl", hash = "sha256:0fe563fc8ed9dc4474cbf70742673fc4391d70f4363f917599a7fa99f042d5a8"},
    {file = "numpy-1.23.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c67b833dbccefe97cdd3f52798d430b9d3430396af7cdb2a0c32954c3ef73894"},
    {file = "numpy-1.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f76025acc8e2114bb664294a07ede0727aa75d63a06d2fae96bf29a81747e4a7"},
    {file = "numpy-1.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12ac457b63ec8ded85d85c1e17d85efd3c2b0967ca39560b307a35a6703a4735"},
    {file = "numpy-1.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95de7dc7dc47a312f6feddd3da2500826defdccbc41608d0031276a24181a2c0"},
    {file = "numpy-1.23.4-cp39-cp39-win32.whl", hash = "sha256:f2f390aa4da44454db40a1f0201401f9036e8d578a25f01a6e237cea238337ef"},
    {file = "numpy-1.23.4-cp39-cp39-win_amd64.whl", hash = "sha256:f260da502d7441a45695199b4e7fd8ca87db659ba1c78f2bbf31f934fe76ae0e"},
    {file = "numpy-1.23.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61be02e3bf810b60ab74e81d6d0d36246dbfb644a462458bb53b595791251911"},
    {file = "numpy-1.23.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296d17aed51161dbad3c67ed6d164e51fcd18dbcd5dd4f9d0a9c6055dce30810"},
    {file = "numpy-1.23.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4d52914c88b4930dafb6c48ba5115a96cbab40f45740239d9f4159c4ba779962"},
    {file = "numpy-1.23.4.tar.gz", hash = "sha256:ed2cc92af0efad20198638c69bb0fc2870a58dabfba6eb722c933b48556c686c"},
]
nvidia-cublas-cu11 = [
    {file = "nvidia_cublas_cu11-11.10.3.66-py3-none-manylinux1_x86_64.whl", hash = "sha256:d32e4d75f94ddfb93ea0a5dda08389bcc65d8916a25cb9f37ac89edaeed3bded"},
    {file = "nvidia_cublas_cu11-11.10.3.66-py3-none-win_amd64.whl", hash = "sha256:8ac17ba6ade3ed56ab898a036f9ae0756f1e81052a317bf98f8c6d18dc3ae49e"},
]
nvidia-cuda-nvrtc-cu11 = [
    {file = "nvidia_cuda_nvrtc_cu11-11.7.99-2-py3-none-manylinux1_x86_64.whl", hash = "sha256:9f1562822ea264b7e34ed5930567e89242d266448e936b85bc97a3370feabb03"},
    {file = "nvidia_cuda_nvrtc_cu11-11.7.99-py3-none-manylinux1_x86_64.whl", hash = "sha256:f7d9610d9b7c331fa0da2d1b2858a4a8315e6d49765091d28711c8946e7425e7"},
    {file = "nvidia_cuda_nvrtc_cu11-11.7.99-py3-none-win_amd64.whl", hash = "sha256:f2effeb1309bdd1b3854fc9b17eaf997808f8b25968ce0c7070945c4265d64a3"},
]
nvidia-cuda-runtime-cu11 = [
    {file = "nvidia_cuda_runtime_cu11-11.7.99-py3-none-manylinux1_x86_64.whl", hash = "sha256:cc768314ae58d2641f07eac350f40f99dcb35719c4faff4bc458a7cd2b119e31"},
    {file = "nvidia_cuda_runtime_cu11-11.7.99-py3-none-win_amd64.whl", hash = "sha256:bc77fa59a7679310df9d5c70ab13c4e34c64ae2124dd1efd7e5474b71be125c7"},
]
nvidia-cudnn-cu11 = [
    {file = "nvidia_cudnn_cu11-8.5.0.96-2-py3-none-manylinux1_x86_64.whl", hash = "sha256:402f40adfc6f418f9dae9ab402e773cfed9beae52333f6d86ae3107a1b9527e7"},
    {file = "nvidia_cudnn_cu11-8.5.0.96-py3-none-manylinux1_x86_64.whl", hash = "sha256:71f8111eb830879ff2836db3cccf03bbd735df9b0d17cd93761732ac50a8a108"},
]
orjson = [
    {file = "orjson-3.8.2-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:43e69b360c2851b45c7dbab3b95f7fa8469df73fab325a683f7389c4db63aa71"},
    {file = "orjson-3.8.2-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:64c5da5c9679ef3d85e9bbcbb62f4ccdc1f1975780caa20f2ec1e37b4da6bd36"},
    {file = "orjson-3.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c632a2157fa9ec098d655287e9e44809615af99837c49f53d96bfbca453c5bd"},
    {file = "orjson-3.8.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f63da6309c282a2b58d4a846f0717f6440356b4872838b9871dc843ed1fe2b38"},
    {file = "orjson-3.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c9be25c313ba2d5478829d949165445c3bd36c62e07092b4ba8dbe5426574d1"},
    {file = "orjson-3.8.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:4bcce53e9e088f82633f784f79551fcd7637943ab56c51654aaf9d4c1d5cfa54"},
    {file = "orjson-3.8.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:33edb5379c6e6337f9383c85fe4080ce3aa1057cc2ce29345b7239461f50cbd6"},
    {file = "orjson-3.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:da35d347115758bbc8bfaf39bb213c42000f2a54e3f504c84374041d20835cd6"},
    {file = "orjson-3.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d755d94a90a941b91b4d39a6b02e289d8ba358af2d1a911edf266be7942609dc"},
    {file = "orjson-3.8.2-cp310-none-win_amd64.whl", hash = "sha256:7ea96923e26390b2142602ebb030e2a4db9351134696e0b219e5106bddf9b48e"},
    {file = "orjson-3.8.2-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:a0d89de876e6f1cef917a2338378a60a98584e1c2e1c67781e20b6ed1c512478"},
    {file = "orjson-3.8.2-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:8d47e7592fe938aec898eb22ea4946298c018133df084bc78442ff18e2c6347c"},
    {file = "orjson-3.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3d9f1043f618d0c64228aab9711e5bd822253c50b6c56223951e32b51f81d62"},
    {file = "orjson-3.8.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed10600e8b08f1e87b656ad38ab316191ce94f2c9adec57035680c0dc9e93c81"},
    {file = "orjson-3.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99c49e49a04bf61fee7aaea6d92ac2b1fcf6507aea894bbdf3fbb25fe792168c"},
    {file = "orjson-3.8.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:1463674f8efe6984902473d7b5ce3edf444c1fcd09dc8aa4779638a28fb9ca01"},
    {file = "orjson-3.8.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c1ef75f1d021d817e5c60a42da0b4b7e3123b1b37415260b8415666ddacc7cd7"},
    {file = "orjson-3.8.2-cp311-none-win_amd64.whl", hash = "sha256:b6007e1ac8564b13b2521720929e8bb3ccd3293d9fdf38f28728dcc06db6248f"},
    {file = "orjson-3.8.2-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:a02c13ae523221576b001071354380e277346722cc6b7fdaacb0fd6db5154b3e"},
    {file = "orjson-3.8.2-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:fa2e565cf8ffdb37ce1887bd1592709ada7f701e61aa4b1e710be94b0aecbab4"},
    {file = "orjson-3.8.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1d8864288f7c5fccc07b43394f83b721ddc999f25dccfb5d0651671a76023f5"},
    {file = "orjson-3.8.2-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1874c05d0bb994601fa2d51605cb910d09343c6ebd36e84a573293523fab772a"},
    {file = "orjson-3.8.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:349387ed6989e5db22e08c9af8d7ca14240803edc50de451d48d41a0e7be30f6"},
    {file = "orjson-3.8.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:4e42b19619d6e97e201053b865ca4e62a48da71165f4081508ada8e1b91c6a30"},
    {file = "orjson-3.8.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:bc112c17e607c59d1501e72afb44226fa53d947d364aed053f0c82d153e29616"},
    {file = "orjson-3.8.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6fda669211f2ed1fc2c8130187ec90c96b4f77b6a250004e666d2ef8ed524e5f"},
    {file = "orjson-3.8.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:aebd4e80fea0f20578fd0452908b9206a6a0d5ae9f5c99b6e665bbcd989e56cd"},
    {file = "orjson-3.8.2-cp37-none-win_amd64.whl", hash = "sha256:9f3cd0394eb6d265beb2a1572b5663bc910883ddbb5cdfbcb660f5a0444e7fd8"},
    {file = "orjson-3.8.2-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:74e7d54d11b3da42558d69a23bf92c2c48fabf69b38432d5eee2c5b09cd4c433"},
    {file = "orjson-3.8.2-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:8cbadc9be748a823f9c743c7631b1ee95d3925a9c0b21de4e862a1d57daa10ec"},
    {file = "orjson-3.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07d5a8c69a2947d9554a00302734fe3d8516415c8b280963c92bc1033477890"},
    {file = "orjson-3.8.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b364ea01d1b71b9f97bf97af9eb79ebee892df302e127a9e2e4f8eaa74d6b98"},
    {file = "orjson-3.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b98a8c825a59db94fbe8e0cce48618624c5a6fb1436467322d90667c08a0bf80"},
    {file = "orjson-3.8.2-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:ab63103f60b516c0fce9b62cb4773f689a82ab56e19ef2387b5a3182f80c0d78"},
    {file = "orjson-3.8.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:73ab3f4288389381ae33ab99f914423b69570c88d626d686764634d5e0eeb909"},
    {file = "orjson-3.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2ab3fd8728e12c36e20c6d9d70c9e15033374682ce5acb6ed6a08a80dacd254d"},
    {file = "orjson-3.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cde11822cf71a7f0daaa84223249b2696a2b6cda7fa587e9fd762dff1a8848e4"},
    {file = "orjson-3.8.2-cp38-none-win_amd64.whl", hash = "sha256:b14765ea5aabfeab1a194abfaa0be62c9fee6480a75ac8c6974b4eeede3340b4"},
    {file = "orjson-3.8.2-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:6068a27d59d989d4f2864c2fc3440eb7126a0cfdfaf8a4ad136b0ffd932026ae"},
    {file = "orjson-3.8.2-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:6bf36fa759a1b941fc552ad76b2d7fb10c1d2a20c056be291ea45eb6ae1da09b"},
    {file = "orjson-3.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f436132e62e647880ca6988974c8e3165a091cb75cbed6c6fd93e931630c22fa"},
    {file = "orjson-3.8.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3ecd8936259a5920b52a99faf62d4efeb9f5e25a0aacf0cce1e9fa7c37af154f"},
    {file = "orjson-3.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c13114b345cda33644f64e92fe5d8737828766cf02fbbc7d28271a95ea546832"},
    {file = "orjson-3.8.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6e43cdc3ddf96bdb751b748b1984b701125abacca8fc2226b808d203916e8cba"},
    {file = "orjson-3.8.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:ee39071da2026b11e4352d6fc3608a7b27ee14bc699fd240f4e604770bc7a255"},
    {file = "orjson-3.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1c3833976ebbeb3b5b6298cb22e23bf18453f6b80802103b7d08f7dd8a61611d"},
    {file = "orjson-3.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b9a34519d3d70935e1cd3797fbed8fbb6f61025182bea0140ca84d95b6f8fbe5"},
    {file = "orjson-3.8.2-cp39-none-win_amd64.whl", hash = "sha256:2734086d9a3dd9591c4be7d05aff9beccc086796d3f243685e56b7973ebac5bc"},
    {file = "orjson-3.8.2.tar.gz", hash = "sha256:a2fb95a45031ccf278e44341027b3035ab99caa32aa173279b1f0a06324f434b"},
]
packaging = [
    {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
    {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
pandocfilters = [
    {file = "pandocfilters-1.5.0-py2.py3-none-any.whl", hash = "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f"},
    {file = "pandocfilters-1.5.0.tar.gz", hash = "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38"},
]
parso = [
    {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
    {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
]
pathspec = [
    {file = "pathspec-0.10.2-py3-none-any.whl", hash = "sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5"},
    {file = "pathspec-0.10.2.tar.gz", hash = "sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0"},
]
pexpect = [
    {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
    {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
]
pickleshare = [
    {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
    {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
]
pillow = [
    {file = "Pillow-9.3.0-1-cp37-cp37m-win32.whl", hash = "sha256:e6ea6b856a74d560d9326c0f5895ef8050126acfdc7ca08ad703eb0081e82b74"},
    {file = "Pillow-9.3.0-1-cp37-cp37m-win_amd64.whl", hash = "sha256:32a44128c4bdca7f31de5be641187367fe2a450ad83b833ef78910397db491aa"},
    {file = "Pillow-9.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:0b7257127d646ff8676ec8a15520013a698d1fdc48bc2a79ba4e53df792526f2"},
    {file = "Pillow-9.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b90f7616ea170e92820775ed47e136208e04c967271c9ef615b6fbd08d9af0e3"},
    {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68943d632f1f9e3dce98908e873b3a090f6cba1cbb1b892a9e8d97c938871fbe"},
    {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be55f8457cd1eac957af0c3f5ece7bc3f033f89b114ef30f710882717670b2a8"},
    {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d77adcd56a42d00cc1be30843d3426aa4e660cab4a61021dc84467123f7a00c"},
    {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:829f97c8e258593b9daa80638aee3789b7df9da5cf1336035016d76f03b8860c"},
    {file = "Pillow-9.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:801ec82e4188e935c7f5e22e006d01611d6b41661bba9fe45b60e7ac1a8f84de"},
    {file = "Pillow-9.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:871b72c3643e516db4ecf20efe735deb27fe30ca17800e661d769faab45a18d7"},
    {file = "Pillow-9.3.0-cp310-cp310-win32.whl", hash = "sha256:655a83b0058ba47c7c52e4e2df5ecf484c1b0b0349805896dd350cbc416bdd91"},
    {file = "Pillow-9.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:9f47eabcd2ded7698106b05c2c338672d16a6f2a485e74481f524e2a23c2794b"},
    {file = "Pillow-9.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:57751894f6618fd4308ed8e0c36c333e2f5469744c34729a27532b3db106ee20"},
    {file = "Pillow-9.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7db8b751ad307d7cf238f02101e8e36a128a6cb199326e867d1398067381bff4"},
    {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3033fbe1feb1b59394615a1cafaee85e49d01b51d54de0cbf6aa8e64182518a1"},
    {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22b012ea2d065fd163ca096f4e37e47cd8b59cf4b0fd47bfca6abb93df70b34c"},
    {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a65733d103311331875c1dca05cb4606997fd33d6acfed695b1232ba1df193"},
    {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:502526a2cbfa431d9fc2a079bdd9061a2397b842bb6bc4239bb176da00993812"},
    {file = "Pillow-9.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:90fb88843d3902fe7c9586d439d1e8c05258f41da473952aa8b328d8b907498c"},
    {file = "Pillow-9.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:89dca0ce00a2b49024df6325925555d406b14aa3efc2f752dbb5940c52c56b11"},
    {file = "Pillow-9.3.0-cp311-cp311-win32.whl", hash = "sha256:3168434d303babf495d4ba58fc22d6604f6e2afb97adc6a423e917dab828939c"},
    {file = "Pillow-9.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:18498994b29e1cf86d505edcb7edbe814d133d2232d256db8c7a8ceb34d18cef"},
    {file = "Pillow-9.3.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:772a91fc0e03eaf922c63badeca75e91baa80fe2f5f87bdaed4280662aad25c9"},
    {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa4107d1b306cdf8953edde0534562607fe8811b6c4d9a486298ad31de733b2"},
    {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4012d06c846dc2b80651b120e2cdd787b013deb39c09f407727ba90015c684f"},
    {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77ec3e7be99629898c9a6d24a09de089fa5356ee408cdffffe62d67bb75fdd72"},
    {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:6c738585d7a9961d8c2821a1eb3dcb978d14e238be3d70f0a706f7fa9316946b"},
    {file = "Pillow-9.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:828989c45c245518065a110434246c44a56a8b2b2f6347d1409c787e6e4651ee"},
    {file = "Pillow-9.3.0-cp37-cp37m-win32.whl", hash = "sha256:82409ffe29d70fd733ff3c1025a602abb3e67405d41b9403b00b01debc4c9a29"},
    {file = "Pillow-9.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:41e0051336807468be450d52b8edd12ac60bebaa97fe10c8b660f116e50b30e4"},
    {file = "Pillow-9.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:b03ae6f1a1878233ac620c98f3459f79fd77c7e3c2b20d460284e1fb370557d4"},
    {file = "Pillow-9.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4390e9ce199fc1951fcfa65795f239a8a4944117b5935a9317fb320e7767b40f"},
    {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40e1ce476a7804b0fb74bcfa80b0a2206ea6a882938eaba917f7a0f004b42502"},
    {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a06a052c5f37b4ed81c613a455a81f9a3a69429b4fd7bb913c3fa98abefc20"},
    {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03150abd92771742d4a8cd6f2fa6246d847dcd2e332a18d0c15cc75bf6703040"},
    {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:15c42fb9dea42465dfd902fb0ecf584b8848ceb28b41ee2b58f866411be33f07"},
    {file = "Pillow-9.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:51e0e543a33ed92db9f5ef69a0356e0b1a7a6b6a71b80df99f1d181ae5875636"},
    {file = "Pillow-9.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3dd6caf940756101205dffc5367babf288a30043d35f80936f9bfb37f8355b32"},
    {file = "Pillow-9.3.0-cp38-cp38-win32.whl", hash = "sha256:f1ff2ee69f10f13a9596480335f406dd1f70c3650349e2be67ca3139280cade0"},
    {file = "Pillow-9.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:276a5ca930c913f714e372b2591a22c4bd3b81a418c0f6635ba832daec1cbcfc"},
    {file = "Pillow-9.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:73bd195e43f3fadecfc50c682f5055ec32ee2c933243cafbfdec69ab1aa87cad"},
    {file = "Pillow-9.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c7c8ae3864846fc95f4611c78129301e203aaa2af813b703c55d10cc1628535"},
    {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0918e03aa0c72ea56edbb00d4d664294815aa11291a11504a377ea018330d3"},
    {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0915e734b33a474d76c28e07292f196cdf2a590a0d25bcc06e64e545f2d146c"},
    {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0372acb5d3598f36ec0914deed2a63f6bcdb7b606da04dc19a88d31bf0c05b"},
    {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ad58d27a5b0262c0c19b47d54c5802db9b34d38bbf886665b626aff83c74bacd"},
    {file = "Pillow-9.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:97aabc5c50312afa5e0a2b07c17d4ac5e865b250986f8afe2b02d772567a380c"},
    {file = "Pillow-9.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9aaa107275d8527e9d6e7670b64aabaaa36e5b6bd71a1015ddd21da0d4e06448"},
    {file = "Pillow-9.3.0-cp39-cp39-win32.whl", hash = "sha256:bac18ab8d2d1e6b4ce25e3424f709aceef668347db8637c2296bcf41acb7cf48"},
    {file = "Pillow-9.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:b472b5ea442148d1c3e2209f20f1e0bb0eb556538690fa70b5e1f79fa0ba8dc2"},
    {file = "Pillow-9.3.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ab388aaa3f6ce52ac1cb8e122c4bd46657c15905904b3120a6248b5b8b0bc228"},
    {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbb8e7f2abee51cef77673be97760abff1674ed32847ce04b4af90f610144c7b"},
    {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca31dd6014cb8b0b2db1e46081b0ca7d936f856da3b39744aef499db5d84d02"},
    {file = "Pillow-9.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c7025dce65566eb6e89f56c9509d4f628fddcedb131d9465cacd3d8bac337e7e"},
    {file = "Pillow-9.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ebf2029c1f464c59b8bdbe5143c79fa2045a581ac53679733d3a91d400ff9efb"},
    {file = "Pillow-9.3.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b59430236b8e58840a0dfb4099a0e8717ffb779c952426a69ae435ca1f57210c"},
    {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12ce4932caf2ddf3e41d17fc9c02d67126935a44b86df6a206cf0d7161548627"},
    {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae5331c23ce118c53b172fa64a4c037eb83c9165aba3a7ba9ddd3ec9fa64a699"},
    {file = "Pillow-9.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0b07fffc13f474264c336298d1b4ce01d9c5a011415b79d4ee5527bb69ae6f65"},
    {file = "Pillow-9.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:073adb2ae23431d3b9bcbcff3fe698b62ed47211d0716b067385538a1b0f28b8"},
    {file = "Pillow-9.3.0.tar.gz", hash = "sha256:c935a22a557a560108d780f9a0fc426dd7459940dc54faa49d83249c8d3e760f"},
]
pkgutil-resolve-name = [
    {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"},
    {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"},
]
platformdirs = [
    {file = "platformdirs-2.5.4-py3-none-any.whl", hash = "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10"},
    {file = "platformdirs-2.5.4.tar.gz", hash = "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7"},
]
pluggy = [
    {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
    {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
]
pre-commit = [
    {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"},
    {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"},
]
prometheus-client = [
    {file = "prometheus_client-0.15.0-py3-none-any.whl", hash = "sha256:db7c05cbd13a0f79975592d112320f2605a325969b270a94b71dcabc47b931d2"},
    {file = "prometheus_client-0.15.0.tar.gz", hash = "sha256:be26aa452490cfcf6da953f9436e95a9f2b4d578ca80094b4458930e5f584ab1"},
]
prompt-toolkit = [
    {file = "prompt_toolkit-3.0.32-py3-none-any.whl", hash = "sha256:24becda58d49ceac4dc26232eb179ef2b21f133fecda7eed6018d341766ed76e"},
    {file = "prompt_toolkit-3.0.32.tar.gz", hash = "sha256:e7f2129cba4ff3b3656bbdda0e74ee00d2f874a8bcdb9dd16f5fec7b3e173cae"},
]
protobuf = [
    {file = "protobuf-4.21.9-cp310-abi3-win32.whl", hash = "sha256:6e0be9f09bf9b6cf497b27425487706fa48c6d1632ddd94dab1a5fe11a422392"},
    {file = "protobuf-4.21.9-cp310-abi3-win_amd64.whl", hash = "sha256:a7d0ea43949d45b836234f4ebb5ba0b22e7432d065394b532cdca8f98415e3cf"},
    {file = "protobuf-4.21.9-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b5ab0b8918c136345ff045d4b3d5f719b505b7c8af45092d7f45e304f55e50a1"},
    {file = "protobuf-4.21.9-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:2c9c2ed7466ad565f18668aa4731c535511c5d9a40c6da39524bccf43e441719"},
    {file = "protobuf-4.21.9-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:e575c57dc8b5b2b2caa436c16d44ef6981f2235eb7179bfc847557886376d740"},
    {file = "protobuf-4.21.9-cp37-cp37m-win32.whl", hash = "sha256:9227c14010acd9ae7702d6467b4625b6fe853175a6b150e539b21d2b2f2b409c"},
    {file = "protobuf-4.21.9-cp37-cp37m-win_amd64.whl", hash = "sha256:a419cc95fca8694804709b8c4f2326266d29659b126a93befe210f5bbc772536"},
    {file = "protobuf-4.21.9-cp38-cp38-win32.whl", hash = "sha256:5b0834e61fb38f34ba8840d7dcb2e5a2f03de0c714e0293b3963b79db26de8ce"},
    {file = "protobuf-4.21.9-cp38-cp38-win_amd64.whl", hash = "sha256:84ea107016244dfc1eecae7684f7ce13c788b9a644cd3fca5b77871366556444"},
    {file = "protobuf-4.21.9-cp39-cp39-win32.whl", hash = "sha256:f9eae277dd240ae19bb06ff4e2346e771252b0e619421965504bd1b1bba7c5fa"},
    {file = "protobuf-4.21.9-cp39-cp39-win_amd64.whl", hash = "sha256:6e312e280fbe3c74ea9e080d9e6080b636798b5e3939242298b591064470b06b"},
    {file = "protobuf-4.21.9-py2.py3-none-any.whl", hash = "sha256:7eb8f2cc41a34e9c956c256e3ac766cf4e1a4c9c925dc757a41a01be3e852965"},
    {file = "protobuf-4.21.9-py3-none-any.whl", hash = "sha256:48e2cd6b88c6ed3d5877a3ea40df79d08374088e89bedc32557348848dff250b"},
    {file = "protobuf-4.21.9.tar.gz", hash = "sha256:61f21493d96d2a77f9ca84fefa105872550ab5ef71d21c458eb80edcf4885a99"},
]
psutil = [
    {file = "psutil-5.9.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8"},
    {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe"},
    {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549"},
    {file = "psutil-5.9.4-cp27-cp27m-win32.whl", hash = "sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad"},
    {file = "psutil-5.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94"},
    {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24"},
    {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7"},
    {file = "psutil-5.9.4-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7"},
    {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1"},
    {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08"},
    {file = "psutil-5.9.4-cp36-abi3-win32.whl", hash = "sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff"},
    {file = "psutil-5.9.4-cp36-abi3-win_amd64.whl", hash = "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"},
    {file = "psutil-5.9.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e"},
    {file = "psutil-5.9.4.tar.gz", hash = "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62"},
]
ptyprocess = [
    {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
    {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
]
pure-eval = [
    {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"},
    {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"},
]
py = [
    {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
    {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
pycparser = [
    {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
    {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
]
pydantic = [
    {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"},
    {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"},
    {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"},
    {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"},
    {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"},
    {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"},
    {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"},
    {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"},
    {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"},
    {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"},
    {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"},
    {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"},
    {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"},
    {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"},
    {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"},
    {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"},
    {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"},
    {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"},
    {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"},
    {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"},
    {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"},
    {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"},
    {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"},
    {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"},
    {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"},
    {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"},
    {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"},
    {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"},
    {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"},
    {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"},
    {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"},
    {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"},
    {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"},
    {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"},
    {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"},
    {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"},
]
pygments = [
    {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"},
    {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"},
]
pyparsing = [
    {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
    {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
]
pyrsistent = [
    {file = "pyrsistent-0.19.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d6982b5a0237e1b7d876b60265564648a69b14017f3b5f908c5be2de3f9abb7a"},
    {file = "pyrsistent-0.19.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:187d5730b0507d9285a96fca9716310d572e5464cadd19f22b63a6976254d77a"},
    {file = "pyrsistent-0.19.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:055ab45d5911d7cae397dc418808d8802fb95262751872c841c170b0dbf51eed"},
    {file = "pyrsistent-0.19.2-cp310-cp310-win32.whl", hash = "sha256:456cb30ca8bff00596519f2c53e42c245c09e1a4543945703acd4312949bfd41"},
    {file = "pyrsistent-0.19.2-cp310-cp310-win_amd64.whl", hash = "sha256:b39725209e06759217d1ac5fcdb510e98670af9e37223985f330b611f62e7425"},
    {file = "pyrsistent-0.19.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2aede922a488861de0ad00c7630a6e2d57e8023e4be72d9d7147a9fcd2d30712"},
    {file = "pyrsistent-0.19.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879b4c2f4d41585c42df4d7654ddffff1239dc4065bc88b745f0341828b83e78"},
    {file = "pyrsistent-0.19.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c43bec251bbd10e3cb58ced80609c5c1eb238da9ca78b964aea410fb820d00d6"},
    {file = "pyrsistent-0.19.2-cp37-cp37m-win32.whl", hash = "sha256:d690b18ac4b3e3cab73b0b7aa7dbe65978a172ff94970ff98d82f2031f8971c2"},
    {file = "pyrsistent-0.19.2-cp37-cp37m-win_amd64.whl", hash = "sha256:3ba4134a3ff0fc7ad225b6b457d1309f4698108fb6b35532d015dca8f5abed73"},
    {file = "pyrsistent-0.19.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a178209e2df710e3f142cbd05313ba0c5ebed0a55d78d9945ac7a4e09d923308"},
    {file = "pyrsistent-0.19.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e371b844cec09d8dc424d940e54bba8f67a03ebea20ff7b7b0d56f526c71d584"},
    {file = "pyrsistent-0.19.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111156137b2e71f3a9936baf27cb322e8024dac3dc54ec7fb9f0bcf3249e68bb"},
    {file = "pyrsistent-0.19.2-cp38-cp38-win32.whl", hash = "sha256:e5d8f84d81e3729c3b506657dddfe46e8ba9c330bf1858ee33108f8bb2adb38a"},
    {file = "pyrsistent-0.19.2-cp38-cp38-win_amd64.whl", hash = "sha256:9cd3e9978d12b5d99cbdc727a3022da0430ad007dacf33d0bf554b96427f33ab"},
    {file = "pyrsistent-0.19.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f1258f4e6c42ad0b20f9cfcc3ada5bd6b83374516cd01c0960e3cb75fdca6770"},
    {file = "pyrsistent-0.19.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21455e2b16000440e896ab99e8304617151981ed40c29e9507ef1c2e4314ee95"},
    {file = "pyrsistent-0.19.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd880614c6237243ff53a0539f1cb26987a6dc8ac6e66e0c5a40617296a045e"},
    {file = "pyrsistent-0.19.2-cp39-cp39-win32.whl", hash = "sha256:71d332b0320642b3261e9fee47ab9e65872c2bd90260e5d225dabeed93cbd42b"},
    {file = "pyrsistent-0.19.2-cp39-cp39-win_amd64.whl", hash = "sha256:dec3eac7549869365fe263831f576c8457f6c833937c68542d08fde73457d291"},
    {file = "pyrsistent-0.19.2-py3-none-any.whl", hash = "sha256:ea6b79a02a28550c98b6ca9c35b9f492beaa54d7c5c9e9949555893c8a9234d0"},
    {file = "pyrsistent-0.19.2.tar.gz", hash = "sha256:bfa0351be89c9fcbcb8c9879b826f4353be10f58f8a677efab0c017bf7137ec2"},
]
pytest = [
    {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
    {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
]
pytest-asyncio = [
    {file = "pytest-asyncio-0.20.2.tar.gz", hash = "sha256:32a87a9836298a881c0ec637ebcc952cfe23a56436bdc0d09d1511941dd8a812"},
    {file = "pytest_asyncio-0.20.2-py3-none-any.whl", hash = "sha256:07e0abf9e6e6b95894a39f688a4a875d63c2128f76c02d03d16ccbc35bcc0f8a"},
]
python-dateutil = [
    {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
    {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
]
pytz = [
    {file = "pytz-2022.6-py2.py3-none-any.whl", hash = "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427"},
    {file = "pytz-2022.6.tar.gz", hash = "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"},
]
pywin32 = [
    {file = "pywin32-305-cp310-cp310-win32.whl", hash = "sha256:421f6cd86e84bbb696d54563c48014b12a23ef95a14e0bdba526be756d89f116"},
    {file = "pywin32-305-cp310-cp310-win_amd64.whl", hash = "sha256:73e819c6bed89f44ff1d690498c0a811948f73777e5f97c494c152b850fad478"},
    {file = "pywin32-305-cp310-cp310-win_arm64.whl", hash = "sha256:742eb905ce2187133a29365b428e6c3b9001d79accdc30aa8969afba1d8470f4"},
    {file = "pywin32-305-cp311-cp311-win32.whl", hash = "sha256:19ca459cd2e66c0e2cc9a09d589f71d827f26d47fe4a9d09175f6aa0256b51c2"},
    {file = "pywin32-305-cp311-cp311-win_amd64.whl", hash = "sha256:326f42ab4cfff56e77e3e595aeaf6c216712bbdd91e464d167c6434b28d65990"},
    {file = "pywin32-305-cp311-cp311-win_arm64.whl", hash = "sha256:4ecd404b2c6eceaca52f8b2e3e91b2187850a1ad3f8b746d0796a98b4cea04db"},
    {file = "pywin32-305-cp36-cp36m-win32.whl", hash = "sha256:48d8b1659284f3c17b68587af047d110d8c44837736b8932c034091683e05863"},
    {file = "pywin32-305-cp36-cp36m-win_amd64.whl", hash = "sha256:13362cc5aa93c2beaf489c9c9017c793722aeb56d3e5166dadd5ef82da021fe1"},
    {file = "pywin32-305-cp37-cp37m-win32.whl", hash = "sha256:a55db448124d1c1484df22fa8bbcbc45c64da5e6eae74ab095b9ea62e6d00496"},
    {file = "pywin32-305-cp37-cp37m-win_amd64.whl", hash = "sha256:109f98980bfb27e78f4df8a51a8198e10b0f347257d1e265bb1a32993d0c973d"},
    {file = "pywin32-305-cp38-cp38-win32.whl", hash = "sha256:9dd98384da775afa009bc04863426cb30596fd78c6f8e4e2e5bbf4edf8029504"},
    {file = "pywin32-305-cp38-cp38-win_amd64.whl", hash = "sha256:56d7a9c6e1a6835f521788f53b5af7912090674bb84ef5611663ee1595860fc7"},
    {file = "pywin32-305-cp39-cp39-win32.whl", hash = "sha256:9d968c677ac4d5cbdaa62fd3014ab241718e619d8e36ef8e11fb930515a1e918"},
    {file = "pywin32-305-cp39-cp39-win_amd64.whl", hash = "sha256:50768c6b7c3f0b38b7fb14dd4104da93ebced5f1a50dc0e834594bff6fbe1271"},
]
pywinpty = [
    {file = "pywinpty-2.0.9-cp310-none-win_amd64.whl", hash = "sha256:30a7b371446a694a6ce5ef906d70ac04e569de5308c42a2bdc9c3bc9275ec51f"},
    {file = "pywinpty-2.0.9-cp311-none-win_amd64.whl", hash = "sha256:d78ef6f4bd7a6c6f94dc1a39ba8fb028540cc39f5cb593e756506db17843125f"},
    {file = "pywinpty-2.0.9-cp37-none-win_amd64.whl", hash = "sha256:5ed36aa087e35a3a183f833631b3e4c1ae92fe2faabfce0fa91b77ed3f0f1382"},
    {file = "pywinpty-2.0.9-cp38-none-win_amd64.whl", hash = "sha256:2352f44ee913faaec0a02d3c112595e56b8af7feeb8100efc6dc1a8685044199"},
    {file = "pywinpty-2.0.9-cp39-none-win_amd64.whl", hash = "sha256:ba75ec55f46c9e17db961d26485b033deb20758b1731e8e208e1e8a387fcf70c"},
    {file = "pywinpty-2.0.9.tar.gz", hash = "sha256:01b6400dd79212f50a2f01af1c65b781290ff39610853db99bf03962eb9a615f"},
]
pyyaml = [
    {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
    {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
    {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
    {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
    {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
    {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
    {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
    {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
    {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
    {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
    {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
    {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
    {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
    {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
    {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
    {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
    {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
    {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
    {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
    {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
    {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
    {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
]
pyzmq = [
    {file = "pyzmq-24.0.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:28b119ba97129d3001673a697b7cce47fe6de1f7255d104c2f01108a5179a066"},
    {file = "pyzmq-24.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bcbebd369493d68162cddb74a9c1fcebd139dfbb7ddb23d8f8e43e6c87bac3a6"},
    {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae61446166983c663cee42c852ed63899e43e484abf080089f771df4b9d272ef"},
    {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f7ac99b15270db8d53f28c3c7b968612993a90a5cf359da354efe96f5372b4"},
    {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca7c3956b03b7663fac4d150f5e6d4f6f38b2462c1e9afd83bcf7019f17913"},
    {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8c78bfe20d4c890cb5580a3b9290f700c570e167d4cdcc55feec07030297a5e3"},
    {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:48f721f070726cd2a6e44f3c33f8ee4b24188e4b816e6dd8ba542c8c3bb5b246"},
    {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:afe1f3bc486d0ce40abb0a0c9adb39aed3bbac36ebdc596487b0cceba55c21c1"},
    {file = "pyzmq-24.0.1-cp310-cp310-win32.whl", hash = "sha256:3e6192dbcefaaa52ed81be88525a54a445f4b4fe2fffcae7fe40ebb58bd06bfd"},
    {file = "pyzmq-24.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:86de64468cad9c6d269f32a6390e210ca5ada568c7a55de8e681ca3b897bb340"},
    {file = "pyzmq-24.0.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:838812c65ed5f7c2bd11f7b098d2e5d01685a3f6d1f82849423b570bae698c00"},
    {file = "pyzmq-24.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dfb992dbcd88d8254471760879d48fb20836d91baa90f181c957122f9592b3dc"},
    {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7abddb2bd5489d30ffeb4b93a428130886c171b4d355ccd226e83254fcb6b9ef"},
    {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94010bd61bc168c103a5b3b0f56ed3b616688192db7cd5b1d626e49f28ff51b3"},
    {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8242543c522d84d033fe79be04cb559b80d7eb98ad81b137ff7e0a9020f00ace"},
    {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ccb94342d13e3bf3ffa6e62f95b5e3f0bc6bfa94558cb37f4b3d09d6feb536ff"},
    {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6640f83df0ae4ae1104d4c62b77e9ef39be85ebe53f636388707d532bee2b7b8"},
    {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a180dbd5ea5d47c2d3b716d5c19cc3fb162d1c8db93b21a1295d69585bfddac1"},
    {file = "pyzmq-24.0.1-cp311-cp311-win32.whl", hash = "sha256:624321120f7e60336be8ec74a172ae7fba5c3ed5bf787cc85f7e9986c9e0ebc2"},
    {file = "pyzmq-24.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:1724117bae69e091309ffb8255412c4651d3f6355560d9af312d547f6c5bc8b8"},
    {file = "pyzmq-24.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:15975747462ec49fdc863af906bab87c43b2491403ab37a6d88410635786b0f4"},
    {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b947e264f0e77d30dcbccbb00f49f900b204b922eb0c3a9f0afd61aaa1cedc3d"},
    {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ec91f1bad66f3ee8c6deb65fa1fe418e8ad803efedd69c35f3b5502f43bd1dc"},
    {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:db03704b3506455d86ec72c3358a779e9b1d07b61220dfb43702b7b668edcd0d"},
    {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e7e66b4e403c2836ac74f26c4b65d8ac0ca1eef41dfcac2d013b7482befaad83"},
    {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:7a23ccc1083c260fa9685c93e3b170baba45aeed4b524deb3f426b0c40c11639"},
    {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fa0ae3275ef706c0309556061185dd0e4c4cd3b7d6f67ae617e4e677c7a41e2e"},
    {file = "pyzmq-24.0.1-cp36-cp36m-win32.whl", hash = "sha256:f01de4ec083daebf210531e2cca3bdb1608dbbbe00a9723e261d92087a1f6ebc"},
    {file = "pyzmq-24.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:de4217b9eb8b541cf2b7fde4401ce9d9a411cc0af85d410f9d6f4333f43640be"},
    {file = "pyzmq-24.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:78068e8678ca023594e4a0ab558905c1033b2d3e806a0ad9e3094e231e115a33"},
    {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77c2713faf25a953c69cf0f723d1b7dd83827b0834e6c41e3fb3bbc6765914a1"},
    {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bb4af15f305056e95ca1bd086239b9ebc6ad55e9f49076d27d80027f72752f6"},
    {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0f14cffd32e9c4c73da66db97853a6aeceaac34acdc0fae9e5bbc9370281864c"},
    {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0108358dab8c6b27ff6b985c2af4b12665c1bc659648284153ee501000f5c107"},
    {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d66689e840e75221b0b290b0befa86f059fb35e1ee6443bce51516d4d61b6b99"},
    {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ae08ac90aa8fa14caafc7a6251bd218bf6dac518b7bff09caaa5e781119ba3f2"},
    {file = "pyzmq-24.0.1-cp37-cp37m-win32.whl", hash = "sha256:8421aa8c9b45ea608c205db9e1c0c855c7e54d0e9c2c2f337ce024f6843cab3b"},
    {file = "pyzmq-24.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54d8b9c5e288362ec8595c1d98666d36f2070fd0c2f76e2b3c60fbad9bd76227"},
    {file = "pyzmq-24.0.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:acbd0a6d61cc954b9f535daaa9ec26b0a60a0d4353c5f7c1438ebc88a359a47e"},
    {file = "pyzmq-24.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:47b11a729d61a47df56346283a4a800fa379ae6a85870d5a2e1e4956c828eedc"},
    {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abe6eb10122f0d746a0d510c2039ae8edb27bc9af29f6d1b05a66cc2401353ff"},
    {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:07bec1a1b22dacf718f2c0e71b49600bb6a31a88f06527dfd0b5aababe3fa3f7"},
    {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0d945a85b70da97ae86113faf9f1b9294efe66bd4a5d6f82f2676d567338b66"},
    {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1b7928bb7580736ffac5baf814097be342ba08d3cfdfb48e52773ec959572287"},
    {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b946da90dc2799bcafa682692c1d2139b2a96ec3c24fa9fc6f5b0da782675330"},
    {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c8840f064b1fb377cffd3efeaad2b190c14d4c8da02316dae07571252d20b31f"},
    {file = "pyzmq-24.0.1-cp38-cp38-win32.whl", hash = "sha256:4854f9edc5208f63f0841c0c667260ae8d6846cfa233c479e29fdc85d42ebd58"},
    {file = "pyzmq-24.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:42d4f97b9795a7aafa152a36fe2ad44549b83a743fd3e77011136def512e6c2a"},
    {file = "pyzmq-24.0.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:52afb0ac962963fff30cf1be775bc51ae083ef4c1e354266ab20e5382057dd62"},
    {file = "pyzmq-24.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bad8210ad4df68c44ff3685cca3cda448ee46e20d13edcff8909eba6ec01ca4"},
    {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dabf1a05318d95b1537fd61d9330ef4313ea1216eea128a17615038859da3b3b"},
    {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5bd3d7dfd9cd058eb68d9a905dec854f86649f64d4ddf21f3ec289341386c44b"},
    {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8012bce6836d3f20a6c9599f81dfa945f433dab4dbd0c4917a6fb1f998ab33d"},
    {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c31805d2c8ade9b11feca4674eee2b9cce1fec3e8ddb7bbdd961a09dc76a80ea"},
    {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3104f4b084ad5d9c0cb87445cc8cfd96bba710bef4a66c2674910127044df209"},
    {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:df0841f94928f8af9c7a1f0aaaffba1fb74607af023a152f59379c01c53aee58"},
    {file = "pyzmq-24.0.1-cp39-cp39-win32.whl", hash = "sha256:a435ef8a3bd95c8a2d316d6e0ff70d0db524f6037411652803e118871d703333"},
    {file = "pyzmq-24.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:2032d9cb994ce3b4cba2b8dfae08c7e25bc14ba484c770d4d3be33c27de8c45b"},
    {file = "pyzmq-24.0.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bb5635c851eef3a7a54becde6da99485eecf7d068bd885ac8e6d173c4ecd68b0"},
    {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:83ea1a398f192957cb986d9206ce229efe0ee75e3c6635baff53ddf39bd718d5"},
    {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:941fab0073f0a54dc33d1a0460cb04e0d85893cb0c5e1476c785000f8b359409"},
    {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e8f482c44ccb5884bf3f638f29bea0f8dc68c97e38b2061769c4cb697f6140d"},
    {file = "pyzmq-24.0.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:613010b5d17906c4367609e6f52e9a2595e35d5cc27d36ff3f1b6fa6e954d944"},
    {file = "pyzmq-24.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:65c94410b5a8355cfcf12fd600a313efee46ce96a09e911ea92cf2acf6708804"},
    {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:20e7eeb1166087db636c06cae04a1ef59298627f56fb17da10528ab52a14c87f"},
    {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2712aee7b3834ace51738c15d9ee152cc5a98dc7d57dd93300461b792ab7b43"},
    {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a7c280185c4da99e0cc06c63bdf91f5b0b71deb70d8717f0ab870a43e376db8"},
    {file = "pyzmq-24.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:858375573c9225cc8e5b49bfac846a77b696b8d5e815711b8d4ba3141e6e8879"},
    {file = "pyzmq-24.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:80093b595921eed1a2cead546a683b9e2ae7f4a4592bb2ab22f70d30174f003a"},
    {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f3f3154fde2b1ff3aa7b4f9326347ebc89c8ef425ca1db8f665175e6d3bd42f"},
    {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb756147314430bee5d10919b8493c0ccb109ddb7f5dfd2fcd7441266a25b75"},
    {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e706bac34e9f50779cb8c39f10b53a4d15aebb97235643d3112ac20bd577b4"},
    {file = "pyzmq-24.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:687700f8371643916a1d2c61f3fdaa630407dd205c38afff936545d7b7466066"},
    {file = "pyzmq-24.0.1.tar.gz", hash = "sha256:216f5d7dbb67166759e59b0479bca82b8acf9bed6015b526b8eb10143fb08e77"},
]
requests = [
    {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
    {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
]
rfc3986 = [
    {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
    {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
]
ruff = [
    {file = "ruff-0.0.165-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:b13d433c38966c5fe7c044de55037c9715495a2941df457a27c691f519e4a94d"},
    {file = "ruff-0.0.165-py3-none-macosx_10_9_x86_64.macosx_10_9_arm64.macosx_10_9_universal2.whl", hash = "sha256:4c69d221ceb75a9a464f9a3d000e795806dedb1d010da874859809cbe38e3d30"},
    {file = "ruff-0.0.165-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3baef2179dd2067db1287c2dcb66b5ab1b1a124746d0f65485cc1129717d6554"},
    {file = "ruff-0.0.165-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d70502afbefac54f85a1754869f9cd3477dc33c9ae6ca2338a11ac2b780ed06"},
    {file = "ruff-0.0.165-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:133f076ceabc25ff5aec017fe8084b3eedd82ece28f287fbd2e1685bb14a2554"},
    {file = "ruff-0.0.165-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c92cc05cceee332ed221702f7a63c19dca2cb87c33bf06b9a085630070c33192"},
    {file = "ruff-0.0.165-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eadca0b7116d49ad6faed505ad181bca39bca111478a4b2f1f8c39a632955c2f"},
    {file = "ruff-0.0.165-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85135ffc825edfcf6fa17ec2e5569aaba3fa7cd096d45a4d5fc896285b266a5b"},
    {file = "ruff-0.0.165-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1a9f6d0139571d05392a1f7f94a4e217768a9f8595910ab2dfe745a0ca1fab7"},
    {file = "ruff-0.0.165-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4109826311fabc68633073c408048448ab870456adf1c40252795131de2624a5"},
    {file = "ruff-0.0.165-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5cac57e0a80f593aebe3975cf9f8c776e13c6236608d2fef2893f7980a2a0510"},
    {file = "ruff-0.0.165-py3-none-musllinux_1_2_i686.whl", hash = "sha256:32f16721360b3e973f1e3fe013a1aa33522b24532925e622417080beda5d7478"},
    {file = "ruff-0.0.165-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e0be5acdd86269963f1fa1c4dd3c3ec37f14c847d889591ff5bc1fd934c0cfa3"},
    {file = "ruff-0.0.165-py3-none-win32.whl", hash = "sha256:dacd94f66c6d42c23c22776d9cc6c726bf42987a38358953bec0e4eec0b72574"},
    {file = "ruff-0.0.165-py3-none-win_amd64.whl", hash = "sha256:c20ba25907d52fae33ea363a741e3ba03fc5e9712cbc3b12572897768f24bcf6"},
    {file = "ruff-0.0.165.tar.gz", hash = "sha256:5468b30e0c5888fd436568a47da31f8c827affbacaba06c1ca8ad1f7f0df9e4e"},
]
send2trash = [
    {file = "Send2Trash-1.8.0-py3-none-any.whl", hash = "sha256:f20eaadfdb517eaca5ce077640cb261c7d2698385a6a0f072a4a5447fd49fa08"},
    {file = "Send2Trash-1.8.0.tar.gz", hash = "sha256:d2c24762fd3759860a0aff155e45871447ea58d2be6bdd39b5c8f966a0c99c2d"},
]
setuptools = [
    {file = "setuptools-65.5.1-py3-none-any.whl", hash = "sha256:d0b9a8433464d5800cbe05094acf5c6d52a91bfac9b52bcfc4d41382be5d5d31"},
    {file = "setuptools-65.5.1.tar.gz", hash = "sha256:e197a19aa8ec9722928f2206f8de752def0e4c9fc6953527360d1c36d94ddb2f"},
]
six = [
    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
sniffio = [
    {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
    {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
]
soupsieve = [
    {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"},
    {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"},
]
stack-data = [
    {file = "stack_data-0.6.1-py3-none-any.whl", hash = "sha256:960cb054d6a1b2fdd9cbd529e365b3c163e8dabf1272e02cfe36b58403cff5c6"},
    {file = "stack_data-0.6.1.tar.gz", hash = "sha256:6c9a10eb5f342415fe085db551d673955611afb821551f554d91772415464315"},
]
starlette = [
    {file = "starlette-0.21.0-py3-none-any.whl", hash = "sha256:0efc058261bbcddeca93cad577efd36d0c8a317e44376bcfc0e097a2b3dc24a7"},
    {file = "starlette-0.21.0.tar.gz", hash = "sha256:b1b52305ee8f7cfc48cde383496f7c11ab897cd7112b33d998b1317dc8ef9027"},
]
terminado = [
    {file = "terminado-0.17.0-py3-none-any.whl", hash = "sha256:bf6fe52accd06d0661d7611cc73202121ec6ee51e46d8185d489ac074ca457c2"},
    {file = "terminado-0.17.0.tar.gz", hash = "sha256:520feaa3aeab8ad64a69ca779be54be9234edb2d0d6567e76c93c2c9a4e6e43f"},
]
tinycss2 = [
    {file = "tinycss2-1.2.1-py3-none-any.whl", hash = "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847"},
    {file = "tinycss2-1.2.1.tar.gz", hash = "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627"},
]
toml = [
    {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
    {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
tomli = [
    {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
    {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
torch = [
    {file = "torch-1.13.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:f68edfea71ade3862039ba66bcedf954190a2db03b0c41a9b79afd72210abd97"},
    {file = "torch-1.13.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d2d2753519415d154de4d3e64d2eaaeefdba6b6fd7d69d5ffaef595988117700"},
    {file = "torch-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:6c227c16626e4ce766cca5351cc62a2358a11e8e466410a298487b9dff159eb1"},
    {file = "torch-1.13.0-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:49a949b8136b32b2ec0724cbf4c6678b54e974b7d68f19f1231eea21cde5c23b"},
    {file = "torch-1.13.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:0fdd38c96230947b1ed870fed4a560252f8d23c3a2bf4dab9d2d42b18f2e67c8"},
    {file = "torch-1.13.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:43db0723fc66ad6486f86dc4890c497937f7cd27429f28f73fb7e4d74b7482e2"},
    {file = "torch-1.13.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e643ac8d086706e82f77b5d4dfcf145a9dd37b69e03e64177fc23821754d2ed7"},
    {file = "torch-1.13.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:bb33a911460475d1594a8c8cb73f58c08293211760796d99cae8c2509b86d7f1"},
    {file = "torch-1.13.0-cp37-cp37m-win_amd64.whl", hash = "sha256:220325d0f4e69ee9edf00c04208244ef7cf22ebce083815ce272c7491f0603f5"},
    {file = "torch-1.13.0-cp37-none-macosx_10_9_x86_64.whl", hash = "sha256:cd1e67db6575e1b173a626077a54e4911133178557aac50683db03a34e2b636a"},
    {file = "torch-1.13.0-cp37-none-macosx_11_0_arm64.whl", hash = "sha256:9197ec216833b836b67e4d68e513d31fb38d9789d7cd998a08fba5b499c38454"},
    {file = "torch-1.13.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fa768432ce4b8ffa29184c79a3376ab3de4a57b302cdf3c026a6be4c5a8ab75b"},
    {file = "torch-1.13.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:635dbb99d981a6483ca533b3dc7be18ef08dd9e1e96fb0bb0e6a99d79e85a130"},
    {file = "torch-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:857c7d5b1624c5fd979f66d2b074765733dba3f5e1cc97b7d6909155a2aae3ce"},
    {file = "torch-1.13.0-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:ef934a21da6f6a516d0a9c712a80d09c56128abdc6af8dc151bee5199b4c3b4e"},
    {file = "torch-1.13.0-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:f01a9ae0d4b69d2fc4145e8beab45b7877342dddbd4838a7d3c11ca7f6680745"},
    {file = "torch-1.13.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:9ac382cedaf2f70afea41380ad8e7c06acef6b5b7e2aef3971cdad666ca6e185"},
    {file = "torch-1.13.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:e20df14d874b024851c58e8bb3846249cb120e677f7463f60c986e3661f88680"},
    {file = "torch-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:4a378f5091307381abfb30eb821174e12986f39b1cf7c4522bf99155256819eb"},
    {file = "torch-1.13.0-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:922a4910613b310fbeb87707f00cb76fec328eb60cc1349ed2173e7c9b6edcd8"},
    {file = "torch-1.13.0-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:47fe6228386bff6d74319a2ffe9d4ed943e6e85473d78e80502518c607d644d2"},
]
tornado = [
    {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"},
    {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"},
    {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"},
    {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"},
    {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"},
    {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"},
    {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"},
    {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"},
    {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"},
    {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"},
    {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"},
]
traitlets = [
    {file = "traitlets-5.5.0-py3-none-any.whl", hash = "sha256:1201b2c9f76097195989cdf7f65db9897593b0dfd69e4ac96016661bb6f0d30f"},
    {file = "traitlets-5.5.0.tar.gz", hash = "sha256:b122f9ff2f2f6c1709dab289a05555be011c87828e911c0cf4074b85cb780a79"},
]
trimesh = [
    {file = "trimesh-3.17.1-py3-none-any.whl", hash = "sha256:a09460ee4e11c32bf9f0643b86241b3e3e2aa86296c4912a0738b76da3034c00"},
    {file = "trimesh-3.17.1.tar.gz", hash = "sha256:025bb2fa3a2e87bdd6873f11db45a7ca19216f2f8b6aed29140fca57e32c298e"},
]
types-pillow = [
    {file = "types-Pillow-9.3.0.1.tar.gz", hash = "sha256:f3b7cada3fa496c78d75253c6b1f07a843d625f42e5639b320a72acaff6f7cfb"},
    {file = "types_Pillow-9.3.0.1-py3-none-any.whl", hash = "sha256:79837755fe9659f29efd1016e9903ac4a500e0c73260483f07296bd6ca47668b"},
]
types-protobuf = [
    {file = "types-protobuf-3.20.4.5.tar.gz", hash = "sha256:e9b45008d106e1d10cc77a29d2d344b85c0f01e2e643aaccf32f69e9e81b0cdd"},
    {file = "types_protobuf-3.20.4.5-py3-none-any.whl", hash = "sha256:97af5ce70d890fdb94cb0c906f5a6624ca2fef58bc04e27990a25509e992a950"},
]
typing-extensions = [
    {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
    {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
]
urllib3 = [
    {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
    {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
]
uvicorn = [
    {file = "uvicorn-0.19.0-py3-none-any.whl", hash = "sha256:cc277f7e73435748e69e075a721841f7c4a95dba06d12a72fe9874acced16f6f"},
    {file = "uvicorn-0.19.0.tar.gz", hash = "sha256:cf538f3018536edb1f4a826311137ab4944ed741d52aeb98846f52215de57f25"},
]
virtualenv = [
    {file = "virtualenv-20.16.7-py3-none-any.whl", hash = "sha256:efd66b00386fdb7dbe4822d172303f40cd05e50e01740b19ea42425cbe653e29"},
    {file = "virtualenv-20.16.7.tar.gz", hash = "sha256:8691e3ff9387f743e00f6bb20f70121f5e4f596cae754531f2b3b3a1b1ac696e"},
]
wcwidth = [
    {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
    {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
]
webencodings = [
    {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
    {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
]
websocket-client = [
    {file = "websocket-client-1.4.2.tar.gz", hash = "sha256:d6e8f90ca8e2dd4e8027c4561adeb9456b54044312dba655e7cae652ceb9ae59"},
    {file = "websocket_client-1.4.2-py3-none-any.whl", hash = "sha256:d6b06432f184438d99ac1f456eaf22fe1ade524c3dd16e661142dc54e9cba574"},
]
wheel = [
    {file = "wheel-0.38.4-py3-none-any.whl", hash = "sha256:b60533f3f5d530e971d6737ca6d58681ee434818fab630c83a734bb10c083ce8"},
    {file = "wheel-0.38.4.tar.gz", hash = "sha256:965f5259b566725405b05e7cf774052044b1ed30119b5d586b2703aafe8719ac"},
]
zipp = [
    {file = "zipp-3.10.0-py3-none-any.whl", hash = "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1"},
    {file = "zipp-3.10.0.tar.gz", hash = "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"},
]

SECURITY.md METASEP
# Security Policy

Security is very important for DocArray and its community. 

## Supported Versions

The latest versions of DocArray are supported.


## Reporting a Vulnerability

If you think you've found a vulnerability, even if you are not sure about it, please report it right away by sending an email to security@jina.ai. Please try to be as explicit as possible, describing all the steps and example code to reproduce the security issue.

Our team will review it thoroughly and get back to you.

Please refrain from publicly discussing a potential security vulnerability on Github issues or in Slack community. It's better to discuss it privately and try to find a solution first, to limit the potential impact as much as possible.

README.md METASEP
# DocArray - Version 2

_**NOTE**: This introduction refers to version 2 of DocArray, a rewrite that is currently at alpha stage.
Not all features that are mentioned here are already implemented.
If you are looking for the version 2 implementation roadmap see [here](https://github.com/docarray/docarray/issues/780),
for the (already released) version 1 of DocArray
see [here](https://github.com/docarray/docarray)._

DocArray is a library for **representing, sending and storing multi-modal data**, with a focus on applications in **ML** and
**Neural Search**.

This means that DocArray lets you do the following things:

## Represent

```python
from docarray import Document
from docarray.typing import TorchTensor, ImageUrl
from typing import Optional


class MyDocument(Document):
    description: str
    image_url: ImageUrl
    image_tensor: Optional[TorchTensor[3, 224, 224]]
    embedding: Optional[TorchTensor[768]]


doc = MyDocument(
    description="This is a photo of a mountain",
    image_url="https://upload.wikimedia.org/wikipedia/commons/2/2f/Alpamayo.jpg",
)
doc.image_tensor = doc.image_url.load()  # load image tensor from URL
doc.embedding = CLIPImageEncoder()(
    doc.image_tensor
)  # create and store embedding using model of your choice
```

- **Model** data of any type (audio, video, text, images, 3D meshes, raw tensors, etc) as a single, unified data structure, the `Document`
  - A `Document` is a juiced-up [Pydantic Model](https://pydantic-docs.helpmanual.io/usage/models/), inheriting all the benefits, while extending it with ML focussed features 

### Use pre-defined `Document`s for common use cases:

```python
from docarray import Image

doc = Image(
    url="https://upload.wikimedia.org/wikipedia/commons/2/2f/Alpamayo.jpg",
)
doc.image_tensor = doc.url.load()  # load image tensor from URL
doc.embedding = CLIPImageEncoder()(
    doc.image_tensor
)  # create and store embedding using model of your choice
```
### Compose nested Documents:

```python
from docarray import Image, Text, Document
import numpy as np


class MultiModalDocument(Document):
    image_doc: Image
    text_doc: Text


doc = MultiModalDocument(
    image_doc=Image(tensor=np.zeros((3, 224, 224))), text_doc=Text(text='hi!')
)
```

### Collect multiple `Documents` into a `DocumentArray`:
```python
from docarray import Image, DocumentArray

da = DocumentArray(
    [
        Image(
            url="https://upload.wikimedia.org/wikipedia/commons/2/2f/Alpamayo.jpg",
        )
        for _ in range(100)
    ]
)
```


## Send
- **Serialize** any `Document` or `DocumentArray` into _protobuf_, _json_, _jsonschema_, _bytes_ or _base64_
- Use in **microservice** architecture: Send over **HTTP** or **gRPC**
- Integrate seamlessly with **FastAPI** and **Jina**

```python
from docarray import Image
from httpx import AsyncClient
import numpy as np

doc = Image(tensor=np.zeros((3, 224, 224)))

# JSON over HTTP
async with AsyncClient(app=app, base_url="http://test") as ac:
    response = await ac.post("/doc/", data=input_doc.json())

# (de)serialize from/to protobuf
Image.from_protobuf(doc.to_protobuf())
```

## Store
- Persist and `DocumentArray` using a **`DocumentStore`**
- Store your Documents in any supported (vector) database: **Elasticsearch**, **Qdrant**, **Weaviate**, **Redis**, **Milvus**, **ANNLite** or **SQLite**
- Leverage DocumentStores to **perform vector search on your multi-modal data**

```python
# NOTE: DocumentStores are not yet implemented in version 2
from docarray import DocumentArray, Image, DocumentStore
import numpy as np

da = DocumentArray([Image(embedding=np.zeros((128,))) for _ in range(1000)])
store = DocumentStore[Image](
    storage='qdrant'
)  # create a DocumentStore with Qdrant as backend
store.insert(da)  # insert the DocumentArray into the DocumentStore
# find the 10 most similar images based on the 'embedding' field
match = store.find(Image(embedding=np.zeros((128,))), field='embedding', top_k=10)
```

If you want to get a deeper understanding of DocArray v2, it is best to do so on the basis of your
use case and background:

## Coming from DocArray

If you are already using DocArray, you will be familiar with its [dataclass API](https://docarray.jina.ai/fundamentals/dataclass/).

_DocArray v2 is that idea, taken seriously._ Every `Document` is created through dataclass-like interface,
courtesy of [Pydantic](https://pydantic-docs.helpmanual.io/usage/models/).

This gives the following advantages:
- **Flexibility:** No need to conform to a fixed set of fields, your data defines the schema
- **Multi-modality:** Easily store multiple modalities and multiple embeddings in the same Document
- **Language agnostic:** At its core, Documents are just dictionaries. This makes it easy to create and send them from any language, not just Python.

## Coming from Pydantic

If you come from Pydantic, you can see Documents as juiced up models, and DocArray as a collection of goodies around them.

- **ML focussed types**: Tensor, TorchTensor, TFTensor, Embedding, ...
- **Types that are alive**: ImageUrl can `.load()` a URL to image tensor, TextUrl can load and tokenize text documents, etc.
- **Pre-built Documents** for different data modalities: Image, Text, 3DMesh, Video, Audio, ... Note that all of these will be valid Pydantic models!
- The concepts of **DocumentArray and DocumentStore**
- Cloud ready: Serialization to **Protobuf** for use with microservices and **gRPC**
- Support for **vector search functionalities**, such as `find()` and `embed()`

## Coming from FastAPI

Documents are Pydantic Models (with a twist), and as such they are fully compatible with FastAPI:

```python
import numpy as np
from fastapi import FastAPI
from httpx import AsyncClient

from docarray import Document, Image
from docarray.typing import NdArray


class InputDoc(Document):
    img: Image


class OutputDoc(Document):
    embedding_clip: NdArray
    embedding_bert: NdArray


input_doc = InputDoc(img=Image(tensor=np.zeros((3, 224, 224))))

app = FastAPI()


@app.post("/doc/", response_model=OutputDoc)
async def create_item(doc: InputDoc) -> OutputDoc:
    ## call my fancy model to generate the embeddings
    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:
    response = await ac.post("/doc/", data=input_doc.json())
    resp_doc = await ac.get("/docs")
    resp_redoc = await ac.get("/redoc")
```

The big advantage here is **first-class support for ML centric data**, such as {Torch, TF, ...}Tensor, Embedding, etc.

This includes handy features such as validating the shape of a tensor:
```python
from docarray import Document
from docarray.typing import TorchTensor
import torch


class MyDoc(Document):
    tensor: TorchTensor[3, 224, 224]


doc = MyDoc(tensor=torch.zeros(3, 224, 224))  # works
doc = MyDoc(tensor=torch.zeros(224, 224, 3))  # works by reshaping
doc = MyDoc(tensor=torch.zeros(224))  # fails validation
```

## Coming from a vector database

If you came across docarray as a universal vector DB client, you can best think of it as **a new kind of ORM for vector databases**.

DocArray's job is to take multi-modal, nested and domain-specific data and to map it to a vector database,
store it there, and thus make it searchable:

```python
# NOTE: DocumentStores are not yet implemented in version 2
from docarray import DocumentArray, Image, Text, DocumentStore, Document
import numpy as np


class MyDoc(Document):
    image: Image
    text: Text
    description: str


def _random_my_doc():
    return MyDoc(
        image=Image(embedding=np.random.random((256,))),
        text=Text(embedding=np.random.random((128,))),
        description='this is a random document',
    )


da = DocumentArray([_random_my_doc() for _ in range(1000)])  # create some data
store = DocumentStore[MyDoc](
    storage='qdrant'
)  # create a DocumentStore with Qdrant as backend
store.insert(da)  # insert the DocumentArray into the DocumentStore

# find the 10 most similar images based on the image embedding field
match = store.find(
    Image(embedding=np.zeros((256,))), field='image__embedding', top_k=10
)
# find the 10 most similar images based on the image embedding field
match = store.find(Image(embedding=np.zeros((128,))), field='text__embedding', top_k=10)
```

## Install the alpha

to try out the alpha you can install it via git:
```shell
pip install "git+https://github.com/docarray/docarra@alphav2-0.1#egg=docarray[common,torch,image]"
```
or from the latest development branch
```shell
pip install "git+https://github.com/docarray/docarray@feat-rewrite-v2#egg=docarray[common,torch,image]"
```

## Further reading

- [V2 announcement blog post](https://github.com/docarray/notes/blob/main/blog/01-announcement.md)
- [Donation to Linux Foundation AI&Data blog post](https://jina.ai/news/donate-docarray-lf-for-inclusive-standard-multimodal-data-model/)
- [Submit ideas, feature requests, and discussions](https://github.com/docarray/docarray/discussions)
- ["Legacy" DocArray github page](https://github.com/docarray/docarray)
- ["Legacy" DocArray documentation](https://docarray.jina.ai/)


docarray/util/find.py METASEP
from typing import List, NamedTuple, Optional, Type, Union

from docarray import Document, DocumentArray
from docarray.typing import Tensor
from docarray.typing.tensor.abstract_tensor import AbstractTensor


class FindResult(NamedTuple):
    documents: DocumentArray
    scores: Tensor


def find(
    index: DocumentArray,
    query: Union[Tensor, Document],
    embedding_field: str = 'embedding',
    metric: str = 'cosine_sim',
    limit: int = 10,
    device: Optional[str] = None,
    descending: Optional[bool] = None,
) -> FindResult:
    """
    Find the closest Documents in the index to the query.
    Supports PyTorch and NumPy embeddings.

    .. note::
        This utility function is likely to be removed once
        Document Stores are available.
        At that point, and in-memory Document Store will serve the same purpose
        by exposing a .find() method.

    .. note::
        This is a simple implementation that assumes the same embedding field name for
        both query and index, does not support nested search, and does not support
        hybrid (multi-vector) search. These shortcoming will be addressed in future
        versions.

    EXAMPLE USAGE

    .. code-block:: python

        from docarray import DocumentArray, Document
        from docarray.typing import TorchTensor
        from docarray.util.find import find


        class MyDocument(Document):
            embedding: TorchTensor


        index = DocumentArray[MyDocument](
            [MyDocument(embedding=torch.rand(128)) for _ in range(100)]
        )

        # use Document as query
        query = MyDocument(embedding=torch.rand(128))
        top_matches, scores = find(
            index=index,
            query=query,
            embedding_field='tensor',
            metric='cosine_sim',
        )

        # use tensor as query
        query = torch.rand(128)
        top_matches, scores = find(
            index=index,
            query=query,
            embedding_field='tensor',
            metric='cosine_sim',
        )

    :param index: the index of Documents to search in
    :param query: the query to search for
    :param embedding_field: the tensor-like field in the index to use
        for the similarity computation
    :param metric: the distance metric to use for the similarity computation.
        Can be one of the following strings:
        'cosine_sim' for cosine similarity, 'euclidean_dist' for euclidean distance,
        'sqeuclidean_dist' for squared euclidean distance
    :param limit: return the top `limit` results
    :param device: the computational device to use,
        can be either `cpu` or a `cuda` device.
    :param descending: sort the results in descending order.
        Per default, this is chosen based on the `metric` argument.
    :return: A named tuple of the form (DocumentArray, Tensor),
        where the first element contains the closes matches for the query,
        and the second element contains the corresponding scores.
    """
    query = _extract_embedding_single(query, embedding_field)
    return find_batched(
        index=index,
        query=query,
        embedding_field=embedding_field,
        metric=metric,
        limit=limit,
        device=device,
        descending=descending,
    )[0]


def find_batched(
    index: DocumentArray,
    query: Union[Tensor, DocumentArray],
    embedding_field: str = 'embedding',
    metric: str = 'cosine_sim',
    limit: int = 10,
    device: Optional[str] = None,
    descending: Optional[bool] = None,
) -> List[FindResult]:
    """
    Find the closest Documents in the index to the queries.
    Supports PyTorch and NumPy embeddings.

    .. note::
        This utility function is likely to be removed once
        Document Stores are available.
        At that point, and in-memory Document Store will serve the same purpose
        by exposing a .find() method.

    .. note::
        This is a simple implementation that assumes the same embedding field name for
        both query and index, does not support nested search, and does not support
        hybrid (multi-vector) search. These shortcoming will be addressed in future
        versions.

        EXAMPLE USAGE

    .. code-block:: python

        from docarray import DocumentArray, Document
        from docarray.typing import TorchTensor
        from docarray.util.find import find


        class MyDocument(Document):
            embedding: TorchTensor


        index = DocumentArray[MyDocument](
            [MyDocument(embedding=torch.rand(128)) for _ in range(100)]
        )

        # use DocumentArray as query
        query = DocumentArray[MyDocument](
            [MyDocument(embedding=torch.rand(128)) for _ in range(3)]
        )
        results = find(
            index=index,
            query=query,
            embedding_field='tensor',
            metric='cosine_sim',
        )
        top_matches, scores = results[0]

        # use tensor as query
        query = torch.rand(3, 128)
        results, scores = find(
            index=index,
            query=query,
            embedding_field='tensor',
            metric='cosine_sim',
        )
        top_matches, scores = results[0]

    :param index: the index of Documents to search in
    :param query: the query to search for
    :param embedding_field: the tensor-like field in the index to use
        for the similarity computation
    :param metric: the distance metric to use for the similarity computation.
        Can be one of the following strings:
        'cosine_sim' for cosine similarity, 'euclidean_dist' for euclidean distance,
        'sqeuclidean_dist' for squared euclidean distance
    :param limit: return the top `limit` results
    :param device: the computational device to use,
        can be either `cpu` or a `cuda` device.
    :param descending: sort the results in descending order.
        Per default, this is chosen based on the `metric` argument.
    :return: a list of named tuples of the form (DocumentArray, Tensor),
        where the first element contains the closes matches for each query,
        and the second element contains the corresponding scores.
    """
    if descending is None:
        descending = metric.endswith('_sim')  # similarity metrics are descending

    embedding_type = _da_attr_type(index, embedding_field)
    comp_backend = embedding_type.get_comp_backend()

    # extract embeddings from query and index
    index_embeddings = _extraxt_embeddings(index, embedding_field, embedding_type)
    query_embeddings = _extraxt_embeddings(query, embedding_field, embedding_type)

    # compute distances and return top results
    metric_fn = getattr(comp_backend.Metrics, metric)
    dists = metric_fn(query_embeddings, index_embeddings, device=device)
    top_scores, top_indices = comp_backend.Retrieval.top_k(
        dists, k=limit, device=device, descending=descending
    )

    results = []
    for indices_per_query, scores_per_query in zip(top_indices, top_scores):
        docs_per_query = []
        for idx in indices_per_query:  # workaround until #930 is fixed
            docs_per_query.append(index[idx])
        docs_per_query = DocumentArray(docs_per_query)
        results.append(FindResult(scores=scores_per_query, documents=docs_per_query))
    return results


def _extract_embedding_single(
    data: Union[DocumentArray, Document, Tensor],
    embedding_field: str,
) -> Tensor:
    """Extract the embeddings from a single query,
    and return it in a batched representation.

    :param data: the data
    :param embedding_field: the embedding field
    :param embedding_type: type of the embedding: torch.Tensor, numpy.ndarray etc.
    :return: the embeddings
    """
    if isinstance(data, Document):
        emb = getattr(data, embedding_field)
    else:  # treat data as tensor
        emb = data
    if len(emb.shape) == 1:
        # all currently supported frameworks provide `.reshape()`. Onc this is not true
        # anymore, we need to add a `.reshape()` method to the computational backend
        emb = emb.reshape(1, -1)
    return emb


def _extraxt_embeddings(
    data: Union[DocumentArray, Document, Tensor],
    embedding_field: str,
    embedding_type: Type,
) -> Tensor:
    """Extract the embeddings from the data.

    :param data: the data
    :param embedding_field: the embedding field
    :param embedding_type: type of the embedding: torch.Tensor, numpy.ndarray etc.
    :return: the embeddings
    """
    # TODO(johannes) put docarray stack in the computational backend
    if isinstance(data, DocumentArray):
        emb = getattr(data, embedding_field)
        if not data.is_stacked():
            emb = embedding_type.__docarray_stack__(emb)
    elif isinstance(data, Document):
        emb = getattr(data, embedding_field)
    else:  # treat data as tensor
        emb = data

    if len(emb.shape) == 1:
        # all currently supported frameworks provide `.reshape()`. Onc this is not true
        # anymore, we need to add a `.reshape()` method to the computational backend
        emb = emb.reshape(1, -1)
    return emb


def _da_attr_type(da: DocumentArray, attr: str) -> Type[Tensor]:
    """Get the type of the attribute according to the Document type
    (schema) of the DocumentArray.

    :param da: the DocumentArray
    :param attr: the attribute name
    :return: the type of the attribute
    """
    field_type = da.document_type.__fields__[attr].type_
    if not issubclass(field_type, AbstractTensor):
        raise ValueError(
            f'attribute {attr} is not a tensor-like type, '
            f'but {field_type.__class__.__name__}'
        )
    return field_type

docarray/util/__init__.py METASEP
from docarray.util.find import find, find_batched

__all__ = ['find', 'find_batched']

docarray/typing/id.py METASEP
from typing import TYPE_CHECKING, Type, TypeVar, Union
from uuid import UUID

from pydantic import BaseConfig, parse_obj_as
from pydantic.fields import ModelField

if TYPE_CHECKING:
    from docarray.proto import NodeProto

from docarray.typing.abstract_type import AbstractType

T = TypeVar('T', bound='ID')


class ID(str, AbstractType):
    """
    Represent an unique ID
    """

    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(
        cls: Type[T],
        value: Union[str, int, UUID],
        field: 'ModelField',
        config: 'BaseConfig',
    ) -> T:

        try:
            id: str = str(value)
            return cls(id)
        except Exception:
            raise ValueError(f'Expected a str, int or UUID, got {type(value)}')

    def _to_node_protobuf(self) -> 'NodeProto':
        """Convert an ID into a NodeProto message. This function should
        be called when the self is nested into another Document that need to be
        converted into a protobuf

        :return: the nested item protobuf message
        """
        from docarray.proto import NodeProto

        return NodeProto(id=self)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'str') -> T:
        """
        read ndarray from a proto msg
        :param pb_msg:
        :return: a string
        """
        return parse_obj_as(cls, pb_msg)

docarray/typing/abstract_type.py METASEP
from abc import abstractmethod
from typing import TYPE_CHECKING, Any, Type, TypeVar

from pydantic import BaseConfig
from pydantic.fields import ModelField

from docarray.document.base_node import BaseNode

if TYPE_CHECKING:
    from docarray.proto import NodeProto

T = TypeVar('T')


class AbstractType(BaseNode):
    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    @abstractmethod
    def validate(
        cls: Type[T],
        value: Any,
        field: 'ModelField',
        config: 'BaseConfig',
    ) -> T:
        ...

    @classmethod
    @abstractmethod
    def from_protobuf(cls: Type[T], pb_msg: T) -> T:
        ...

    @abstractmethod
    def _to_node_protobuf(self: T) -> 'NodeProto':
        ...

docarray/typing/__init__.py METASEP
from docarray.typing.id import ID
from docarray.typing.tensor import NdArray, Tensor
from docarray.typing.tensor.embedding import Embedding
from docarray.typing.url import AnyUrl, ImageUrl, Mesh3DUrl, PointCloud3DUrl, TextUrl

__all__ = [
    'NdArray',
    'Embedding',
    'ImageUrl',
    'TextUrl',
    'Mesh3DUrl',
    'PointCloud3DUrl',
    'AnyUrl',
    'ID',
    'Tensor',
]

try:
    import torch  # noqa: F401
except ImportError:
    pass
else:
    from docarray.typing.tensor import TorchEmbedding, TorchTensor  # noqa: F401

    __all__.extend(['TorchEmbedding', 'TorchTensor'])

docarray/proto/__init__.py METASEP
from docarray.proto.pb2.docarray_pb2 import (
    DocumentArrayListProto,
    DocumentArrayProto,
    DocumentArrayStackedProto,
    DocumentProto,
    NdArrayProto,
    NodeProto,
    UnionArrayProto,
)

__all__ = [
    'DocumentArrayProto',
    'DocumentProto',
    'NdArrayProto',
    'NodeProto',
    'DocumentArrayStackedProto',
    'DocumentArrayListProto',
    'UnionArrayProto',
]

docarray/predefined_document/text.py METASEP
from typing import Optional

from docarray.document import BaseDocument
from docarray.typing import TextUrl
from docarray.typing.tensor.embedding import Embedding


class Text(BaseDocument):
    """
    Document for handling text.
    It can contain a TextUrl (`Text.url`), a str (`Text.text`),
    and an Embedding (`Text.embedding`).

    EXAMPLE USAGE:

    You can use this Document directly:

    .. code-block:: python

        from docarray import Text

        # use it directly
        txt_doc = Text(url='http://www.jina.ai/')
        txt_doc.text = txt_doc.url.load()
        model = MyEmbeddingModel()
        txt_doc.embedding = model(txt_doc.text)

    You can extend this Document:

    .. code-block:: python

        from docarray import Text
        from docarray.typing import Embedding
        from typing import Optional

        # extend it
        class MyText(Text):
            second_embedding: Optional[Embedding]


        txt_doc = MyText(url='http://www.jina.ai/')
        txt_doc.text = txt_doc.url.load()
        model = MyEmbeddingModel()
        txt_doc.embedding = model(txt_doc.text)
        txt_doc.second_embedding = model(txt_doc.text)


    You can use this Document for composition:

    .. code-block:: python

        from docarray import Document, Image, Text

        # compose it
        class MultiModalDoc(Document):
            image_doc: Image
            text_doc: Text


        mmdoc = MultiModalDoc(
            image_doc=Image(url="http://www.jina.ai/image.jpg"),
            text_doc=Text(text="hello world, how are you doing?"),
        )
        mmdoc.text_doc.text = mmdoc.text_doc.url.load()
    """

    text: Optional[str] = None
    url: Optional[TextUrl] = None
    embedding: Optional[Embedding] = None

docarray/predefined_document/point_cloud.py METASEP
from typing import Optional

from docarray.document import BaseDocument
from docarray.typing import Embedding, PointCloud3DUrl, Tensor


class PointCloud3D(BaseDocument):
    """
    Document for handling point clouds for 3D data representation.

    Point cloud is a representation of a 3D mesh. It is made by repeatedly and uniformly
    sampling points within the surface of the 3D body. Compared to the mesh
    representation, the point cloud is a fixed size ndarray (shape=(n_samples, 3)) and
    hence easier for deep learning algorithms to handle.

    A PointCloud3D Document can contain an PointCloud3DUrl (`PointCloud3D.url`), a
    Tensor (`PointCloud3D.tensor`), and an Embedding (`PointCloud3D.embedding`).

    EXAMPLE USAGE:

    You can use this Document directly:

    .. code-block:: python

        from docarray import PointCloud3D

        # use it directly
        pc = PointCloud3D(url='https://people.sc.fsu.edu/~jburkardt/data/obj/al.obj')
        pc.tensor = pc.url.load(samples=100)
        model = MyEmbeddingModel()
        pc.embedding = model(pc.tensor)

    You can extend this Document:

    .. code-block:: python

        from docarray import PointCloud3D
        from docarray.typing import Embedding
        from typing import Optional

        # extend it
        class MyPointCloud3D(PointCloud3D):
            second_embedding: Optional[Embedding]


        pc = MyPointCloud3D(url='https://people.sc.fsu.edu/~jburkardt/data/obj/al.obj')
        pc.tensor = pc.url.load(samples=100)
        model = MyEmbeddingModel()
        pc.embedding = model(pc.tensor)
        pc.second_embedding = model(pc.tensor)


    You can use this Document for composition:

    .. code-block:: python

        from docarray import Document, PointCloud3D, Text

        # compose it
        class MultiModalDoc(Document):
            point_cloud: PointCloud3D
            text: Text


        mmdoc = MultiModalDoc(
            point_cloud=PointCloud3D(
                url='https://people.sc.fsu.edu/~jburkardt/data/obj/al.obj'
            ),
            text=Text(text='hello world, how are you doing?'),
        )
        mmdoc.point_cloud.tensor = mmdoc.point_cloud.url.load(samples=100)
    """

    url: Optional[PointCloud3DUrl]
    tensor: Optional[Tensor]
    embedding: Optional[Embedding]

docarray/predefined_document/mesh.py METASEP
from typing import Optional

from docarray.document import BaseDocument
from docarray.typing import Embedding, Mesh3DUrl, Tensor


class Mesh3D(BaseDocument):
    """
    Document for handling meshes for 3D data representation.

    A mesh is a representation for 3D data and contains vertices and faces information.
    Vertices are points in a 3D space, represented as a tensor of shape (n_points, 3).
    Faces are triangular surfaces that can be defined by three points in 3D space,
    corresponding to the three vertices of a triangle. Faces can be represented as a
    tensor of shape (n_faces, 3). Each number in that tensor refers to an index of a
    vertex in the tensor of vertices.

    The Mesh3D Document can contain an Mesh3DUrl (`Mesh3D.url`), a Tensor of vertices
    (`Mesh3D.vertices`), a Tensor of faces (`Mesh3D.faces`) and an Embedding
    (`Mesh3D.embedding`).

    EXAMPLE USAGE:

    You can use this Document directly:

    .. code-block:: python

        from docarray import Mesh3D

        # use it directly
        mesh = Mesh3D(url='https://people.sc.fsu.edu/~jburkardt/data/obj/al.obj')
        mesh.vertices, mesh.faces = mesh.url.load()
        model = MyEmbeddingModel()
        mesh.embedding = model(mesh.vertices)

    You can extend this Document:

    .. code-block:: python

        from docarray import Mesh3D
        from docarray.typing import Embedding
        from typing import Optional

        # extend it
        class MyMesh3D(Mesh3D):
            name: Optional[Text]


        mesh = MyMesh3D(url='https://people.sc.fsu.edu/~jburkardt/data/obj/al.obj')
        mesh.vertices, mesh.faces = mesh.url.load()
        model = MyEmbeddingModel()
        mesh.embedding = model(mesh.vertices)
        mesh.name = 'my first mesh'


    You can use this Document for composition:

    .. code-block:: python

        from docarray import Document, Mesh3D, Text

        # compose it
        class MultiModalDoc(Document):
            mesh: Mesh3D
            text: Text


        mmdoc = MultiModalDoc(
            mesh=Mesh3D(url='https://people.sc.fsu.edu/~jburkardt/data/obj/al.obj'),
            text=Text(text='hello world, how are you doing?'),
        )
        mmdoc.mesh.vertices, mmdoc.mesh.faces = mmdoc.mesh.url.load()
    """

    url: Optional[Mesh3DUrl]
    vertices: Optional[Tensor]
    faces: Optional[Tensor]
    embedding: Optional[Embedding]

docarray/predefined_document/image.py METASEP
from typing import Optional

from docarray.document import BaseDocument
from docarray.typing import Embedding, ImageUrl, Tensor


class Image(BaseDocument):
    """
    Document for handling images.
    It can contain an ImageUrl (`Image.url`), a Tensor (`Image.tensor`),
    and an Embedding (`Image.embedding`).

    EXAMPLE USAGE:

    You can use this Document directly:

    .. code-block:: python

        from docarray import Image

        # use it directly
        image = Image(url='http://www.jina.ai/image.jpg')
        image.tensor = image.url.load()
        model = MyEmbeddingModel()
        image.embedding = model(image.tensor)

    You can extend this Document:

    .. code-block:: python

        from docarray import Image
        from docarray.typing import Embedding
        from typing import Optional

        # extend it
        class MyImage(Image):
            second_embedding: Optional[Embedding]


        image = MyImage(url='http://www.jina.ai/image.jpg')
        image.tensor = image.url.load()
        model = MyEmbeddingModel()
        image.embedding = model(image.tensor)
        image.second_embedding = model(image.tensor)


    You can use this Document for composition:

    .. code-block:: python

        from docarray import Document, Image, Text

        # compose it
        class MultiModalDoc(Document):
            image: Image
            text: Text


        mmdoc = MultiModalDoc(
            image=Image(url="http://www.jina.ai/image.jpg"),
            text=Text(text="hello world, how are you doing?"),
        )
        mmdoc.image.tensor = mmdoc.image.url.load()
    """

    url: Optional[ImageUrl]
    tensor: Optional[Tensor]
    embedding: Optional[Embedding]

docarray/predefined_document/__init__.py METASEP
from docarray.predefined_document.image import Image
from docarray.predefined_document.mesh import Mesh3D
from docarray.predefined_document.point_cloud import PointCloud3D
from docarray.predefined_document.text import Text

__all__ = ['Text', 'Image', 'Mesh3D', 'PointCloud3D']

docarray/document/document.py METASEP
import os
from typing import Type

import orjson
from pydantic import BaseModel, Field
from pydantic import parse_obj_as

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.mixins import ProtoMixin
from docarray.typing import ID


class BaseDocument(BaseModel, ProtoMixin, AbstractDocument, BaseNode):
    """
    The base class for Document
    """

    id: ID = Field(default_factory=lambda: parse_obj_as(ID, os.urandom(16).hex()))

    class Config:
        json_loads = orjson.loads
        json_dumps = orjson_dumps

    @classmethod
    def _get_nested_document_class(cls, field: str) -> Type['BaseDocument']:
        """
        Accessing the nested python Class define in the schema. Could be useful for
        reconstruction of Document in serialization/deserilization
        :param field: name of the field
        :return:
        """
        return cls.__fields__[field].type_

docarray/document/base_node.py METASEP
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from docarray.proto import NodeProto


class BaseNode(ABC):
    """
    A DocumentNode is an object than can be nested inside a Document.
    A Document itself is a DocumentNode as well as prebuilt type
    """

    @abstractmethod
    def _to_node_protobuf(self) -> 'NodeProto':
        """Convert itself into a NodeProto message. This function should
        be called when the self is nested into another Document that need to be
        converted into a protobuf

        :return: the nested item protobuf message
        """
        ...

docarray/document/any_document.py METASEP
from typing import Type

from .document import BaseDocument


class AnyDocument(BaseDocument):
    """
    AnyDocument is a Document that is not tied to any schema
    """

    def __init__(self, **kwargs):
        super().__init__()
        self.__dict__.update(kwargs)

    @classmethod
    def _get_nested_document_class(cls, field: str) -> Type['BaseDocument']:
        """
        Accessing the nested python Class define in the schema.
        Could be useful for reconstruction of Document in
        serialization/deserilization
        :param field: name of the field
        :return:
        """
        return AnyDocument

docarray/document/abstract_document.py METASEP
from abc import abstractmethod
from typing import TYPE_CHECKING, Any, Dict, Iterable, Optional, Type, TypeVar

from pydantic.fields import ModelField

if TYPE_CHECKING:
    from pydantic.typing import SetStr

    from docarray.document.mixins.proto import ProtoMixin


T = TypeVar('T', bound='AbstractDocument')


class AbstractDocument(Iterable):
    __fields__: Dict[str, ModelField]

    @classmethod
    @abstractmethod
    def _get_nested_document_class(cls, field: str) -> Type['ProtoMixin']:
        ...

    @classmethod
    @abstractmethod
    def construct(
        cls: Type[T], _fields_set: Optional['SetStr'] = None, **values: Any
    ) -> T:
        """
        construct document without calling validation
        """
        ...

docarray/document/__init__.py METASEP
from docarray.document.any_document import AnyDocument
from docarray.document.base_node import BaseNode
from docarray.document.document import BaseDocument

__all__ = ['AnyDocument', 'BaseDocument', 'BaseNode']

docarray/computation/torch_backend.py METASEP
from typing import List, Optional, Tuple, Union

import torch

from docarray.computation.abstract_comp_backend import AbstractComputationalBackend


def _unsqueeze_if_single_axis(*matrices: torch.Tensor) -> List[torch.Tensor]:
    """Unsqueezes tensors that only have one axis, at dim 0.
    This ensures that all outputs can be treated as matrices, not vectors.

    :param matrices: Matrices to be unsqueezed
    :return: List of the input matrices,
        where single axis matrices are unsqueezed at dim 0.
    """
    unsqueezed = []
    for m in matrices:
        if len(m.shape) == 1:
            unsqueezed.append(m.unsqueeze(0))
        else:
            unsqueezed.append(m)
    return unsqueezed


def _usqueeze_if_scalar(t: torch.Tensor):
    if len(t.shape) == 0:  # avoid scalar output
        t = t.unsqueeze(0)
    return t


class TorchCompBackend(AbstractComputationalBackend[torch.Tensor]):
    """
    Computational backend for PyTorch.
    """

    @staticmethod
    def stack(
        tensors: Union[List['torch.Tensor'], Tuple['torch.Tensor']], dim: int = 0
    ) -> 'torch.Tensor':
        return torch.stack(tensors, dim=dim)

    class Retrieval(AbstractComputationalBackend.Retrieval[torch.Tensor]):
        """
        Abstract class for retrieval and ranking functionalities
        """

        @staticmethod
        def top_k(
            values: 'torch.Tensor',
            k: int,
            descending: bool = False,
            device: Optional[str] = None,
        ) -> Tuple['torch.Tensor', 'torch.Tensor']:
            """
            Retrieves the top k smallest values in `values`,
            and returns them alongside their indices in the input `values`.
            Can also be used to retrieve the top k largest values,
            by setting the `descending` flag.

            :param values: Torch tensor of values to rank.
                Should be of shape (n_queries, n_values_per_query).
                Inputs of shape (n_values_per_query,) will be expanded
                to (1, n_values_per_query).
            :param k: number of values to retrieve
            :param descending: retrieve largest values instead of smallest values
            :param device: the computational device to use,
                can be either `cpu` or a `cuda` device.
            :return: Tuple containing the retrieved values, and their indices.
                Both ar of shape (n_queries, k)
            """
            if device is not None:
                values = values.to(device)
            if len(values.shape) <= 1:
                values = values.view(1, -1)
            len_values = values.shape[-1] if len(values.shape) > 1 else len(values)
            k = min(k, len_values)
            return torch.topk(
                input=values, k=k, largest=descending, sorted=True, dim=-1
            )

    class Metrics(AbstractComputationalBackend.Metrics[torch.Tensor]):
        """
        Abstract base class for metrics (distances and similarities).
        """

        @staticmethod
        def cosine_sim(
            x_mat: torch.Tensor,
            y_mat: torch.Tensor,
            eps: float = 1e-7,
            device: Optional[str] = None,
        ) -> torch.Tensor:
            """Pairwise cosine similarities between all vectors in x_mat and y_mat.

            :param x_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each example.
            :param y_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each example.
            :param eps: a small jitter to avoid divde by zero
            :param device: the device to use for pytorch computations.
                Either 'cpu' or a 'cuda' device.
                If not provided, the devices of x_mat and y_mat are used.
            :return: torch Tensor  of shape (n_vectors, n_vectors) containing all
                pairwise cosine distances.
                The index [i_x, i_y] contains the cosine distance between
                x_mat[i_x] and y_mat[i_y].
            """
            if device is not None:
                x_mat = x_mat.to(device)
                y_mat = y_mat.to(device)

            x_mat, y_mat = _unsqueeze_if_single_axis(x_mat, y_mat)

            a_n, b_n = x_mat.norm(dim=1)[:, None], y_mat.norm(dim=1)[:, None]
            a_norm = x_mat / torch.clamp(a_n, min=eps)
            b_norm = y_mat / torch.clamp(b_n, min=eps)
            sims = torch.mm(a_norm, b_norm.transpose(0, 1)).squeeze()
            return _usqueeze_if_scalar(sims)

        @staticmethod
        def euclidean_dist(
            x_mat: torch.Tensor, y_mat: torch.Tensor, device: Optional[str] = None
        ) -> torch.Tensor:
            """Pairwise Euclidian distances between all vectors in x_mat and y_mat.

            :param x_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each example.
            :param y_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each example.
            :param device: the device to use for pytorch computations.
                Either 'cpu' or a 'cuda' device.
                If not provided, the devices of x_mat and y_mat are used.
            :return: torch Tensor  of shape (n_vectors, n_vectors) containing all
                pairwise euclidian distances.
                The index [i_x, i_y] contains the euclidian distance between
                x_mat[i_x] and y_mat[i_y].
            """
            if device is not None:
                x_mat = x_mat.to(device)
                y_mat = y_mat.to(device)

            x_mat, y_mat = _unsqueeze_if_single_axis(x_mat, y_mat)

            dists = torch.cdist(x_mat, y_mat).squeeze()
            return _usqueeze_if_scalar(dists)

        @staticmethod
        def sqeuclidean_dist(
            x_mat: torch.Tensor, y_mat: torch.Tensor, device: Optional[str] = None
        ) -> torch.Tensor:
            """Pairwise Squared Euclidian distances between all vectors in
            x_mat and y_mat.

            :param x_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each example.
            :param y_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each example.
            :param eps: a small jitter to avoid divde by zero
            :param device: the device to use for pytorch computations.
                Either 'cpu' or a 'cuda' device.
                If not provided, the devices of x_mat and y_mat are used.
            :return: torch Tensor of shape (n_vectors, n_vectors) containing all
                pairwise Squared Euclidian distances.
                The index [i_x, i_y] contains the cosine Squared Euclidian between
                x_mat[i_x] and y_mat[i_y].
            """
            if device is not None:
                x_mat = x_mat.to(device)
                y_mat = y_mat.to(device)

            x_mat, y_mat = _unsqueeze_if_single_axis(x_mat, y_mat)

            return _usqueeze_if_scalar((torch.cdist(x_mat, y_mat) ** 2).squeeze())

docarray/computation/numpy_backend.py METASEP
import warnings
from typing import List, Optional, Tuple, Union

import numpy as np

from docarray.computation import AbstractComputationalBackend


def _expand_if_single_axis(*matrices: np.ndarray) -> List[np.ndarray]:
    """Expands arrays that only have one axis, at dim 0.
    This ensures that all outputs can be treated as matrices, not vectors.

    :param matrices: Matrices to be expanded
    :return: List of the input matrices,
        where single axis matrices are expanded at dim 0.
    """
    expanded = []
    for m in matrices:
        if len(m.shape) == 1:
            expanded.append(np.expand_dims(m, axis=0))
        else:
            expanded.append(m)
    return expanded


def _expand_if_scalar(arr: np.ndarray) -> np.ndarray:
    if len(arr.shape) == 0:  # avoid scalar output
        arr = np.expand_dims(arr, axis=0)
    return arr


class NumpyCompBackend(AbstractComputationalBackend[np.ndarray]):
    """
    Computational backend for Numpy.
    """

    @staticmethod
    def stack(
        tensors: Union[List['np.ndarray'], Tuple['np.ndarray']], dim: int = 0
    ) -> 'np.ndarray':
        return np.stack(tensors, axis=dim)

    class Retrieval(AbstractComputationalBackend.Retrieval[np.ndarray]):
        """
        Abstract class for retrieval and ranking functionalities
        """

        @staticmethod
        def top_k(
            values: 'np.ndarray',
            k: int,
            descending: bool = False,
            device: Optional[str] = None,
        ) -> Tuple['np.ndarray', 'np.ndarray']:
            """
            Retrieves the top k smallest values in `values`,
            and returns them alongside their indices in the input `values`.
            Can also be used to retrieve the top k largest values,
            by setting the `descending` flag.

            :param values: Torch tensor of values to rank.
                Should be of shape (n_queries, n_values_per_query).
                Inputs of shape (n_values_per_query,) will be expanded
                to (1, n_values_per_query).
            :param k: number of values to retrieve
            :param descending: retrieve largest values instead of smallest values
            :param device: Not supported for this backend
            :return: Tuple containing the retrieved values, and their indices.
                Both ar of shape (n_queries, k)
            """
            if device is not None:
                warnings.warn('`device` is not supported for numpy operations')

            if len(values.shape) == 1:
                values = np.expand_dims(values, axis=0)

            if descending:
                values = -values

            if k >= values.shape[1]:
                idx = values.argsort(axis=1)[:, :k]
                values = np.take_along_axis(values, idx, axis=1)
            else:
                idx_ps = values.argpartition(kth=k, axis=1)[:, :k]
                values = np.take_along_axis(values, idx_ps, axis=1)
                idx_fs = values.argsort(axis=1)
                idx = np.take_along_axis(idx_ps, idx_fs, axis=1)
                values = np.take_along_axis(values, idx_fs, axis=1)

            if descending:
                values = -values

            return values, idx

    class Metrics(AbstractComputationalBackend.Metrics[np.ndarray]):
        """
        Abstract base class for metrics (distances and similarities).
        """

        @staticmethod
        def cosine_sim(
            x_mat: np.ndarray,
            y_mat: np.ndarray,
            eps: float = 1e-7,
            device: Optional[str] = None,
        ) -> np.ndarray:
            """Pairwise cosine similarities between all vectors in x_mat and y_mat.

            :param x_mat: np.ndarray of shape (n_vectors, n_dim), where n_vectors is
                the number of vectors and n_dim is the number of dimensions of each
                example.
            :param y_mat: np.ndarray of shape (n_vectors, n_dim), where n_vectors is
                the number of vectors and n_dim is the number of dimensions of each
                example.
            :param eps: a small jitter to avoid divde by zero
            :param device: Not supported for this backend
            :return: np.ndarray  of shape (n_vectors, n_vectors) containing all
                pairwise cosine distances.
                The index [i_x, i_y] contains the cosine distance between
                x_mat[i_x] and y_mat[i_y].
            """
            if device is not None:
                warnings.warn('`device` is not supported for numpy operations')

            x_mat, y_mat = _expand_if_single_axis(x_mat, y_mat)

            sims = np.clip(
                (np.dot(x_mat, y_mat.T) + eps)
                / (
                    np.outer(
                        np.linalg.norm(x_mat, axis=1), np.linalg.norm(y_mat, axis=1)
                    )
                    + eps
                ),
                -1,
                1,
            ).squeeze()
            return _expand_if_scalar(sims)

        @classmethod
        def euclidean_dist(
            cls, x_mat: np.ndarray, y_mat: np.ndarray, device: Optional[str] = None
        ) -> np.ndarray:
            """Pairwise Euclidian distances between all vectors in x_mat and y_mat.

            :param x_mat: np.ndarray of shape (n_vectors, n_dim), where n_vectors is
                the number of vectors and n_dim is the number of dimensions of each
                example.
            :param y_mat: np.ndarray of shape (n_vectors, n_dim), where n_vectors is
                the number of vectors and n_dim is the number of dimensions of each
                example.
            :param eps: a small jitter to avoid divde by zero
            :param device: Not supported for this backend
            :return: np.ndarray  of shape (n_vectors, n_vectors) containing all
                pairwise euclidian distances.
                The index [i_x, i_y] contains the euclidian distance between
                x_mat[i_x] and y_mat[i_y].
            """
            if device is not None:
                warnings.warn('`device` is not supported for numpy operations')

            x_mat, y_mat = _expand_if_single_axis(x_mat, y_mat)

            return _expand_if_scalar(
                np.sqrt(cls.sqeuclidean_dist(x_mat, y_mat)).squeeze()
            )

        @staticmethod
        def sqeuclidean_dist(
            x_mat: np.ndarray,
            y_mat: np.ndarray,
            device: Optional[str] = None,
        ) -> np.ndarray:
            """Pairwise Squared Euclidian distances between all vectors in
            x_mat and y_mat.

            :param x_mat: np.ndarray of shape (n_vectors, n_dim), where n_vectors is
                the number of vectors and n_dim is the number of dimensions of each
                example.
            :param y_mat: np.ndarray of shape (n_vectors, n_dim), where n_vectors is
                the number of vectors and n_dim is the number of dimensions of each
                example.
            :param device: Not supported for this backend
            :return: np.ndarray  of shape (n_vectors, n_vectors) containing all
                pairwise Squared Euclidian distances.
                The index [i_x, i_y] contains the cosine Squared Euclidian between
                x_mat[i_x] and y_mat[i_y].
            """
            eps: float = 1e-7  # avoid problems with numerical inaccuracies

            if device is not None:
                warnings.warn('`device` is not supported for numpy operations')

            x_mat, y_mat = _expand_if_single_axis(x_mat, y_mat)

            dists = (
                np.sum(y_mat**2, axis=1)
                + np.sum(x_mat**2, axis=1)[:, np.newaxis]
                - 2 * np.dot(x_mat, y_mat.T)
            ).squeeze()

            # remove numerical artifacts
            dists = np.where(np.logical_and(dists < 0, dists > -eps), 0, dists)
            return _expand_if_scalar(dists)

docarray/computation/abstract_comp_backend.py METASEP
import typing
from abc import ABC, abstractmethod
from typing import List, Optional, Tuple, TypeVar, Union

# In practice all of the below will be the same type
TTensor = TypeVar('TTensor')
TTensorRetrieval = TypeVar('TTensorRetrieval')
TTensorMetrics = TypeVar('TTensorMetrics')


class AbstractComputationalBackend(ABC, typing.Generic[TTensor]):
    """
    Abstract base class for computational backends.
    Every supported tensor/ML framework (numpy, torch etc.) should define its own
    computational backend exposing common functionality expressed in that framework.
    That way, DocArray can leverage native implementations from all frameworks.
    """

    @staticmethod
    @abstractmethod
    def stack(
        tensors: Union[List['TTensor'], Tuple['TTensor']], dim: int = 0
    ) -> 'TTensor':
        """
        Stack a list of tensors along a new axis.
        """
        ...

    class Retrieval(ABC, typing.Generic[TTensorRetrieval]):
        """
        Abstract class for retrieval and ranking functionalities
        """

        @staticmethod
        @abstractmethod
        def top_k(
            values: 'TTensorRetrieval',
            k: int,
            descending: bool = False,
            device: Optional[str] = None,
        ) -> Tuple['TTensorRetrieval', 'TTensorRetrieval']:
            """
            Retrieves the top k smallest values in `values`,
            and returns them alongside their indices in the input `values`.
            Can also be used to retrieve the top k largest values,
            by setting the `descending` flag to True.

            :param values: Tensor of values to rank.
                Should be of shape (n_queries, n_values_per_query).
                Inputs of shape (n_values_per_query,) will be expanded
                to (1, n_values_per_query).
            :param k: number of values to retrieve
            :param descending: retrieve largest values instead of smallest values
            :param device: the computational device to use.
            :return: Tuple containing the retrieved values, and their indices.
                Both ar of shape (n_queries, k)
            """
            ...

    class Metrics(ABC, typing.Generic[TTensorMetrics]):
        """
        Abstract base class for metrics (distances and similarities).
        """

        @staticmethod
        @abstractmethod
        def cosine_sim(
            x_mat: 'TTensorMetrics',
            y_mat: 'TTensorMetrics',
            eps: float = 1e-7,
            device: Optional[str] = None,
        ) -> 'TTensorMetrics':
            """Pairwise cosine similarities between all vectors in x_mat and y_mat.

            :param x_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each example.
            :param y_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each example.
            :param eps: a small jitter to avoid divde by zero
            :param device: the device to use for computations.
                If not provided, the devices of x_mat and y_mat are used.
            :return: Tensor  of shape (n_vectors, n_vectors) containing all pairwise
                cosine distances.
                The index [i_x, i_y] contains the cosine distance between
                x_mat[i_x] and y_mat[i_y].
            """
            ...

        @staticmethod
        @abstractmethod
        def euclidean_dist(
            x_mat: 'TTensorMetrics',
            y_mat: 'TTensorMetrics',
            device: Optional[str] = None,
        ) -> 'TTensorMetrics':
            """Pairwise Euclidian distances between all vectors in x_mat and y_mat.

            :param x_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each example.
            :param y_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each example.
            :param device: the device to use for pytorch computations.
                If not provided, the devices of x_mat and y_mat are used.
            :return: Tensor of shape (n_vectors, n_vectors) containing all pairwise
                euclidian distances.
                The index [i_x, i_y] contains the euclidian distance between
                x_mat[i_x] and y_mat[i_y].
            """
            ...

        @staticmethod
        @abstractmethod
        def sqeuclidean_dist(
            x_mat: 'TTensorMetrics',
            y_mat: 'TTensorMetrics',
            device: Optional[str] = None,
        ) -> 'TTensorMetrics':
            """Pairwise Squared Euclidian distances between all vectors
                in x_mat and y_mat.

            :param x_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each
                example.
            :param y_mat: tensor of shape (n_vectors, n_dim), where n_vectors is the
                number of vectors and n_dim is the number of dimensions of each
                example.
            :param device: the device to use for pytorch computations.
                If not provided, the devices of x_mat and y_mat are used.
            :return: Tensor of shape (n_vectors, n_vectors) containing all pairwise
                euclidian distances.
                The index [i_x, i_y] contains the euclidian distance between
                x_mat[i_x] and y_mat[i_y].
            """
            ...

docarray/computation/__init__.py METASEP
from docarray.computation.abstract_comp_backend import AbstractComputationalBackend

__all__ = ['AbstractComputationalBackend']

docarray/py.typed METASEP

docarray/__init__.py METASEP
__version__ = '0.1.0'

from docarray.array import DocumentArray
from docarray.document.document import BaseDocument as Document
from docarray.predefined_document import Image, Mesh3D, PointCloud3D, Text

__all__ = ['Document', 'DocumentArray', 'Image', 'Text', 'Mesh3D', 'PointCloud3D']

docarray/array/array.py METASEP
from collections import defaultdict
from contextlib import contextmanager
from functools import wraps
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Optional,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AbstractDocumentArray
from docarray.array.mixins import GetAttributeArrayMixin, ProtoArrayMixin
from docarray.document import AnyDocument, BaseDocument, BaseNode
from docarray.typing import NdArray

if TYPE_CHECKING:
    from docarray.typing import TorchTensor

try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True


def _stacked_mode_blocker(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        if self.is_stacked():
            raise RuntimeError(
                f'Cannot call {func.__name__} when the document array is in stack mode'
            )
        return func(self, *args, **kwargs)

    wrapper.__doc__ = (
        wrapper.__doc__ + ' \n This method is not available in stacked mode call'
    )

    return wrapper


T = TypeVar('T', bound='DocumentArray')


class DocumentArray(
    list,
    ProtoArrayMixin,
    GetAttributeArrayMixin,
    AbstractDocumentArray,
    BaseNode,
):
    """
    A DocumentArray is a container of Documents.

    :param docs: iterable of Document

    A DocumentArray is a list of Documents of any schema. However, many
    DocumentArray features are only available if these Documents are
    homogeneous and follow the same schema. To be precise, in this schema you can use
    the `DocumentArray[MyDocument]` syntax where MyDocument is a Document class
    (i.e. schema). This creates a DocumentArray that can only contain Documents of
    the type 'MyDocument'.

    EXAMPLE USAGE
    .. code-block:: python
        from docarray import Document, DocumentArray
        from docarray.typing import NdArray, ImageUrl


        class Image(Document):
            tensor: Optional[NdArray[100]]
            url: ImageUrl


        da = DocumentArray[Image](
            Image(url='http://url.com/foo.png') for _ in range(10)
        )  # noqa: E510


    If your DocumentArray is homogeneous (i.e. follows the same schema), you can access
    fields at the DocumentArray level (for example `da.tensor`). You can also set
    fields, with `da.tensor = np.random.random([10, 100])`


    A DocumentArray can be in one of two modes: unstacked mode and stacked mode.


    **Unstacked mode (default)**:
    In this case a DocumentArray is a list of Documents and each Document owns its data.
    The getter and setter shown above return a list of the fields of each Document
    (or DocumentArray if the field is a nested Document). This list/DocumentArray
    is created on the fly. The setter sets the field of each Document to the value
    of the list/DocumentArray/Tensor passed as parameters.

    This list-like behavior is not always optimal, especially when you want
    to process data in batches or perform operations involving matrix computation.
    This is where the stack mode of the DocumentArray comes in handy.

    In **stacked mode**, tensor-like fields of every Document are stored on the
    DocumentArray level as one stacked tensor. This enables operations on the entire
    batch without iterating over the DocumentArray.
    In this mode the Documents in the DocumentArray no longer own the data but just
    reference the data in the DocumentArray's tensor.
    Operations like `da.append` are not supported because
    they are too slow. For these operations you should use unstacked
    mode instead.

    To switch from stacked to unstacked mode (or vice-versa), call `da.unstack()` or
    `da.stack`. There are also two context managers for these modes:
    `with da.stack_mode():` and `with da.unstack_mode():`

    see {meth}`.stack` and {meth}`.unstack` for more information.

    You should use unstacked mode if you want to:

    * Insert, append, delete, or shuffle Documents in your DocumentArray.
    * Separate the DocumentArray into smaller batches later on.

    You should use stacked mode if you want to:

    * Process the entire DocumentArray as one batch.
    * Use the DocumentArray in an ML model for training or inference.

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self, docs: Iterable[BaseDocument]):
        super().__init__(doc_ for doc_ in docs)

        self._columns: Optional[
            Dict[str, Union['TorchTensor', AbstractDocumentArray, NdArray, None]]
        ] = None

    def __class_getitem__(cls, item: Type[BaseDocument]):
        if not issubclass(item, BaseDocument):
            raise ValueError(
                f'DocumentArray[item] item should be a Document not a {item} '
            )

        class _DocumentArrayTyped(cls):  # type: ignore
            document_type: Type[BaseDocument] = item

        for field in _DocumentArrayTyped.document_type.__fields__.keys():

            def _property_generator(val: str):
                def _getter(self):
                    return self._get_array_attribute(val)

                def _setter(self, value):
                    self._set_array_attribute(val, value)

                # need docstring for the property
                return property(fget=_getter, fset=_setter)

            setattr(_DocumentArrayTyped, field, _property_generator(field))
            # this generates property on the fly based on the schema of the item

        _DocumentArrayTyped.__name__ = f'DocumentArray[{item.__name__}]'
        _DocumentArrayTyped.__qualname__ = f'DocumentArray[{item.__name__}]'

        return _DocumentArrayTyped

    def __list_getitem__(self, item):
        return super().__getitem__(item)

    def __getitem__(self, item):  # note this should handle slices
        if self.is_stacked():
            doc = super().__getitem__(item)
            # NOTE: this could be speed up by using a cache
            for field in self._columns.keys():
                setattr(doc, field, self._columns[field][item])
            return doc
        else:
            return self.__list_getitem__(item)

    def __get_stacked_document__(self, item):
        """
        In stacked mode this returns the Document where the stacked fields are put to
        None In unstacked mode (default) this is the same as __getitem__

        :param item:
        :return: Document
        """
        doc = super().__getitem__(item)
        if self.is_stacked():
            for field in self._columns.keys():
                setattr(doc, field, None)
        return doc

    def __list_iter__(self):
        return super().__iter__()

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __iter_over_stacked_documents__(self):
        """
        This iterates over the document but with the stacked fields put to None
        :return:
        """
        for i in range(len(self)):
            yield self.__get_stacked_document__(i)

    def stack(self: T) -> T:
        """
        Puts the DocumentArray into stacked mode.
        :return: itself

        When entering stacked mode DocumentArray creates a column for:

         - Each Tensor field of each Document
         - Nested Documents that contain at least one Tensor field.

        This is useful to perform operations on the whole
        array at once. In stacked mode, accessing or setting the DocumentArray's fields
        accesses or sets the columns of the array.

        EXAMPLE USAGE
        .. code-block:: python
            from docarray import Document, DocumentArray
            from docarray.typing import NdArray


            class Image(Document):
                tensor: NdArray[100]


            batch = DocumentArray[Image](
                [Image(tensor=np.zeros((100))) for _ in range(10)]
            )  # noqa: E510

            batch.stack()

            print(batch[0].tensor[0])
            # >>> 0

            print(batch.tensor.shape)
            # >>> (10, 3, 224, 224)

            batch.tensor = np.ones((10, 100))

            print(batch[0].tensor[0])
            # >>> 1

            batch.append(Image(tensor=np.zeros((100))))
            # >>> raise RuntimeError('Cannot call append when the document array is in
            # >>> stack mode'

        see {meth}`.unstack` for more information on how to switch to unstack mode
        """

        if not self.is_stacked():

            self._columns = dict()

            for field_name, field in self.document_type.__fields__.items():

                is_torch_subclass = (
                    issubclass(field.type_, torch.Tensor) if torch_imported else False
                )

                if (
                    is_torch_subclass
                    or issubclass(field.type_, BaseDocument)
                    or issubclass(field.type_, NdArray)
                ):
                    self._columns[field_name] = None

            columns_to_stack: DefaultDict[
                str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
            ] = defaultdict(  # type: ignore
                list  # type: ignore
            )  # type: ignore

            for doc in self.__list_iter__():
                for field_to_stack in self._column_fields():
                    columns_to_stack[field_to_stack].append(
                        getattr(doc, field_to_stack)
                    )
                    setattr(doc, field_to_stack, None)

            for field_to_stack, to_stack in columns_to_stack.items():

                type_ = self.document_type.__fields__[field_to_stack].type_
                if issubclass(type_, BaseDocument):
                    self._columns[field_to_stack] = DocumentArray[type_](  # type: ignore # noqa: E501
                        to_stack
                    ).stack()
                else:
                    self._columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return self

    def unstack(self: T) -> T:
        """
        Puts the DocumentArray into unstacked mode.
        :return: itself


        Calling unstack will unstack all columns of the DocumentArray and restore
        the data of each Document in the DocumentArray.

        EXAMPLE USAGE
        .. code-block:: python
            from docarray import Document, DocumentArray, Text
            from docarray.typing import NdArray


            class Image(Document):
                tensor: NdArray[100]


            batch = DocumentArray[Image](
                [Image(tensor=np.zeros((100))) for _ in range(10)]
            )  # noqa: E510

            batch.stack()
            batch.unstack()
            print(type(batch.tensor))
            # >>> list
            print(len(batch.tensor))
            # >>> 10

            batch.append(Image(tensor=np.zeros((100))))
            # this work just fine

            batch.tensor = np.ones((10, 100))
            # this iterates over the ndarray and assigns each row to a Document

            print(batch[0].tensor[0])
            # >>> 1

        see {meth}`.stack` for more information on switching to stack mode
        """
        if self.is_stacked() and self._columns:

            for i, doc in enumerate(self.__list_iter__()):
                for field in self._column_fields():
                    val = self._columns[field]
                    if val is not None:
                        setattr(doc, field, val[i])
                    else:
                        raise RuntimeError('Internal error, one of the column is None')
                    # NOTE: here we might need to copy the tensor
                    # see here
                    # https://discuss.pytorch.org/t/what-happened-to-a-view-of-a-tensor-when-the-original-tensor-is-deleted/167294 # noqa: E501

            for field in self._column_fields():
                # list needed here otherwise we are modifying the dict while iterating
                del self._columns[field]

            self._columns = None

        return self

    @contextmanager
    def stacked_mode(self):
        """
        Context manager to put the DocumentArray in stacked mode and unstack it when
        exiting the context manager.

        EXAMPLE USAGE
        .. code-block:: python
            with da.stacked_mode():
                ...
        """
        try:
            yield self.stack()
        finally:
            self.unstack()

    @contextmanager
    def unstacked_mode(self):
        """
        Context manager to put the DocumentArray in unstacked mode and stack it when
        exiting the context manager.

        EXAMPLE USAGE
        .. code-block:: python
            with da.unstacked_mode():
                ...
        """
        try:
            yield self.unstack()
        finally:
            self.stack()

    def is_stacked(self) -> bool:
        """
        Return True if the document array is in stack mode
        """
        return self._columns is not None

    def _column_fields(self) -> List[str]:
        """
        return the list of fields that are columns of the DocumentArray
        :return: the list of keys of the columns
        """
        if self.is_stacked() and self._columns is not None:
            # need to repeat myself here bc of mypy
            return list(self._columns.keys())
        else:
            return []

    append = _stacked_mode_blocker(list.append)
    extend = _stacked_mode_blocker(list.extend)
    clear = _stacked_mode_blocker(list.clear)
    insert = _stacked_mode_blocker(list.insert)
    pop = _stacked_mode_blocker(list.pop)
    remove = _stacked_mode_blocker(list.remove)
    reverse = _stacked_mode_blocker(list.reverse)
    sort = _stacked_mode_blocker(list.sort)

docarray/array/abstract_array.py METASEP
from abc import abstractmethod
from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Sequence, Type, Union

from docarray.document import BaseDocument

if TYPE_CHECKING:
    from docarray.typing import NdArray, TorchTensor


class AbstractDocumentArray(Sequence):

    document_type: Type[BaseDocument]
    _columns: Optional[
        Dict[str, Union['TorchTensor', 'AbstractDocumentArray', 'NdArray', None]]
    ]  # here columns are the holder of the data in tensor modes

    @abstractmethod
    def __init__(self, docs: Iterable[BaseDocument]):
        ...

    @abstractmethod
    def __class_getitem__(
        cls, item: Type[BaseDocument]
    ) -> Type['AbstractDocumentArray']:
        ...

    @abstractmethod
    def is_stacked(self) -> bool:
        ...

    @abstractmethod
    def _column_fields(self) -> List[str]:
        ...

    @abstractmethod
    def __iter_over_stacked_documents__(self) -> Iterable[BaseDocument]:
        ...

docarray/array/__init__.py METASEP
from docarray.array.array import DocumentArray

__all__ = ['DocumentArray']

docarray/array/array_stacked.py METASEP

from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
infile
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
infile
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
infile
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
infile
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
infile
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
infile
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
                    document_array=DocumentArrayProto(stack=column.to_protobuf())
                )
            elif isinstance(column, AbstractTensor):
infile
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
infile
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
                    document_array=DocumentArrayProto(stack=column.to_protobuf())
                )
            elif isinstance(column, AbstractTensor):
                columns_proto[field] = UnionArrayProto(ndarray=column.to_protobuf())

        return DocumentArrayStackedProto(list_=da_proto, columns=columns_proto)

    def unstack(self: T) -> DocumentArray:
        """Convert DocumentArrayStacked into a DocumentArray.

        Note this destroys the arguments and returns a new DocumentArray
        """
        for i, doc in enumerate(self._docs):
            for field in self._columns.keys():
                val = self._columns[field]
                setattr(doc, field, val[i])

                # NOTE: here we might need to copy the tensor
                # see here
                # https://discuss.pytorch.org/t/what-happened-to-a-view-of-a-tensor
                # -when-the-original-tensor-is-deleted/167294 # noqa: E501

        for field in list(self._columns.keys()):
            # list needed here otherwise we are modifying the dict while iterating
            del self._columns[field]

        da_list = self._docs
        return da_list

    @contextmanager
    def unstacked_mode(self):
        """
        Context manager to put the DocumentArrayStacked in unstacked mode and stack it
        when exiting the context manager.
        EXAMPLE USAGE
        .. code-block:: python
            with da.unstacked_mode():
                ...
        """
        try:
infile
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
                    document_array=DocumentArrayProto(stack=column.to_protobuf())
                )
            elif isinstance(column, AbstractTensor):
                columns_proto[field] = UnionArrayProto(ndarray=column.to_protobuf())

        return DocumentArrayStackedProto(list_=da_proto, columns=columns_proto)

    def unstack(self: T) -> DocumentArray:
        """Convert DocumentArrayStacked into a DocumentArray.

        Note this destroys the arguments and returns a new DocumentArray
        """
        for i, doc in enumerate(self._docs):
            for field in self._columns.keys():
                val = self._columns[field]
                setattr(doc, field, val[i])

                # NOTE: here we might need to copy the tensor
                # see here
                # https://discuss.pytorch.org/t/what-happened-to-a-view-of-a-tensor
                # -when-the-original-tensor-is-deleted/167294 # noqa: E501

        for field in list(self._columns.keys()):
            # list needed here otherwise we are modifying the dict while iterating
            del self._columns[field]

        da_list = self._docs
        return da_list

    @contextmanager
    def unstacked_mode(self):
        """
        Context manager to put the DocumentArrayStacked in unstacked mode and stack it
        when exiting the context manager.
        EXAMPLE USAGE
        .. code-block:: python
            with da.unstacked_mode():
                ...
        """
        try:
            da_unstacked = DocumentArrayStacked.unstack(self)
            yield da_unstacked
        finally:
            self._docs = da_unstacked
infile
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
                    document_array=DocumentArrayProto(stack=column.to_protobuf())
                )
            elif isinstance(column, AbstractTensor):
                columns_proto[field] = UnionArrayProto(ndarray=column.to_protobuf())

        return DocumentArrayStackedProto(list_=da_proto, columns=columns_proto)

    def unstack(self: T) -> DocumentArray:
        """Convert DocumentArrayStacked into a DocumentArray.

        Note this destroys the arguments and returns a new DocumentArray
        """
        for i, doc in enumerate(self._docs):
            for field in self._columns.keys():
                val = self._columns[field]
                setattr(doc, field, val[i])

                # NOTE: here we might need to copy the tensor
                # see here
                # https://discuss.pytorch.org/t/what-happened-to-a-view-of-a-tensor
                # -when-the-original-tensor-is-deleted/167294 # noqa: E501

        for field in list(self._columns.keys()):
            # list needed here otherwise we are modifying the dict while iterating
            del self._columns[field]

        da_list = self._docs
        return da_list

    @contextmanager
    def unstacked_mode(self):
        """
        Context manager to put the DocumentArrayStacked in unstacked mode and stack it
        when exiting the context manager.
        EXAMPLE USAGE
        .. code-block:: python
            with da.unstacked_mode():
                ...
        """
        try:
            da_unstacked = DocumentArrayStacked.unstack(self)
            yield da_unstacked
        finally:
            self._docs = da_unstacked
            self.from_document_array(da_unstacked)

    @classmethod
    def validate(
        cls: Type[T],
        value: Union[T, Iterable[BaseDocument]],
        field: 'ModelField',
        config: 'BaseConfig',
    ) -> T:
        if isinstance(value, cls):
            return value
        elif isinstance(value, Iterable):
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
                    document_array=DocumentArrayProto(stack=column.to_protobuf())
                )
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
                    document_array=DocumentArrayProto(stack=column.to_protobuf())
                )
            elif isinstance(column, AbstractTensor):
                columns_proto[field] = UnionArrayProto(ndarray=column.to_protobuf())

        return DocumentArrayStackedProto(list_=da_proto, columns=columns_proto)

    def unstack(self: T) -> DocumentArray:
        """Convert DocumentArrayStacked into a DocumentArray.

        Note this destroys the arguments and returns a new DocumentArray
        """
        for i, doc in enumerate(self._docs):
            for field in self._columns.keys():
                val = self._columns[field]
                setattr(doc, field, val[i])

                # NOTE: here we might need to copy the tensor
                # see here
                # https://discuss.pytorch.org/t/what-happened-to-a-view-of-a-tensor
                # -when-the-original-tensor-is-deleted/167294 # noqa: E501

        for field in list(self._columns.keys()):
            # list needed here otherwise we are modifying the dict while iterating
            del self._columns[field]

        da_list = self._docs
        return da_list

    @contextmanager
    def unstacked_mode(self):
        """
        Context manager to put the DocumentArrayStacked in unstacked mode and stack it
        when exiting the context manager.
        EXAMPLE USAGE
        .. code-block:: python
            with da.unstacked_mode():
                ...
        """
        try:
            da_unstacked = DocumentArrayStacked.unstack(self)
            yield da_unstacked
        finally:
            self._docs = da_unstacked
            self.from_document_array(da_unstacked)

    @classmethod
    def validate(
        cls: Type[T],
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
inproject
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

non_informative
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
non_informative
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
                    document_array=DocumentArrayProto(stack=column.to_protobuf())
                )
            elif isinstance(column, AbstractTensor):
                columns_proto[field] = UnionArrayProto(ndarray=column.to_protobuf())

        return DocumentArrayStackedProto(list_=da_proto, columns=columns_proto)

    def unstack(self: T) -> DocumentArray:
        """Convert DocumentArrayStacked into a DocumentArray.

        Note this destroys the arguments and returns a new DocumentArray
        """
        for i, doc in enumerate(self._docs):
            for field in self._columns.keys():
                val = self._columns[field]
                setattr(doc, field, val[i])

                # NOTE: here we might need to copy the tensor
                # see here
                # https://discuss.pytorch.org/t/what-happened-to-a-view-of-a-tensor
                # -when-the-original-tensor-is-deleted/167294 # noqa: E501

        for field in list(self._columns.keys()):
            # list needed here otherwise we are modifying the dict while iterating
            del self._columns[field]

        da_list = self._docs
        return da_list

    @contextmanager
    def unstacked_mode(self):
        """
non_informative
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
                    document_array=DocumentArrayProto(stack=column.to_protobuf())
                )
            elif isinstance(column, AbstractTensor):
                columns_proto[field] = UnionArrayProto(ndarray=column.to_protobuf())

        return DocumentArrayStackedProto(list_=da_proto, columns=columns_proto)

    def unstack(self: T) -> DocumentArray:
        """Convert DocumentArrayStacked into a DocumentArray.

        Note this destroys the arguments and returns a new DocumentArray
        """
        for i, doc in enumerate(self._docs):
            for field in self._columns.keys():
                val = self._columns[field]
                setattr(doc, field, val[i])

                # NOTE: here we might need to copy the tensor
                # see here
                # https://discuss.pytorch.org/t/what-happened-to-a-view-of-a-tensor
                # -when-the-original-tensor-is-deleted/167294 # noqa: E501

        for field in list(self._columns.keys()):
            # list needed here otherwise we are modifying the dict while iterating
            del self._columns[field]

        da_list = self._docs
        return da_list

    @contextmanager
    def unstacked_mode(self):
        """
        Context manager to put the DocumentArrayStacked in unstacked mode and stack it
        when exiting the context manager.
        EXAMPLE USAGE
non_informative
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
                    document_array=DocumentArrayProto(stack=column.to_protobuf())
                )
            elif isinstance(column, AbstractTensor):
                columns_proto[field] = UnionArrayProto(ndarray=column.to_protobuf())

        return DocumentArrayStackedProto(list_=da_proto, columns=columns_proto)

    def unstack(self: T) -> DocumentArray:
        """Convert DocumentArrayStacked into a DocumentArray.

        Note this destroys the arguments and returns a new DocumentArray
        """
        for i, doc in enumerate(self._docs):
            for field in self._columns.keys():
                val = self._columns[field]
                setattr(doc, field, val[i])
non_informative
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
random
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
random
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
                    document_array=DocumentArrayProto(stack=column.to_protobuf())
                )
            elif isinstance(column, AbstractTensor):
                columns_proto[field] = UnionArrayProto(ndarray=column.to_protobuf())

        return DocumentArrayStackedProto(list_=da_proto, columns=columns_proto)

    def unstack(self: T) -> DocumentArray:
        """Convert DocumentArrayStacked into a DocumentArray.

        Note this destroys the arguments and returns a new DocumentArray
        """
        for i, doc in enumerate(self._docs):
            for field in self._columns.keys():
random
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
        )
        da = cls(DocumentArray([]))

        da._docs = docs
        da._columns = pb_msg.columns
        return da

    def to_protobuf(self) -> 'DocumentArrayStackedProto':
        """Convert DocumentArray into a Protobuf message"""
        from docarray.proto import (
            DocumentArrayProto,
            DocumentArrayStackedProto,
            UnionArrayProto,
        )

        da_proto = DocumentArrayProto()
        for doc in self.__iter_without_columns__():
            da_proto.docs.append(doc.to_protobuf())

        columns_proto: Dict[str, UnionArrayProto] = dict()
        for field, column in self._columns.items():
            if isinstance(column, DocumentArrayStacked):
                columns_proto[field] = UnionArrayProto(
                    document_array=DocumentArrayProto(stack=column.to_protobuf())
                )
            elif isinstance(column, AbstractTensor):
                columns_proto[field] = UnionArrayProto(ndarray=column.to_protobuf())

        return DocumentArrayStackedProto(list_=da_proto, columns=columns_proto)

    def unstack(self: T) -> DocumentArray:
        """Convert DocumentArrayStacked into a DocumentArray.

        Note this destroys the arguments and returns a new DocumentArray
        """
        for i, doc in enumerate(self._docs):
            for field in self._columns.keys():
                val = self._columns[field]
                setattr(doc, field, val[i])

                # NOTE: here we might need to copy the tensor
                # see here
                # https://discuss.pytorch.org/t/what-happened-to-a-view-of-a-tensor
                # -when-the-original-tensor-is-deleted/167294 # noqa: E501

        for field in list(self._columns.keys()):
            # list needed here otherwise we are modifying the dict while iterating
            del self._columns[field]

        da_list = self._docs
        return da_list

    @contextmanager
    def unstacked_mode(self):
        """
        Context manager to put the DocumentArrayStacked in unstacked mode and stack it
        when exiting the context manager.
        EXAMPLE USAGE
        .. code-block:: python
            with da.unstacked_mode():
                ...
        """
        try:
            da_unstacked = DocumentArrayStacked.unstack(self)
            yield da_unstacked
        finally:
random
from collections import defaultdict
from contextlib import contextmanager
from typing import (
    TYPE_CHECKING,
    DefaultDict,
    Dict,
    Iterable,
    List,
    Type,
    TypeVar,
    Union,
)

from docarray.array.abstract_array import AnyDocumentArray
from docarray.array.array import DocumentArray
from docarray.document import AnyDocument, BaseDocument
from docarray.typing import NdArray
from docarray.typing.tensor.abstract_tensor import AbstractTensor

if TYPE_CHECKING:
    from pydantic import BaseConfig
    from pydantic.fields import ModelField

    from docarray.proto import DocumentArrayStackedProto
    from docarray.typing import TorchTensor


try:
    import torch
except ImportError:
    torch_imported = False
else:
    from docarray.typing import TorchTensor

    torch_imported = True

T = TypeVar('T', bound='DocumentArrayStacked')


class DocumentArrayStacked(AnyDocumentArray):
    """
    DocumentArrayStacked is a container of Documents appropriates to perform
    computation that require batches of data (ex: matrix multiplication, distance
    calculation, deep learning forward pass)

    A DocumentArrayStacked is similar to {class}`~docarray.array.DocumentArray`
    but the field of the Document that are {class}`~docarray.typing.Tensor` are stacked
    into a batches of Tensor. Like {class}`~docarray.array.DocumentArray`  you
    can be precise a Document schema by using the `DocumentArray[MyDocument]`
    syntax where MyDocument is a Document class  (i.e. schema).
    This creates a DocumentArray that can only contains Documents of
    the type 'MyDocument'.

    :param docs: a DocumentArray

    """

    document_type: Type[BaseDocument] = AnyDocument

    def __init__(self: T, docs: DocumentArray):
        self._docs = docs

        self._columns: Dict[
            str, Union['TorchTensor', T, NdArray]
        ] = self._create_columns(docs)

    def from_document_array(self: T, docs: DocumentArray):
        self._docs = docs
        self._columns = self._create_columns(docs)

    @classmethod
    def _create_columns(
        cls: Type[T], docs: DocumentArray
    ) -> Dict[str, Union['TorchTensor', T, NdArray]]:

        columns_fields = list()
        for field_name, field in cls.document_type.__fields__.items():

            is_torch_subclass = (
                issubclass(field.type_, torch.Tensor) if torch_imported else False
            )

            if (
                is_torch_subclass
                or issubclass(field.type_, BaseDocument)
                or issubclass(field.type_, NdArray)
            ):
                columns_fields.append(field_name)

        columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()

        columns_to_stack: DefaultDict[
            str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]
        ] = defaultdict(  # type: ignore
            list  # type: ignore
        )  # type: ignore

        for doc in docs:
            for field_to_stack in columns_fields:
                columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))
                setattr(doc, field_to_stack, None)

        for field_to_stack, to_stack in columns_to_stack.items():

            type_ = cls.document_type.__fields__[field_to_stack].type_
            if issubclass(type_, BaseDocument):
                columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(
                    to_stack
                )
            else:
                columns[field_to_stack] = type_.__docarray_stack__(to_stack)

        return columns

    def _get_array_attribute(
        self: T,
        field: str,
    ) -> Union[List, T, 'TorchTensor', 'NdArray']:
        """Return all values of the fields from all docs this array contains

        :param field: name of the fields to extract
        :return: Returns a list of the field value for each document
        in the array like container
        """
        if field in self._columns.keys():
            return self._columns[field]
        else:
            return getattr(self._docs, field)

    def _set_array_attribute(
        self: T,
        field: str,
        values: Union[List, T, 'TorchTensor', 'NdArray'],
    ):
        """Set all Documents in this DocumentArray using the passed values

        :param field: name of the fields to extract
        :values: the values to set at the DocumentArray level
        """
        if field in self._columns.keys() and not isinstance(values, List):
            self._columns[field] = values
        else:
            setattr(self._docs, field, values)

    def __getitem__(self, item):  # note this should handle slices
        doc = self._docs[item]
        # NOTE: this could be speed up by using a cache
        for field in self._columns.keys():
            setattr(doc, field, self._columns[field][item])
        return doc

    def __iter__(self):
        for i in range(len(self)):
            yield self[i]

    def __getitem_without_columns__(self, item):  # note this should handle slices
        """Return the document at the given index with the columns item put to None"""
        doc = self._docs[item]
        for field in self._columns.keys():
            setattr(doc, field, None)
        return doc

    def __iter_without_columns__(self):
        for i in range(len(self)):
            yield self.__getitem_without_columns__(i)

    def __len__(self):
        return len(self._docs)

    @classmethod
    def from_protobuf(cls: Type[T], pb_msg: 'DocumentArrayStackedProto') -> T:
        """create a Document from a protobuf message"""

        docs = DocumentArray(
            cls.document_type.from_protobuf(doc_proto)
            for doc_proto in pb_msg.list_.docs
random
        ] = self._create_columns(docs)             da_proto.docs.append(doc.to_protobuf())         for doc in self.__iter_without_columns__():             yield self.__getitem_without_columns__(i)         self._columns = self._create_columns(docs)             if isinstance(column, DocumentArrayStacked):                 columns_proto[field] = UnionArrayProto(ndarray=column.to_protobuf())             cls.document_type.from_protobuf(doc_proto)             da_unstacked = DocumentArrayStacked.unstack(self)             self.from_document_array(da_unstacked)         columns: Dict[str, Union['TorchTensor', T, NdArray]] = dict()             return cls(DocumentArray(value))                     document_array=DocumentArrayProto(stack=column.to_protobuf())         cls: Type[T], docs: DocumentArray     ) -> Dict[str, Union['TorchTensor', T, NdArray]]:             elif isinstance(column, AbstractTensor):             if issubclass(type_, BaseDocument):                 columns[field_to_stack] = DocumentArrayStacked.__class_getitem__(type_)(         docs = DocumentArray(                 columns[field_to_stack] = type_.__docarray_stack__(to_stack)         da = cls(DocumentArray([]))                 or issubclass(field.type_, BaseDocument)                 or issubclass(field.type_, NdArray)         value: Union[T, Iterable[BaseDocument]],     document_type: Type[BaseDocument] = AnyDocument             str, Union[List['TorchTensor'], List[NdArray], List[BaseDocument]]             str, Union['TorchTensor', T, NdArray]     def from_document_array(self: T, docs: DocumentArray):         in the array like container         Context manager to put the DocumentArrayStacked in unstacked mode and stack it         .. code-block:: python                 columns_to_stack[field_to_stack].append(getattr(doc, field_to_stack))         for field, column in self._columns.items():                 val = self._columns[field]             self._docs = da_unstacked         )