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