holoviz / param

Param: Make your Python code clearer and more reliable by declaring Parameters
https://param.holoviz.org
BSD 3-Clause "New" or "Revised" License
427 stars 73 forks source link

Ability to apply @param.depends to all (nested) Parameters of a class #910

Open samimia-swks opened 7 months ago

samimia-swks commented 7 months ago

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:

I am wondering if there is a solution here or if this (admittedly weird) use case is unsupported.

samimia-swks commented 7 months ago

any pointers here folks?