Skip to content

Commit d097488

Browse files
authored
Ensure explicitly set sizing options are respected (#8680)
1 parent 6fecfd3 commit d097488

6 files changed

Lines changed: 93 additions & 7 deletions

File tree

panel/io/document.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ def write_events(
310310
if state._unblocked(doc):
311311
_dispatch_write_task(doc, _run_write_futures, doc)
312312
else:
313-
doc.add_next_tick_callback(partial(_run_write_futures, doc))
313+
doc.add_next_tick_callback(partial(_run_write_futures, doc)) # type: ignore[arg-type]
314314

315315
def schedule_write_events(
316316
doc: Document,

panel/io/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ async def wrapped(*args, **kw):
254254
state._handle_exception(e)
255255
if unlock:
256256
wrapped.nolock = True # type: ignore
257-
state.curdoc.add_next_tick_callback(wrapped)
257+
state.curdoc.add_next_tick_callback(wrapped) # type: ignore[arg-type]
258258

259259
param.parameterized.async_executor = async_execute
260260

panel/layout/base.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,14 @@ def _compute_sizing_mode(self, children, props):
9999

100100
width_expanded = smode in ('stretch_width', 'stretch_both', 'scale_width', 'scale_both')
101101
height_expanded = smode in ('stretch_height', 'stretch_both', 'scale_height', 'scale_both')
102-
expand_width |= width_expanded
103-
expand_height |= height_expanded
102+
# Only inherit a child's responsiveness along an axis if the
103+
# corresponding policy was not explicitly set to a non-max
104+
# value. An explicit width_policy/height_policy of "max"
105+
# already forces expansion via the initializer above.
106+
if not getattr(self, '_explicit_width_policy', False):
107+
expand_width |= width_expanded
108+
if not getattr(self, '_explicit_height_policy', False):
109+
expand_height |= height_expanded
104110
if width_expanded:
105111
width = child.min_width
106112
else:
@@ -135,14 +141,18 @@ def _compute_sizing_mode(self, children, props):
135141
height += margin*2
136142
heights.append(height)
137143

138-
# Infer new sizing mode based on children
144+
# Infer new sizing mode based on children unless the user
145+
# explicitly supplied a sizing_mode, in which case that setting is
146+
# honored rather than inherited from the children.
139147
mode = 'scale' if scale else 'stretch'
140148
if self._direction == 'horizontal':
141149
allow_height_scale = all_expand_height
142150
else:
143151
allow_height_scale = True
144152

145-
if expand_width and expand_height and not self.width and not self.height:
153+
if getattr(self, '_explicit_sizing_mode', False):
154+
pass
155+
elif expand_width and expand_height and not self.width and not self.height:
146156
if allow_height_scale or 'both' in (sizing_mode or ''):
147157
sizing_mode = f'{mode}_both'
148158
else:

panel/tests/layout/test_base.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,3 +681,55 @@ def test_compute_sizing_mode_stretch_margin_four_tuple(dim, document, comm):
681681
new_props = col._compute_sizing_mode(root.children, {'margin': margin})
682682

683683
assert new_props == {f'min_{dim}': 115, 'sizing_mode': f'stretch_{dim}'}
684+
685+
def test_compute_sizing_mode_explicit_sizing_mode_not_inherited(document, comm):
686+
md = Markdown('foo', sizing_mode='stretch_width')
687+
col = Column(md, sizing_mode='fixed')
688+
689+
root = col.get_root(document, comm=comm)
690+
691+
assert root.sizing_mode == 'fixed'
692+
693+
def test_compute_sizing_mode_explicit_width_policy_max_still_inherited(document, comm):
694+
md = Markdown('foo', sizing_mode='stretch_width')
695+
col = Column(md, width_policy='max')
696+
697+
root = col.get_root(document, comm=comm)
698+
699+
assert root.sizing_mode == 'stretch_width'
700+
assert root.width_policy == 'max'
701+
702+
def test_compute_sizing_mode_explicit_width_policy_not_max_not_inherited(document, comm):
703+
md = Markdown('foo', sizing_mode='stretch_width')
704+
col = Column(md, width_policy='fixed')
705+
706+
root = col.get_root(document, comm=comm)
707+
708+
assert root.sizing_mode is None
709+
assert root.width_policy == 'fixed'
710+
711+
def test_compute_sizing_mode_explicit_height_policy_not_max_not_inherited(document, comm):
712+
md = Markdown('foo', sizing_mode='stretch_height')
713+
col = Column(md, height_policy='fixed')
714+
715+
root = col.get_root(document, comm=comm)
716+
717+
assert root.sizing_mode is None
718+
assert root.height_policy == 'fixed'
719+
720+
def test_compute_sizing_mode_inherited_without_explicit_sizing(document, comm):
721+
md = Markdown('foo', sizing_mode='stretch_width')
722+
col = Column(md)
723+
724+
root = col.get_root(document, comm=comm)
725+
726+
assert root.sizing_mode == 'stretch_width'
727+
728+
def test_compute_sizing_mode_dynamic_sizing_mode_not_inherited(document, comm):
729+
md = Markdown('foo', sizing_mode='stretch_width')
730+
col = Column(md)
731+
col.sizing_mode = 'fixed'
732+
733+
root = col.get_root(document, comm=comm)
734+
735+
assert root.sizing_mode == 'fixed'

panel/tests/test_interact.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ def test(a):
233233
assert new_pane._models[column.ref['id']][0] is new_div
234234

235235
interact_pane._cleanup(column)
236-
assert len(interact_pane._internal_callbacks) == 5
236+
assert len(interact_pane._internal_callbacks) == 6
237237

238238

239239
def test_interact_throttled():

panel/viewable.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,13 @@ class Layoutable(param.Parameterized):
262262
__abstract = True
263263

264264
def __init__(self, **params):
265+
# Track which sizing parameters were explicitly supplied by the
266+
# user (as opposed to their defaults or values inferred from
267+
# children). This is computed before the config driven defaults
268+
# below are applied so config defaults are not treated as explicit.
269+
self._explicit_sizing_mode = params.get('sizing_mode') is not None
270+
self._explicit_width_policy = params.get('width_policy') not in (None, 'auto')
271+
self._explicit_height_policy = params.get('height_policy') not in (None, 'auto')
265272
sizing_mode = params.get('sizing_mode')
266273
if (sizing_mode in ('stretch_width', 'scale_width', 'stretch_both', 'scale_both') and
267274
params.get('width') is not None):
@@ -313,6 +320,23 @@ def __init__(self, **params):
313320
if 'design' not in params and self.param.design.default is None:
314321
params['design'] = config.design
315322
super().__init__(**params)
323+
# Track dynamic (post-init) updates to the sizing parameters so
324+
# that explicitly set values are honored rather than being
325+
# overridden by values inferred from a layout's children. Child
326+
# inference writes to the underlying model rather than the
327+
# parameter, so it does not trigger this watcher.
328+
watcher = self.param.watch(
329+
self._update_explicit_sizing,
330+
['sizing_mode', 'width_policy', 'height_policy']
331+
)
332+
if not hasattr(self, '_internal_callbacks'):
333+
self._internal_callbacks = []
334+
self._internal_callbacks.append(watcher)
335+
336+
def _update_explicit_sizing(self, *events):
337+
for event in events:
338+
explicit = event.new is not None if event.name == 'sizing_mode' else event.new != 'auto'
339+
setattr(self, f'_explicit_{event.name}', explicit)
316340

317341

318342
class ServableMixin:

0 commit comments

Comments
 (0)