Skip to content

Commit e32b1b4

Browse files
committed
refactor(frozen_dataclass): Enhance implementation for clarity and type safety
1 parent b96c9ce commit e32b1b4

File tree

1 file changed

+11
-5
lines changed

1 file changed

+11
-5
lines changed

src/libtmux/_internal/frozen_dataclass.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from __future__ import annotations
99

1010
import dataclasses
11+
import functools
1112
import typing as t
1213

1314
from typing_extensions import dataclass_transform
@@ -38,10 +39,15 @@ def frozen_dataclass(cls: type[_T]) -> type[_T]:
3839
# A. Convert to a dataclass with frozen=False
3940
cls = dataclasses.dataclass(cls)
4041

42+
# B. Explicitly annotate and initialize the `_frozen` attribute for static analysis
43+
cls.__annotations__["_frozen"] = bool
44+
setattr(cls, "_frozen", False)
45+
4146
# Save the original __init__ to use in our hooks
4247
original_init = cls.__init__
4348

4449
# C. Create a new __init__ that will call the original and then set _frozen flag
50+
@functools.wraps(original_init)
4551
def __init__(self: t.Any, *args: t.Any, **kwargs: t.Any) -> None:
4652
# Call the original __init__
4753
original_init(self, *args, **kwargs)
@@ -52,6 +58,7 @@ def __init__(self: t.Any, *args: t.Any, **kwargs: t.Any) -> None:
5258
def __setattr__(self: t.Any, name: str, value: t.Any) -> None:
5359
# If _frozen is set and we're trying to set a field, block it
5460
if getattr(self, "_frozen", False) and not name.startswith("_"):
61+
# Allow mutation of private (_-prefixed) attributes after initialization
5562
error_msg = f"{cls.__name__} is immutable: cannot modify field '{name}'"
5663
raise AttributeError(error_msg)
5764

@@ -68,10 +75,9 @@ def __delattr__(self: t.Any, name: str) -> None:
6875
# Allow the deletion
6976
object.__delattr__(self, name)
7077

71-
# F. Inject into the class
72-
# Add type ignore directives to silence mypy "Cannot assign to a method" errors
73-
cls.__init__ = __init__ # type: ignore[method-assign]
74-
cls.__setattr__ = __setattr__ # type: ignore[method-assign]
75-
cls.__delattr__ = __delattr__ # type: ignore[method-assign]
78+
# F. Inject into the class using setattr to avoid explicit mypy type ignores
79+
setattr(cls, "__init__", __init__)
80+
setattr(cls, "__setattr__", __setattr__)
81+
setattr(cls, "__delattr__", __delattr__)
7682

7783
return cls

0 commit comments

Comments
 (0)