Repository: jina-ai/serve
Pull request: fix: fix listview insert, append

Problem statement:
bug: appending to list in document tags
**Describe the bug**
When using a python list as a value for the `Document` tags appending to the list raise `AttributeError: insert`.

Reproduce
```
from jina import Document

doc = Document()
doc.tags['key'] = []
doc.tags['key'].append(1)
```

Traceback:
```
Traceback (most recent call last):
  File "test.py", line 5, in <module>
    doc.tags['key'].append(1)
  File "/usr/local/Cellar/python@3.8/3.8.10/Frameworks/Python.framework/Versions/3.8/lib/python3.8/_collections_abc.py", line 962, in append
    self.insert(len(self), value)
  File "/Users/sebastianlettner/Desktop/tmp/.venv/lib/python3.8/site-packages/jina/types/list.py", line 31, in insert
    self._pb_body.insert(index, object)
AttributeError: insert
```

**Describe how you solve it**
Get the list and convert it, append and re-assign the full list.
````
from jina import Document

doc = Document()
doc.tags['key'] = [] 
l = list(doc.tags.get('key'))
l.append(1)
doc.tags['key'] = l
````

---

<!-- Optional, but really help us locate the problem faster -->

**Environment**
```
- jina 2.0.18
- jina-proto 0.0.85
- jina-vcs-tag (unset)
- libzmq 4.3.4
- pyzmq 1.21.2
- protobuf 3.17.3
- proto-backend cpp
- grpcio 1.39.0
- pyyaml 5.4.1
- python 3.8.10
- platform Darwin
- platform-release 19.6.0
- platform-version Darwin Kernel Version 19.6.0: Thu Oct 29 22:56:45 PDT 2020;
  root:xnu-6153.141.2.2~1/RELEASE_X86_64
- architecture x86_64
- processor i386
- uid 190070690681122
- session-id 2d9e1a1a-000d-11ec-a253-acde48001122
- uptime 2021-08-18T12:15:13.759193
- ci-vendor (unset)
* JINA_ARRAY_QUANT (unset)
* JINA_CONTROL_PORT (unset)
* JINA_DEFAULT_HOST (unset)
* JINA_DISABLE_UVLOOP (unset)
* JINA_FULL_CLI (unset)
* JINA_HUBBLE_REGISTRY (unset)
* JINA_HUB_CACHE_DIR (unset)
* JINA_HUB_ROOT (unset)
* JINA_LOG_CONFIG (unset)
* JINA_LOG_ID (unset)
* JINA_LOG_LEVEL (unset)
* JINA_LOG_NO_COLOR (unset)
* JINA_LOG_WORKSPACE (unset)
* JINA_OPTIMIZER_TRIAL_WORKSPACE(unset)
* JINA_POD_NAME (unset)
* JINA_RANDOM_PORT_MAX (unset)
* JINA_RANDOM_PORT_MIN (unset)
* JINA_VCS_VERSION (unset)
* JINA_MP_START_METHOD (unset)
```

Hints from issue discussion:
Will try to take a look. ListView and StructView are tricky
Solved by #3212

Commit message: fix: update tests/unit/types/test_list_view.py

Co-authored-by: Yongxuan Zhang <44033547+Yongxuanzhang@users.noreply.github.com>

Patch to review:
diff --git a/jina/types/list.py b/jina/types/list.py
index 2c0cf42372887..42d19e3578009 100644
--- a/jina/types/list.py
+++ b/jina/types/list.py
@@ -28,7 +28,10 @@ def insert(self, index: int, object: Any) -> None:
         :param index: Position of the insertion.
         :param object: The object that needs to be inserted.
         """
-        self._pb_body.insert(index, object)
+        if index >= len(self):
+            self._pb_body.append(object)
+        else:
+            self[index] = object
 
     def __getitem__(self, i: Union[int, slice]):
         if i >= len(self):
diff --git a/tests/unit/types/document/test_document.py b/tests/unit/types/document/test_document.py
index 8754a3af03b32..dc7a786752513 100644
--- a/tests/unit/types/document/test_document.py
+++ b/tests/unit/types/document/test_document.py
@@ -1102,3 +1102,12 @@ def test_empty_sparse_array():
     doc.embedding = matrix
     assert isinstance(doc.embedding, coo_matrix)
     assert (doc.embedding != matrix).nnz == 0
+
+
+def test_tags_list_append():
+    doc = Document()
+    doc.tags['key'] = []
+    assert len(doc.tags['key']) == 0
+    doc.tags['key'].append(1)
+    assert len(doc.tags['key']) == 1
+    assert doc.tags['key'][0] == 1
diff --git a/tests/unit/types/test_list_view.py b/tests/unit/types/test_list_view.py
index 690661f1de48b..52a4321d36f72 100644
--- a/tests/unit/types/test_list_view.py
+++ b/tests/unit/types/test_list_view.py
@@ -153,3 +153,27 @@ def test_list_view_access_out_of_bounds(list_proto):
     view = ListView(list_proto)
     with pytest.raises(IndexError):
         _ = view[100]
+
+
+def test_list_view_insert():
+    lv = ListValue()
+    view = ListView(lv)
+    assert len(view) == 0
+    view.insert(0, 10)
+    assert len(view) == 1
+    assert view[0] == 10
+    view.insert(20, 20)
+    assert len(view) == 2
+    assert view[1] == 20
+    view.insert(0, 30)
+    assert len(view) == 3
+    assert view[0] == 30
+
+
+def test_list_view_append_empty():
+    lv = ListValue()
+    view = ListView(lv)
+    assert len(view) == 0
+    view.append(10)
+    assert len(view) == 1
+    assert view[0] == 10


Code review comments:
diff_hunk: @@ -153,3 +153,27 @@ def test_list_view_access_out_of_bounds(list_proto):
     view = ListView(list_proto)
     with pytest.raises(IndexError):
         _ = view[100]
+
+
+def test_list_view_insert():
+    lv = ListValue()
+    view = ListView(lv)
+    assert len(view) == 0
+    view.insert(0, 10)
+    assert len(view) == 1
+    assert view[0] == 10
+    view.insert(20, 20)
+    assert len(view) == 2
+    assert view[1] == 20
+    view.insert(0, 30)
+    assert len(view) == 3
path: tests/unit/types/test_list_view.py
text: @author:
```suggestion
    assert len(view) == 2
```