diff --git a/panel/io/embed.py b/panel/io/embed.py
index 15fb7e397a..ad7e78edb9 100644
--- a/panel/io/embed.py
+++ b/panel/io/embed.py
@@ -319,12 +319,12 @@ def is_embeddable(object):
if len(cross_product) > max_states:
if config._doc_build:
return
- param.main.param.warning('The cross product of different application '
- 'states is very large to explore (N=%d), consider '
- 'reducing the number of options on the widgets or '
- 'increase the max_states specified in the function '
- 'to remove this warning' %
- len(cross_product))
+ param.main.param.warning(
+ 'The cross product of different application states is very large '
+ f'to explore (N={len(cross_product)}), consider reducing the number '
+ 'of options on the widgets or increase the max_states specified '
+ 'in the function to remove this warning.'
+ )
nested_dict = lambda: defaultdict(nested_dict)
state_dict = nested_dict()
diff --git a/panel/io/server.py b/panel/io/server.py
index 238d99120d..0a9bf39ad4 100644
--- a/panel/io/server.py
+++ b/panel/io/server.py
@@ -107,9 +107,9 @@ def _origin_url(url: str) -> str:
def _server_url(url: str, port: int) -> str:
if url.startswith("http"):
- return '%s:%d%s' % (url.rsplit(':', 1)[0], port, "/")
+ return f"{url.rsplit(':', 1)[0]}:{port}/"
else:
- return 'http://%s:%d%s' % (url.split(':')[0], port, "/")
+ return f"http://{url.split(':')[0]}:{port}/"
def async_execute(func: Callable[..., None]) -> None:
"""
diff --git a/panel/layout/base.py b/panel/layout/base.py
index 51d3ac0871..692315c6db 100644
--- a/panel/layout/base.py
+++ b/panel/layout/base.py
@@ -70,10 +70,11 @@ def __repr__(self, depth: int = 0, max_depth: int = 10) -> str:
spacer = '\n' + (' ' * (depth+1))
cls = type(self).__name__
params = param_reprs(self, ['objects'])
- objs = ['[%d] %s' % (i, obj.__repr__(depth+1)) for i, obj in enumerate(self)]
+ objs = [f'[{i}] {obj.__repr__(depth+1)}' for i, obj in enumerate(self)]
if not params and not objs:
return super().__repr__(depth+1)
- elif not params:
+
+ if not params:
template = '{cls}{spacer}{objs}'
elif not objs:
template = '{cls}({params})'
@@ -81,7 +82,8 @@ def __repr__(self, depth: int = 0, max_depth: int = 10) -> str:
template = '{cls}({params}){spacer}{objs}'
return template.format(
cls=cls, params=', '.join(params),
- objs=str(spacer).join(objs), spacer=spacer)
+ objs=str(spacer).join(objs), spacer=spacer
+ )
#----------------------------------------------------------------
# Callback API
@@ -393,34 +395,40 @@ def __contains__(self, obj: Viewable) -> bool:
def __setitem__(self, index: int | slice, panes: Iterable[Any]) -> None:
new_objects = list(self)
+ name = type(self).__name__
if not isinstance(index, slice):
start, end = index, index+1
if start > len(self.objects):
- raise IndexError('Index %d out of bounds on %s '
- 'containing %d objects.' %
- (end, type(self).__name__, len(self.objects)))
+ raise IndexError(
+ f'Index {end} out of bounds on {name} containing '
+ f'{len(self.objects)} objects.'
+ )
panes = [panes]
else:
start = index.start or 0
end = len(self) if index.stop is None else index.stop
if index.start is None and index.stop is None:
if not isinstance(panes, list):
- raise IndexError('Expected a list of objects to '
- f'replace the objects in the {type(self).__name__}, '
- f'got a {type(panes).__name__} type.')
+ raise IndexError(
+ 'Expected a list of objects to replace the '
+ f'objects in the {name}, got a '
+ f'{type(panes).__name__} type.'
+ )
expected = len(panes)
new_objects = [None]*expected # type: ignore
end = expected
elif end > len(self.objects):
- raise IndexError('Index %d out of bounds on %s '
- 'containing %d objects.' %
- (end, type(self).__name__, len(self.objects)))
+ raise IndexError(
+ f'Index {end} out of bounds on {name} containing '
+ f'{len(self.objects)} objects.'
+ )
else:
expected = end-start
if not isinstance(panes, list) or len(panes) != expected:
- raise IndexError('Expected a list of %d objects to set '
- 'on the %s to match the supplied slice.' %
- (expected, type(self).__name__))
+ raise IndexError(
+ f'Expected a list of {expected} objects to set '
+ f'on the {name} to match the supplied slice.'
+ )
for i, pane in zip(range(start, end), panes):
new_objects[i] = pane
@@ -445,8 +453,10 @@ def clone(self, *objects: Any, **params: Any) -> ListLike:
else:
objects = self.objects
elif 'objects' in params:
- raise ValueError(f"A {type(self).__name__}'s objects should be supplied either "
- "as arguments or as a keyword, not both.")
+ raise ValueError(
+ f"A {type(self).__name__}'s objects should be supplied either "
+ "as arguments or as a keyword, not both."
+ )
p = dict(self.param.values(), **params)
del p['objects']
return type(self)(*objects, **p)
@@ -645,11 +655,13 @@ def __radd__(self, other: Iterable[Any]) -> NamedListLike:
def __setitem__(self, index: int | slice, panes: Iterable[Any]) -> None:
new_objects = list(self)
+ name = type(self).__name__
if not isinstance(index, slice):
if index > len(self.objects):
- raise IndexError('Index %d out of bounds on %s '
- 'containing %d objects.' %
- (index, type(self).__name__, len(self.objects)))
+ raise IndexError(
+ f'Index {index} out of bounds on {name} '
+ f'containing {len(self.objects)} objects.'
+ )
start, end = index, index+1
panes = [panes]
else:
@@ -657,23 +669,28 @@ def __setitem__(self, index: int | slice, panes: Iterable[Any]) -> None:
end = len(self.objects) if index.stop is None else index.stop
if index.start is None and index.stop is None:
if not isinstance(panes, list):
- raise IndexError('Expected a list of objects to '
- f'replace the objects in the {type(self).__name__}, '
- f'got a {type(panes).__name__} type.')
+ raise IndexError(
+ 'Expected a list of objects to replace '
+ f'the objects in the {name}, got a '
+ f'{type(panes).__name__} type.'
+ )
expected = len(panes)
new_objects = [None]*expected # type: ignore
self._names = [None]*len(panes)
end = expected
else:
expected = end-start
- if end > len(self.objects):
- raise IndexError('Index %d out of bounds on %s '
- 'containing %d objects.' %
- (end, type(self).__name__, len(self.objects)))
+ nobjs = len(self.objects)
+ if end > nobjs:
+ raise IndexError(
+ f'Index {end} out of bounds on {name} '
+ 'containing {nobjs} objects.'
+ )
if not isinstance(panes, list) or len(panes) != expected:
- raise IndexError('Expected a list of %d objects to set '
- 'on the %s to match the supplied slice.' %
- (expected, type(self).__name__))
+ raise IndexError(
+ f'Expected a list of {expected} objects to set '
+ f'on the {name} to match the supplied slice.'
+ )
for i, pane in zip(range(start, end), panes):
new_objects[i], self._names[i] = self._to_object_and_name(pane)
self.objects = new_objects
@@ -694,9 +711,10 @@ def clone(self, *objects: Any, **params: Any) -> NamedListLike:
if objects:
overrides = objects
elif 'objects' in params:
- raise ValueError('Tabs objects should be supplied either '
- 'as positional arguments or as a keyword, '
- 'not both.')
+ raise ValueError(
+ 'Tabs objects should be supplied either as positional '
+ 'arguments or as a keyword, not both.'
+ )
elif 'objects' in params:
overrides = params.pop('objects')
else:
diff --git a/panel/layout/grid.py b/panel/layout/grid.py
index 44c3fb02d0..d4dae9b349 100644
--- a/panel/layout/grid.py
+++ b/panel/layout/grid.py
@@ -554,12 +554,14 @@ def __setitem__(self, index, obj):
continue
if old_obj not in objects:
objects.append(old_obj)
- overlapping += ' (%d, %d): %s\n\n' % (yidx, xidx, old_obj)
- overlap_text = ('Specified region overlaps with the following '
- 'existing object(s) in the grid:\n\n'+overlapping+
- 'The following shows a view of the grid '
- '(empty: 0, occupied: 1, overlapping: 2):\n\n'+
- str(grid.astype('uint8')))
+ overlapping += f' ({yidx}, {xidx}: {old_obj}\n\n'
+ overlap_text = (
+ 'Specified region overlaps with the following '
+ 'existing object(s) in the grid:\n\n'+overlapping+
+ 'The following shows a view of the grid '
+ '(empty: 0, occupied: 1, overlapping: 2):\n\n'+
+ str(grid.astype('uint8'))
+ )
if self.mode == 'error':
raise IndexError(overlap_text)
elif self.mode == 'warn':
diff --git a/panel/layout/tabs.py b/panel/layout/tabs.py
index 5a2e66333b..9d753dcffb 100644
--- a/panel/layout/tabs.py
+++ b/panel/layout/tabs.py
@@ -161,10 +161,11 @@ def _get_objects(self, model, old_objects, doc, root, comm=None):
from ..pane.base import RerenderError, panel
new_models, old_models = [], []
if len(self._names) != len(self):
- raise ValueError('Tab names do not match objects, ensure '
- 'that the Tabs.objects are not modified '
- 'directly. Found %d names, expected %d.' %
- (len(self._names), len(self)))
+ raise ValueError(
+ 'Tab names do not match objects, ensure that the '
+ 'Tabs.objects are not modified directly. '
+ f'Found {len(self._names)} names, expected {len(self)}.'
+ )
for i, (name, pane) in enumerate(zip(self._names, self)):
pane = panel(pane, name=name)
self.objects[i] = pane
diff --git a/panel/pane/vtk/synchronizable_serializer.py b/panel/pane/vtk/synchronizable_serializer.py
index 9f0c35db82..ec9788de7d 100644
--- a/panel/pane/vtk/synchronizable_serializer.py
+++ b/panel/pane/vtk/synchronizable_serializer.py
@@ -725,7 +725,7 @@ def genericMapperSerializer(parent, mapper, mapperId, context, depth):
for port in range(mapper.GetNumberOfInputPorts()): # Glyph3DMapper can define input data objects on 2 ports (input, source)
dataObject = mapper.GetInputDataObject(port, 0)
if dataObject:
- dataObjectId = '%s-dataset-%d' % (mapperId, port)
+ dataObjectId = f'{mapperId}-dataset-{port}'
if parent.IsA('vtkActor') and not mapper.IsA('vtkTexture'):
# vtk-js actors can render only surfacic datasets
# => we ensure to convert the dataset in polydata
diff --git a/panel/pipeline.py b/panel/pipeline.py
index aa475b983a..da1d067052 100644
--- a/panel/pipeline.py
+++ b/panel/pipeline.py
@@ -239,7 +239,7 @@ def __repr__(self):
else:
cls_name = stage.__name__
params = ', '.join(param_reprs(stage))
- repr_str += '\n [%d] %s: %s(%s)' % (i, name, cls_name, params)
+ repr_str += f'\n [{i}] {name}: {cls_name}({params})'
return repr_str
def __str__(self):
diff --git a/panel/reactive.py b/panel/reactive.py
index 7fc101cec2..f95ed0a7d3 100644
--- a/panel/reactive.py
+++ b/panel/reactive.py
@@ -2048,7 +2048,7 @@ def _get_template(self) -> tuple[str, list[str], Mapping[str, list[tuple[str, li
for parent, child_name in self._parser.children.items():
if (parent, child_name) in self._parser.looped:
for i, _ in enumerate(getattr(self, child_name)):
- html = html.replace('${%s[%d]}' % (child_name, i), '')
+ html = html.replace(f'${{{child_name}[{i}]}}', '')
else:
html = html.replace(f'${{{child_name}}}', '')
return html, parser.nodes, p_attrs
diff --git a/panel/tests/io/test_embed.py b/panel/tests/io/test_embed.py
index 85f590ac36..8c351f0999 100644
--- a/panel/tests/io/test_embed.py
+++ b/panel/tests/io/test_embed.py
@@ -442,7 +442,7 @@ def link(target, event):
assert event1['kind'] == 'ModelChanged'
assert event1['attr'] == 'text'
assert event1['model'] == model.children[0].children[0].ref
- assert event1['new'] == '%d' % values[k]
+ assert event1['new'] == f'{values[k]:.0f}'
assert event2['kind'] == 'ModelChanged'
assert event2['attr'] == 'text'
diff --git a/panel/tests/test_param.py b/panel/tests/test_param.py
index 18dfb99b91..dcd9a7f6a8 100644
--- a/panel/tests/test_param.py
+++ b/panel/tests/test_param.py
@@ -1147,11 +1147,11 @@ class View(param.Parameterized):
@param.depends('a')
def view(self):
- return Div(text='%d' % self.a)
+ return Div(text=str(int(self.a)))
@param.depends('b.param')
def subobject_view(self):
- return Div(text='%d' % self.b.a)
+ return Div(text=str(int(self.b.a)))
@param.depends('a')
def mpl_view(self):
@@ -1166,7 +1166,7 @@ def test_get_param_function_pane_type():
test = View()
def view(a):
- return Div(text='%d' % a)
+ return Div(text=str(int(a)))
assert PaneBase.get_pane_type(view) is not ParamFunction
assert PaneBase.get_pane_type(param.depends(test.param.a)(view)) is ParamFunction
@@ -1177,7 +1177,7 @@ def test_param_function_pane(document, comm):
@param.depends(test.param.a)
def view(a):
- return Div(text='%d' % a)
+ return Div(text=str(int(a)))
pane = panel(view)
inner_pane = pane._pane
@@ -1218,7 +1218,7 @@ def test_param_function_pane_defer_load(document, comm):
@param.depends(test.param.a)
def view(a):
- return Div(text='%d' % a)
+ return Div(text=str(int(a)))
pane = panel(view, defer_load=True)
inner_pane = pane._pane
diff --git a/panel/tests/util.py b/panel/tests/util.py
index 212b84a4f5..1d351321ac 100644
--- a/panel/tests/util.py
+++ b/panel/tests/util.py
@@ -468,7 +468,7 @@ def http_serve_directory(directory=".", port=0):
httpd.timeout = 0.5
httpd.server_bind()
- address = "http://%s:%d" % (httpd.server_name, httpd.server_port)
+ address = f"http://{httpd.server_name}:{httpd.server_port}"
httpd.server_activate()
diff --git a/panel/tests/widgets/test_slider.py b/panel/tests/widgets/test_slider.py
index a588805b5a..45c2cbb448 100644
--- a/panel/tests/widgets/test_slider.py
+++ b/panel/tests/widgets/test_slider.py
@@ -480,7 +480,7 @@ def test_discrete_slider_disabled(document, comm):
def test_discrete_date_slider(document, comm):
- dates = {'2016-01-0%d' % i: datetime(2016, 1, i) for i in range(1, 4)}
+ dates = {f'2016-01-0{i:d}' % i: datetime(2016, 1, i) for i in range(1, 4)}
discrete_slider = DiscreteSlider(name='DiscreteSlider', value=dates['2016-01-02'],
options=dates)