holoviz / holoviz/param

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

Open
#910 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
521
Forks
86
Avg merge
1d 13h
Merged PRs (30d)
35

Description

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:
```python
# 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?
- `Dynamic` parameters and the 'pull' model: doesn't supported nested parameters
- Programmatically decorating `update_derived()` with something like this in `__init__()` of `TopConstants`, where `list_of_all_params` is obtained by manipulation of the `.param.values()` dict:
```python
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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.