|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations |
|
|
|
from typing import TypeVar |
|
|
|
import attrs |
|
|
|
T = TypeVar("T") |
|
|
|
|
|
def _is_attrs_instance(obj: object) -> bool: |
|
""" |
|
Helper function to check if an object is an instance of an attrs-defined class. |
|
|
|
Args: |
|
obj: The object to check. |
|
|
|
Returns: |
|
bool: True if the object is an instance of an attrs-defined class, False otherwise. |
|
""" |
|
return hasattr(obj, "__attrs_attrs__") |
|
|
|
|
|
def make_freezable(cls: T) -> T: |
|
""" |
|
A decorator that adds the capability to freeze instances of an attrs-defined class. |
|
|
|
NOTE: This requires the wrapped attrs to be defined with attrs.define(slots=False) because we need |
|
to hack on a "_is_frozen" attribute. |
|
|
|
This decorator enhances an attrs-defined class with the ability to be "frozen" at runtime. |
|
Once an instance is frozen, its attributes cannot be changed. It also recursively freezes |
|
any attrs-defined objects that are attributes of the class. |
|
|
|
Usage: |
|
@make_freezable |
|
@attrs.define(slots=False) |
|
class MyClass: |
|
attribute1: int |
|
attribute2: str |
|
|
|
obj = MyClass(1, 'a') |
|
obj.freeze() # Freeze the instance |
|
obj.attribute1 = 2 # Raises AttributeError |
|
|
|
Args: |
|
cls: The class to be decorated. |
|
|
|
Returns: |
|
The decorated class with added freezing capability. |
|
""" |
|
|
|
if not hasattr(cls, "__dict__"): |
|
raise TypeError( |
|
"make_freezable cannot be used with classes that do not define __dict__. Make sure that the wrapped " |
|
"class was defined with `@attrs.define(slots=False)`" |
|
) |
|
|
|
original_setattr = cls.__setattr__ |
|
|
|
def setattr_override(self, key, value) -> None: |
|
""" |
|
Override __setattr__ to allow modifications during initialization |
|
and prevent modifications once the instance is frozen. |
|
""" |
|
if hasattr(self, "_is_frozen") and self._is_frozen and key != "_is_frozen": |
|
raise AttributeError("Cannot modify frozen instance") |
|
original_setattr(self, key, value) |
|
|
|
cls.__setattr__ = setattr_override |
|
|
|
def freeze(self: object) -> None: |
|
""" |
|
Freeze the instance and all its attrs-defined attributes. |
|
""" |
|
for _, value in attrs.asdict(self, recurse=False).items(): |
|
if _is_attrs_instance(value) and hasattr(value, "freeze"): |
|
value.freeze() |
|
self._is_frozen = True |
|
|
|
cls.freeze = freeze |
|
|
|
return cls |
|
|
|
|
|
@make_freezable |
|
@attrs.define(slots=False) |
|
class DDPConfig: |
|
|
|
find_unused_parameters: bool = False |
|
|
|
static_graph: bool = True |
|
|
|
broadcast_buffers: bool = True |
|
|