diff --git a/panel/pane/deckgl.py b/panel/pane/deckgl.py index fbfd1e30a1..091674028d 100644 --- a/panel/pane/deckgl.py +++ b/panel/pane/deckgl.py @@ -278,10 +278,9 @@ def _get_model( self, doc: Document, root: Model | None = None, parent: Model | None = None, comm: Comm | None = None ) -> Model: - if self._bokeh_model is None: - DeckGL._bokeh_model = lazy_load( - 'panel.models.deckgl', 'DeckGLPlot', isinstance(comm, JupyterComm), root - ) + DeckGL._bokeh_model = lazy_load( + 'panel.models.deckgl', 'DeckGLPlot', isinstance(comm, JupyterComm), root + ) properties = self._get_properties(doc) data = properties.pop('data') sources: list[ColumnDataSource] = [] diff --git a/panel/pane/echarts.py b/panel/pane/echarts.py index be7a5ace9e..915afb719e 100644 --- a/panel/pane/echarts.py +++ b/panel/pane/echarts.py @@ -126,10 +126,9 @@ def _get_model( self, doc: Document, root: Model | None = None, parent: Model | None = None, comm: Comm | None = None ) -> Model: - if self._bokeh_model is None: - ECharts._bokeh_model = lazy_load( - 'panel.models.echarts', 'ECharts', isinstance(comm, JupyterComm), root - ) + ECharts._bokeh_model = lazy_load( + 'panel.models.echarts', 'ECharts', isinstance(comm, JupyterComm), root + ) model = super()._get_model(doc, root, parent, comm) self._register_events('echarts_event', model=model, doc=doc, comm=comm) return model diff --git a/panel/pane/perspective.py b/panel/pane/perspective.py index 1dacf109f3..5acbdaefab 100644 --- a/panel/pane/perspective.py +++ b/panel/pane/perspective.py @@ -477,10 +477,9 @@ def _get_model( self, doc: Document, root: Model | None = None, parent: Model | None = None, comm: Comm | None = None ) -> Model: - if not self._bokeh_model: - Perspective._bokeh_model = lazy_load( - 'panel.models.perspective', 'Perspective', isinstance(comm, JupyterComm), root - ) + Perspective._bokeh_model = lazy_load( + 'panel.models.perspective', 'Perspective', isinstance(comm, JupyterComm), root + ) model = super()._get_model(doc, root, parent, comm) self._register_events('perspective-click', model=model, doc=doc, comm=comm) return model diff --git a/panel/pane/plotly.py b/panel/pane/plotly.py index 0fc2cec3c1..5ffd31fef7 100644 --- a/panel/pane/plotly.py +++ b/panel/pane/plotly.py @@ -313,10 +313,9 @@ def _get_model( self, doc: Document, root: Model | None = None, parent: Model | None = None, comm: Comm | None = None ) -> Model: - if not self._bokeh_model: - Plotly._bokeh_model = lazy_load( - 'panel.models.plotly', 'PlotlyPlot', isinstance(comm, JupyterComm), root - ) + Plotly._bokeh_model = lazy_load( + 'panel.models.plotly', 'PlotlyPlot', isinstance(comm, JupyterComm), root + ) model = super()._get_model(doc, root, parent, comm) self._register_events('plotly_event', model=model, doc=doc, comm=comm) return model diff --git a/panel/pane/vega.py b/panel/pane/vega.py index 00bebf6ab4..f61a5bf059 100644 --- a/panel/pane/vega.py +++ b/panel/pane/vega.py @@ -288,10 +288,9 @@ def _get_model( self, doc: Document, root: Model | None = None, parent: Model | None = None, comm: Comm | None = None ) -> Model: - if self._bokeh_model is None: - Vega._bokeh_model = lazy_load( - 'panel.models.vega', 'VegaPlot', isinstance(comm, JupyterComm), root - ) + Vega._bokeh_model = lazy_load( + 'panel.models.vega', 'VegaPlot', isinstance(comm, JupyterComm), root + ) model = super()._get_model(doc, root, parent, comm) self._register_events('vega_event', model=model, doc=doc, comm=comm) return model diff --git a/panel/pane/vizzu.py b/panel/pane/vizzu.py index 38386f64e9..e1f7d3931c 100644 --- a/panel/pane/vizzu.py +++ b/panel/pane/vizzu.py @@ -151,10 +151,9 @@ def _get_model( self, doc: Document, root: Model | None = None, parent: Model | None = None, comm: Comm | None = None ) -> Model: - if self._bokeh_model is None: - Vizzu._bokeh_model = lazy_load( - 'panel.models.vizzu', 'VizzuChart', isinstance(comm, JupyterComm), root - ) + Vizzu._bokeh_model = lazy_load( + 'panel.models.vizzu', 'VizzuChart', isinstance(comm, JupyterComm), root + ) model = super()._get_model(doc, root, parent, comm) self._register_events('vizzu_event', model=model, doc=doc, comm=comm) return model diff --git a/panel/template/base.py b/panel/template/base.py index cd99b96347..a596c502e9 100644 --- a/panel/template/base.py +++ b/panel/template/base.py @@ -97,10 +97,10 @@ class BaseTemplate(param.Parameterized, MimeRenderMixin, ServableMixin, Resource ############# # pathlib.Path pointing to local CSS file(s) - _css: ClassVar[list[os.PathLike | str]] = [] + _css: ClassVar[list[Path | str]] = [] # pathlib.Path pointing to local JS file(s) - _js: ClassVar[os.PathLike | str | list[Path | str] | None] = None + _js: ClassVar[Path | str | list[Path | str] | None] = None # External resources _resources = { @@ -392,6 +392,8 @@ def resolve_resources( # CSS files base_css = self._css + if not isinstance(base_css, list): + base_css = [base_css] if base_css else [] for css in base_css: tmpl_name = name for scls in cls.__mro__[1:]: diff --git a/panel/widgets/input.py b/panel/widgets/input.py index 2f0243a70a..f5d811282c 100644 --- a/panel/widgets/input.py +++ b/panel/widgets/input.py @@ -383,11 +383,10 @@ def _get_model( self, doc: Document, root: Model | None = None, parent: Model | None = None, comm: Comm | None = None ) -> Model: - if self._widget_type is None: - FileDropper._widget_type = lazy_load( - 'panel.models.file_dropper', 'FileDropper', isinstance(comm, JupyterComm), root, - ext='filedropper' - ) + FileDropper._widget_type = lazy_load( + 'panel.models.file_dropper', 'FileDropper', isinstance(comm, JupyterComm), root, + ext='filedropper' + ) model = super()._get_model(doc, root, parent, comm) self._register_events('delete_event', 'upload_event', model=model, doc=doc, comm=comm) return model diff --git a/panel/widgets/misc.py b/panel/widgets/misc.py index 39aad0c7e7..39c0534cd2 100644 --- a/panel/widgets/misc.py +++ b/panel/widgets/misc.py @@ -335,9 +335,8 @@ class JSONEditor(Widget): } def _get_model(self, doc, root=None, parent=None, comm=None): - if self._widget_type is None: - JSONEditor._widget_type = lazy_load( - "panel.models.jsoneditor", "JSONEditor", isinstance(comm, JupyterComm) - ) + JSONEditor._widget_type = lazy_load( + "panel.models.jsoneditor", "JSONEditor", isinstance(comm, JupyterComm) + ) model = super()._get_model(doc, root, parent, comm) return model diff --git a/panel/widgets/select.py b/panel/widgets/select.py index ba8bdfbb95..f2938bcf71 100644 --- a/panel/widgets/select.py +++ b/panel/widgets/select.py @@ -293,7 +293,7 @@ def _process_param_change(self, msg: dict[str, Any]) -> dict[str, Any]: } else: options = { - group: [str(v) for v in self.groups[group]] + group: [str(v) for v in self.groups[group]] # type: ignore for group in groups.keys() } msg['options'] = options diff --git a/panel/widgets/tables.py b/panel/widgets/tables.py index ed399a55e6..7a62e441f6 100644 --- a/panel/widgets/tables.py +++ b/panel/widgets/tables.py @@ -7,7 +7,7 @@ from functools import partial from types import FunctionType, MethodType from typing import ( - TYPE_CHECKING, Any, ClassVar, Literal, Sequence, TypedDict, + TYPE_CHECKING, Any, ClassVar, Literal, Sequence, TypedDict, cast, ) import numpy as np @@ -27,7 +27,7 @@ from ..io.resources import CDN_DIST, CSS_URLS from ..io.state import state -from ..reactive import Reactive, ReactiveData, TDataColumn +from ..reactive import Reactive, ReactiveData from ..util import ( BOKEH_GE_3_6, clone_model, datetime_as_utctimestamp, isdatetime, lazy_load, styler_update, updating, @@ -47,6 +47,7 @@ from ..models.tabulator import ( CellClickEvent, SelectionEvent, TableEditEvent, ) + from ..reactive import TDataColumn class FilterSpec(TypedDict, total=False): headerFilter: str | bool @@ -54,27 +55,27 @@ class FilterSpec(TypedDict, total=False): headerFilterFunc: str headerFilterPlaceholder: str - class ColumnSpec(TypedDict, total=False): - editable: bool - editor: str | CellEditor - editorParams: dict[str, Any] - field: str - frozen: bool - headerHozAlign: Literal["center", "left", "right"] - headerSort: bool - headerTooltip: str - hozAlign: Literal["center", "left", "right"] - formatter: str | CellFormatter - formatterParams: dict[str, Any] - sorter: str - title: str - titleFormatter: str | CellFormatter - titleFormatterParams: dict[str, Any] - width: str | int - - class GroupSpec(TypedDict): - columns: list[ColumnSpec] - title: str +class ColumnSpec(TypedDict, total=False): + editable: bool + editor: str | CellEditor + editorParams: dict[str, Any] + field: str + frozen: bool + headerHozAlign: Literal["center", "left", "right"] + headerSort: bool + headerTooltip: str + hozAlign: Literal["center", "left", "right"] + formatter: str | CellFormatter + formatterParams: dict[str, Any] + sorter: str + title: str + titleFormatter: str | CellFormatter + titleFormatterParams: dict[str, Any] + width: str | int + +class GroupSpec(TypedDict): + columns: Sequence[ColumnSpec] + title: str def _convert_datetime_array_ignore_list(v): @@ -1851,10 +1852,9 @@ def _get_model( self, doc: Document, root: Model | None = None, parent: Model | None = None, comm: Comm | None = None ) -> Model: - if self._widget_type is None: - Tabulator._widget_type = lazy_load( - 'panel.models.tabulator', 'DataTabulator', isinstance(comm, JupyterComm), root - ) + Tabulator._widget_type = lazy_load( + 'panel.models.tabulator', 'DataTabulator', isinstance(comm, JupyterComm), root + ) model = super()._get_model(doc, root, parent, comm) root = root or model self._child_panels, removed, expanded = self._get_children() @@ -1935,7 +1935,7 @@ def _get_filter_spec(self, column: TableColumn) -> FilterSpec: def _config_columns(self, column_objs: list[TableColumn]) -> Sequence[ColumnSpec | GroupSpec]: column_objs = list(column_objs) groups: dict[str, GroupSpec] = {} - columns: list[ColumnSpec] = [] + columns: Sequence[ColumnSpec | GroupSpec] = [] selectable = self.selectable if self.row_content: columns.append({ @@ -1988,7 +1988,7 @@ def _config_columns(self, column_objs: list[TableColumn]) -> Sequence[ColumnSpec elif not self.sortable: col_dict['headerSort'] = self.sortable if isinstance(self.text_align, str): - col_dict['hozAlign'] = self.text_align + col_dict['hozAlign'] = self.text_align # type: ignore elif field in self.text_align: col_dict['hozAlign'] = self.text_align[field] if isinstance(self.header_align, str): @@ -2052,7 +2052,7 @@ def _config_columns(self, column_objs: list[TableColumn]) -> Sequence[ColumnSpec if isinstance(index, tuple): if columns: children = columns - last = children[-1] + last = cast(GroupSpec, children[-1]) for group in index[:-1]: if 'title' in last and last['title'] == group: new = False @@ -2063,7 +2063,7 @@ def _config_columns(self, column_objs: list[TableColumn]) -> Sequence[ColumnSpec 'columns': [], 'title': group, }) - last = children[-1] + last = cast(GroupSpec, children[-1]) if new: children = last['columns'] children.append(col_dict) @@ -2073,7 +2073,7 @@ def _config_columns(self, column_objs: list[TableColumn]) -> Sequence[ColumnSpec if group in groups: groups[group]['columns'].append(col_dict) continue - group_dict = { + group_dict: GroupSpec = { 'title': group, 'columns': [col_dict] } @@ -2083,7 +2083,7 @@ def _config_columns(self, column_objs: list[TableColumn]) -> Sequence[ColumnSpec columns.append(col_dict) return columns - def _get_configuration(self, columns: list[dict[str, Any]]) -> dict[str, Any]: + def _get_configuration(self, columns: list[TableColumn]) -> dict[str, Any]: """ Returns the Tabulator configuration. """ diff --git a/panel/widgets/terminal.py b/panel/widgets/terminal.py index 766cf4ba13..fca01cbd99 100644 --- a/panel/widgets/terminal.py +++ b/panel/widgets/terminal.py @@ -288,10 +288,9 @@ def write(self, __s): return len(self.output) def _get_model(self, doc, root=None, parent=None, comm=None): - if self._widget_type is None: - Terminal._widget_type = lazy_load( - 'panel.models.terminal', 'Terminal', isinstance(comm, JupyterComm), root - ) + Terminal._widget_type = lazy_load( + 'panel.models.terminal', 'Terminal', isinstance(comm, JupyterComm), root + ) model = super()._get_model(doc, root, parent, comm) model.output = self.output self._register_events('keystroke', model=model, doc=doc, comm=comm) diff --git a/panel/widgets/texteditor.py b/panel/widgets/texteditor.py index f8b52dfb8c..0966b86f9c 100644 --- a/panel/widgets/texteditor.py +++ b/panel/widgets/texteditor.py @@ -55,9 +55,8 @@ def _get_model( self, doc: Document, root: Model | None = None, parent: Model | None = None, comm: Comm | None = None ) -> Model: - if self._widget_type is None: - TextEditor._widget_type = lazy_load( - 'panel.models.quill', 'QuillInput', isinstance(comm, JupyterComm), - root, ext='texteditor' - ) + TextEditor._widget_type = lazy_load( + 'panel.models.quill', 'QuillInput', isinstance(comm, JupyterComm), + root, ext='texteditor' + ) return super()._get_model(doc, root, parent, comm) diff --git a/panel/widgets/widget.py b/panel/widgets/widget.py index 0e0797d162..628d1cde87 100644 --- a/panel/widgets/widget.py +++ b/panel/widgets/widget.py @@ -2,7 +2,7 @@ from collections.abc import Iterable, Mapping from inspect import Parameter -from numbers import Integral, Number, Real +from numbers import Integral, Real from typing import Any empty = Parameter.empty @@ -37,18 +37,19 @@ def get_interact_value(self): def _get_min_max_value( - min: Number, max: Number, value: Number | None = None, step: Number | None = None -) -> tuple[Number, Number, Number]: + minimum: int | float, maximum: int | float, value: int | float | None = None, step: int | float | None = None +) -> tuple[int | float, int | float, int | float]: """Return min, max, value given input values with possible None.""" # Either min and max need to be given, or value needs to be given if value is None: - if min is None or max is None: - raise ValueError(f'unable to infer range, value from: ({min}, {max}, {value})') - diff = max - min - value = min + (diff / 2) + if minimum is None or max is maximum: + raise ValueError(f'unable to infer range, value from: ({minimum}, {maximum}, {value})') + + diff = maximum - minimum + value = minimum + (diff / 2) # Ensure that value has the same type as diff if not isinstance(value, type(diff)): - value = min + (diff // 2) + value = minimum + (diff // 2) else: # value is not None if not isinstance(value, Real): raise TypeError(f'expected a real number, got: {value!r}') @@ -60,25 +61,25 @@ def _get_min_max_value( vrange = (-value, 3*value) else: vrange = (3*value, -value) - if min is None: - min = vrange[0] - if max is None: - max = vrange[1] + if minimum is None: + minimum = vrange[0] + if maximum is None: + maximum = vrange[1] if step is not None: # ensure value is on a step - tick = int((value - min) / step) - value = min + tick * step - if not min <= value <= max: - raise ValueError(f'value must be between min and max (min={min}, value={value}, max={max})') - return min, max, value + tick = int((value - minimum) / step) + value = minimum + tick * step + if not (minimum <= value <= maximum): + raise ValueError(f'value must be between min and max (min={minimum}, value={value}, max={maximum})') + return minimum, maximum, value -def _matches(o: str, pattern: str) -> bool: +def _matches(o: tuple[Any, ...], pattern: tuple[type, ...]) -> bool: """Match a pattern of types in a sequence.""" if not len(o) == len(pattern): return False - comps = zip(o,pattern) - return all(isinstance(obj,kind) for obj,kind in comps) + comps = zip(o, pattern) + return all(isinstance(obj, kind) for obj, kind in comps) class widget(param.ParameterizedFunction):