Copyable Python objects
Objects like the cable cell decor are copyable in the C++ interface, but while one can construct a copy of decor (and others) with explicit use of the constructor, i.e. arbor.decor(other_decor), we can't use e.g. copy.copy to make a copy.
import arbor
# This works, but is not obvious (to me?).
d1 = arbor.decor()
d1.paint('(all)', 'pas')
d2 = arbor.decor(d1)
d2.paint('(all)', 'hh') # Leaves d1 unchanged
# This doesn't though.
from copy import copy
d1 = arbor.decor()
d2 = copy(d1)
Afaik copy.copy creates shallow copies, whereas copy.deepcopy creates, well, deep copies. How this interferes with the bindings I don't know, but I have personally never had a good use-case for copy.copy, it was always copy.deepcopy that did what I wanted. Since Python is not aware of the underlying Pybind object, it may still not work. We do not implement __copy__ or __deepcopy__ anywhere afaik.
Using arbor.decors constructor, which is Pybind-aware, does therefore work.
Implementing __copy__ might be related to implementing __repr__, see https://github.com/arbor-sim/arbor/issues/952
Pybind11 makes it fairly easy to support copy and deepcopy: we just need to add the methods in the binding definition. Or we make the classes picklable, which is a bit more work.
Question is if we have to implement all those functions (which are going to be duplicating the copy constructor), or can we get away with just one? Calling copy.copy(d1) fails with a pickle error here. So maybe implementing the pickle interface is enough?
https://pybind11.readthedocs.io/en/stable/advanced/classes.html#pickling-support
2 cents: I prefer an explicit .copy() method on the objects themselves as well.