Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

param.watch cannot handle parameters from Parameterized, unlike @param.depends #721

Open
phi6ias opened this issue Mar 20, 2023 · 2 comments
Labels
type-bug Bug report
Milestone

Comments

@phi6ias
Copy link

phi6ias commented Mar 20, 2023

ALL software version info

param 1.12.2

run in powershell prompt using panel serve running Bokeh server 2.4.3 on Tornado 6.2

run in VS Code jupyter notebook
Version: 1.76.2 (user setup)
Commit: ee2b180d582a7f601fa6ecfdad8d9fd269ab1884
Date: 2023-03-14T17:55:54.936Z
Electron: 19.1.11
Chromium: 102.0.5005.196
Node.js: 16.14.2
V8: 10.2.154.26-electron.0
OS: Windows_NT x64 10.0.19045
Sandboxed: No

Description of expected behavior and the observed behavior

When using the decorator @param.depends() it is possible to use a parameter from another class that is stored as a Parameterized instance in the current class, similar as described in this thread --> Holoviz Discourse: Shared Parameter?
However, trying the same with the lower-level self.param.watch() yields an error message.

In the example below, the decorated function works, while the param.watched throws an error on executing the cell comprising anB = Bclass(otherclass=anA). If the "param.watch'ed" function is changed to a decorated, then again everything works fine.

Am I missing something? Should the behaviour not be the same?

Complete, minimal, self-contained example code that reproduces the issue

import param

class Aclass(param.Parameterized):

    Aa = param.Integer(12)
    Ab = param.String("qwe")

    def __init__(self, **params):
        super().__init__(**params)

    def incrAa(self):
        self.Aa += 1

    def moreAb(self):
        self.Ab += self.Ab


class Bclass(param.Parameterized):

    theA = param.Parameterized()
    Ba = param.Integer(3)
    Bb = param.String("asd")

    def __init__(self, otherclass: param.Parameterized, **params):
        self.theA = otherclass
        super().__init__(**params)
        self.param.watch(self.moreAtoB, ['theA.Ab'], queued=True, precedence=1)

    @param.depends('theA.Aa', watch=True)
    def addAtoB(self):
        print("\nBclass - self.theA.Aa: ", self.theA.Aa)

    def moreAtoB(self, *events):
        print("\nBclass - self.theA.Ab: ", self.theA.Ab)


anA = Aclass()
anB = Bclass(otherclass=anA)

anA.incrAa()
anA.moreAb()

Stack traceback and/or browser JavaScript console output

Traceback (most recent call last):
File "c:...\envs...\lib\site-packages\IPython\core\interactiveshell.py", line 3433, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "C:\Users...\AppData\Local\Temp\ipykernel_4172\3905510252.py", line 1, in
anB = Bclass(otherclass=anA)
File "C:\Users...\AppData\Local\Temp\ipykernel_4172\3622346801.py", line 10, in init
self.param.watch(self.moreAtoB, ['theA.Ab'], queued=True, precedence=1)
File "c:...\envs...\lib\site-packages\param\parameterized.py", line 2450, in watch
return self_._watch(fn, parameter_names, what, onlychanged, queued, precedence)
File "c:...\envs...\lib\site-packages\param\parameterized.py", line 2457, in watch
self
._register_watcher('append', watcher, what)
File "c:...\envs...\lib\site-packages\param\parameterized.py", line 2392, in _register_watcher
raise ValueError("%s parameter was not found in list of "
ValueError: theA.Ab parameter was not found in list of parameters of class Bclass

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "c:...\envs...\lib\site-packages\IPython\core\interactiveshell.py", line 2052, in showtraceback
stb = self.InteractiveTB.structured_traceback(
File "c:...\envs...\lib\site-packages\IPython\core\ultratb.py", line 1112, in structured_traceback
return FormattedTB.structured_traceback(
File "c:...\envs...\lib\site-packages\IPython\core\ultratb.py", line 1006, in structured_traceback
return VerboseTB.structured_traceback(
File "c:...\envs...\lib\site-packages\IPython\core\ultratb.py", line 859, in structured_traceback
formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
File "c:...\envs...\lib\site-packages\IPython\core\ultratb.py", line 812, in format_exception_as_a_whole
frames.append(self.format_record(r))
File "c:...\envs...\lib\site-packages\IPython\core\ultratb.py", line 730, in format_record
result += ''.join(_format_traceback_lines(frame_info.lines, Colors, self.has_colors, lvals))
File "c:...\envs...\lib\site-packages\stack_data\utils.py", line 145, in cached_property_wrapper
value = obj.dict[self.func.name] = self.func(obj)
File "c:...\envs...\lib\site-packages\stack_data\core.py", line 734, in lines
pieces = self.included_pieces
File "c:...\envs...\lib\site-packages\stack_data\utils.py", line 145, in cached_property_wrapper
value = obj.dict[self.func.name] = self.func(obj)
File "c:...\envs...\lib\site-packages\stack_data\core.py", line 677, in included_pieces
scope_pieces = self.scope_pieces
File "c:...\envs...\lib\site-packages\stack_data\utils.py", line 145, in cached_property_wrapper
value = obj.dict[self.func.name] = self.func(obj)
File "c:...\envs...\lib\site-packages\stack_data\core.py", line 617, in scope_pieces
for piece in self.source.pieces
File "c:...\envs...\lib\site-packages\stack_data\utils.py", line 145, in cached_property_wrapper
value = obj.dict[self.func.name] = self.func(obj)
File "c:...\envs...\lib\site-packages\stack_data\core.py", line 106, in pieces
return list(self._clean_pieces())
File "c:...\envs...\lib\site-packages\stack_data\core.py", line 130, in _clean_pieces
raise AssertionError("Pieces mismatches: %s" % mismatches)
AssertionError: Pieces mismatches: [{680, 681}, {696, 695}, {708, 709}, {714, 715}]

Screenshots or screencasts of the bug in action

@maximlt
Copy link
Member

maximlt commented Apr 5, 2023

Thanks for the issue report, I can indeed reproduce it with this simplified version:

import param

class Other(param.Parameterized):
    
    x = param.Integer(1)

other = Other()
    
class P(param.Parameterized):

    other = param.Parameter(other)
    
    def __init__(self, **params):
        super().__init__(**params)
        self.param.watch(self.debug, ['other.x'])
    
    def debug(self, *events):
        print('DEBUG')

p = P()

Traceback:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[118], line 20
     17     def debug(self, *events):
     18         print('DEBUG')
---> 20 p = P()

Cell In[118], line 15, in P.__init__(self, **params)
     13 def __init__(self, **params):
     14     super().__init__(**params)
---> 15     self.param.watch(self.debug, ['other.x'])

File ~/dev/param/param/parameterized.py:2454, in Parameters.watch(self_, fn, parameter_names, what, onlychanged, queued, precedence)
   2450 if precedence < 0:
   2451     raise ValueError("User-defined watch callbacks must declare "
   2452                      "a positive precedence. Negative precedences "
   2453                      "are reserved for internal Watchers.")
-> 2454 return self_._watch(fn, parameter_names, what, onlychanged, queued, precedence)

File ~/dev/param/param/parameterized.py:2461, in Parameters._watch(self_, fn, parameter_names, what, onlychanged, queued, precedence)
   2457 parameter_names = tuple(parameter_names) if isinstance(parameter_names, list) else (parameter_names,)
   2458 watcher = Watcher(inst=self_.self, cls=self_.cls, fn=fn, mode='args',
   2459                   onlychanged=onlychanged, parameter_names=parameter_names,
   2460                   what=what, queued=queued, precedence=precedence)
-> 2461 self_._register_watcher('append', watcher, what)
   2462 return watcher

File ~/dev/param/param/parameterized.py:2396, in Parameters._register_watcher(self_, action, watcher, what)
   2394 for parameter_name in parameter_names:
   2395     if parameter_name not in self_.cls.param:
-> 2396         raise ValueError("%s parameter was not found in list of "
   2397                          "parameters of class %s" %
   2398                          (parameter_name, self_.cls.__name__))
   2400     if self_.self is not None and what == "value":
   2401         watchers = self_.self._param_watchers

ValueError: other.x parameter was not found in list of parameters of class P

@maximlt maximlt added the type-bug Bug report label Apr 5, 2023
@maximlt maximlt added this to the v2.x milestone Apr 5, 2023
@phi6ias
Copy link
Author

phi6ias commented Apr 5, 2023

Hi @maximlt

what I did find out in the meantime is that defining the watch on the objects param instead of the class param does work. I don't know if that is the intended behaviour, which is more for you to decide.

import param

class Other(param.Parameterized):
    
    x = param.Integer(1)

other = Other()
    
class P(param.Parameterized):

    other_ = param.Parameter(other)
    
    def __init__(self, **params):
        super().__init__(**params)
        self.other_.param.watch(self.debug, ['x'])
    
    def debug(self, *events):
        print('DEBUG')

p = P()
other.x=2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug Bug report
Projects
None yet
Development

No branches or pull requests

2 participants