Repository: yaqwsx/KiKit (language: python)

Issue:
Unable to create a deep copy of BaseValue objects
### Prerequisites

- [X] I have read [FAQ](https://github.com/yaqwsx/KiKit/blob/master/doc/faq.md)
- [X] I use the KiKit at least 1.1.0 (older version are not supported)

### KiKit version

1.1.2

### KiCAD version

Application: KiCad PCB Editor  Version: 6.0.8-f2edbf62ab~116~ubuntu22.04.1, release build  Libraries: 	wxWidgets 3.0.5 	libcurl/7.85.0 OpenSSL/3.0.5 zlib/1.2.11 brotli/1.0.9 zstd/1.5.2 libidn2/2.3.3 libpsl/0.21.0 (+libidn2/2.3.2) libssh/0.9.6/openssl/zlib nghttp2/1.49.0 librtmp/2.3  Platform: Linux 5.19.11-051911-generic x86_64, 64 bit, Little endian, wxGTK, KDE, x11  Build Info: 	Date: Sep 30 2022 16:52:15 	wxWidgets: 3.0.5 (wchar_t,wx containers,compatible with 2.8) GTK+ 3.24 	Boost: 1.74.0 	OCC: 7.5.2 	Curl: 7.85.0 	ngspice: 36 	Compiler: GCC 11.2.0 with C++ ABI 1016  Build settings: 	KICAD_USE_OCC=ON 	KICAD_SPICE=ON

### Operating system

Ubuntu 22.10

### Description

I am using KiKit functionality in my own code and want to be able to store deep copies of objects that contain among others `TabAnnotation` objects.
`TabAnnotation` uses the `units.readLength()` function that returns a `BaseValue` object.
I think, the `BaseValue` object is missing the `__deeopcopy__()` special method, see here:
> In order for a class to define its own copy implementation, it can define special methods __copy__() and __deepcopy__(). The former is called to implement the shallow copy operation; no additional arguments are passed. The latter is called to implement the deep copy operation; it is passed one argument, the memo dictionary. If the __deepcopy__() implementation needs to make a deep copy of a component, it should call the [deepcopy()](https://docs.python.org/3/library/copy.html#copy.deepcopy) function with the component as first argument and the memo dictionary as second argument. The memo dictionary should be treated as an opaque object.

The above is taken from https://docs.python.org/3/library/copy.html#copy.deepcopy.

I get the following error message on creating deep copies of my class, that contains in the end `BaseValue` objects:

![image](https://user-images.githubusercontent.com/4668732/203312409-1b2c0881-bc08-42d7-afb3-c9c16496be78.png)


### Steps to Reproduce

Try to perform a deep copy of an object that contains a `BaseValue` instance.


Discussion / hints:

Fix patch:
diff --git a/kikit/units.py b/kikit/units.py
index 2f859289..1ffe5350 100644
--- a/kikit/units.py
+++ b/kikit/units.py
@@ -1,6 +1,7 @@
 import re
 import math
 from pcbnewTransition import pcbnew
+from copy import deepcopy
 
 class UnitError(RuntimeError):
     pass
@@ -22,6 +23,16 @@ class BaseValue(int):
     """
     Value in base units that remembers its original string representation.
     """
+    def __deepcopy__(self, memo):
+        cls = self.__class__
+        value = self
+        strRepr = self.str
+        result = cls.__new__(cls, value, strRepr)
+        memo[id(self)] = result
+        for k, v in self.__dict__.items():
+            setattr(result, k, deepcopy(v, memo))
+        return result
+
     def __new__(cls, value, strRepr):
         x = super().__new__(cls, value)
         x.str = strRepr


Test patch:
diff --git a/test/units/test_units.py b/test/units/test_units.py
index 3f4b4ff8..cd474c4b 100644
--- a/test/units/test_units.py
+++ b/test/units/test_units.py
@@ -1,6 +1,7 @@
 import pytest
 from pcbnewTransition.pcbnew import FromMM, FromMils
 from kikit.units import readLength, readAngle, UnitError
+from copy import deepcopy
 
 def test_readLength():
     assert readLength("4.24mm") == FromMM(4.24)
@@ -13,3 +14,9 @@ def test_readLength():
     assert readLength("1mil") == FromMils(1)
     assert readLength("1inch") == FromMils(1000)
 
+
+def test_baseValue_deepcopy():
+    a = readLength("1.23mm")
+    b = deepcopy(a)
+
+    assert a.__dict__ == b.__dict__