Repository: iiif-prezi/iiif-prezi3 (language: python)

Issue:
Auto-conversion of `str` to `list` when appropriate
Following on from the discussion about `LngString` in #115, we might also want to consider adding some code that can perform auto-conversion of strings to lists when required by properties. Since we already provide some helpers for things like `id` and `label`that will convert a string value to the correct type for Pydantic, this would also make the behaviour of the library more consistent.

An example of the str->list is `behavior` on a Manifest which must be a list (per https://iiif.io/api/presentation/3.0/#behavior), but might often only be a single value and therefore more intuitive to set as a string. 
i.e currently you must do:
```python
from iiif_prezi3 import Manifest
m = Manifest(id="https://example.org/manifest", behavior=["paged"])
```
whereas the following seems more intuitive:
```python
from iiif_prezi3 import Manifest
m = Manifest(id="https://example.org/manifest", behavior="paged")
```

Auto-conversion of `str` to `list` when appropriate
Following on from the discussion about `LngString` in #115, we might also want to consider adding some code that can perform auto-conversion of strings to lists when required by properties. Since we already provide some helpers for things like `id` and `label`that will convert a string value to the correct type for Pydantic, this would also make the behaviour of the library more consistent.

An example of the str->list is `behavior` on a Manifest which must be a list (per https://iiif.io/api/presentation/3.0/#behavior), but might often only be a single value and therefore more intuitive to set as a string. 
i.e currently you must do:
```python
from iiif_prezi3 import Manifest
m = Manifest(id="https://example.org/manifest", behavior=["paged"])
```
whereas the following seems more intuitive:
```python
from iiif_prezi3 import Manifest
m = Manifest(id="https://example.org/manifest", behavior="paged")
```



Discussion / hints:

Fix patch:
diff --git a/iiif_prezi3/helpers/auto_fields.py b/iiif_prezi3/helpers/auto_fields.py
index d664592..727f7b8 100644
--- a/iiif_prezi3/helpers/auto_fields.py
+++ b/iiif_prezi3/helpers/auto_fields.py
@@ -1,8 +1,9 @@
 import uuid
 
 from ..config.config import Config, register_config
-from ..skeleton import (AnnotationPage, Canvas, Class, KeyValueString, Range,
-                        Reference)
+from ..skeleton import (AnnotationPage, Canvas, Class, HomepageItem,
+                        KeyValueString, NavPlace, ProviderItem, Range,
+                        Reference, ResourceItem, ServiceItem1)
 
 
 class AutoConfig(Config):
@@ -143,14 +144,43 @@ def manipulate_value(self, what, value=None):
             return value
 
 
+class AutoListConfig(Config):
+    def __init__(self):
+        self.properties = ['behavior', 'service', 'services', 'features', 'thumbnail', 'logo', 'rendering', 'homepage', 'metadata',
+                           'partOf', 'provider', 'seeAlso']
+
+
+class AutoList(Auto):
+    def __init__(self, cfg, name=""):
+        super().__init__(cfg, name)
+
+    def manipulate_value(self, what, value=None):
+        if not value:
+            return None
+        if not isinstance(value, list):
+            return [value]
+        else:
+            return value
+
+
+class AutoLanguageListConfig(Config):
+    def __init__(self):
+        self.properties = ['language']
+
+
 aicfg = AutoIdConfig()
 alcfg = AutoLangConfig()
 aitcfg = AutoItemsConfig()
+alstcfg = AutoListConfig()
+allstcfg = AutoLanguageListConfig()
 ai = AutoId(aicfg)
 al = AutoLang(alcfg)
 ait = AutoItems(aitcfg)
-
+alst = AutoList(alstcfg, name="General")
+allst = AutoList(allstcfg, name="Language")
 # Set up some obvious defaults
 ai.register_on_class(AnnotationPage, Class)
 al.register_on_class(KeyValueString, Class, Reference)
 ait.register_on_class(Canvas, Range)
+alst.register_on_class(Class, AnnotationPage, ResourceItem, ServiceItem1, NavPlace, Reference, ProviderItem)
+allst.register_on_class(HomepageItem)


Test patch:
diff --git a/tests/test_helpers_autofields.py b/tests/test_helpers_autofields.py
index 23b60fe..b31baa3 100644
--- a/tests/test_helpers_autofields.py
+++ b/tests/test_helpers_autofields.py
@@ -65,3 +65,49 @@ def test_custom_slug(self):
         self.assertEqual('http://example.org/iiif/mani/0', m.id)
         config.configs['helpers.auto_fields.AutoId'].auto_type = curr_type
         del (config.configs['helpers.auto_fields.AutoId'].translation["Manifest"])
+
+    def test_autolist_constructor(self):
+        """Test that list properties are set correctly during construction."""
+        m = Manifest(label="AutoList Test", behavior="paged")
+        self.assertEqual(m.behavior, ["paged"])
+        m2 = Manifest(label="AutoList Test", behavior=["continuous"])
+        self.assertEqual(m2.behavior, ["continuous"])
+
+    def test_autolist_setattr(self):
+        """Test that list properties are set correctly during manipulation."""
+        m = Manifest(label="AutoList Test", behavior="paged")
+        self.assertEqual(m.behavior, ["paged"])
+        m.behavior = "continuous"
+        self.assertEqual(m.behavior, ["continuous"])
+
+    def test_autolist_object_constructor(self):
+        """Test that list properties are set during construction, when the list contains objects."""
+        m = Manifest(label="AutoList Test", rendering={"type": "Text", "label": "Download OCR", "format": "text/plain"})
+        self.assertIsInstance(m.rendering, list)
+        self.assertEqual(m.rendering[0].format, "text/plain")
+        m = Manifest(label="AutoList Test", rendering=[{"type": "Text", "label": "Download OCR", "format": "text/plain"}])
+        self.assertIsInstance(m.rendering, list)
+        self.assertEqual(m.rendering[0].format, "text/plain")
+
+    def test_autolist_object_setattr(self):
+        """Test that list properties are set correctly during manipulation, when the list contains objects."""
+        m = Manifest(label="AutoList Test", rendering={"type": "Text", "label": "Download OCR", "format": "text/plain"})
+        self.assertIsInstance(m.rendering, list)
+        self.assertEqual(m.rendering[0].format, "text/plain")
+        m.rendering = {"type": "Text", "label": "Download OCR", "format": "application/pdf"}
+        self.assertIsInstance(m.rendering, list)
+        self.assertEqual(m.rendering[0].format, "application/pdf")
+
+    def test_autolist_nested_homepage(self):
+        """Test that autolist properties are set correctly when the list is nested for homepage items."""
+        m = Manifest(label="Nested Autolist Test", provider={"homepage": {"type": "Text", "format": "text/html", "language": "en"}})
+        self.assertIsInstance(m.provider, list)
+        self.assertIsInstance(m.provider[0].homepage, list)
+        self.assertIsInstance(m.provider[0].homepage[0].language, list)
+
+    def test_autolist_nested(self):
+        """Test that autolist properties are set correctly when the list is nested."""
+        m = Manifest(label="Nested Autolist Test", provider={"logo": {"id": "https://example.org/logo", "type": "Image", "format": "image/png", "service": {"type": "ImageService3", "profile": "level2"}}})
+        self.assertIsInstance(m.provider, list)
+        self.assertIsInstance(m.provider[0].logo, list)
+        self.assertIsInstance(m.provider[0].logo[0].service, list)