Skip to content

Commit

Permalink
Speculatively evaluate refs to issue FutureWarning
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Sep 24, 2023
1 parent d39ff79 commit 545ad6a
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 7 deletions.
37 changes: 31 additions & 6 deletions param/parameterized.py
Original file line number Diff line number Diff line change
Expand Up @@ -1850,19 +1850,39 @@ def _setup_params(self_, **params):
f"keyword argument {name!r}"
)

pobj = objects[name]
if name not in objects or not pobj.allow_refs:
# i.e. if not desc it's setting an attribute in __dict__, not a Parameter
pobj = objects.get(name)
if pobj is None or not pobj.allow_refs:
# Until Parameter.allow_refs=True by default we have to
# speculatively evaluate a values to check whether they
# contain a reference and warn the user that the
# behavior may change in future.
if name not in self_.cls._param__private.explicit_no_refs:
try:
ref, _, _, _ = self_._resolve_ref(pobj, val)
except Exception:
ref = None
if ref:
warnings.warn(
f"Parameter {name!r} is being given a valid parameter "
f"reference {val} but is implicitly allow_ref=False. "
"In future references like these will be resolved to "
"their underlying value unless allow_ref=False. "
"Please explicitly set allow_ref on the Parameter "
"definition to declare whethe references should be "
"resolved or not.",
category=_ParamFutureWarning,
stacklevel=2,
)
setattr(self, name, val)
continue

# Resolve references
ref, ref_deps, val, is_async = self_._resolve_ref(pobj, val)
ref, ref_deps, resolved, is_async = self_._resolve_ref(pobj, val)
if ref is not None:
refs[name] = ref
deps[name] = ref_deps
if not is_async:
setattr(self, name, val)
setattr(self, name, resolved)
return refs, deps

def _setup_refs(self_, refs):
Expand Down Expand Up @@ -3473,6 +3493,9 @@ def __param_inheritance(mcs, param_name, param):
callables[slot] = default_val
else:
slot_values[slot] = default_val
elif slot == 'allow_refs' and not slot_values[slot]:
# Track Parameters that explicitly declared no refs
mcs._param__private.explicit_no_refs.append(param.name)

# Now set the actual slot values
for slot, value in slot_values.items():
Expand Down Expand Up @@ -3835,7 +3858,8 @@ class _ClassPrivate:
'renamed',
'params',
'initialized',
'signature'
'signature',
'explicit_no_refs',
]

def __init__(
Expand All @@ -3858,6 +3882,7 @@ def __init__(
self.params = {} if params is None else params
self.initialized = False
self.signature = None
self.explicit_no_refs = []

def __getstate__(self):
return {slot: getattr(self, slot) for slot in self.__slots__}
Expand Down
2 changes: 1 addition & 1 deletion param/reactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class Wrapper(Parameterized):
Simple wrapper to allow updating literal values easily.
"""

object = Parameter()
object = Parameter(allow_refs=False)


class Trigger(Parameterized):
Expand Down
10 changes: 10 additions & 0 deletions tests/testrefs.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import param
import pytest

from param.reactive import bind, reactive

Expand All @@ -11,6 +12,15 @@ class Parameters(param.Parameterized):
string_list = param.List(default=[], item_type=str, allow_refs=True, nested_refs=True)


def test_parameterized_warns_explicit_no_ref():
class ImplicitRefsParameters(param.Parameterized):
parameter = param.Parameter(default="string")

p = Parameters()
with pytest.raises(Exception) as e:
ImplicitRefsParameters(parameter=p.param.string)
assert "Parameter 'parameter' is being given a valid parameter reference <param.parameterized.String" in str(e)

def test_parameter_ref():
p = Parameters()
p2 = Parameters(string=p.param.string)
Expand Down

0 comments on commit 545ad6a

Please sign in to comment.