diff --git a/HISTORY.md b/HISTORY.md index 032874f..16f36ca 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,7 @@ # History +## v4.0.0 (26-12-25) +* Upgrades Pydantic API from V1 to V2 + ## v3.3.0 (18-09-25) * Drops python 3.8 and 3.9. Moves to 3.10 as minimum * Drops poetry package management. Replaces with uv diff --git a/dicomtrolley/core.py b/dicomtrolley/core.py index 7c677ee..f64f7f6 100644 --- a/dicomtrolley/core.py +++ b/dicomtrolley/core.py @@ -13,7 +13,7 @@ TypeVar, ) -from pydantic import Field, ValidationError, field_validator +from pydantic import ConfigDict, Field, ValidationError, field_validator from pydantic.main import BaseModel from pydicom.datadict import tag_for_keyword from pydicom.dataset import Dataset @@ -187,8 +187,8 @@ class DICOMObject(BaseModel, DICOMDownloadable): a DICOM dataset """ - class Config: - arbitrary_types_allowed = True # allows the use of Dataset type below + # allows the use of Dataset type below + model_config = ConfigDict(arbitrary_types_allowed=True) uid: str data: Dataset @@ -614,9 +614,8 @@ class Query(BaseModel): max_study_date: Optional[datetime] = None min_study_date: Optional[datetime] = None include_fields: Optional[List[str]] = Field(default_factory=list) - - class Config: - extra = "forbid" # raise ValueError when passing an unknown keyword to init + # raise ValueError when passing an unknown keyword to init + model_config = ConfigDict(extra="forbid") @classmethod def init_from_query( @@ -643,7 +642,7 @@ def init_from_query( this class """ # remove empty, None and 0 values - params = {key: val for key, val in query.dict().items() if val} + params = {key: val for key, val in query.model_dump().items() if val} try: return cls(**params) except ValidationError as e: @@ -672,7 +671,9 @@ def include_fields_check(cls, include_fields, values): # noqa: B902, N805 def to_short_string(self): """A more information-dense str repr. For human reading""" - filled_fields = {key: val for key, val in self.dict().items() if val} + filled_fields = { + key: val for key, val in self.model_dump().items() if val + } filled_fields["query_level"] = filled_fields["query_level"].value return f"{type(self).__name__}: {filled_fields}" diff --git a/dicomtrolley/dicom_qr.py b/dicomtrolley/dicom_qr.py index 1f97d1b..95c33ed 100644 --- a/dicomtrolley/dicom_qr.py +++ b/dicomtrolley/dicom_qr.py @@ -5,6 +5,7 @@ from typing import Dict, List +from pydantic import ConfigDict from pydicom.datadict import tag_for_keyword from pydicom.dataset import Dataset from pynetdicom import AE, debug_logger @@ -59,9 +60,8 @@ class DICOMQuery(ExtendedQuery): Modality: str = "" ProtocolName: str = "" StudyID: str = "" - - class Config: - extra = "forbid" # raise ValueError when passing an unknown keyword to init + # raise ValueError when passing an unknown keyword to init + model_config = ConfigDict(extra="forbid") @staticmethod def get_default_include_fields(query_level): @@ -95,7 +95,7 @@ def as_parameters(self) -> Dict[str, str]: # remove non-DICOM parameters and replace with DICOM tags based on them parameters = { - x: y for x, y in self.dict().items() + x: y for x, y in self.model_dump().items() } # all params for query parameters["StudyDate"] = self.get_study_date( parameters.pop("min_study_date"), parameters.pop("max_study_date") diff --git a/dicomtrolley/mint.py b/dicomtrolley/mint.py index a89f014..8aaa425 100644 --- a/dicomtrolley/mint.py +++ b/dicomtrolley/mint.py @@ -160,10 +160,10 @@ class MintQuery(ExtendedQuery): limit: int = 0 # how many results to return. 0 = all @model_validator(mode="after") - def min_max_study_date_xor(cls, values): # noqa: B902, N805 + def min_max_study_date_xor(self): # noqa: B902, N805 """Min and max should both be given or both be empty""" - min_date = values.min_study_date - max_date = values.max_study_date + min_date = self.min_study_date + max_date = self.max_study_date if min_date and not max_date: raise ValueError( f"min_study_date parameter was passed" @@ -175,7 +175,7 @@ def min_max_study_date_xor(cls, values): # noqa: B902, N805 f"max_study_date parameter was passed ({max_date}), " f"but min_study_date was not. Both need to be given" ) - return values + return self @model_validator(mode="after") def include_fields_check(self): @@ -204,7 +204,7 @@ def __str__(self): def as_parameters(self): """All non-empty query parameters. For use as url parameters""" - parameters = {x: y for x, y in self.dict().items() if y} + parameters = {x: y for x, y in self.model_dump().items() if y} if "min_study_date" in parameters: parameters["min_study_date"] = parameters[ @@ -332,6 +332,6 @@ def parse_attribs(element): return dataset -MintInstance.update_forward_refs() # enables pydantic validation -MintSeries.update_forward_refs() -MintStudy.update_forward_refs() +MintInstance.model_rebuild() # enables pydantic validation +MintSeries.model_rebuild() +MintStudy.model_rebuild() diff --git a/dicomtrolley/qido_rs.py b/dicomtrolley/qido_rs.py index ef8ad28..955aa53 100644 --- a/dicomtrolley/qido_rs.py +++ b/dicomtrolley/qido_rs.py @@ -132,7 +132,7 @@ def uri_search_params(self) -> Dict[str, Union[str, List[str]]]: } other_search_params = { key: val - for key, val in self.dict().items() + for key, val in self.model_dump().items() if key not in exclude_fields } @@ -182,7 +182,7 @@ def assert_parents_filled(a_hierarchy, value_dict): else: return assert_parents_filled(a_hierarchy, value_dict) - assert_parents_filled(order, self.dict()) + assert_parents_filled(order, self.model_dump()) return self @model_validator(mode="after") @@ -198,7 +198,7 @@ def assert_key_exists(values_in, query_level_in, missing_key_in): f"a QIDO-RS relational query" ) - values = self.dict() + values = self.model_dump() if query_level == QueryLevels.STUDY: pass # Fine. you can always look for some studies elif query_level == QueryLevels.SERIES: diff --git a/pyproject.toml b/pyproject.toml index d820cf9..032de22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dicomtrolley" -version = "3.3.0" +version = "4.0.0" description = "Retrieve medical images via WADO-URI, WADO-RS, QIDO-RS, MINT, RAD69 and DICOM-QR" authors = [{name="Sjoerd Kerkstra", email ="sjoerd.kerkstra@radboudumc.nl"}] readme = "README.md"