Repository: magpylib/magpylib

Issue:
Source collection order bug
### Magpylib version

4.x (Latest)

### What happened?

From @BillyTheKidPhysicist via Email:

... I have found that if I have a collection of sources and iterate through them as “for src in collection”, the order changed mid iteration if inside the loop I do “src.copy()”. I’ve attached code that reproduces this if you are interested. I may be misunderstanding something though, in which case disregard.



### Code example

```Python
from magpylib.magnet import Cuboid
from magpylib import Collection

thing1=Cuboid(magnetization=(1,0,0),position=(1,2,3),dimension=(5,5,5))
thing2=Cuboid(magnetization=(1,0,0),position=(10,2,3),dimension=(5,5,5))
thing3=Cuboid(magnetization=(1,0,0),position=(30,2,3),dimension=(5,5,5))
foo=Collection(thing1,thing2,thing3)
print(foo.sources_all)

#this has the expected order
print('expected--------')
for src in foo:
    print(src)

#That has unexpected order
print('different------')
for src in foo:
    src.copy()
    print(src)

#now this order has changed as well
print(foo.sources_all)
```


### Additional context

_No response_

Discussion:

Patch:
diff --git a/magpylib/_src/obj_classes/class_BaseGeo.py b/magpylib/_src/obj_classes/class_BaseGeo.py
index fd69894f..4ae51830 100644
--- a/magpylib/_src/obj_classes/class_BaseGeo.py
+++ b/magpylib/_src/obj_classes/class_BaseGeo.py
@@ -338,11 +338,13 @@ class BaseGeo(BaseTransform):
         # pylint: disable=import-outside-toplevel
         from copy import deepcopy
 
-        if not self.parent is None:
-            parent = self.parent
-            self.parent = None
+        # avoid deepcopying the deep dependency upwards the tree structure
+        if self.parent is not None:
+            # using private attributes to avoid triggering `.add` method (see #530 bug)
+            parent = self._parent
+            self._parent = None
             obj_copy = deepcopy(self)
-            self.parent = parent
+            self._parent = parent
         else:
             obj_copy = deepcopy(self)
 


Test patch:
diff --git a/tests/test_obj_BaseGeo.py b/tests/test_obj_BaseGeo.py
index c899da2b..a0d57b69 100644
--- a/tests/test_obj_BaseGeo.py
+++ b/tests/test_obj_BaseGeo.py
@@ -384,6 +384,25 @@ def test_copy_parents():
     assert y.parent is None
 
 
+def test_copy_order():
+    """Make sure copying objects of a collection does not affect order of children (#530)"""
+    from magpylib.magnet import Cuboid
+    from magpylib import Collection
+
+    thing1 = Cuboid(style_label="t1")
+    thing2 = Cuboid(style_label="t2")
+    thing3 = Cuboid(style_label="t3")
+    foo = Collection(thing1, thing2, thing3)
+
+    desc_before = foo.describe(format="label", return_string=True)
+
+    thing1.copy()
+
+    desc_after = foo.describe(format="label", return_string=True)
+
+    assert desc_after == desc_before
+
+
 def test_describe():
     """testing descibe method"""
     # pylint: disable=protected-access