diff --git a/param/parameterized.py b/param/parameterized.py index 398deb2c..575f0c58 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -2215,6 +2215,14 @@ def objects(self_, instance=True): instance parameters to be returned by setting instance='existing'. """ + if self_.self is not None and not self_.self._param__private.initialized and instance is True: + raise RuntimeError( + 'Cannot look up instantiated Parameter objects until the Parameterized instance ' + 'has been fully initialized. Ensure you have called super().__init__(**params) ' + 'in your Parameterized constructor before trying to access instance ' + 'parameter objects.' + ) + cls = self_.cls # We cache the parameters because this method is called often, # and parameters are rarely added (and cannot be deleted) @@ -2252,6 +2260,13 @@ def trigger(self_, *param_names): changed for a Parameter of type Event, setting it to True so that it is clear which Event parameter has been triggered. """ + if self_.self is not None and not self_.self._param__private.initialized: + raise RuntimeError( + 'Triggering watchers on a partially initialized Parameterized instance ' + 'is not supported. Ensure you have called super().__init__(**params) in ' + 'the Parameterized instance constructor before trying to set up a watcher.' + ) + trigger_params = [p for p in self_.self_or_cls.param if hasattr(self_.self_or_cls.param[p], '_autotrigger_value')] triggers = {p:self_.self_or_cls.param[p]._autotrigger_value @@ -2695,6 +2710,13 @@ def _spec_to_obj(self_, spec, dynamic=True, intermediate=True): return deps, dynamic_deps def _register_watcher(self_, action, watcher, what='value'): + if self_.self is not None and not self_.self._param__private.initialized: + raise RuntimeError( + '(Un)registering a watcher on a partially initialized Parameterized instance ' + 'is not supported. Ensure you have called super().__init__(**) in ' + 'the Parameterized instance constructor before trying to set up a watcher.' + ) + parameter_names = watcher.parameter_names for parameter_name in parameter_names: if parameter_name not in self_.cls.param: diff --git a/tests/testparameterizedobject.py b/tests/testparameterizedobject.py index 1a62719e..7ebae0ff 100644 --- a/tests/testparameterizedobject.py +++ b/tests/testparameterizedobject.py @@ -495,6 +495,48 @@ def __init__(self, x=1): with pytest.raises(TypeError, match="Read-only parameter 'x' cannot be modified"): P() + def test_param_error_unsafe_ops_before_initialized(self): + class P(param.Parameterized): + + x = param.Parameter() + + def __init__(self, **params): + with pytest.raises( + RuntimeError, + match=re.escape( + 'Cannot look up instantiated Parameter objects until the Parameterized instance ' + 'has been fully initialized. Ensure you have called super().__init__(**params) ' + 'in your Parameterized constructor before trying to access instance ' + 'parameter objects.' + ) + ): + self.param.objects() + + with pytest.raises( + RuntimeError, + match=re.escape( + 'Triggering watchers on a partially initialized Parameterized instance ' + 'is not supported. Ensure you have called super().__init__(**params) in ' + 'the Parameterized instance constructor before trying to set up a watcher.' + ) + ): + self.param.trigger('x') + + with pytest.raises( + RuntimeError, + match=re.escape( + '(Un)registering a watcher on a partially initialized Parameterized instance ' + 'is not supported. Ensure you have called super().__init__(**) in ' + 'the Parameterized instance constructor before trying to set up a watcher.' + ) + ): + self.param.watch(print, 'x') + + self.param.objects(instance=False) + super().__init__(**params) + + P() + def test_parameter_constant_iadd_allowed(self): # Testing https://github.com/holoviz/param/pull/400 class P(param.Parameterized):