Skip to content

Commit

Permalink
set the instance namespace earlier if needed
Browse files Browse the repository at this point in the history
  • Loading branch information
maximlt committed Jul 19, 2023
1 parent 86794ed commit 69eb0b3
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 15 deletions.
25 changes: 11 additions & 14 deletions param/parameterized.py
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,9 @@ def __set__(self, obj, val):
_old = self.default
self.default = val
else:
# When setting a Parameter before calling super.
if not isinstance(obj._param__private, _InstancePrivate):
obj._param__private = _InstancePrivate()
_old = obj._param__private.values.get(self.name, self.default)
obj._param__private.values[self.name] = val

Expand Down Expand Up @@ -3611,17 +3614,14 @@ class _ClassPrivate:
Whethe the class has been renamed by a super class
params: dict
Dict of parameter_name:parameter
values: dict
Dict of parameter_name:value, populated when a Parameter is set before
super().__init__ is called.
"""

__slots__ = [
'parameters_state',
'disable_instance_params',
'renamed',
'params',
'values',
'initialized',
]

def __init__(
Expand All @@ -3630,7 +3630,6 @@ def __init__(
disable_instance_params=False,
renamed=False,
params=None,
values=None,
):
if parameters_state is None:
parameters_state = {
Expand All @@ -3643,7 +3642,7 @@ def __init__(
self.disable_instance_params = disable_instance_params
self.renamed = renamed
self.params = {} if params is None else params
self.values = {} if values is None else params
self.initialized = False

def __getstate__(self):
return {slot: getattr(self, slot) for slot in self.__slots__}
Expand Down Expand Up @@ -3760,14 +3759,12 @@ class Foo(Parameterized):
def __init__(self, **params):
global object_count

# Setting a Parameter in an __init__ block before calling super().__init__
# fills `values` on the class private namespace, that has to be passed
# to the instance private namespace. `values` on the class is cleared
# as it's only meant to be transient data.
values = type(self)._param__private.values
cvalues = values.copy()
values.clear()
self._param__private = _InstancePrivate(values=cvalues)
# Setting a Parameter value in an __init__ block before calling
# Parameterized.__init__ (via super() generally) already sets the
# _InstancePrivate namespace over the _ClassPrivate namespace
# (see Parameter.__set__) so we shouldn't override it here.
if not isinstance(self._param__private, _InstancePrivate):
self._param__private = _InstancePrivate()
self._param_watchers = {}

# Skip generating a custom instance name when a class in the hierarchy
Expand Down
41 changes: 40 additions & 1 deletion tests/testparameterizedobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,46 @@ def cb(self):

assert p.x == 1
assert count == 0
assert not P._param__private.values

def test_instantiation_set_before_super_contrived(self):
# https://github.com/holoviz/param/pull/790#discussion_r1263483293
class P(param.Parameterized):

value = param.String(default="A")

def __init__(self, depth=0):
self.value = 'B'
if depth < 2:
self.sub = P(depth+1)
super().__init__()

p = P()

assert p.value == 'B'
assert p.sub.value == 'B'

def test_instantiation_set_before_super_subclass(self):
# Inspired by a HoloViews use case (GenericElementPlot, GenericOverlayPlot)
class A(param.Parameterized):

def __init__(self, batched=False, **params):
self.batched = batched
super().__init__(**params)

class B(A):

batched = param.Boolean()

def __init__(self, batched=True, **params):
super().__init__(batched=batched, **params)

a = A()
assert a.batched is False

# When b is instantiated the `batched` Parameter of B is set before
# Parameterized.__init__ is called.
b = B()
assert b.batched is True

@pytest.mark.xfail(
raises=AttributeError,
Expand Down

0 comments on commit 69eb0b3

Please sign in to comment.