diff --git a/doc/reference/deprecations.md b/doc/reference/deprecations.md index 3935c34d..2e56eac7 100644 --- a/doc/reference/deprecations.md +++ b/doc/reference/deprecations.md @@ -4,6 +4,7 @@ List of currently deprecated APIs: | Warning | Description | |-|-| +| `ParamPendingDeprecationWarning` since `2.3.0` | `param.parameterized` module / Setting a parameter value before full instance initialization | | `ParamFutureWarning` since `2.2.0`, `ParamDeprecationWarning` since `2.0.0` | Parameter slots / `List._class`: use instead `item_type` | | `ParamFutureWarning` since `2.2.0`, `ParamDeprecationWarning` since `2.0.0` | Parameter slots / `Number.set_hook`: no replacement | | `ParamFutureWarning` since `2.2.0`, `ParamDeprecationWarning` since `2.0.0` | `param.__init__` module / `param.produce_value`: no replacement | diff --git a/param/parameterized.py b/param/parameterized.py index 48855b7d..4ee1fcd2 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -39,6 +39,7 @@ from ._utils import ( DEFAULT_SIGNATURE, ParamFutureWarning as _ParamFutureWarning, + ParamPendingDeprecationWarning as _ParamPendingDeprecationWarning, Skip, _deprecated, _deprecate_positional_args, @@ -1574,6 +1575,15 @@ def __set__(self, obj, val): else: # When setting a Parameter before calling super. if not isinstance(obj._param__private, _InstancePrivate): + warnings.warn( + f"Setting the Parameter {self.name!r} to {val!r} before " + f"the Parameterized class {type(obj).__name__!r} is fully " + "instantiated is deprecated and will raise an error in " + "a future version. Ensure the value is set after calling " + "`super().__init__(**params)` in the constructor.", + category=_ParamPendingDeprecationWarning, + stacklevel=4, + ) obj._param__private = _InstancePrivate( explicit_no_refs=type(obj)._param__private.explicit_no_refs ) diff --git a/tests/testdeprecations.py b/tests/testdeprecations.py index a4e4162a..70b3b9ab 100644 --- a/tests/testdeprecations.py +++ b/tests/testdeprecations.py @@ -97,6 +97,16 @@ def test_deprecate_all_equal(self): with pytest.raises(param._utils.ParamFutureWarning): param.parameterized.all_equal(1, 1) + def test_deprecate_setting_parameter_before_init(self): + class P(param.Parameterized): + x = param.Parameter() + + def __init__(self, **params): + self.x = 10 + super().__init__(**params) + with pytest.raises(param._utils.ParamPendingDeprecationWarning): + P() + class TestDeprecateParameters: diff --git a/tests/testparameterizedobject.py b/tests/testparameterizedobject.py index 9bb85d00..1061eda2 100644 --- a/tests/testparameterizedobject.py +++ b/tests/testparameterizedobject.py @@ -2,6 +2,7 @@ import inspect import re import unittest +import warnings import param import numbergen @@ -400,7 +401,9 @@ def cb(self): nonlocal count count += 1 - p = P() + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', message='Setting the Parameter', category=param._utils.ParamPendingDeprecationWarning) + p = P() assert p.x == 1 assert count == 0 @@ -417,7 +420,9 @@ def __init__(self, depth=0): self.sub = P(depth+1) super().__init__() - p = P() + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', message='Setting the Parameter', category=param._utils.ParamPendingDeprecationWarning) + p = P() assert p.value == 'B' assert p.sub.value == 'B' @@ -442,7 +447,9 @@ def __init__(self, batched=True, **params): # When b is instantiated the `batched` Parameter of B is set before # Parameterized.__init__ is called. - b = B() + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', message='Setting the Parameter', category=param._utils.ParamPendingDeprecationWarning) + b = B() assert b.batched is True def test_instantiation_param_objects_before_super_subclass(self):