Ability to apply @param.depends to all (nested) Parameters of a class
I am developing a framework using param that aids engineers (who are not so savvy in Python) in defining 'constants' and associated metadata related to an engineering design. The engineer user would a simple python Parameterized class to organize these constants, and the framework goes through the class, and generates files for another tool that needs to consume these 'constants'.
The hierarchy is something like this:
# part of the framework
class Constant(param.Parametrized):
val = param.Integer()
metadata = param.String()
....
# written by engineer A
class ModuleConstants(param.Parametrized):
constX = param.ClassSelector(default=Constant(val = 10, metadata = "xxx"), class_=param.Parameterized)
constY = param.ClassSelector(default=Constant(val = 20), class_=param.Parameterized)
@param.depends('constX.val', watch=True)
def update_derived(self):
self.constY.val = self.constX.val * 2
# written by engineer B
class TopConstants(param.Parametrized):
const1 = param.ClassSelector(default=Constant(val = 2), class_=param.Parameterized)
const2 = param.ClassSelector(default=Constant(val = 4), class_=param.Parameterized)
child = param.ClassSelector(default=ModuleConstants(), class_=param.Parameterized)
@param.depends('const1.val', watch=True)
def update_derived(self):
self.child.constX.val = self.const1.val + 10
t = TopConstants()
t.const1 = 100
print(t.child.constY) # we expect (100 + 10) * 2 = 220
So there could be an unlimited number of nested Constant parameters here, and there are dependencies between the val parameter of the Constant parameters at different levels of hierarchy.
The problem we have been running into is that because we could have tens of these Constant parameters defined, it's very easy to forget for the engineers to include the list of dependencies in the @param.depends decorator. What I want is for update_derived() to be called if any of the Constant.val integers change, regardless of how many levels deep the Constant is nested. Performance is really not an issue here so I don't mind all the redundant operations this would cause. There are no circular dependencies to worry about.
I am only slightly better at python than these forgetful engineers myself, and I have been unsuccessful in working around this issue. I have tried:
-
.param.watch()(lower level API): doesn't support nested parameters? -
Dynamicparameters and the 'pull' model: doesn't supported nested parameters - Programmatically decorating
update_derived()with something like this in__init__()ofTopConstants, wherelist_of_all_paramsis obtained by manipulation of the.param.values()dict:
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.update_derived = param.depends(*list_of_all_params, watch=True)(self.update_derived)
This compiles but doesn't seem to register a watcher.
- Reactive Expressions: the documentation shows how to create an expression from two parameters, but not how to assign that back to another parameter
I am wondering if there is a solution here or if this (admittedly weird) use case is unsupported.
any pointers here folks?