diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f6ee97d71e..de63bbd429 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -4,7 +4,7 @@
default_stages: [commit]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.5.0
+ rev: v4.6.0
hooks:
- id: check-builtin-literals
- id: check-case-conflict
@@ -17,7 +17,7 @@ repos:
exclude: \.min\.js$
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.1.9
+ rev: v0.4.5
hooks:
- id: ruff
files: panel/
@@ -33,13 +33,13 @@ repos:
args: [-i, tags]
exclude: '^lite/files/.*/'
- repo: https://github.com/codespell-project/codespell
- rev: v2.2.6
+ rev: v2.3.0
hooks:
- id: codespell
additional_dependencies:
- tomli
- - repo: https://github.com/pre-commit/mirrors-prettier
- rev: v4.0.0-alpha.8
+ - repo: https://github.com/hoxbro/prettier-pre-commit
+ rev: v3.2.5
hooks:
- id: prettier
entry: prettier --write --ignore-unknown --no-error-on-unmatched-pattern
@@ -50,7 +50,7 @@ repos:
- id: oxipng
stages: [manual]
- repo: https://github.com/pre-commit/mirrors-eslint
- rev: v8.56.0
+ rev: v9.3.0
hooks:
- id: eslint
args: ['-c', 'panel/.eslintrc.js', 'panel/*.ts', 'panel/models/**/*.ts']
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index d8dc9bfe29..bea506eb72 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -13,7 +13,7 @@ If you are a leading community member: **Please act as a role model**.
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
-identity and expression, level of experience, education, socio-economic status,
+identity and expression, level of experience, education, socioeconomic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
diff --git a/examples/reference/widgets/ToggleGroup.ipynb b/examples/reference/widgets/ToggleGroup.ipynb
index 4719dd571d..e8cb8304a5 100644
--- a/examples/reference/widgets/ToggleGroup.ipynb
+++ b/examples/reference/widgets/ToggleGroup.ipynb
@@ -122,7 +122,7 @@
"source": [
"### Controls\n",
"\n",
- "The `ToggleGroup` widget (concretly the created, underlying widget) exposes a number of options which can be changed from both Python and Javascript. Try out the effect of these parameters interactively:"
+ "The `ToggleGroup` widget (concretely the created, underlying widget) exposes a number of options which can be changed from both Python and Javascript. Try out the effect of these parameters interactively:"
]
},
{
diff --git a/panel/command/__init__.py b/panel/command/__init__.py
index 38b2354ce0..e30816964f 100644
--- a/panel/command/__init__.py
+++ b/panel/command/__init__.py
@@ -79,7 +79,7 @@ def main(args=None):
if len(sys.argv) == 1:
all_commands = sorted([c.name for c in bokeh_commands])
- die("ERROR: Must specify subcommand, one of: %s" % nice_join(all_commands))
+ die(f"ERROR: Must specify subcommand, one of: {nice_join(all_commands)}")
if sys.argv[1] in ('--help', '-h'):
args = parser.parse_args(sys.argv[1:])
diff --git a/panel/command/serve.py b/panel/command/serve.py
index 153eb20ff3..fe21ecbec3 100644
--- a/panel/command/serve.py
+++ b/panel/command/serve.py
@@ -343,7 +343,7 @@ def customize_kwargs(self, args, server_kwargs):
pattern = REST_PROVIDERS[args.rest_provider](files, args.rest_endpoint)
patterns.extend(pattern)
elif args.rest_provider is not None:
- raise ValueError("rest-provider %r not recognized." % args.rest_provider)
+ raise ValueError(f"rest-provider {args.rest_provider!r} not recognized.")
config.autoreload = args.autoreload
config.global_loading_spinner = args.global_loading_spinner
@@ -379,7 +379,7 @@ def customize_kwargs(self, args, server_kwargs):
if args.liveness:
argvs = {f: args.args for f in files}
applications = build_single_handler_applications(files, argvs)
- patterns += [(r"/%s" % args.liveness_endpoint, LivenessHandler, dict(applications=applications))]
+ patterns += [(rf"/{args.liveness_endpoint}", LivenessHandler, dict(applications=applications))]
config.profiler = args.profiler
if args.admin:
diff --git a/panel/config.py b/panel/config.py
index ea1f23f70f..e02de70bfc 100644
--- a/panel/config.py
+++ b/panel/config.py
@@ -746,8 +746,8 @@ def __call__(self, *args, **params):
state._extensions.append(arg)
ReactiveHTMLMetaclass._loaded_extensions.add(arg)
else:
- self.param.warning('%s extension not recognized and '
- 'will be skipped.' % arg)
+ self.param.warning(f'{arg} extension not recognized and '
+ 'will be skipped.')
for k, v in params.items():
if k == 'design' and isinstance(v, str):
diff --git a/panel/interact.py b/panel/interact.py
index e9b447b27a..e069cc0498 100644
--- a/panel/interact.py
+++ b/panel/interact.py
@@ -109,7 +109,7 @@ def __init__(self, object, params={}, **kwargs):
self._inner_layout = Row(self._pane)
widgets = [widget for _, widget in widgets if isinstance(widget, Widget)]
if 'name' in params:
- widgets.insert(0, HTML('
%s
' % self.name))
+ widgets.insert(0, HTML(f'{self.name}
'))
self.widget_box = Column(*widgets)
self.layout.objects = [self.widget_box, self._inner_layout]
self._link_widgets()
diff --git a/panel/io/admin.py b/panel/io/admin.py
index 90d865302a..29c20e6655 100644
--- a/panel/io/admin.py
+++ b/panel/io/admin.py
@@ -257,7 +257,7 @@ def update_cds(new, nb=False):
else:
msg = new.getMessage()
line_color = 'black'
- if msg.startswith('Session %s logged' % sid):
+ if msg.startswith(f'Session {sid} logged'):
etype = 'logging'
line_color = EVENT_TYPES.get(etype)
elif msg.startswith(LOG_SESSION_DESTROYED % sid):
diff --git a/panel/io/model.py b/panel/io/model.py
index 76b416f9f9..322af5748f 100644
--- a/panel/io/model.py
+++ b/panel/io/model.py
@@ -157,7 +157,7 @@ def bokeh_repr(obj: Model, depth: int = 0, ignored: Optional[Iterable[str]] = No
if k in ignored:
continue
if isinstance(v, Model):
- v = '%s()' % type(v).__name__
+ v = f'{type(v).__name__}()'
else:
v = repr(v)
if len(v) > 30:
@@ -168,7 +168,7 @@ def bokeh_repr(obj: Model, depth: int = 0, ignored: Optional[Iterable[str]] = No
r += f'{cls}(children=[\n'
for child_obj in obj.children: # type: ignore
r += textwrap.indent(bokeh_repr(child_obj, depth=depth+1) + ',\n', ' ')
- r += '], %s)' % props_repr
+ r += f'], {props_repr})'
else:
r += f'{cls}({props_repr})'
return r
diff --git a/panel/io/rest.py b/panel/io/rest.py
index ccd8aab2c2..47ecc7f904 100644
--- a/panel/io/rest.py
+++ b/panel/io/rest.py
@@ -122,7 +122,7 @@ def tranquilizer_rest_provider(files, endpoint):
"""
app = build_tranquilize_application(files)
tr = WSGIContainer(app)
- return [(r"^/%s/.*" % endpoint, web.FallbackHandler, dict(fallback=tr))]
+ return [(rf"^/{endpoint}/.*", web.FallbackHandler, dict(fallback=tr))]
def param_rest_provider(files, endpoint):
@@ -169,7 +169,7 @@ def param_rest_provider(files, endpoint):
if endpoint and not endpoint.endswith('/'):
endpoint += '/'
- return [((r"^/%s.*" % endpoint if endpoint else r"^.*"), ParamHandler, dict(root=endpoint))]
+ return [((rf"^/{endpoint}.*" if endpoint else r"^.*"), ParamHandler, dict(root=endpoint))]
REST_PROVIDERS = {
diff --git a/panel/io/save.py b/panel/io/save.py
index d73fd36d99..dd51985feb 100644
--- a/panel/io/save.py
+++ b/panel/io/save.py
@@ -243,8 +243,8 @@ def save(
resources = INLINE
mode = 'inline'
else:
- raise ValueError("Resources %r not recognized, specify one "
- "of 'CDN' or 'INLINE'." % resources)
+ raise ValueError(f"Resources {resources!r} not recognized, specify one "
+ "of 'CDN' or 'INLINE'.")
elif isinstance(resources, BkResources):
mode = resources.mode
diff --git a/panel/io/server.py b/panel/io/server.py
index 6fbcf683d5..6a16095315 100644
--- a/panel/io/server.py
+++ b/panel/io/server.py
@@ -857,9 +857,9 @@ def get_static_routes(static_dirs):
"this is reserved for internal use.")
path = fullpath(path)
if not os.path.isdir(path):
- raise ValueError("Cannot serve non-existent path %s" % path)
+ raise ValueError(f"Cannot serve non-existent path {path}")
patterns.append(
- (r"%s/(.*)" % slug, StaticFileHandler, {"path": path})
+ (rf"{slug}/(.*)", StaticFileHandler, {"path": path})
)
patterns.append((
f'/{COMPONENT_PATH}(.*)', ComponentResourceHandler, {}
@@ -1085,7 +1085,7 @@ def get_server(
if liveness:
liveness_endpoint = 'liveness' if isinstance(liveness, bool) else liveness
- extra_patterns += [(r"/%s" % liveness_endpoint, LivenessHandler, dict(applications=apps))]
+ extra_patterns += [(rf"/{liveness_endpoint}", LivenessHandler, dict(applications=apps))]
opts = dict(kwargs)
if loop:
diff --git a/panel/layout/base.py b/panel/layout/base.py
index c86ef73899..7063425fd0 100644
--- a/panel/layout/base.py
+++ b/panel/layout/base.py
@@ -81,7 +81,7 @@ 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=('%s' % spacer).join(objs), spacer=spacer)
+ objs=str(spacer).join(objs), spacer=spacer)
#----------------------------------------------------------------
# Callback API
@@ -435,9 +435,8 @@ def clone(self, *objects: Any, **params: Any) -> 'ListLike':
else:
objects = self.objects
elif 'objects' in params:
- raise ValueError("A %s's objects should be supplied either "
- "as arguments or as a keyword, not both."
- % type(self).__name__)
+ 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)
@@ -552,9 +551,9 @@ class NamedListLike(param.Parameterized):
def __init__(self, *items: list[Any | tuple[str, Any]], **params: Any):
if 'objects' in params:
if items:
- raise ValueError('%s objects should be supplied either '
+ raise ValueError(f'{type(self).__name__} objects should be supplied either '
'as positional arguments or as a keyword, '
- 'not both.' % type(self).__name__)
+ 'not both.')
items = params.pop('objects')
params['objects'], self._names = self._to_objects_and_names(items)
super().__init__(**params)
@@ -814,9 +813,9 @@ def __init__(self, *objects: Any, **params: Any):
from ..pane import panel
if objects:
if 'objects' in params:
- raise ValueError("A %s's objects should be supplied either "
+ raise ValueError(f"A {type(self).__name__}'s objects should be supplied either "
"as positional arguments or as a keyword, "
- "not both." % type(self).__name__)
+ "not both.")
params['objects'] = [panel(pane) for pane in objects]
elif 'objects' in params:
objects = params['objects']
diff --git a/panel/layout/flex.py b/panel/layout/flex.py
index 09571fc506..2014e18270 100644
--- a/panel/layout/flex.py
+++ b/panel/layout/flex.py
@@ -71,9 +71,9 @@ def __init__(self, *objects, **params):
params['sizing_mode'] = 'stretch_height'
if objects:
if 'objects' in params:
- raise ValueError("A %s's objects should be supplied either "
+ raise ValueError(f"A {type(self).__name__}'s objects should be supplied either "
"as positional arguments or as a keyword, "
- "not both." % type(self).__name__)
+ "not both.")
params['objects'] = [panel(pane) for pane in objects]
elif 'objects' in params:
objects = params['objects']
diff --git a/panel/links.py b/panel/links.py
index c2cd88d3be..e2e7d8ab84 100644
--- a/panel/links.py
+++ b/panel/links.py
@@ -77,7 +77,7 @@ def assert_target_syncable(
elif p not in target.param and p not in list(target._rename.values()):
matches = difflib.get_close_matches(p, list(target.param))
if matches:
- matches_repr = ' Similar parameters include: %r' % matches
+ matches_repr = f' Similar parameters include: {matches!r}'
else:
matches_repr = ''
raise ValueError(
@@ -146,7 +146,7 @@ def __init__(
a JS code snippet to execute.
"""
if source is None:
- raise ValueError('%s must define a source' % type(self).__name__)
+ raise ValueError(f'{type(self).__name__} must define a source')
# Source is stored as a weakref to allow it to be garbage collected
self._source = None if source is None else weakref.ref(source)
if not args:
@@ -289,7 +289,7 @@ class Link(Callback):
def __init__(self, source: 'Reactive', target: Optional['JSLinkTarget'] = None, **params):
if self._requires_target and target is None:
- raise ValueError('%s must define a target.' % type(self).__name__)
+ raise ValueError(f'{type(self).__name__} must define a target.')
# Source is stored as a weakref to allow it to be garbage collected
self._target = None if target is None else weakref.ref(target)
super().__init__(source, **params)
@@ -709,8 +709,7 @@ def _initialize_models(
setattr(tgt_model, tgt_spec, value)
if tgt_model is None and not link.code:
raise ValueError('Model could not be resolved on target '
- '%s and no custom code was specified.' %
- type(self.target).__name__)
+ f'{type(self.target).__name__} and no custom code was specified.')
def _process_references(self, references: dict[str, str]) -> None:
"""
diff --git a/panel/models/reactive_html.py b/panel/models/reactive_html.py
index e48678838d..30972a1695 100644
--- a/panel/models/reactive_html.py
+++ b/panel/models/reactive_html.py
@@ -105,17 +105,17 @@ def handle_data(self, data):
if match[2:-1] not in self.loop_var_map[var]:
self.loop_var_map[var].append(match[2:-1])
if var.endswith('.index0'):
- matches.append('${%s }}]}' % var)
+ matches.append('${%s }}]}' % var) # noqa: UP031
else:
- matches.append('${%s}' % var)
+ matches.append('${%s}' % var) # noqa: UP031
literal_matches = []
for match in self._literal_re.findall(data):
match = match[2:-2].strip()
if match.endswith('.index0'):
- literal_matches.append('{{%s }}]}' % match)
+ literal_matches.append('{{%s }}]}' % match) # noqa: UP031
else:
- literal_matches.append('{{ %s }}' % match)
+ literal_matches.append('{{ %s }}' % match) # noqa: UP031
# Detect templating for loops
list_loop = re.findall(list_iter_re, data)
@@ -175,7 +175,7 @@ def handle_data(self, data):
if match and (match.strip() in self.loop_map or '[' in match) and self._open_for:
if match.strip() in self.loop_map:
loop_match = self.loop_map[match.strip()]
- matches[matches.index('${%s}' % match)] = '${%s}' % loop_match
+ matches[matches.index(f'${{{match}}}')] = f'${{{loop_match}}}'
match = loop_match
elif '[' in match:
match, _ = match.split('[')
diff --git a/panel/pane/base.py b/panel/pane/base.py
index 366a82c892..a50bd542ec 100644
--- a/panel/pane/base.py
+++ b/panel/pane/base.py
@@ -454,8 +454,8 @@ def get_pane_type(cls, obj: Any, **kwargs) -> type['PaneBase']:
raise ValueError('If a Pane declares no priority '
'the applies method should return a '
'priority value specific to the '
- 'object type or False, but the %s pane '
- 'declares no priority.' % p.__name__)
+ f'object type or False, but the {p.__name__} pane '
+ 'declares no priority.')
elif priority is None or priority is False:
continue
descendents.append((priority, applies, p))
@@ -469,7 +469,7 @@ def get_pane_type(cls, obj: Any, **kwargs) -> type['PaneBase']:
if not applies:
continue
return pane_type
- raise TypeError('%s type could not be rendered.' % type(obj).__name__)
+ raise TypeError(f'{type(obj).__name__} type could not be rendered.')
class ModelPane(PaneBase):
diff --git a/panel/pane/holoviews.py b/panel/pane/holoviews.py
index ce3f04214b..0f727619b0 100644
--- a/panel/pane/holoviews.py
+++ b/panel/pane/holoviews.py
@@ -591,7 +591,7 @@ def widgets_from_dimensions(cls, object, widget_types=None, widgets_type='indivi
object = object.hmap
if isinstance(object, DynamicMap) and object.unbounded:
- dims = ', '.join('%r' % dim for dim in object.unbounded)
+ dims = ', '.join(f'{dim!r}' for dim in object.unbounded)
msg = ('DynamicMap cannot be displayed without explicit indexing '
'as {dims} dimension(s) are unbounded. '
'\nSet dimensions bounds with the DynamicMap redim.range '
diff --git a/panel/pane/image.py b/panel/pane/image.py
index a9a1c2b146..e98c0f60db 100644
--- a/panel/pane/image.py
+++ b/panel/pane/image.py
@@ -425,8 +425,8 @@ def applies(cls, obj: Any) -> float | bool | None:
def _type_error(self, object):
if isinstance(object, str):
- raise ValueError("%s pane cannot parse string that is not a filename, "
- "URL or a SVG XML contents." % type(self).__name__)
+ raise ValueError(f"{type(self).__name__} pane cannot parse string that is not a filename, "
+ "URL or a SVG XML contents.")
super()._type_error(object)
def _data(self, obj):
diff --git a/panel/pane/vtk/synchronizable_deserializer.py b/panel/pane/vtk/synchronizable_deserializer.py
index 26d928207b..b32e3009ec 100644
--- a/panel/pane/vtk/synchronizable_deserializer.py
+++ b/panel/pane/vtk/synchronizable_deserializer.py
@@ -31,7 +31,7 @@ def capitalize(name):
def fill_array(vtk_arr, state, zf):
vtk_arr.SetNumberOfComponents(state['numberOfComponents'])
vtk_arr.SetNumberOfTuples(state['size']//state['numberOfComponents'])
- data = zf.read('data/%s' % state['hash'])
+ data = zf.read('data/{}'.format(state['hash']))
dataType = arrayTypesMapping[vtk_arr.GetDataType()]
elementSize = struct.calcsize(dataType)
if vtk_arr.GetDataType() == 12:
diff --git a/panel/pane/vtk/synchronizable_serializer.py b/panel/pane/vtk/synchronizable_serializer.py
index cee47f78e5..9d9d2668c6 100644
--- a/panel/pane/vtk/synchronizable_serializer.py
+++ b/panel/pane/vtk/synchronizable_serializer.py
@@ -71,7 +71,7 @@ def getJSArrayType(dataArray):
def zipCompression(name, data):
with io.BytesIO() as in_memory:
with zipfile.ZipFile(in_memory, mode="w") as zf:
- zf.writestr('data/%s' % name,
+ zf.writestr(f'data/{name}',
data, zipfile.ZIP_DEFLATED)
in_memory.seek(0)
return in_memory.read()
@@ -393,7 +393,7 @@ def pad(depth):
def wrapId(idStr):
- return 'instance:${%s}' % idStr
+ return f'instance:${{{idStr}}}'
# -----------------------------------------------------------------------------
@@ -570,7 +570,7 @@ def extractRequiredFields(extractedFields, parent, dataset, context, requestedFi
def annotationSerializer(parent, prop, propId, context, depth):
if context.debugSerializers:
- print('%s!!!Annotations are not handled directly by vtk.js but by bokeh model' % pad(depth))
+ print(f'{pad(depth)}!!!Annotations are not handled directly by vtk.js but by bokeh model')
context.addAnnotation(parent, prop, propId)
@@ -1243,8 +1243,7 @@ def rendererSerializer(parent, instance, objId, context, depth):
dependencies.append(viewPropInstance)
viewPropIds.append(viewPropId)
- calls += context.buildDependencyCallList('%s-props' %
- objId, viewPropIds, 'addViewProp', 'removeViewProp')
+ calls += context.buildDependencyCallList(f'{objId}-props', viewPropIds, 'addViewProp', 'removeViewProp')
return {
'parent': context.getReferenceId(parent),
diff --git a/panel/pane/vtk/vtk.py b/panel/pane/vtk/vtk.py
index 06e610d4e2..0e1c9bf49d 100644
--- a/panel/pane/vtk/vtk.py
+++ b/panel/pane/vtk/vtk.py
@@ -326,7 +326,7 @@ def export_scene(self, filename='vtk_scene', all_data_arrays=False):
with zipfile.ZipFile(filename, mode='w') as zf:
zf.writestr('index.json', json.dumps(scene))
for name, data in arrays.items():
- zf.writestr('data/%s' % name, data, zipfile.ZIP_DEFLATED)
+ zf.writestr(f'data/{name}', data, zipfile.ZIP_DEFLATED)
zf.writestr('annotations.json', json.dumps(annotations))
return filename
diff --git a/panel/param.py b/panel/param.py
index 96a775775c..119a5ba946 100644
--- a/panel/param.py
+++ b/panel/param.py
@@ -270,8 +270,7 @@ def __init__(self, object=None, **params):
self.layout = self._expand_layout = layout(self._widget_box, **kwargs)
else:
raise ValueError('expand_layout expected to be a panel.layout.Panel'
- 'type or instance, found %s type.' %
- type(layout).__name__)
+ f'type or instance, found {type(layout).__name__} type.')
self.param.watch(self._update_widgets, [
'object', 'parameters', 'name', 'display_threshold', 'expand_button',
'expand', 'expand_layout', 'widgets', 'show_labels', 'show_name',
@@ -294,7 +293,7 @@ def __repr__(self, depth=0):
params.append(f'{p}={abbreviated_repr(v)}')
except RuntimeError:
params.append('{}={}'.format(p, '...'))
- obj = 'None' if self.object is None else '%s' % type(self.object).__name__
+ obj = 'None' if self.object is None else f'{type(self.object).__name__}'
template = '{cls}({obj}, {params})' if params else '{cls}({obj})'
return template.format(cls=cls, params=', '.join(params), obj=obj)
@@ -1314,7 +1313,7 @@ def __call__(self, parameterized):
with open(fullpath(fname), 'r') as f:
spec = json.load(f)
except Exception:
- warnobj.warning('Could not load JSON file %r' % spec)
+ warnobj.warning(f'Could not load JSON file {spec!r}')
else:
spec = json.loads(env_var)
diff --git a/panel/pipeline.py b/panel/pipeline.py
index 729a3a6e0d..84eacc7eab 100644
--- a/panel/pipeline.py
+++ b/panel/pipeline.py
@@ -226,7 +226,7 @@ def __panel__(self):
def _validate(self, stage):
if any(stage is s for n, (s, kw) in self._stages.items()):
- raise ValueError('Stage %s is already in pipeline' % stage)
+ raise ValueError(f'Stage {stage} is already in pipeline')
elif not ((isinstance(stage, type) and issubclass(stage, param.Parameterized))
or isinstance(stage, param.Parameterized)):
raise ValueError('Pipeline stages must be Parameterized classes or instances.')
@@ -550,7 +550,7 @@ def add_stage(self, name, stage, **kwargs):
self._validate(stage)
for k in kwargs:
if k not in self.param:
- raise ValueError("Keyword argument %s is not a valid parameter. " % k)
+ raise ValueError(f"Keyword argument {k} is not a valid parameter.")
if not self._linear and self._graph:
raise RuntimeError("Cannot add stage after graph has been defined.")
@@ -609,8 +609,7 @@ def define_graph(self, graph, force=False):
root = get_root(graph)
if not is_traversable(root, graph, stages):
- raise ValueError('Graph is not fully traversable from stage: %s.'
- % root)
+ raise ValueError(f'Graph is not fully traversable from stage: {root}.')
reinit = root is not self._stage
self._stage = root
diff --git a/panel/reactive.py b/panel/reactive.py
index 7ec5a079ac..dbe987854b 100644
--- a/panel/reactive.py
+++ b/panel/reactive.py
@@ -1795,11 +1795,11 @@ def _get_template(self) -> tuple[str, list[str], Mapping[str, list[tuple[str, li
for parent_var, obj in self._parser.loop_map.items():
for var in self._parser.loop_var_map[parent_var]:
template_string = template_string.replace(
- '${%s}' % var, '${%s[{{ loop.index0 }}]}' % obj)
+ '${%s}' % var, '${%s[{{ loop.index0 }}]}' % obj) # noqa: UP031
# Add index to templated loop node ids
for dom_node, _ in self._parser.looped:
- replacement = 'id="%s-{{ loop.index0 }}"' % dom_node
+ replacement = 'id="%s-{{ loop.index0 }}"' % dom_node # noqa: UP031
if f'id="{dom_node}"' in template_string:
template_string = template_string.replace(
f'id="{dom_node}"', replacement)
@@ -1853,7 +1853,7 @@ def _get_template(self) -> tuple[str, list[str], Mapping[str, list[tuple[str, li
for i, _ in enumerate(getattr(self, child_name)):
html = html.replace('${%s[%d]}' % (child_name, i), '')
else:
- html = html.replace('${%s}' % child_name, '')
+ html = html.replace(f'${{{child_name}}}', '')
return html, parser.nodes, p_attrs
@property
diff --git a/panel/template/base.py b/panel/template/base.py
index 7d3eba3de6..71c582597d 100644
--- a/panel/template/base.py
+++ b/panel/template/base.py
@@ -962,10 +962,10 @@ def add_panel(self, name: str, panel: Viewable, tags: list[str] = []) -> None:
A Panel component to embed in the template.
"""
if name in self._render_items:
- raise ValueError('The name %s has already been used for '
+ raise ValueError(f'The name {name} has already been used for '
'another panel. Ensure each panel '
'has a unique name by which it can be '
- 'referenced in the template.' % name)
+ 'referenced in the template.')
self._render_items[name] = (_panel(panel), tags)
self._layout[0].object = repr(self) # type: ignore
@@ -982,8 +982,8 @@ def add_variable(self, name: str, value: Any) -> None:
Any valid Jinja2 variable type.
"""
if name in self._render_variables:
- raise ValueError('The name %s has already been used for '
+ raise ValueError(f'The name {name} has already been used for '
'another variable. Ensure each variable '
'has a unique name by which it can be '
- 'referenced in the template.' % name)
+ 'referenced in the template.')
self._render_variables[name] = value
diff --git a/panel/tests/io/test_embed.py b/panel/tests/io/test_embed.py
index 597cc53eb0..85f590ac36 100644
--- a/panel/tests/io/test_embed.py
+++ b/panel/tests/io/test_embed.py
@@ -90,7 +90,7 @@ def link(target, event):
assert event['kind'] == 'ModelChanged'
assert event['attr'] == 'text'
assert event['model'] == model.children[1].ref
- assert event['new'] == '<pre>%s</pre>' % k
+ assert event['new'] == f'<pre>{k}</pre>'
def test_embed_float_slider_explicit_values(document, comm):
select = FloatSlider()
@@ -114,12 +114,12 @@ def link(target, event):
assert event1['kind'] == 'ModelChanged'
assert event1['attr'] == 'text'
assert event1['model'] == model.children[0].children[0].ref
- assert event1['new'] == '%s' % states[k]
+ assert event1['new'] == f'{states[k]}'
assert event2['kind'] == 'ModelChanged'
assert event2['attr'] == 'text'
assert event2['model'] == model.children[1].ref
- assert event2['new'] == '<pre>%s</pre>' % states[k]
+ assert event2['new'] == f'<pre>{states[k]}</pre>'
def test_embed_editable_float_slider_explicit_values(document, comm):
select = EditableFloatSlider()
@@ -143,7 +143,7 @@ def link(target, event):
assert event1['kind'] == 'ModelChanged'
assert event1['attr'] == 'text'
assert event1['model'] == model.children[0].children[1].children[0].ref
- assert event1['new'] == '%s' % states[k]
+ assert event1['new'] == f'{states[k]}'
assert event2['kind'] == 'ModelChanged'
assert event2['attr'] == 'value'
@@ -208,7 +208,7 @@ def link(target, event):
assert event['kind'] == 'ModelChanged'
assert event['attr'] == 'text'
assert event['model'] == model.children[1].ref
- assert event['new'] == '<pre>%s</pre>' % k
+ assert event['new'] == f'<pre>{k}</pre>'
def test_embed_select_str_explicit_values_not_found(document, comm):
@@ -258,13 +258,13 @@ def test_embed_select_str_link_two_steps(document, comm):
assert event['kind'] == 'ModelChanged'
assert event['attr'] == 'text'
assert event['model'] == model.children[1].ref
- assert event['new'] == '<pre>%s</pre>' % k
+ assert event['new'] == f'<pre>{k}</pre>'
event = events[1]
assert event['kind'] == 'ModelChanged'
assert event['attr'] == 'text'
assert event['model'] == model.children[2].ref
- assert event['new'] == '<pre>%s</pre>' % k
+ assert event['new'] == f'<pre>{k}</pre>'
def test_embed_select_str_link_with_secondary_watch(document, comm):
@@ -287,7 +287,7 @@ def test_embed_select_str_link_with_secondary_watch(document, comm):
assert event['kind'] == 'ModelChanged'
assert event['attr'] == 'text'
assert event['model'] == model.children[1].ref
- assert event['new'] == '<pre>%s</pre>' % k
+ assert event['new'] == f'<pre>{k}</pre>'
def test_embed_select_str_jslink(document, comm):
@@ -447,7 +447,7 @@ def link(target, event):
assert event2['kind'] == 'ModelChanged'
assert event2['attr'] == 'text'
assert event2['model'] == model.children[1].ref
- assert event2['new'] == '<pre>%.1f</pre>' % values[k]
+ assert event2['new'] == f'<pre>{values[k]:.1f}</pre>'
def test_embed_slider_str_jslink(document, comm):
@@ -600,7 +600,7 @@ def link(target, event):
event = events[0]
assert event['kind'] == 'ModelChanged'
assert event['attr'] == 'text'
- assert event['new'] == '<pre>%s</pre>' % v
+ assert event['new'] == f'<pre>{v}</pre>'
def test_embed_widget_disabled(document, comm):
select = Select(options=['A', 'B', 'C'], disabled=True)
diff --git a/panel/tests/layout/test_base.py b/panel/tests/layout/test_base.py
index 61c3022fcc..2b180e9a0d 100644
--- a/panel/tests/layout/test_base.py
+++ b/panel/tests/layout/test_base.py
@@ -135,7 +135,7 @@ def test_layout_repr(panel):
layout = panel(div1, div2)
name = panel.__name__
- assert repr(layout) == '%s\n [0] Bokeh(Div)\n [1] Bokeh(Div)' % name
+ assert repr(layout) == f'{name}\n [0] Bokeh(Div)\n [1] Bokeh(Div)'
@pytest.mark.parametrize('panel', [Card, Column, Row])
diff --git a/panel/tests/test_links.py b/panel/tests/test_links.py
index de84cdf3c4..1cfa18de66 100644
--- a/panel/tests/test_links.py
+++ b/panel/tests/test_links.py
@@ -352,4 +352,4 @@ def test_link_with_customcode(document, comm):
link_customjs = range_slider.js_property_callbacks['change:value'][-1]
assert link_customjs.args['source'] is range_slider
assert link_customjs.args['x_range'] is x_range
- assert link_customjs.code == "try { %s } catch(err) { console.log(err) }" % code
+ assert link_customjs.code == f"try {{ {code} }} catch(err) {{ console.log(err) }}"
diff --git a/panel/tests/ui/widgets/test_tabulator.py b/panel/tests/ui/widgets/test_tabulator.py
index 823595e146..09b5b61c8e 100644
--- a/panel/tests/ui/widgets/test_tabulator.py
+++ b/panel/tests/ui/widgets/test_tabulator.py
@@ -2247,7 +2247,7 @@ def test_tabulator_patching_no_event(page, df_mixed):
def color_false(val):
color = 'red' if not val else 'black'
- return 'color: %s' % color
+ return f'color: {color}'
def highlight_max(s):
is_max = s == s.max()
diff --git a/panel/tests/util.py b/panel/tests/util.py
index eeaefae783..9098480669 100644
--- a/panel/tests/util.py
+++ b/panel/tests/util.py
@@ -119,7 +119,7 @@ def check_layoutable_properties(layoutable, model):
def wait_until(fn, page=None, timeout=5000, interval=100):
"""
- Exercice a test function in a loop until it evaluates to True
+ Exercise a test function in a loop until it evaluates to True
or times out.
The function can either be a simple lambda that returns True or False:
diff --git a/panel/util/__init__.py b/panel/util/__init__.py
index ea25890586..5add871963 100644
--- a/panel/util/__init__.py
+++ b/panel/util/__init__.py
@@ -88,7 +88,7 @@ def indexOf(obj, objs):
return i
except Exception:
pass
- raise ValueError('%s not in list' % obj)
+ raise ValueError(f'{obj} not in list')
def param_name(name: str) -> str:
diff --git a/panel/viewable.py b/panel/viewable.py
index d8d6501fba..2649363d16 100644
--- a/panel/viewable.py
+++ b/panel/viewable.py
@@ -495,7 +495,7 @@ def _on_stdout(self, ref: str, stdout: Any) -> None:
if ref not in state._handles or config.console_output in [None, 'disable']:
return
handle, accumulator = state._handles[ref]
- formatted = ["%s" % o for o in stdout]
+ formatted = [f"{o}" for o in stdout]
if config.console_output == 'accumulate':
accumulator.extend(formatted)
elif config.console_output == 'replace':
diff --git a/panel/widgets/misc.py b/panel/widgets/misc.py
index 9bccdb9997..23e48cf2f8 100644
--- a/panel/widgets/misc.py
+++ b/panel/widgets/misc.py
@@ -211,7 +211,7 @@ def _sync_data(self, fileobj):
if isinstance(fileobj, (str, Path)):
fileobj = Path(fileobj)
if not fileobj.exists():
- raise FileNotFoundError('File "%s" not found.' % fileobj)
+ raise FileNotFoundError(f'File "{fileobj}" not found.')
with open(fileobj, 'rb') as f:
b64 = b64encode(f.read()).decode("utf-8")
if filename is None:
@@ -225,8 +225,7 @@ def _sync_data(self, fileobj):
raise ValueError('Must provide filename if file-like '
'object is provided.')
else:
- raise ValueError('Cannot transfer unknown object of type %s' %
- type(fileobj).__name__)
+ raise ValueError(f'Cannot transfer unknown object of type {type(fileobj).__name__}')
ext = filename.split('.')[-1]
stype, mtype = None, None
diff --git a/panel/widgets/select.py b/panel/widgets/select.py
index 41e46f6888..b563f807ba 100644
--- a/panel/widgets/select.py
+++ b/panel/widgets/select.py
@@ -144,8 +144,7 @@ def _get_embed_state(self, root, values=None, max_opts=3):
values = self.values
elif any(v not in self.values for v in values):
raise ValueError("Supplied embed states were not found "
- "in the %s widgets values list." %
- type(self).__name__)
+ f"in the {type(self).__name__} widgets values list.")
return (self, self._models[root.ref['id']][0], values,
lambda x: x.value, 'value', 'cb_obj.value')
@@ -1005,8 +1004,7 @@ def _get_embed_state(self, root, values=None, max_opts=3):
values = self.values
elif any(v not in self.values for v in values):
raise ValueError("Supplied embed states were not found in "
- "the %s widgets values list." %
- type(self).__name__)
+ f"the {type(self).__name__} widgets values list.")
return (self, self._models[root.ref['id']][0], values,
lambda x: x.active, 'active', 'cb_obj.active')
@@ -1209,7 +1207,7 @@ def __new__(cls, widget_type='button', behavior='check', **params):
else:
if isinstance(params.get('value'), list):
raise ValueError('Radio buttons require a single value, '
- 'found: %s' % params['value'])
+ 'found: {}'.format(params['value']))
if widget_type == 'button':
return RadioButtonGroup(**params)
else:
diff --git a/panel/widgets/slider.py b/panel/widgets/slider.py
index cd49d30b54..12ffab2376 100644
--- a/panel/widgets/slider.py
+++ b/panel/widgets/slider.py
@@ -138,8 +138,8 @@ def _get_embed_state(self, root, values=None, max_opts=3):
step = dtype(span/(max_opts-1))
values = [dtype(v) for v in np.arange(start, end+step, step)]
elif any(v < start or v > end for v in values):
- raise ValueError('Supplied embed states for %s widget outside '
- 'of valid range.' % type(self).__name__)
+ raise ValueError(f'Supplied embed states for {type(self).__name__} widget outside '
+ 'of valid range.')
# Replace model
layout_opts = {k: v for k, v in self.param.values().items()
@@ -374,10 +374,9 @@ def __init__(self, **params):
if self.value is None and None not in self.values and self.options:
self.value = self.values[0]
elif self.value not in self.values and not (self.value is None or self.options):
- raise ValueError('Value %s not a valid option, '
+ raise ValueError(f'Value {self.value} not a valid option, '
'ensure that the supplied value '
- 'is one of the declared options.'
- % self.value)
+ 'is one of the declared options.')
self._text = StaticText(
margin=(5, 0, 0, 5), styles={'white-space': 'nowrap'}
@@ -512,7 +511,7 @@ def _get_embed_state(self, root, values=None, max_opts=3):
values = self.values
elif any(v not in self.values for v in values):
raise ValueError("Supplieed embed states were not found "
- "in the %s widgets' values list." % type(self).__name__)
+ f"in the {type(self).__name__} widgets' values list.")
return self, model, values, lambda x: x.value, 'value', 'cb_obj.value'
@property
@@ -520,7 +519,7 @@ def labels(self):
"""The list of labels to display"""
title = (self.name + ': ' if self.name else '')
if isinstance(self.options, dict):
- return [title + ('%s' % o) for o in self.options]
+ return [title + (f'{o}') for o in self.options]
else:
return [title + ('%s' % (o if isinstance(o, str) else (self.formatter % o)))
for o in self.options]
diff --git a/panel/widgets/tables.py b/panel/widgets/tables.py
index 7c2860b99b..754700c0fe 100644
--- a/panel/widgets/tables.py
+++ b/panel/widgets/tables.py
@@ -247,7 +247,7 @@ def _get_column_definitions(self, col_names: list[str], df: pd.DataFrame) -> lis
title = self.titles.get(col, str(col))
if col in indexes and len(indexes) > 1 and self.hierarchical:
- title = 'Index: %s' % ' | '.join(indexes)
+ title = 'Index: {}'.format(' | '.join(indexes))
elif col in self.indexes and col.startswith('level_'):
title = ''
column = TableColumn(field=str(col), title=title,
diff --git a/panel/widgets/widget.py b/panel/widgets/widget.py
index 63e7161030..30d1fb7404 100644
--- a/panel/widgets/widget.py
+++ b/panel/widgets/widget.py
@@ -51,7 +51,7 @@ def _get_min_max_value(
value = min + (diff // 2)
else: # value is not None
if not isinstance(value, Real):
- raise TypeError('expected a real number, got: %r' % value)
+ raise TypeError(f'expected a real number, got: {value!r}')
# Infer min/max from value
if value == 0:
# This gives (0, 1) of the correct type
@@ -163,7 +163,7 @@ def widget_from_tuple(o, name, default=empty):
elif _matches(o, (Real, Real, Real)):
step = o[2]
if step <= 0:
- raise ValueError("step must be >= 0, not %r" % step)
+ raise ValueError(f"step must be >= 0, not {step!r}")
min, max, value = _get_min_max_value(o[0], o[1], step=step)
if all(isinstance(_, Integral) for _ in o) and int_default:
cls = IntSlider
@@ -173,7 +173,7 @@ def widget_from_tuple(o, name, default=empty):
elif _matches(o, (Real, Real, Real, Real)):
step = o[2]
if step <= 0:
- raise ValueError("step must be >= 0, not %r" % step)
+ raise ValueError(f"step must be >= 0, not {step!r}")
min, max, value = _get_min_max_value(o[0], o[1], value=o[3], step=step)
if all(isinstance(_, Integral) for _ in o):
cls = IntSlider