holoviz / panel

Panel: The powerful data exploration & web app framework for Python
https://panel.holoviz.org
BSD 3-Clause "New" or "Revised" License
4.74k stars 516 forks source link

Mutable Defaults in Class Declarations #3267

Open Finnem opened 2 years ago

Finnem commented 2 years ago

Some of panels classes use mutable defaults in their declarations. This can lead to some unexpected behaviour. For me this happened when updating the scripts dictionary of ReactiveHTML during class initialization.

Description of expected behavior and the observed behavior

Exptected: ReactiveHTML._scripts behaves like a instance-bound attribute. Observed: ReactiveHTML._scripts is shared between all ReactiveHTML instances.

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

class Test(ReactiveHTML):
    def __init__(self, x, **params):
        self._scripts.update({
            "potato" : f"{x}"
        })
a = Test(1)
print(f"a.scripts: {a._scripts}")
b = Test(2)
print(f"a.scripts: {a._scripts}")

Stack traceback and/or browser JavaScript console output

a.scripts: {'potato': '1'} a.scripts: {'potato': '2'}

philippjfr commented 2 years ago

The scripts are parsed by the ReactiveHTMLMetaclass, i.e. at class definition time, so should not really be mutated at the instance level. Therefore I'm leaning towards simply replacing _scripts with a frozendict in the metaclass.