From bae9e9a3500b04ef2bd21b89386bbc9ed31adbea Mon Sep 17 00:00:00 2001
From: Robin De Schepper <robin.deschepper93@gmail.com>
Date: Tue, 28 Sep 2021 11:35:43 +0200
Subject: [PATCH] Added an empty catalogue (#1677)

Expose default (empty) catalogue constructor to Python
---
 doc/python/mechanisms.rst           |  9 +++++++++
 python/mechanism.cpp                |  1 +
 python/test/unit/test_catalogues.py | 26 ++++++++++++++++++++++++++
 3 files changed, 36 insertions(+)

diff --git a/doc/python/mechanisms.rst b/doc/python/mechanisms.rst
index cb19925f..1a690de7 100644
--- a/doc/python/mechanisms.rst
+++ b/doc/python/mechanisms.rst
@@ -257,6 +257,15 @@ Mechanism catalogues
     2. A further hierarchy of *derived* mechanisms, that allow specialization of
        global parameters, ion bindings, and implementations.
 
+    .. py:method:: __init__(catalogue=None)
+
+        Create an empty or copied catalogue.
+
+        :param catalogue: ``catalogue`` to copy
+        :type catalogue: :class:`catalogue`
+        :return: empty or copied catalogue
+        :rtype: :class:`catalogue`
+
     .. py:method:: __contains__(name)
 
         Test if mechanism with *name* is in the catalogue.
diff --git a/python/mechanism.cpp b/python/mechanism.cpp
index 16f9f25e..0d74a21f 100644
--- a/python/mechanism.cpp
+++ b/python/mechanism.cpp
@@ -152,6 +152,7 @@ void register_mechanisms(pybind11::module& m) {
         .def("__next__", &py_mech_cat_item_iterator::next);
 
     cat
+        .def(pybind11::init())
         .def(pybind11::init<const arb::mechanism_catalogue&>())
         .def("__contains__", &arb::mechanism_catalogue::has,
              "name"_a, "Is 'name' in the catalogue?")
diff --git a/python/test/unit/test_catalogues.py b/python/test/unit/test_catalogues.py
index f81f3f41..4009192b 100644
--- a/python/test/unit/test_catalogues.py
+++ b/python/test/unit/test_catalogues.py
@@ -70,6 +70,32 @@ class Catalogues(unittest.TestCase):
         sim = arb.simulation(rcp, dom, ctx)
         sim.run(tfinal=30)
 
+    def test_empty(self):
+        def len(cat):
+            return sum(1 for _ in cat)
+
+        def hash_(cat):
+            return hash(" ".join(sorted(cat)))
+
+        cat = arb.catalogue()
+        ref = arb.default_catalogue()
+        other = arb.default_catalogue()
+        # Test empty constructor
+        self.assertEqual(0, len(cat), "Expected no mechanisms in `arbor.catalogue()`.")
+        # Test empty extend
+        other.extend(cat, "")
+        self.assertEqual(hash_(ref), hash_(other), "Extending cat with empty should not change cat.")
+        self.assertEqual(0, len(cat), "Extending cat with empty should not change empty.")
+        other.extend(cat, "prefix/")
+        self.assertEqual(hash_(ref), hash_(other), "Extending cat with prefixed empty should not change cat.")
+        self.assertEqual(0, len(cat), "Extending cat with prefixed empty should not change empty.")
+        cat.extend(other, "")
+        self.assertEqual(hash_(other), hash_(cat), "Extending empty with cat should turn empty into cat.")
+        cat = arb.catalogue()
+        cat.extend(other, "prefix/")
+        self.assertNotEqual(hash_(other), hash_(cat), "Extending empty with prefixed cat should not yield cat")
+
+
 
 def suite():
     # specify class and test functions in tuple (here: all tests starting with 'test' from class Contexts
-- 
GitLab