Repository: BlueBrain/MorphIO (language: python)

Issue:
saving custum types in .asc
We just bumped into this obscure error if we do the following (from neurom):

```
m = nm.load_morphology('test.asc')
for section in m.neurites[0].iter_sections():
    section.morphio_section.type = SectionType.custom5
m.write('test_custom.asc')
```

we get this crash:

```
----> 1 m.write("t.asc")

IndexError: map::at
```

Saving to .swc works, I guess in .asc one has to map the custom types to a header name of the section list, or something like that? 

It's not at all urgent at all, it's just to let you know!


Discussion / hints:
Thanks for the great bug-report; we'll look into it.
Always a pleasure to report bugs without trying to find a fix! :)
Hi Alexis. Would that entail we should allow writing the swc custom properties in the ascii format? Even though these types are not specified/available for ascii files, I don't see any harm in making them available to and fro that format.


Perhaps we have to throw an exception, unless the types we know exist in ASC exist?  If someone w/ access to neurolucida can list all the types, we can have those; and anything else throws?
Yes, whatever you prefer, it just happened to our MSc student, and it's an impossible crash to understand as it is. Indeed, if we really need to save this in a file, we'd better use the format that allows this custom types. Maybe throw an error saying we can only save these custom types in .swc format?
but again, it's not urgent at all, we don't need to save these custom (we revert back to normal section types before saving).
FYI I skimmed this (https://youtu.be/Q7xztgAMBh8?t=948), and it looks like the drop down box only has these options:
![image](https://user-images.githubusercontent.com/13150119/150494159-d241e238-d74a-4d4e-9e8a-1314736690fb.png)

> Maybe throw an error saying we can only save these custom types in .swc format?

Seems best to me.


Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e53ef8611..221f3fd27 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ Fixes:
 * Fixed documentation links (#350)
 * Python version check >=3.7 (#356)
 * Updated pybind11 submodule to v2.8.1 (#362)
+* Added exception for attempting to write unsupported section types into ascii (#370)
 
 v3.3.2
 ======
diff --git a/include/morphio/errorMessages.h b/include/morphio/errorMessages.h
index c419fce98..71fd46556 100644
--- a/include/morphio/errorMessages.h
+++ b/include/morphio/errorMessages.h
@@ -215,6 +215,9 @@ class ErrorMessages
     //              WRITERS
     ////////////////////////////////////////////////////////////////////////////////
 
+    /** Unsupported morphology section type error message */
+    std::string ERROR_UNSUPPORTED_SECTION_TYPE(const SectionType& type) const;
+
     /** Wrong morphology file extension error message */
     std::string ERROR_WRONG_EXTENSION(const std::string& filename) const;
 
diff --git a/src/errorMessages.cpp b/src/errorMessages.cpp
index 901c4eb46..5cb1fdde8 100644
--- a/src/errorMessages.cpp
+++ b/src/errorMessages.cpp
@@ -218,6 +218,12 @@ std::string ErrorMessages::ERROR_UNCOMPATIBLE_FLAGS(morphio::Option flag1,
 //              WRITERS
 ////////////////////////////////////////////////////////////////////////////////
 
+std::string ErrorMessages::ERROR_UNSUPPORTED_SECTION_TYPE(const SectionType& type) const {
+    return ("Attempted to write unsupported section type: " + std::to_string(type) +
+            ".\n"
+            "Please try writing to a different format that supports the section type.");
+}
+
 std::string ErrorMessages::ERROR_WRONG_EXTENSION(const std::string& filename) const {
     return "Filename: " + filename + " must have one of the following extensions: swc, asc or h5";
 }
diff --git a/src/mut/writers.cpp b/src/mut/writers.cpp
index 070c61b5b..3e3cd2241 100644
--- a/src/mut/writers.cpp
+++ b/src/mut/writers.cpp
@@ -154,7 +154,13 @@ static void _write_asc_points(std::ofstream& myfile,
 static void _write_asc_section(std::ofstream& myfile,
                                const Morphology& morpho,
                                const std::shared_ptr<Section>& section,
+                               const std::map<morphio::SectionType, std::string>& header,
                                size_t indentLevel) {
+    // allowed types are only the ones available in the header
+    if (header.count(section->type()) == 0) {
+        throw WriterError(readers::ErrorMessages().ERROR_UNSUPPORTED_SECTION_TYPE(section->type()));
+    }
+
     std::string indent(indentLevel, ' ');
     _write_asc_points(myfile, section->points(), section->diameters(), indentLevel);
 
@@ -163,7 +169,7 @@ static void _write_asc_section(std::ofstream& myfile,
         size_t nChildren = children.size();
         for (unsigned int i = 0; i < nChildren; ++i) {
             myfile << indent << (i == 0 ? "(\n" : "|\n");
-            _write_asc_section(myfile, morpho, children[i], indentLevel + 2);
+            _write_asc_section(myfile, morpho, children[i], header, indentLevel + 2);
         }
         myfile << indent << ")\n";
     }
@@ -201,9 +207,14 @@ void asc(const Morphology& morphology, const std::string& filename) {
         printError(Warning::WRITE_NO_SOMA, readers::ErrorMessages().WARNING_WRITE_NO_SOMA());
     }
 
-    for (const auto& section : morphology.rootSections()) {
+    for (const std::shared_ptr<Section>& section : morphology.rootSections()) {
+        // allowed types are only the ones available in the header
+        if (header.count(section->type()) == 0) {
+            throw WriterError(
+                readers::ErrorMessages().ERROR_UNSUPPORTED_SECTION_TYPE(section->type()));
+        }
         myfile << header.at(section->type());
-        _write_asc_section(myfile, morphology, section, 2);
+        _write_asc_section(myfile, morphology, section, header, 2);
         myfile << ")\n\n";
     }
 


Test patch:
diff --git a/tests/test_6_writers.py b/tests/test_6_writers.py
index 056d8e08e..65cee939d 100644
--- a/tests/test_6_writers.py
+++ b/tests/test_6_writers.py
@@ -311,3 +311,41 @@ def test_single_point_root_section():
     with TemporaryDirectory('test_single_point_root_section') as tmp_folder:
         with pytest.raises(SectionBuilderError):
             m.write(Path(tmp_folder, "h5/empty_vasculature.h5"))
+
+
+def test_write_custom_property__throws():
+
+    morpho = Morphology()
+    morpho.soma.points = [[0, 0, 0]]
+    morpho.soma.diameters = [2]
+
+    custom = morpho.append_root_section(
+        PointLevel([[0, 0, 0], [0, 5, 0]], [2, 2]),
+        SectionType.custom5
+    )
+
+    with TemporaryDirectory('test_write_basic') as tmp_folder:
+        with pytest.raises(WriterError):
+            morpho.write(Path(tmp_folder, "test_write.asc"))
+
+    morpho = Morphology()
+    morpho.soma.points = [[0, 0, 0]]
+    morpho.soma.diameters = [2]
+
+    custom = morpho.append_root_section(
+        PointLevel([[0, 0, 0], [0, 5, 0]], [2, 2]),
+        SectionType.custom5
+    )
+
+    dendrite = morpho.append_root_section(PointLevel([[0, 0, 0], [0, 5, 0]], [2, 2]),
+                                          SectionType.basal_dendrite)
+
+    dendrite.append_section(PointLevel([[0, 5, 0], [-5, 5, 0]], [2, 3]), SectionType.custom5)
+
+    with TemporaryDirectory('test_write_basic') as tmp_folder:
+        with pytest.raises(WriterError):
+            morpho.write(Path(tmp_folder, "test_write.asc"))
+
+
+
+