ImagingDataCommons / highdicom

High-level DICOM abstractions for the Python programming language
https://highdicom.readthedocs.io
MIT License
164 stars 34 forks source link

Pydicom 2.4.0 breaks highdicom (TypeError: unhashable type: Dataset) #233

Closed CPBridge closed 1 year ago

CPBridge commented 1 year ago

The latest update to pydicom breaks several things in highdicom. I will add more detail later, but for now if you are experiencing issues try downgrading to pydicom==2.3.1 and if that fixes things please note the details here

erikogabrielsson commented 1 year ago

Hi @CPBridge,

I got an issue in WsiDicomizer related to this, where doing deepcopy on a SpecimenStaining (and maybe other content classes to) throws. Using 2.3.1 works.

from copy import deepcopy
from highdicom import SpecimenStaining
import pydicom
import highdicom
print("pydicom version:", pydicom.__version__)
print("highdicom version: ", highdicom.__version__)
dataset = SpecimenStaining(["hematoxylin stain"])
deepcopy(dataset)
pydicom version: 2.4.0
highdicom version: 0.21.1
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 8
      6 print("highdicom version", highdicom.__version__)
      7 dataset = SpecimenStaining(["hematoxylin stain"])
----> 8 deepcopy(dataset)

File [c:\Users\er-gac\.pyenv\pyenv-win\versions\3.11.2\Lib\copy.py:153](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/copy.py:153), in deepcopy(x, memo, _nil)
    151 copier = getattr(x, "__deepcopy__", None)
    152 if copier is not None:
--> 153     y = copier(memo)
    154 else:
    155     reductor = dispatch_table.get(cls)

File [c:\Users\er-gac\.pyenv\pyenv-win\versions\3.11.2\Lib\site-packages\pydicom\sequence.py:86](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/pydicom/sequence.py:86), in Sequence.__deepcopy__(self, memo)
     84 if memo is not None:
     85     memo[id(self)] = copied
---> 86 copied.__dict__.update(deepcopy(self.__dict__, memo))
     87 for ds in copied:
     88     ds.parent_seq = copied  # type:ignore

File [c:\Users\er-gac\.pyenv\pyenv-win\versions\3.11.2\Lib\copy.py:146](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/copy.py:146), in deepcopy(x, memo, _nil)
    144 copier = _deepcopy_dispatch.get(cls)
    145 if copier is not None:
--> 146     y = copier(x, memo)
    147 else:
    148     if issubclass(cls, type):

File [c:\Users\er-gac\.pyenv\pyenv-win\versions\3.11.2\Lib\copy.py:231](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/copy.py:231), in _deepcopy_dict(x, memo, deepcopy)
    229 memo[id(x)] = y
    230 for key, value in x.items():
--> 231     y[deepcopy(key, memo)] = deepcopy(value, memo)
    232 return y

File [c:\Users\er-gac\.pyenv\pyenv-win\versions\3.11.2\Lib\copy.py:172](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/copy.py:172), in deepcopy(x, memo, _nil)
    170                 y = x
    171             else:
--> 172                 y = _reconstruct(x, memo, *rv)
    174 # If is its own copy, don't memoize.
    175 if y is not x:

File [c:\Users\er-gac\.pyenv\pyenv-win\versions\3.11.2\Lib\copy.py:298](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/copy.py:298), in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
    296         key = deepcopy(key, memo)
    297         value = deepcopy(value, memo)
--> 298         y[key] = value
    299 else:
    300     for key, value in dictiter:

TypeError: unhashable type: 'Dataset'
CPBridge commented 1 year ago

Thanks @erikogabrielsson appreciate the concise example.

Still looking into this, but I suspect that this is related to this: https://github.com/pydicom/pydicom/pull/1734

(which was ironically in response to a request I made of the pydicom team). Oooops

CPBridge commented 1 year ago

I have found the problem and submitted a fix at the pydicom level.

See https://github.com/pydicom/pydicom/pull/1813 and https://github.com/pydicom/pydicom/pull/1814

CPBridge commented 1 year ago

Closing this issue as it is fixed with pydicom 2.4.1, just released.

For those who find this on the future note that this error only happens with one specific version of pydicom (2.4.0) the immediate preceding and following versions (2.3 1 and 2.4.1 respectively) should not have this issue.