Skip to content

Commit 0458a79

Browse files
committed
fix(frozen_dataclass): improve type hints and simplify doctests in implementation file
1 parent 816cec5 commit 0458a79

File tree

1 file changed

+33
-80
lines changed

1 file changed

+33
-80
lines changed

src/libtmux/_internal/frozen_dataclass_sealable.py

Lines changed: 33 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -27,79 +27,6 @@
2727
- Private attributes
2828
- Deep copying on sealing
2929
- Slots
30-
31-
Example:
32-
```python
33-
from dataclasses import dataclass, field
34-
from typing import Optional, List
35-
from libtmux._internal.frozen_dataclass_sealable import (
36-
frozen_dataclass_sealable,
37-
mutable_during_init,
38-
)
39-
40-
# Mutable base class
41-
@dataclass
42-
class BasePane:
43-
pane_id: str
44-
width: int
45-
height: int
46-
47-
def resize(self, width: int, height: int) -> None:
48-
self.width = width
49-
self.height = height
50-
51-
# Frozen subclass with field-level mutability
52-
@frozen_dataclass_sealable
53-
class PaneSnapshot(BasePane):
54-
# Regular immutable fields
55-
captured_content: List[str] = field(default_factory=list)
56-
57-
# This field can be modified during initialization
58-
parent_window: Optional['WindowSnapshot'] = field(
59-
default=None,
60-
metadata={"mutable_during_init": True}
61-
)
62-
63-
# Override method to block mutation
64-
def resize(self, width: int, height: int) -> None:
65-
raise NotImplementedError("Snapshot is immutable. resize() not allowed.")
66-
67-
# Frozen class with circular reference
68-
@frozen_dataclass_sealable
69-
class WindowSnapshot:
70-
window_id: str
71-
name: str
72-
73-
# This field can be modified during initialization
74-
panes: List[PaneSnapshot] = field(
75-
default_factory=list,
76-
metadata={"mutable_during_init": True}
77-
)
78-
79-
# Create objects with circular references
80-
window = WindowSnapshot(window_id="win1", name="Main") # Not sealed yet
81-
pane1 = PaneSnapshot(pane_id="1", width=80, height=24) # Not sealed yet
82-
pane2 = PaneSnapshot(pane_id="2", width=80, height=24) # Not sealed yet
83-
84-
# Establish circular references
85-
window.panes.append(pane1)
86-
window.panes.append(pane2)
87-
pane1.parent_window = window
88-
pane2.parent_window = window
89-
90-
# Seal all objects
91-
window.seal()
92-
pane1.seal()
93-
pane2.seal()
94-
95-
# Now all objects are fully immutable
96-
```
97-
98-
Implementation Notes:
99-
- Uses a custom `__setattr__` to enforce immutability rules
100-
- Internal attributes (starting with '_') can still be modified
101-
- Known limitation: the contents of mutable objects (lists, dicts) can still
102-
be modified even after sealing
10330
"""
10431

10532
from __future__ import annotations
@@ -199,23 +126,49 @@ def is_sealable(cls_or_obj: t.Any) -> bool:
199126
"""Check if a class or object is sealable.
200127
201128
Args:
202-
cls_or_obj: A class or object to check
129+
cls_or_obj: The class or object to check
203130
204-
Returns:
131+
Returns
132+
-------
205133
True if the class or object is sealable, False otherwise
134+
135+
Examples:
136+
>>> from dataclasses import dataclass
137+
>>> from libtmux._internal.frozen_dataclass_sealable import (
138+
... frozen_dataclass_sealable, is_sealable
139+
... )
140+
141+
>>> # Regular class is not sealable
142+
>>> @dataclass
143+
... class Regular:
144+
... value: int
145+
146+
>>> is_sealable(Regular)
147+
False
148+
>>> regular = Regular(value=42)
149+
>>> is_sealable(regular)
150+
False
151+
152+
>>> # Non-class objects are not sealable
153+
>>> is_sealable("string")
154+
False
155+
>>> is_sealable(42)
156+
False
157+
>>> is_sealable(None)
158+
False
206159
"""
207-
# Check if it's a class
160+
# If it's a class, check if it has a seal method
208161
if isinstance(cls_or_obj, type):
209-
return hasattr(cls_or_obj, "seal") and callable(cls_or_obj.seal)
162+
return hasattr(cls_or_obj, "seal") and callable(getattr(cls_or_obj, "seal"))
210163

211-
# It's an object instance
212-
return hasattr(cls_or_obj, "seal") and callable(cls_or_obj.seal)
164+
# If it's an instance, check if it has a seal method
165+
return hasattr(cls_or_obj, "seal") and callable(getattr(cls_or_obj, "seal"))
213166

214167

215168
@dataclass_transform(frozen_default=True)
216169
def frozen_dataclass_sealable(
217170
cls: type | None = None, /, **kwargs: t.Any
218-
) -> t.Any: # mypy doesn't handle complex return types well here
171+
) -> t.Callable[[type], type] | type:
219172
"""Create a dataclass that is immutable, with field-level mutability control.
220173
221174
Enhances the standard dataclass with:

0 commit comments

Comments
 (0)