mdomke / schwifty

IBAN parsing and validation
https://schwifty.readthedocs.io
MIT License
206 stars 80 forks source link

IBAN object cannot be deepcopied #216

Closed binaryDiv closed 1 month ago

binaryDiv commented 2 months ago

Currently, when trying a copy.deepcopy() on an IBAN object, an exception is raised. This can happen for example when deepcopying a dictionary that contains an IBAN object.

A simple copy() works perfectly fine, it even returns a new instance:

>>> from schwifty import IBAN
>>> import copy
>>> 
>>> iban = IBAN('DE91100000000123456789')
>>> iban
<IBAN=DE91100000000123456789>
>>> 
>>> iban_copy = copy.copy(iban)
>>> id(iban), id(iban_copy)
(125298528552432, 125298507391216)

However, a deepcopy() results in the following exception:

>>> iban_deepcopy = copy.deepcopy(iban)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.12/copy.py", line 162, in deepcopy
    y = _reconstruct(x, memo, *rv)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/copy.py", line 259, in _reconstruct
    state = deepcopy(state, memo)
            ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/copy.py", line 136, in deepcopy
    y = copier(x, memo)
        ^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/copy.py", line 221, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
                             ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/copy.py", line 162, in deepcopy
    y = _reconstruct(x, memo, *rv)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/copy.py", line 253, in _reconstruct
    y = func(*args)
        ^^^^^^^^^^^
  File "/usr/lib/python3.12/copyreg.py", line 99, in __newobj__
    return cls.__new__(cls, *args)
           ^^^^^^^^^^^^^^^^^^^^^^^
TypeError: BBAN.__new__() missing 1 required positional argument: 'value'

I've tested this on the latest version of schwifty (2024.6.1) and Python 3.12.4.

binaryDiv commented 2 months ago

Adding the following implementation for __deepcopy__ to the BBAN class seems to fix the issue. However, I'm not sure if this is the best solution or if that implementation should be done in the common.Base class.

def __deepcopy__(self, memo):
    cls = type(self)
    return cls(str(self.country_code), str(self))
mdomke commented 1 month ago

@binaryDiv Thanks for the heads-up! I added a corresponding implementation to the common.Base-class.