From 0dd009441414ae3c37030b7b4d2fbcacea67413e Mon Sep 17 00:00:00 2001 From: Savio Mak <96855131+Glinte@users.noreply.github.com> Date: Fri, 8 Aug 2025 13:55:04 +0800 Subject: [PATCH] Allow deletion of frozen fields --- semimutable/__init__.py | 11 +++++++++++ tests/test_semimutable_dataclass.py | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/semimutable/__init__.py b/semimutable/__init__.py index 57078d2..2dd0bbd 100644 --- a/semimutable/__init__.py +++ b/semimutable/__init__.py @@ -94,6 +94,17 @@ def __set__(self, instance: object, value: T) -> None: setattr(instance, self._private_name, value) + def __delete__(self, instance: object) -> None: + try: + delattr(instance, self._private_name) + except AttributeError: + pass + public_name = self._private_name[len(FROZEN_PREFIX) :] + try: + instance.__dict__.pop(public_name, None) + except AttributeError: + pass + error = RuntimeError( "This field is created via field(frozen=True) but the @semimutable.dataclass decorator is not used on the dataclass. " diff --git a/tests/test_semimutable_dataclass.py b/tests/test_semimutable_dataclass.py index 52e0087..33b093c 100644 --- a/tests/test_semimutable_dataclass.py +++ b/tests/test_semimutable_dataclass.py @@ -27,6 +27,17 @@ class Sm: sm.y = 42 +def test_frozen_field_can_be_deleted(): + @dataclass(slots=True) + class Sm: + x: int = field(frozen=True) + + sm = Sm(x=1) + del sm.x + with pytest.raises(AttributeError): + sm.x + + def test_plain_dataclass_is_refused(): with pytest.raises(RuntimeError):