adds warning message when super().__init__() is not called at the start of the constructor

This commit is contained in:
Mose Müller 2023-12-06 08:42:48 +01:00
parent e8a0a7c000
commit a97a55712e
3 changed files with 34 additions and 5 deletions

View File

@ -22,10 +22,15 @@ class Observable(ObservableObject):
self.__dict__[name] = self._initialise_new_objects(name, value) self.__dict__[name] = self._initialise_new_objects(name, value)
def __setattr__(self, name: str, value: Any) -> None: def __setattr__(self, name: str, value: Any) -> None:
if hasattr(self, "_observers"): if not hasattr(self, "_observers") and name != "_observers":
self._remove_observer_if_observable(name) logger.warning(
value = self._initialise_new_objects(name, value) "Ensure that super().__init__() is called at the start of the '%s' "
self._notify_change_start(name) "constructor! Failing to do so may lead to unexpected behavior.",
type(self).__name__,
)
self._observers = {}
value = self._handle_observable_setattr(name, value)
super().__setattr__(name, value) super().__setattr__(name, value)
@ -42,6 +47,15 @@ class Observable(ObservableObject):
return value return value
def _handle_observable_setattr(self, name: str, value: Any) -> Any:
if name == "_observers":
return value
self._remove_observer_if_observable(name)
value = self._initialise_new_objects(name, value)
self._notify_change_start(name)
return value
def _remove_observer_if_observable(self, name: str) -> None: def _remove_observer_if_observable(self, name: str) -> None:
if not is_property_attribute(self, name): if not is_property_attribute(self, name):
current_value = getattr(self, name, None) current_value = getattr(self, name, None)

View File

@ -13,6 +13,7 @@ class ObservableObject(ABC):
_dict_mapping: ClassVar[dict[int, "_ObservableDict"]] = {} _dict_mapping: ClassVar[dict[int, "_ObservableDict"]] = {}
def __init__(self) -> None: def __init__(self) -> None:
if not hasattr(self, "_observers"):
self._observers: dict[str, list["ObservableObject | Observer"]] = {} self._observers: dict[str, list["ObservableObject | Observer"]] = {}
def add_observer( def add_observer(

View File

@ -13,6 +13,20 @@ class MyObserver(Observer):
logger.info("'%s' changed to '%s'", full_access_path, value) logger.info("'%s' changed to '%s'", full_access_path, value)
def test_constructor_error_message(caplog: pytest.LogCaptureFixture) -> None:
class MyObservable(Observable):
def __init__(self) -> None:
self.attr = 1
super().__init__()
MyObservable()
assert (
"Ensure that super().__init__() is called at the start of the 'MyObservable' "
"constructor! Failing to do so may lead to unexpected behavior." in caplog.text
)
def test_simple_class_attribute(caplog: pytest.LogCaptureFixture) -> None: def test_simple_class_attribute(caplog: pytest.LogCaptureFixture) -> None:
class MyObservable(Observable): class MyObservable(Observable):
int_attribute = 10 int_attribute = 10