Skip to content

Commit e31d28d

Browse files
authored
bugfix: Add access by operator [] to pydantic.BaseModel base structures for keep backward compatybility (#579)
1 parent c0db122 commit e31d28d

4 files changed

Lines changed: 56 additions & 1 deletion

File tree

package/PartSegCore/algorithm_describe_base.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import warnings
55
from abc import ABC, ABCMeta, abstractmethod
66
from enum import Enum
7+
from functools import wraps
78

89
from nme import REGISTER, class_to_str
910
from pydantic import BaseModel as PydanticBaseModel
@@ -416,7 +417,29 @@ def get_default(cls):
416417
return cls(name=name, values=cls[name].get_default_values())
417418

418419

419-
class ROIExtractionProfile(BaseModel):
420+
class ROIExtractionProfileMeta(ModelMetaclass):
421+
def __new__(cls, name, bases, attrs, **kwargs):
422+
cls2 = super().__new__(cls, name, bases, attrs, **kwargs)
423+
424+
def allow_positional_args(func):
425+
@wraps(func)
426+
def _wraps(self, *args, **kwargs):
427+
if len(args) > 0:
428+
warnings.warn(
429+
"Positional arguments are deprecated, use keyword arguments instead",
430+
FutureWarning,
431+
stacklevel=2,
432+
)
433+
kwargs.update(dict(zip(self.__fields__, args)))
434+
return func(self, **kwargs)
435+
436+
return _wraps
437+
438+
cls2.__init__ = allow_positional_args(cls2.__init__)
439+
return cls2
440+
441+
442+
class ROIExtractionProfile(BaseModel, metaclass=ROIExtractionProfileMeta): # pylint: disable=E1139
420443
"""
421444
:ivar str ~.name: name for segmentation profile
422445
:ivar str ~.algorithm: Name of algorithm

package/PartSegCore/utils.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import inspect
33
import itertools
44
import typing
5+
import warnings
56
import weakref
67
from abc import ABC, abstractmethod
78
from collections import defaultdict
@@ -408,6 +409,12 @@ class BaseModel(PydanticBaseModel):
408409
class Config:
409410
extra = "forbid"
410411

412+
def __getitem__(self, item):
413+
warnings.warn("Access to attribute by [] is deprecated. Use . instead", FutureWarning, stacklevel=2)
414+
if item in self.__fields__:
415+
return getattr(self, item)
416+
raise KeyError(f"{item} not found in {self.__class__.__name__}")
417+
411418

412419
def iterate_names(base_name: str, data_dict, max_length=None) -> typing.Optional[str]:
413420
if base_name not in data_dict:

package/tests/test_PartSegCore/test_algorithm_describe_base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
AlgorithmDescribeBase,
1212
AlgorithmProperty,
1313
AlgorithmSelection,
14+
ROIExtractionProfile,
1415
_GetDescriptionClass,
1516
base_model_to_algorithm_property,
1617
)
@@ -318,3 +319,9 @@ def get_fields(cls) -> typing.List[typing.Union[AlgorithmProperty, str]]:
318319
assert len(SampleSubAlgorithm.get_fields_dict()) == 2
319320
with pytest.warns(FutureWarning, match=r"Class has __argument_class__ defined"):
320321
assert SampleSubAlgorithm.get_default_values() == {"name": 1, "name2": 3.0}
322+
323+
324+
def test_roi_extraction_profile():
325+
ROIExtractionProfile(name="aaa", algorithm="aaa", values={})
326+
with pytest.warns(FutureWarning):
327+
ROIExtractionProfile("aaa", "aaa", {})

package/tests/test_PartSegCore/test_utils.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from PartSegCore.json_hooks import PartSegEncoder, partseg_object_hook
88
from PartSegCore.utils import (
9+
BaseModel,
910
CallbackFun,
1011
CallbackMethod,
1112
EventedDict,
@@ -277,3 +278,20 @@ def test_iterate_names():
277278
for _ in range(85):
278279
input_set.add(iterate_names("a" * 5, input_set))
279280
assert iterate_names("a" * 5, input_set) is None
281+
282+
283+
def test_base_model_getitem():
284+
class SampleModel(BaseModel):
285+
a: int = 1
286+
b: float = 2.0
287+
c: str = "3"
288+
289+
ob = SampleModel()
290+
with pytest.warns(FutureWarning, match=r"Access to attribute by \[\] is deprecated\. Use \. instead"):
291+
assert ob["a"] == 1
292+
with pytest.warns(FutureWarning, match=r"Access to attribute by \[\] is deprecated\. Use \. instead"):
293+
assert ob["b"] == 2.0
294+
with pytest.warns(FutureWarning, match=r"Access to attribute by \[\] is deprecated\. Use \. instead"):
295+
assert ob["c"] == "3"
296+
with pytest.raises(KeyError):
297+
ob["d"] # pylint: disable=pointless-statement

0 commit comments

Comments
 (0)