Repository: BlueBrain/MorphIO (language: python)

Issue:
Add intersphinx feature
The Intersphinx extension allows to reference an object from another code in a documentation generated with Sphinx.
The doc of MorphIO is generated using Doxygen so it can not be cross linked with intersphinx by default. Maybe using this method would be nice to overcome this:
https://docs.typo3.org/typo3cms/extensions/sphinx/2.5.1/AdvancedUsersManual/AdvancedCrossLinks/Index.html#doxygen-documentation
So we could reference the morphio types in other documentations.


Discussion / hints:

Fix patch:
diff --git a/binds/python/bind_immutable.cpp b/binds/python/bind_immutable.cpp
index 7c44ab9ee..9fa609df7 100644
--- a/binds/python/bind_immutable.cpp
+++ b/binds/python/bind_immutable.cpp
@@ -12,7 +12,6 @@
 #include <morphio/mut/endoplasmic_reticulum.h>
 #include <morphio/mut/glial_cell.h>
 #include <morphio/mut/mitochondria.h>
-//#include <morphio/mut/dendritic_spine.h>
 #include <morphio/mut/morphology.h>
 #include <morphio/soma.h>
 #include <morphio/types.h>
@@ -24,13 +23,48 @@
 
 namespace py = pybind11;
 
-void bind_immutable_module(py::module& m) {
+immutable_binding_classes bind_immutable_classes(py::module& m) {
     using namespace py::literals;
 
     // http://pybind11.readthedocs.io/en/stable/advanced/pycpp/utilities.html?highlight=iostream#capturing-standard-output-from-ostream
     py::add_ostream_redirect(m, "ostream_redirect");
 
-    py::class_<morphio::Morphology>(m, "Morphology")
+    return immutable_binding_classes{
+        py::class_<morphio::Morphology>(m,
+                                        "Morphology",
+                                        "Class representing a complete morphology"),
+        py::class_<morphio::GlialCell, morphio::Morphology>(m,
+                                                            "GlialCell",
+                                                            "Class representing a Glial Cell"),
+        py::class_<morphio::Mitochondria>(
+            m,
+            "Mitochondria",
+            "The entry-point class to access mitochondrial data\n"
+            "By design, it is the equivalent of the Morphology class but at the mitochondrial "
+            "level\n"
+            "As the Morphology class, it implements a section accessor and a root section "
+            "accessor\n"
+            "returning views on the Properties object for the queried mitochondrial section"),
+        py::class_<morphio::EndoplasmicReticulum>(
+            m,
+            "EndoplasmicReticulum",
+            "The entry-point class to access endoplasmic reticulum data\n"
+            "Spec "
+            "https://bbpteam.epfl.ch/documentation/projects/Morphology%20Documentation/latest/"
+            "h5v1.html"),
+        py::class_<morphio::Soma>(m, "Soma", "Class representing a Soma"),
+        py::class_<morphio::Section>(m, "Section", "Class representing a Section"),
+        py::class_<morphio::MitoSection>(m,
+                                         "MitoSection",
+                                         "Class representing a Mitochondrial Section"),
+        py::class_<morphio::DendriticSpine, morphio::Morphology>(
+            m, "DendriticSpine", "Class representing a Dendritic Spine")};
+}
+
+void bind_immutable_methods(immutable_binding_classes& immutable_classes) {
+    using namespace py::literals;
+
+    immutable_classes.Morphology_class
         .def(py::init<const std::string&, unsigned int>(),
              "filename"_a,
              "options"_a = morphio::enums::Option::NO_MODIFIER)
@@ -160,27 +194,22 @@ void bind_immutable_module(py::module& m) {
             },
             py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */,
             "Section iterator that runs successively on every neurite\n"
+            "\n"
             "iter_type controls the order of iteration on sections of a given neurite. 2 values "
             "can be passed:\n"
-            "- morphio.IterType.depth_first (default)\n"
-            "- morphio.IterType.breadth_first (default)\n"
+            "\n"
+            "- ``morphio.IterType.depth_first`` (default)\n"
+            "- ``morphio.IterType.breadth_first``\n",
             "iter_type"_a = IterType::DEPTH_FIRST);
 
-    py::class_<morphio::GlialCell, morphio::Morphology>(m, "GlialCell")
-        .def(py::init<const std::string&>())
+    immutable_classes.GlialCell_class.def(py::init<const std::string&>())
         .def(py::init(
                  [](py::object arg) { return std::make_unique<morphio::GlialCell>(py::str(arg)); }),
              "filename"_a,
              "Additional Ctor that accepts as filename any python object that implements __repr__ "
              "or __str__");
 
-    py::class_<morphio::Mitochondria>(
-        m,
-        "Mitochondria",
-        "The entry-point class to access mitochondrial data\n"
-        "By design, it is the equivalent of the Morphology class but at the mitochondrial level\n"
-        "As the Morphology class, it implements a section accessor and a root section accessor\n"
-        "returning views on the Properties object for the queried mitochondrial section")
+    immutable_classes.Mitochondria_class
         .def("section",
              &morphio::Mitochondria::section,
              "Returns the mithochondrial section with the given ID",
@@ -193,13 +222,7 @@ void bind_immutable_module(py::module& m) {
             &morphio::Mitochondria::rootSections,
             "Returns a list of all root sections (section whose parent ID is -1)");
 
-    py::class_<morphio::EndoplasmicReticulum>(
-        m,
-        "EndoplasmicReticulum",
-        "The entry-point class to access endoplasmic reticulum data\n"
-        "Spec "
-        "https://bbpteam.epfl.ch/documentation/projects/Morphology%20Documentation/latest/"
-        "h5v1.html")
+    immutable_classes.EndoplasmicReticulum_class
         .def_property_readonly("section_indices",
                                &morphio::EndoplasmicReticulum::sectionIndices,
                                "Returns the list of neuronal section indices")
@@ -213,9 +236,7 @@ void bind_immutable_module(py::module& m) {
                                &morphio::EndoplasmicReticulum::filamentCounts,
                                "Returns the number of filaments for each neuronal section");
 
-
-    py::class_<morphio::Soma>(m, "Soma")
-        .def(py::init<const morphio::Soma&>())
+    immutable_classes.Soma_class.def(py::init<const morphio::Soma&>())
         .def_property_readonly(
             "points",
             [](morphio::Soma* soma) { return span_array_to_ndarray(soma->points()); },
@@ -240,7 +261,7 @@ void bind_immutable_module(py::module& m) {
                                "Returns the soma surface\n\n"
                                "Note: the soma surface computation depends on the soma type");
 
-    py::class_<morphio::Section>(m, "Section")
+    immutable_classes.Section_class
         .def("__str__",
              [](const morphio::Section& section) {
                  std::stringstream ss;
@@ -319,14 +340,15 @@ void bind_immutable_module(py::module& m) {
             "Section iterator\n"
             "\n"
             "iter_type controls the iteration order. 3 values can be passed:\n"
-            "- morphio.IterType.depth_first (default)\n"
-            "- morphio.IterType.breadth_first\n"
-            "- morphio.IterType.upstream\n",
+            "\n"
+            "- ``morphio.IterType.depth_first`` (default)\n"
+            "- ``morphio.IterType.breadth_first``\n"
+            "- ``morphio.IterType.upstream``\n",
             "iter_type"_a = IterType::DEPTH_FIRST);
 
 
-    py::class_<morphio::MitoSection>(m, "MitoSection")
-        // Topology-related member functions
+    // Topology-related member functions
+    immutable_classes.MitoSection_class
         .def_property_readonly("parent",
                                &morphio::MitoSection::parent,
                                "Returns the parent mitochondrial section of this section\n"
@@ -393,7 +415,7 @@ void bind_immutable_module(py::module& m) {
             "at each root section",
             "iter_type"_a = IterType::DEPTH_FIRST);
 
-    py::class_<morphio::DendriticSpine, morphio::Morphology>(m, "DendriticSpine")
+    immutable_classes.DendriticSpine_class
         .def(py::init([](py::object arg) {
                  return std::make_unique<morphio::DendriticSpine>(py::str(arg));
              }),
@@ -484,10 +506,12 @@ void bind_immutable_module(py::module& m) {
             },
             py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */,
             "Section iterator that runs successively on every neurite\n"
+            "\n"
             "iter_type controls the order of iteration on sections of a given neurite. 2 values "
             "can be passed:\n"
-            "- morphio.IterType.depth_first (default)\n"
-            "- morphio.IterType.breadth_first (default)\n",
+            "\n"
+            "- ``morphio.IterType.depth_first`` (default)\n"
+            "- ``morphio.IterType.breadth_first``\n",
             "iter_type"_a = IterType::DEPTH_FIRST)
         .def(
             "write",
diff --git a/binds/python/bind_immutable.h b/binds/python/bind_immutable.h
index 5f9ed61e6..653a2d081 100644
--- a/binds/python/bind_immutable.h
+++ b/binds/python/bind_immutable.h
@@ -1,4 +1,21 @@
 #pragma once
+
 #include <pybind11/pybind11.h>
 
-void bind_immutable_module(pybind11::module& m);
+#include <morphio/dendritic_spine.h>
+#include <morphio/endoplasmic_reticulum.h>
+#include <morphio/enums.h>
+#include <morphio/glial_cell.h>
+#include <morphio/mut/dendritic_spine.h>
+#include <morphio/mut/endoplasmic_reticulum.h>
+#include <morphio/mut/glial_cell.h>
+#include <morphio/mut/mitochondria.h>
+#include <morphio/mut/morphology.h>
+#include <morphio/soma.h>
+#include <morphio/types.h>
+
+#include "bindings_utils.h"
+
+immutable_binding_classes bind_immutable_classes(pybind11::module& m);
+
+void bind_immutable_methods(immutable_binding_classes& immutable_classes);
diff --git a/binds/python/bind_mutable.cpp b/binds/python/bind_mutable.cpp
index fd20097c6..d72613aef 100644
--- a/binds/python/bind_mutable.cpp
+++ b/binds/python/bind_mutable.cpp
@@ -1,8 +1,8 @@
 #include "bind_mutable.h"
 
-#include <pybind11/stl.h>
 #include <pybind11/numpy.h>
 #include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
 
 #include <morphio/endoplasmic_reticulum.h>
 #include <morphio/mut/dendritic_spine.h>
@@ -19,11 +19,34 @@
 
 namespace py = pybind11;
 
-void bind_mutable_module(py::module& m) {
+mutable_binding_classes bind_mutable_classes(py::module& m) {
     using namespace py::literals;
 
-    py::class_<morphio::mut::Morphology>(m, "Morphology")
-        .def(py::init<>())
+    return mutable_binding_classes{
+        py::class_<morphio::mut::Morphology>(m,
+                                             "Morphology",
+                                             "Class representing a mutable Morphology"),
+        py::class_<morphio::mut::GlialCell, morphio::mut::Morphology>(
+            m, "GlialCell", "Class representing a mutable Glial Cell"),
+        py::class_<morphio::mut::Mitochondria>(m,
+                                               "Mitochondria",
+                                               "Class representing a mutable Mitochondria"),
+        py::class_<morphio::mut::MitoSection, std::shared_ptr<morphio::mut::MitoSection>>(
+            m, "MitoSection", "Class representing a mutable Mitochondrial Section"),
+        py::class_<morphio::mut::Section, std::shared_ptr<morphio::mut::Section>>(
+            m, "Section", "Class representing a mutable Section"),
+        py::class_<morphio::mut::Soma, std::shared_ptr<morphio::mut::Soma>>(
+            m, "Soma", "Class representing a mutable Soma"),
+        py::class_<morphio::mut::EndoplasmicReticulum>(
+            m, "EndoplasmicReticulum", "Class representing a mutable Endoplasmic Reticulum"),
+        py::class_<morphio::mut::DendriticSpine, morphio::mut::Morphology>(
+            m, "DendriticSpine", "Class representing a mutable Dendritic Spine")};
+}
+
+void bind_mutable_methods(mutable_binding_classes& mutable_classes) {
+    using namespace py::literals;
+
+    mutable_classes.Morphology_mut_class.def(py::init<>())
         .def(py::init<const std::string&, unsigned int>(),
              "filename"_a,
              "options"_a = morphio::enums::Option::NO_MODIFIER)
@@ -34,8 +57,7 @@ void bind_mutable_module(py::module& m) {
              "morphology"_a,
              "options"_a = morphio::enums::Option::NO_MODIFIER)
         .def(py::init([](py::object arg, unsigned int options) {
-                 return std::make_unique<morphio::mut::Morphology>(
-                     py::str(arg), options);
+                 return std::make_unique<morphio::mut::Morphology>(py::str(arg), options);
              }),
              "filename"_a,
              "options"_a = morphio::enums::Option::NO_MODIFIER,
@@ -132,7 +154,7 @@ void bind_mutable_module(py::module& m) {
         .def_property_readonly("version", &morphio::mut::Morphology::version, "Returns the version")
 
         .def("remove_unifurcations",
-             static_cast<void (morphio::mut::Morphology::*) ()>(
+             static_cast<void (morphio::mut::Morphology::*)()>(
                  &morphio::mut::Morphology::removeUnifurcations),
              "Fixes the morphology single child sections and issues warnings"
              "if the section starts and ends are inconsistent")
@@ -155,8 +177,9 @@ void bind_mutable_module(py::module& m) {
                     return py::make_iterator(morph->breadth_begin(), morph->breadth_end());
                 case IterType::UPSTREAM:
                 default:
-                    throw morphio::MorphioError("Only iteration types depth_first and "
-                                                "breadth_first are supported");
+                    throw morphio::MorphioError(
+                        "Only iteration types depth_first and "
+                        "breadth_first are supported");
                 }
             },
             py::keep_alive<0, 1>() /* Essential: keep object alive
@@ -164,10 +187,12 @@ void bind_mutable_module(py::module& m) {
             ,
             "Section iterator that runs successively on every "
             "neurite\n"
+            "\n"
             "iter_type controls the order of iteration on sections of "
             "a given neurite. 2 values can be passed:\n"
-            "- morphio.IterType.depth_first (default)\n"
-            "- morphio.IterType.breadth_first",
+            "\n"
+            "- ``morphio.IterType.depth_first`` (default)\n"
+            "- ``morphio.IterType.breadth_first``\n",
             "iter_type"_a = IterType::DEPTH_FIRST)
         .def("append_root_section",
              static_cast<std::shared_ptr<morphio::mut::Section> (
@@ -178,8 +203,7 @@ void bind_mutable_module(py::module& m) {
              "mutable_section"_a,
              "recursive"_a = false);
 
-    py::class_<morphio::mut::GlialCell, morphio::mut::Morphology>(m, "GlialCell")
-        .def(py::init<>())
+    mutable_classes.GlialCell_mut_class.def(py::init<>())
         .def(py::init([](py::object arg) {
                  return std::make_unique<morphio::mut::GlialCell>(py::str(arg));
              }),
@@ -188,8 +212,7 @@ void bind_mutable_module(py::module& m) {
              "object that implements __repr__ or __str__");
 
 
-    py::class_<morphio::mut::Mitochondria>(m, "Mitochondria")
-        .def(py::init<>())
+    mutable_classes.Mitochondria_mut_class.def(py::init<>())
         .def_property_readonly("root_sections",
                                &morphio::mut::Mitochondria::rootSections,
                                "Returns a list of all root sections IDs "
@@ -279,8 +302,7 @@ void bind_mutable_module(py::module& m) {
     using mitosection_floats_f = std::vector<morphio::floatType>& (morphio::mut::MitoSection::*) ();
     using mitosection_ints_f = std::vector<uint32_t>& (morphio::mut::MitoSection::*) ();
 
-    py::class_<morphio::mut::MitoSection, std::shared_ptr<morphio::mut::MitoSection>>(m,
-                                                                                      "MitoSection")
+    mutable_classes.MitoSection_mut_class
         .def_property_readonly("id", &morphio::mut::MitoSection::id, "Return the section ID")
         .def_property(
             "diameters",
@@ -336,7 +358,7 @@ void bind_mutable_module(py::module& m) {
              "recursive"_a = false);
 
 
-    py::class_<morphio::mut::Section, std::shared_ptr<morphio::mut::Section>>(m, "Section")
+    mutable_classes.Section_mut_class
         .def("__str__",
              [](const morphio::mut::Section& section) {
                  std::stringstream ss;
@@ -414,17 +436,19 @@ void bind_mutable_module(py::module& m) {
                 case IterType::UPSTREAM:
                     return py::make_iterator(section->upstream_begin(), section->upstream_end());
                 default:
-                    throw morphio::MorphioError("Only iteration types depth_first, breadth_first and "
-                                                "upstream are supported");
+                    throw morphio::MorphioError(
+                        "Only iteration types depth_first, breadth_first and "
+                        "upstream are supported");
                 }
             },
             py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */,
             "Section iterator\n"
             "\n"
             "iter_type controls the iteration order. 3 values can be passed:\n"
-            "- morphio.IterType.depth_first (default)\n"
-            "- morphio.IterType.breadth_first\n"
-            "- morphio.IterType.upstream\n",
+            "\n"
+            "- ``morphio.IterType.depth_first`` (default)\n"
+            "- ``morphio.IterType.breadth_first``\n"
+            "- ``morphio.IterType.upstream``\n",
             "iter_type"_a = IterType::DEPTH_FIRST)
 
         // Editing
@@ -455,8 +479,7 @@ void bind_mutable_module(py::module& m) {
              "point_level_properties"_a,
              "section_type"_a = morphio::SectionType::SECTION_UNDEFINED);
 
-    py::class_<morphio::mut::Soma, std::shared_ptr<morphio::mut::Soma>>(m, "Soma")
-        .def(py::init<const morphio::Property::PointLevel&>())
+    mutable_classes.Soma_mut_class.def(py::init<const morphio::Property::PointLevel&>())
         .def_property(
             "points",
             [](morphio::mut::Soma* soma) {
@@ -491,8 +514,7 @@ void bind_mutable_module(py::module& m) {
             [](morphio::mut::Soma* soma) { return py::array(3, soma->center().data()); },
             "Returns the center of gravity of the soma points");
 
-    py::class_<morphio::mut::EndoplasmicReticulum>(m, "EndoplasmicReticulum")
-        .def(py::init<>())
+    mutable_classes.EndoplasmicReticulum_mut_class.def(py::init<>())
         .def(py::init<const std::vector<uint32_t>&,
                       const std::vector<morphio::floatType>&,
                       const std::vector<morphio::floatType>&,
@@ -545,8 +567,7 @@ void bind_mutable_module(py::module& m) {
             },
             "Returns the number of filaments for each neuronal section");
 
-    py::class_<morphio::mut::DendriticSpine, morphio::mut::Morphology>(m, "DendriticSpine")
-        .def(py::init<>())
+    mutable_classes.DendriticSpine_mut_class.def(py::init<>())
         .def(py::init([](py::object arg) {
                  return std::make_unique<morphio::mut::DendriticSpine>(py::str(arg));
              }),
diff --git a/binds/python/bind_mutable.h b/binds/python/bind_mutable.h
index 4581d10e1..bffa2edef 100644
--- a/binds/python/bind_mutable.h
+++ b/binds/python/bind_mutable.h
@@ -2,4 +2,21 @@
 
 #include <pybind11/pybind11.h>
 
-void bind_mutable_module(pybind11::module& m);
+#include <morphio/dendritic_spine.h>
+#include <morphio/endoplasmic_reticulum.h>
+#include <morphio/enums.h>
+#include <morphio/glial_cell.h>
+#include <morphio/mut/dendritic_spine.h>
+#include <morphio/mut/endoplasmic_reticulum.h>
+#include <morphio/mut/glial_cell.h>
+#include <morphio/mut/mitochondria.h>
+#include <morphio/mut/morphology.h>
+#include <morphio/soma.h>
+#include <morphio/types.h>
+
+#include "bindings_utils.h"
+
+
+mutable_binding_classes bind_mutable_classes(pybind11::module& m);
+
+void bind_mutable_methods(mutable_binding_classes& mutable_classes);
diff --git a/binds/python/bind_vasculature.cpp b/binds/python/bind_vasculature.cpp
index 82e394b29..06d1cf227 100644
--- a/binds/python/bind_vasculature.cpp
+++ b/binds/python/bind_vasculature.cpp
@@ -15,7 +15,9 @@ namespace py = pybind11;
 void bind_vasculature(py::module& m) {
     using namespace py::literals;
 
-    py::class_<morphio::vasculature::Vasculature>(m, "Vasculature")
+    py::class_<morphio::vasculature::Vasculature>(m,
+                                                  "Vasculature",
+                                                  "Class representing a Vasculature")
         .def(py::init<const std::string&>(), "filename"_a)
         .def(py::init([](py::object arg) {
                  return std::make_unique<morphio::vasculature::Vasculature>(py::str(arg));
@@ -36,7 +38,6 @@ void bind_vasculature(py::module& m) {
              "throw RawDataError if the id is out of range",
              "section_id"_a)
 
-
         .def_property_readonly(
             "section_offsets",
             [](morphio::vasculature::Vasculature& vasculature) {
@@ -98,7 +99,9 @@ void bind_vasculature(py::module& m) {
             "Iterate on all sections of the graph");
 
 
-    py::class_<morphio::vasculature::Section>(m, "Section")
+    py::class_<morphio::vasculature::Section>(m,
+                                              "Section",
+                                              "Class representing a mutable Vasculature Section")
         .def("__str__",
              [](const morphio::vasculature::Section& section) {
                  std::stringstream ss;
diff --git a/binds/python/bindings_utils.h b/binds/python/bindings_utils.h
index 4d6ff2ebb..cf465a412 100644
--- a/binds/python/bindings_utils.h
+++ b/binds/python/bindings_utils.h
@@ -5,6 +5,20 @@
 
 #include <morphio/types.h>
 
+#include <morphio/dendritic_spine.h>
+#include <morphio/endoplasmic_reticulum.h>
+#include <morphio/glial_cell.h>
+#include <morphio/mitochondria.h>
+#include <morphio/morphology.h>
+#include <morphio/mut/dendritic_spine.h>
+#include <morphio/mut/endoplasmic_reticulum.h>
+#include <morphio/mut/glial_cell.h>
+#include <morphio/mut/mitochondria.h>
+#include <morphio/mut/morphology.h>
+#include <morphio/mut/soma.h>
+#include <morphio/soma.h>
+
+
 namespace py = pybind11;
 
 morphio::Points array_to_points(py::array_t<morphio::floatType>& buf);
@@ -48,3 +62,28 @@ inline py::array_t<typename Sequence::value_type> as_pyarray(Sequence&& seq) {
                      capsule           // numpy array references this parent
     );
 }
+
+struct immutable_binding_classes {
+    // Immutable classes
+    py::class_<morphio::Morphology> Morphology_class;
+    py::class_<morphio::GlialCell, morphio::Morphology> GlialCell_class;
+    py::class_<morphio::Mitochondria> Mitochondria_class;
+    py::class_<morphio::EndoplasmicReticulum> EndoplasmicReticulum_class;
+    py::class_<morphio::Soma> Soma_class;
+    py::class_<morphio::Section> Section_class;
+    py::class_<morphio::MitoSection> MitoSection_class;
+    py::class_<morphio::DendriticSpine, morphio::Morphology> DendriticSpine_class;
+};
+
+struct mutable_binding_classes {
+    // Mutable classes
+    py::class_<morphio::mut::Morphology> Morphology_mut_class;
+    py::class_<morphio::mut::GlialCell, morphio::mut::Morphology> GlialCell_mut_class;
+    py::class_<morphio::mut::Mitochondria> Mitochondria_mut_class;
+    py::class_<morphio::mut::MitoSection, std::shared_ptr<morphio::mut::MitoSection>>
+        MitoSection_mut_class;
+    py::class_<morphio::mut::Section, std::shared_ptr<morphio::mut::Section>> Section_mut_class;
+    py::class_<morphio::mut::Soma, std::shared_ptr<morphio::mut::Soma>> Soma_mut_class;
+    py::class_<morphio::mut::EndoplasmicReticulum> EndoplasmicReticulum_mut_class;
+    py::class_<morphio::mut::DendriticSpine, morphio::mut::Morphology> DendriticSpine_mut_class;
+};
diff --git a/binds/python/morphio.cpp b/binds/python/morphio.cpp
index 4afa5693c..1ae62d76f 100644
--- a/binds/python/morphio.cpp
+++ b/binds/python/morphio.cpp
@@ -5,14 +5,21 @@
 #include "bind_mutable.h"
 #include "bind_vasculature.h"
 
+#include "bindings_utils.h"
+
+
 namespace py = pybind11;
 
 PYBIND11_MODULE(_morphio, m) {
     bind_misc(m);
-    bind_immutable_module(m);
+    immutable_binding_classes all_immutable_binding_classes = bind_immutable_classes(m);
 
     py::module mut_module = m.def_submodule("mut");
-    bind_mutable_module(mut_module);
+
+    mutable_binding_classes all_mutable_binding_classes = bind_mutable_classes(mut_module);
+
+    bind_immutable_methods(all_immutable_binding_classes);
+    bind_mutable_methods(all_mutable_binding_classes);
 
     py::module vasc_module = m.def_submodule("vasculature");
     bind_vasculature(vasc_module);
diff --git a/doc/Makefile b/doc/Makefile
index d0c3cbf10..ea4094f20 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -11,6 +11,7 @@ BUILDDIR      = build
 # Put it first so that "make" without argument is like "make help".
 help:
 	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+# 	@source/build_intersphinx_objects.sh
 
 .PHONY: help Makefile
 
diff --git a/doc/source/Doxyfile b/doc/source/Doxyfile
index d6084f73d..5eefa09ca 100644
--- a/doc/source/Doxyfile
+++ b/doc/source/Doxyfile
@@ -1984,7 +1984,7 @@ MAN_LINKS              = NO
 # captures the structure of the code including all documentation.
 # The default value is: NO.
 
-GENERATE_XML           = NO
+GENERATE_XML           = YES
 
 # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
 # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 290c52781..10eb93d73 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1,4 +1,7 @@
+.. image:: logo/BBP-MorphIO.jpg
+
 .. include:: ../../README.rst
+    :start-line: 2
 
 .. toctree::
    :hidden:
diff --git a/doc/source/markers.rst b/doc/source/markers.rst
index f93bce2c2..1433fcecc 100644
--- a/doc/source/markers.rst
+++ b/doc/source/markers.rst
@@ -6,7 +6,7 @@ Specification
 A marker is an `s-expression <https://en.wikipedia.org/wiki/S-expression>`__ of the
 Neurolucida file that contains additional information about the morphology. For example:
 
-.. code-block:: lisp
+.. code-block:: hspec
 
   ("pia" ;  String-named marker starts. Such marker can exist only at the top level.
     (Closed)
@@ -19,7 +19,7 @@ Neurolucida file that contains additional information about the morphology. For
 
 or
 
-.. code-block:: lisp
+.. code-block:: hspec
 
     ; ...
     ( -277.14  -119.13   -18.02     0.69)
@@ -36,7 +36,7 @@ The following s-expressions are parsed as markers:
 
 * Any s-exp with a top level string. Like:
 
-    .. code-block:: lisp
+    .. code-block:: hspec
 
       ("pia"
       (Closed)
@@ -84,7 +84,7 @@ The following s-expressions are parsed as markers:
 
     Example:
 
-    .. code-block:: lisp
+    .. code-block:: hspec
 
         (FilledCircle
         (Color RGB (64, 0, 128))
@@ -97,7 +97,7 @@ The following s-expressions are parsed as markers:
 
     Example:
 
-    .. code-block:: lisp
+    .. code-block:: hspec
 
         : ...
         ( -269.77  -129.47   -22.57     0.92)  ;
diff --git a/doc/source/python.rst b/doc/source/python.rst
index 42c0bd531..ebfb7c093 100644
--- a/doc/source/python.rst
+++ b/doc/source/python.rst
@@ -1,4 +1,12 @@
 Python API
 ==========
 
+Immutable objects
+*****************
+
 .. automodule:: morphio
+
+Mutable objects
+***************
+
+.. automodule:: morphio.mut
diff --git a/doc/source/specification_neurolucida.rst b/doc/source/specification_neurolucida.rst
index 30eca4b70..dc1f76b0f 100644
--- a/doc/source/specification_neurolucida.rst
+++ b/doc/source/specification_neurolucida.rst
@@ -12,7 +12,7 @@ Soma
 * Files without a soma are valid.
 * Multiple soma
   ASC files with *multiple* CellBody tags will raise an error.
-  `Unit test <https://github.com/BlueBrain/MorphIO/blob/5e111f3141f7a1ee72e0260111ce569741d80acb/tests/test_neurolucida.py#L58>`_
+  `Unit test <https://github.com/BlueBrain/MorphIO/blob/5e111f3141f7a1ee72e0260111ce569741d80acb/tests/test_neurolucida.py#L58>`__
 
 Duplicate points
 ----------------
@@ -20,7 +20,7 @@ Duplicate points
 When reading an ASC file, the last point of a section will be added as the first point of the
 child sections if not already present. That means these two representations are equivalent:
 
-.. code-block:: lisp
+.. code-block:: hspec
 
    ( (Dendrite)
      (3 -4 0 2)
@@ -35,7 +35,7 @@ child sections if not already present. That means these two representations are
    )
 
 
-.. code-block:: lisp
+.. code-block:: hspec
 
    ( (Dendrite)
      (3 -4 0 2)
@@ -51,8 +51,8 @@ child sections if not already present. That means these two representations are
      )
    )
 
-`Unit test <https://github.com/BlueBrain/MorphIO/blob/a60b52dfe403ef289455ee2221c1b4fce6418978/tests/test_neurolucida.py#L162>`_\ ,
-`Unit test <https://github.com/BlueBrain/MorphIO/blob/a60b52dfe403ef289455ee2221c1b4fce6418978/tests/test_writers.py#L191>`_
+`Unit test <https://github.com/BlueBrain/MorphIO/blob/a60b52dfe403ef289455ee2221c1b4fce6418978/tests/test_neurolucida.py#L162>`__\ ,
+`Unit test <https://github.com/BlueBrain/MorphIO/blob/a60b52dfe403ef289455ee2221c1b4fce6418978/tests/test_writers.py#L191>`__
 
 Note: As of today, it is **OK** for a duplicated point to have a different radius than the
 original point.
@@ -60,7 +60,7 @@ original point.
 When writing the file the duplicate point is **not** automatically added. However, a warning will
 be displayed if the first point of a section differs from the last point of the previous section.
 
-`Unit test <https://github.com/BlueBrain/MorphIO/blob/a60b52dfe403ef289455ee2221c1b4fce6418978/tests/test_mut.py#L125>`_
+`Unit test <https://github.com/BlueBrain/MorphIO/blob/a60b52dfe403ef289455ee2221c1b4fce6418978/tests/test_mut.py#L125>`__
 
 
 Single point section
@@ -70,7 +70,7 @@ is a duplicate of the parent section last point or not.
 
 * If it is a duplicate, the section is discarded. Example:
 
-    .. code-block:: lisp
+    .. code-block:: hspec
     
        ((Dendrite)
          (3 -4 0 2)
@@ -82,21 +82,21 @@ is a duplicate of the parent section last point or not.
 
     will become:
 
-    .. code-block:: lisp
+    .. code-block:: hspec
     
        ((Dendrite)
          (3 -4 0 2)
          (3 -10 0 2)
        )
 
-    See `here <https://github.com/BlueBrain/MorphIO/blob/5e111f3141f7a1ee72e0260111ce569741d80acb/tests/test_neurolucida.py#L350>`_
-    and `here for a more complex case <https://github.com/BlueBrain/MorphIO/blob/5e111f3141f7a1ee72e0260111ce569741d80acb/tests/test_neurolucida.py#L374>`_
+    See `here <https://github.com/BlueBrain/MorphIO/blob/5e111f3141f7a1ee72e0260111ce569741d80acb/tests/test_neurolucida.py#L350>`__
+    and `here for a more complex case <https://github.com/BlueBrain/MorphIO/blob/5e111f3141f7a1ee72e0260111ce569741d80acb/tests/test_neurolucida.py#L374>`__
 
 
 * If the point is *not* a duplicate, then the duplicate point is prepended at the beggining of
-    the section and the section is considered perfectly valid. See `here <https://github.com/BlueBrain/MorphIO/blob/5e111f3141f7a1ee72e0260111ce569741d80acb/tests/test_neurolucida.py#L253>`_
+    the section and the section is considered perfectly valid. See `here <https://github.com/BlueBrain/MorphIO/blob/5e111f3141f7a1ee72e0260111ce569741d80acb/tests/test_neurolucida.py#L253>`__
 
-    .. code-block:: lisp
+    .. code-block:: hspec
     
        ((Dendrite)
            (3 -4 0 2)
@@ -108,7 +108,7 @@ is a duplicate of the parent section last point or not.
 
     will be equivalent to:
 
-    .. code-block:: lisp
+    .. code-block:: hspec
 
       ((Dendrite)
           (3 -4 0 2)
@@ -123,7 +123,7 @@ Single child section
 --------------------
 Section with only one child section will have their child merged with.
 
-.. code-block:: lisp
+.. code-block:: hspec
 
    ((Dendrite)
     (3 -4 0 2)
@@ -144,7 +144,7 @@ Section with only one child section will have their child merged with.
 
 will be interpreted the same as:
 
-.. code-block:: lisp
+.. code-block:: hspec
 
    ((Dendrite)
     (3 -4 0 2)
@@ -160,14 +160,14 @@ will be interpreted the same as:
     )
    )
 
-`Unit test <https://github.com/BlueBrain/MorphIO/blob/5e111f3141f7a1ee72e0260111ce569741d80acb/tests/test_neurolucida.py#L285>`_
+`Unit test <https://github.com/BlueBrain/MorphIO/blob/5e111f3141f7a1ee72e0260111ce569741d80acb/tests/test_neurolucida.py#L285>`__
 
 Empty siblings
 **************
 
 File with empty siblings are handled correctly:
 
-.. code-block:: lisp
+.. code-block:: hspec
 
    ((Dendrite)
     (3 -4 0 2)
@@ -184,7 +184,7 @@ File with empty siblings are handled correctly:
 
 will be interpreted the same as:
 
-.. code-block:: lisp
+.. code-block:: hspec
 
    ((Dendrite)
     (3 -4 0 2)


Test patch:
diff --git a/tests/test_0_API.py b/tests/test_0_API.py
index 8001e1305..2f5e1c471 100644
--- a/tests/test_0_API.py
+++ b/tests/test_0_API.py
@@ -45,5 +45,15 @@ def methods(cls):
     assert (methods(morphio.EndoplasmicReticulum) ==
                  methods(morphio.mut.EndoplasmicReticulum))
 
-    assert (methods(morphio.Mitochondria),
-                 methods(morphio.mut.Mitochondria))
+    only_in_mut_mitochondria = {
+        'append_root_section',
+        'breadth_begin',
+        'breadth_end',
+        'children',
+        'depth_begin',
+        'is_root',
+        'parent',
+        'upstream_begin',
+    }
+    assert (methods(morphio.Mitochondria) ==
+                 methods(morphio.mut.Mitochondria) - only_in_mut_mitochondria)