diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 73353d59c4..e1d5c48d7a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: exclude: \.min\.js$ - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.276 + rev: v0.0.284 hooks: - id: ruff args: [holoviews] diff --git a/holoviews/core/data/__init__.py b/holoviews/core/data/__init__.py index 8a89627ae8..3c449a7946 100644 --- a/holoviews/core/data/__init__.py +++ b/holoviews/core/data/__init__.py @@ -454,7 +454,7 @@ def closest(self, coords=[], **kwargs): if len(kwargs) > 1: raise NotImplementedError("Closest method currently only " "supports 1D indexes") - samples = list(kwargs.values())[0] + samples = next(iter(kwargs.values())) coords = samples if isinstance(samples, list) else [samples] xs = self.dimension_values(0) diff --git a/holoviews/core/data/dictionary.py b/holoviews/core/data/dictionary.py index 8f93e6f9c8..53dcd1f838 100644 --- a/holoviews/core/data/dictionary.py +++ b/holoviews/core/data/dictionary.py @@ -141,7 +141,7 @@ def unpack_scalar(cls, dataset, data): """ if len(data) != 1: return data - key = list(data.keys())[0] + key = next(iter(data.keys())) if len(data[key]) == 1 and key in dataset.vdims: scalar = data[key][0] @@ -331,7 +331,7 @@ def select(cls, dataset, selection_mask=None, **selection): data[k] = v else: data[k] = v[selection_mask] - if indexed and len(list(data.values())[0]) == 1 and len(dataset.vdims) == 1: + if indexed and len(next(iter(data.values()))) == 1 and len(dataset.vdims) == 1: value = data[dataset.vdims[0].name] return value if isscalar(value) else value[0] return data diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index f7cefa6a9b..a92c779142 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -132,9 +132,9 @@ def init(cls, eltype, data, kdims, vdims): 'by the key dimensions. Expected %d-D array, ' 'found %d-D array.' % (vdim, len(expected), len(shape))) elif any((s!=e and (s+1)!=e) for s, e in zip(shape, valid_shape)): - raise error('Key dimension values and value array {} ' - 'shapes do not match. Expected shape {}, ' - 'actual shape: {}'.format(vdim, valid_shape, shape), cls) + raise error(f'Key dimension values and value array {vdim} ' + f'shapes do not match. Expected shape {valid_shape}, ' + f'actual shape: {shape}', cls) return data, {'kdims':kdims, 'vdims':vdims}, {} @@ -557,8 +557,8 @@ def select(cls, dataset, selection_mask=None, **selection): if irregular: if np.isscalar(ind) or isinstance(ind, (set, list)): raise IndexError("Indexing not supported for irregularly " - "sampled data. {} value along {} dimension." - "must be a slice or 2D boolean mask.".format(ind, dim)) + f"sampled data. {ind} value along {dim} dimension." + "must be a slice or 2D boolean mask.") mask = mask.max(axis=i) elif dataset._binned: edges = cls.coords(dataset, dim, False, edges=True) @@ -566,8 +566,8 @@ def select(cls, dataset, selection_mask=None, **selection): if np.isscalar(ind): emin, emax = edges.min(), edges.max() if ind < emin: - raise IndexError("Index {} less than lower bound " - "of {} for {} dimension.".format(ind, emin, dim)) + raise IndexError(f"Index {ind} less than lower bound " + f"of {emin} for {dim} dimension.") elif ind >= emax: raise IndexError("Index {} more than or equal to upper bound " "of {} for {} dimension.".format(ind, emax, dim)) diff --git a/holoviews/core/data/image.py b/holoviews/core/data/image.py index a6a15d78d8..5e556af243 100644 --- a/holoviews/core/data/image.py +++ b/holoviews/core/data/image.py @@ -55,9 +55,9 @@ def init(cls, eltype, data, kdims, vdims): shape = data.shape[:2] error = DataError if len(shape) > 1 and not eltype._binned else ValueError if shape != expected and not (not expected and shape == (1,)): - raise error('Key dimension values and value array {} ' - 'shapes do not match. Expected shape {}, ' - 'actual shape: {}'.format(vdims[0], expected, shape), cls) + raise error(f'Key dimension values and value array {vdims[0]} ' + f'shapes do not match. Expected shape {expected}, ' + f'actual shape: {shape}', cls) if not isinstance(data, np.ndarray) or data.ndim not in [2, 3]: raise ValueError('ImageInterface expects a 2D array.') @@ -297,7 +297,7 @@ def unpack_scalar(cls, dataset, data): """ if np.isscalar(data) or len(data) != 1: return data - key = list(data.keys())[0] + key = next(iter(data.keys())) if len(data[key]) == 1 and key in dataset.vdims: return data[key][0] diff --git a/holoviews/core/data/multipath.py b/holoviews/core/data/multipath.py index b71d57d586..aea4bf4901 100644 --- a/holoviews/core/data/multipath.py +++ b/holoviews/core/data/multipath.py @@ -59,8 +59,8 @@ def init(cls, eltype, data, kdims, vdims): if hasattr(Interface.interfaces.get(dt), 'has_holes')] geom_type = d.get('geom_type') if geom_type is not None and geom_type not in cls.geom_types: - raise DataError("Geometry type '{}' not recognized, " - "must be one of {}.".format(geom_type, cls.geom_types)) + raise DataError(f"Geometry type '{geom_type}' not recognized, " + f"must be one of {cls.geom_types}.") else: datatype = [dt for dt in datatype if hasattr(Interface.interfaces.get(dt), 'geom_type')] diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index 1893f67b29..8b124e5384 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -229,13 +229,13 @@ def retrieve_unit_and_label(dim): undeclared.append(c) if undeclared and eltype.param.kdims.bounds[1] not in (0, None): raise DataError( - 'The coordinates on the {!r} DataArray do not match the ' + f'The coordinates on the {vdim.name!r} DataArray do not match the ' 'provided key dimensions (kdims). The following coords ' - 'were left unspecified: {!r}. If you are requesting a ' + f'were left unspecified: {undeclared!r}. If you are requesting a ' 'lower dimensional view such as a histogram cast ' 'the xarray to a columnar format using the .to_dataframe ' 'or .to_dask_dataframe methods before providing it to ' - 'HoloViews.'.format(vdim.name, undeclared)) + 'HoloViews.') return data, {'kdims': kdims, 'vdims': vdims}, {} diff --git a/holoviews/core/dimension.py b/holoviews/core/dimension.py index 95137db1da..b6dbf0884b 100644 --- a/holoviews/core/dimension.py +++ b/holoviews/core/dimension.py @@ -997,7 +997,7 @@ def get_dimension_index(self, dimension): dim = dimension_name(dimension) try: dimensions = self.kdims+self.vdims - return [i for i, d in enumerate(dimensions) if d == dim][0] + return next(i for i, d in enumerate(dimensions) if d == dim) except IndexError: raise Exception(f"Dimension {dim} not found in {self.__class__.__name__}.") diff --git a/holoviews/core/ndmapping.py b/holoviews/core/ndmapping.py index 9358f371a9..f12d41058b 100644 --- a/holoviews/core/ndmapping.py +++ b/holoviews/core/ndmapping.py @@ -170,8 +170,8 @@ def _add_item(self, dim_vals, data, sort=True, update=True): for dim, val in valid_vals: if dim.values and val is not None and val not in dim.values: - raise KeyError('{} dimension value {} not in' - ' specified dimension values.'.format(dim, repr(val))) + raise KeyError(f'{dim} dimension value {val!r} not in' + ' specified dimension values.') # Updates nested data structures rather than simply overriding them. if (update and (dim_vals in self.data) diff --git a/holoviews/core/options.py b/holoviews/core/options.py index 1bec9b1f79..cf341e2936 100644 --- a/holoviews/core/options.py +++ b/holoviews/core/options.py @@ -1292,7 +1292,7 @@ def lookup(cls, backend, obj): idlist = ",".join([str(el) for el in sorted(ids)]) raise Exception("Object contains elements combined across " "multiple custom trees (ids %s)" % idlist) - return cls._custom_options[backend][list(ids)[0]] + return cls._custom_options[backend][next(iter(ids))] @classmethod def transfer_options(cls, obj, new_obj, backend=None, names=None, level=3): diff --git a/holoviews/core/overlay.py b/holoviews/core/overlay.py index a94cd7238b..c8042dd3be 100644 --- a/holoviews/core/overlay.py +++ b/holoviews/core/overlay.py @@ -239,8 +239,8 @@ def group(self): values = {el.group for el in elements} types = {type(el) for el in elements} if values: - group = list(values)[0] - vtype = list(types)[0].__name__ + group = next(iter(values)) + vtype = next(iter(types)).__name__ else: group, vtype = [], '' if len(values) == 1 and group != vtype: @@ -262,7 +262,7 @@ def label(self): labels = {el.label for el in self if not el._auxiliary_component} if len(labels) == 1: - return list(labels)[0] + return next(iter(labels)) else: return '' diff --git a/holoviews/core/pprint.py b/holoviews/core/pprint.py index b2e6369d8e..7ca8dd600b 100644 --- a/holoviews/core/pprint.py +++ b/holoviews/core/pprint.py @@ -146,7 +146,7 @@ def info(cls, obj, ansi=False, backend='matplotlib', visualization=True, plot_class = backend_registry.get(obj if isclass else type(obj), None) # Special case to handle PlotSelectors if hasattr(plot_class, 'plot_classes'): - plot_class = list(plot_class.plot_classes.values())[0] + plot_class = next(iter(plot_class.plot_classes.values())) if visualization is False or plot_class is None: @@ -189,13 +189,13 @@ def target_info(cls, obj, ansi=False): element_info = None if len(element_set) == 1: - element_info = f'Element: {list(element_set)[0]}' + element_info = f'Element: {next(iter(element_set))}' elif len(element_set) > 1: element_info = 'Elements:\n %s' % '\n '.join(sorted(element_set)) container_info = None if len(container_set) == 1: - container_info = f'Container: {list(container_set)[0]}' + container_info = f'Container: {next(iter(container_set))}' elif len(container_set) > 1: container_info = 'Containers:\n %s' % '\n '.join(sorted(container_set)) heading = cls.heading('Target Specifications', ansi=ansi, char="-") diff --git a/holoviews/core/util.py b/holoviews/core/util.py index d03d5c83eb..8fbfc1bea9 100644 --- a/holoviews/core/util.py +++ b/holoviews/core/util.py @@ -2024,7 +2024,7 @@ def bound_range(vals, density, time_unit='us'): full_precision_density = compute_density(low, high, len(vals)-1) with np.errstate(over='ignore'): density = round(full_precision_density, sys.float_info.dig) - if density == 0 or density == np.inf: + if density in (0, np.inf): density = full_precision_density if density == 0: raise ValueError('Could not determine Image density, ensure it has a non-zero range.') diff --git a/holoviews/element/chart.py b/holoviews/element/chart.py index 5466237f94..62e8a055d9 100644 --- a/holoviews/element/chart.py +++ b/holoviews/element/chart.py @@ -4,7 +4,7 @@ from ..core import util from ..core import Dimension, Dataset, Element2D, NdOverlay, Overlay from ..core.dimension import process_dimensions -from .geom import Rectangles, Points, VectorField # noqa: backward compatible import +from .geom import Rectangles, Points, VectorField # noqa: F401 backward compatible import from .selection import Selection1DExpr diff --git a/holoviews/element/raster.py b/holoviews/element/raster.py index 9856e0b50f..7850ba3437 100644 --- a/holoviews/element/raster.py +++ b/holoviews/element/raster.py @@ -126,7 +126,7 @@ def sample(self, samples=[], bounds=None, **sample_values): params['kdims'] = self.kdims return Table(table_data, **params) else: - dimension, sample_coord = list(sample_values.items())[0] + dimension, sample_coord = next(iter(sample_values.items())) if isinstance(sample_coord, slice): raise ValueError( 'Raster sampling requires coordinates not slices,' diff --git a/holoviews/operation/datashader.py b/holoviews/operation/datashader.py index 1101f8d599..a5ff8ac9bf 100644 --- a/holoviews/operation/datashader.py +++ b/holoviews/operation/datashader.py @@ -101,8 +101,8 @@ def _get_aggregator(cls, element, agg, add_field=True): if isinstance(agg, str): if agg not in cls._agg_methods: agg_methods = sorted(cls._agg_methods) - raise ValueError("Aggregation method '{!r}' is not known; " - "aggregator must be one of: {!r}".format(agg, agg_methods)) + raise ValueError(f"Aggregation method '{agg!r}' is not known; " + f"aggregator must be one of: {agg_methods!r}") if agg == 'count_cat': agg = cls._agg_methods[agg]('__temp__') else: @@ -968,10 +968,10 @@ def _precompute(self, element, agg): else: p, n = 'vertices', 'simplexes' self.param.warning( - "TriMesh {} were provided as dask DataFrame but {} " + f"TriMesh {p} were provided as dask DataFrame but {n} " "were not. Datashader will not use dask to parallelize " "rasterization unless both are provided as dask " - "DataFrames.".format(p, n)) + "DataFrames.") simplices = element.dframe(simplex_dims) verts = element.nodes.dframe(vert_dims) for c, dtype in zip(simplices.columns[:3], simplices.dtypes): diff --git a/holoviews/operation/element.py b/holoviews/operation/element.py index e8373c2089..58cde6da18 100644 --- a/holoviews/operation/element.py +++ b/holoviews/operation/element.py @@ -711,7 +711,7 @@ def _process(self, element, key=None): if self.p.dimension: selected_dim = self.p.dimension else: - selected_dim = [d.name for d in element.vdims + element.kdims][0] + selected_dim = next(d.name for d in element.vdims + element.kdims) dim = element.get_dimension(selected_dim) if hasattr(element, 'interface'): diff --git a/holoviews/plotting/bokeh/callbacks.py b/holoviews/plotting/bokeh/callbacks.py index 12345edd67..d219f518ff 100644 --- a/holoviews/plotting/bokeh/callbacks.py +++ b/holoviews/plotting/bokeh/callbacks.py @@ -1023,7 +1023,7 @@ def _update_cds_vdims(self, data): if dim in data: continue values = element.dimension_values(d) - if len(values) != len(list(data.values())[0]): + if len(values) != len(next(iter(data.values()))): values = np.concatenate([values, [stream.empty_value]]) data[dim] = values diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py index 631e054bd9..6165b17c12 100644 --- a/holoviews/plotting/bokeh/chart.py +++ b/holoviews/plotting/bokeh/chart.py @@ -155,7 +155,7 @@ def get_batched_data(self, element, ranges): continue # Apply static styles - nvals = len(list(eldata.values())[0]) + nvals = len(next(iter(eldata.values()))) sdata, smapping = expand_batched_style(style, self._batched_style_opts, elmapping, nvals) if 'angle' in sdata and '__angle' not in data and 'marker' in data: @@ -864,7 +864,7 @@ def _add_color_data(self, ds, ranges, style, cdim, data, mapping, factors, color for k, cd in cdata.items(): if isinstance(cmapper, CategoricalColorMapper) and cd.dtype.kind in 'uif': cd = categorize_array(cd, cdim) - if k not in data or len(data[k]) != [len(data[key]) for key in data if key != k][0]: + if k not in data or len(data[k]) != next(len(data[key]) for key in data if key != k): data[k].append(cd) else: data[k][-1] = cd diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index 54661f0364..236fa8faa1 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -361,7 +361,7 @@ def _get_hover_data(self, data, element, dimensions=None): for k, v in self.overlay_dims.items(): dim = util.dimension_sanitizer(k.name) if dim not in data: - data[dim] = [v for _ in range(len(list(data.values())[0]))] + data[dim] = [v] * len(next(iter(data.values()))) def _shared_axis_range(self, plots, specs, range_type, axis_type, pos): """ @@ -1400,15 +1400,15 @@ def _apply_transforms(self, element, data, ranges, style, group=None): elif isinstance(element, Graph) and v in element.nodes: v = dim(element.nodes.get_dimension(v)) elif any(d==v for d in self.overlay_dims): - v = dim([d for d in self.overlay_dims if d==v][0]) + v = dim(next(d for d in self.overlay_dims if d==v)) if (not isinstance(v, dim) or (group is not None and not k.startswith(group))): continue elif (not v.applies(element) and v.dimension not in self.overlay_dims): new_style.pop(k) self.param.warning( - 'Specified {} dim transform {!r} could not be applied, ' - 'as not all dimensions could be resolved.'.format(k, v)) + f'Specified {k} dim transform {v!r} could not be applied, ' + 'as not all dimensions could be resolved.') continue if v.dimension in self.overlay_dims: @@ -1433,7 +1433,7 @@ def _apply_transforms(self, element, data, ranges, style, group=None): 'to overlay your data along the dimension.'.format( style=k, dim=v.dimension, element=element, backend=self.renderer.backend)) - elif data and len(val) != len(list(data.values())[0]): + elif data and len(val) != len(next(iter(data.values()))): if isinstance(element, VectorField): val = np.tile(val, 3) elif isinstance(element, Path) and not isinstance(element, Contours): @@ -1638,7 +1638,7 @@ def _update_glyph(self, renderer, properties, mapping, glyph, source, data): prop = 'value' if 'label' in lp else 'field' label = {prop: legend} elif isinstance(item.label, dict): - label = {list(item.label)[0]: legend} + label = {next(iter(item.label)): legend} else: label = {'value': legend} item.label = label diff --git a/holoviews/plotting/bokeh/path.py b/holoviews/plotting/bokeh/path.py index b20176fb9f..589dea09bb 100644 --- a/holoviews/plotting/bokeh/path.py +++ b/holoviews/plotting/bokeh/path.py @@ -72,7 +72,7 @@ def _get_hover_data(self, data, element): for k, v in self.overlay_dims.items(): dim = util.dimension_sanitizer(k.name) if dim not in data: - data[dim] = [v for _ in range(len(list(data.values())[0]))] + data[dim] = [v] * len(next(iter(data.values()))) def get_data(self, element, ranges, style): @@ -159,7 +159,7 @@ def get_batched_data(self, element, ranges=None): continue # Apply static styles - nvals = len(list(eldata.values())[0]) + nvals = len(next(iter(eldata.values()))) sdata, smapping = expand_batched_style(style, self._batched_style_opts, elmapping, nvals) elmapping.update({k: v for k, v in smapping.items() if k not in elmapping}) @@ -219,7 +219,7 @@ def _get_hover_data(self, data, element): for k, v in self.overlay_dims.items(): dim = util.dimension_sanitizer(k.name) if dim not in data: - data[dim] = [v for _ in range(len(list(data.values())[0]))] + data[dim] = [v] * len(next(iter(data.values()))) def get_data(self, element, ranges, style): if self._has_holes is None: diff --git a/holoviews/plotting/mpl/chart.py b/holoviews/plotting/mpl/chart.py index ed7d73bcd9..5e9b371cc8 100644 --- a/holoviews/plotting/mpl/chart.py +++ b/holoviews/plotting/mpl/chart.py @@ -335,11 +335,9 @@ def initialize_plot(self, ranges=None): if 'vmin' in style: raise ValueError('Mapping a continuous dimension to a ' 'color on a HistogramPlot is not ' - 'supported by the {backend} backend. ' + f'supported by the {self.renderer.backend} backend. ' 'To map a dimension to a color supply ' - 'an explicit list of rgba colors.'.format( - backend=self.renderer.backend - ) + 'an explicit list of rgba colors.' ) # Plot bars and make any adjustments @@ -631,8 +629,8 @@ def _compute_styles(self, element, ranges, style): if sizes is None: eltype = type(element).__name__ self.param.warning( - '{} dimension is not numeric, cannot use to ' - 'scale {} size.'.format(sdim.pprint_label, eltype)) + f'{sdim.pprint_label} dimension is not numeric, cannot use to ' + f'scale {eltype} size.') else: style['s'] = sizes style['edgecolors'] = style.pop('edgecolors', 'none') diff --git a/holoviews/plotting/mpl/element.py b/holoviews/plotting/mpl/element.py index b6ee5e6657..be415a4b2f 100644 --- a/holoviews/plotting/mpl/element.py +++ b/holoviews/plotting/mpl/element.py @@ -106,8 +106,8 @@ def __init__(self, element, **params): try: hook(self, element) except Exception as e: - self.param.warning("Plotting hook {!r} could not be " - "applied:\n\n {}".format(hook, e)) + self.param.warning(f"Plotting hook {hook!r} could not be " + f"applied:\n\n {e}") def _finalize_axis(self, key, element=None, title=None, dimensions=None, ranges=None, xticks=None, yticks=None, zticks=None, xlabel=None, ylabel=None, zlabel=None): @@ -616,15 +616,15 @@ def _apply_transforms(self, element, ranges, style): elif v in element or (isinstance(element, Graph) and v in element.nodes): v = dim(v) elif any(d==v for d in self.overlay_dims): - v = dim([d for d in self.overlay_dims if d==v][0]) + v = dim(next(d for d in self.overlay_dims if d==v)) if not isinstance(v, dim): continue elif (not v.applies(element) and v.dimension not in self.overlay_dims): new_style.pop(k) self.param.warning( - 'Specified {} dim transform {!r} could not be ' - 'applied, as not all dimensions could be resolved.'.format(k, v)) + f'Specified {k} dim transform {v!r} could not be ' + 'applied, as not all dimensions could be resolved.') continue if v.dimension in self.overlay_dims: @@ -643,14 +643,12 @@ def _apply_transforms(self, element, ranges, style): if not np.isscalar(val) and k in self._nonvectorized_styles: element = type(element).__name__ - raise ValueError('Mapping a dimension to the "{style}" ' + raise ValueError(f'Mapping a dimension to the "{k}" ' 'style option is not supported by the ' - '{element} element using the {backend} ' - 'backend. To map the "{dim}" dimension ' - 'to the {style} use a groupby operation ' - 'to overlay your data along the dimension.'.format( - style=k, dim=v.dimension, element=element, - backend=self.renderer.backend)) + f'{element} element using the {self.renderer.backend} ' + f'backend. To map the "{v.dimension}" dimension ' + f'to the {k} use a groupby operation ' + 'to overlay your data along the dimension.') style_groups = getattr(self, '_style_groups', []) groups = [sg for sg in style_groups if k.startswith(sg)] diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py index 09bc53212d..8249c4f6bf 100644 --- a/holoviews/plotting/mpl/plot.py +++ b/holoviews/plotting/mpl/plot.py @@ -252,7 +252,7 @@ def anim(self, start=0, stop=None, fps=30): def update(self, key): - if len(self) == 1 and ((key == 0) or (key == self.keys[0])) and not self.drawn: + if len(self) == 1 and key in (0, self.keys[0]) and not self.drawn: return self.initialize_plot() return self.__getitem__(key) diff --git a/holoviews/plotting/plot.py b/holoviews/plotting/plot.py index 5bd2110937..29d60f51b0 100644 --- a/holoviews/plotting/plot.py +++ b/holoviews/plotting/plot.py @@ -666,7 +666,7 @@ def _merge_group_ranges(cls, ranges): if 'factors' in ranges: all_factors = ranges['factors'] factor_dtypes = {fs.dtype for fs in all_factors} if all_factors else [] - dtype = list(factor_dtypes)[0] if len(factor_dtypes) == 1 else None + dtype = next(iter(factor_dtypes)) if len(factor_dtypes) == 1 else None expanded = [v for fctrs in all_factors for v in fctrs] if dtype is not None: try: @@ -939,7 +939,7 @@ def _get_projection(cls, obj): return custom_projs[0] if custom_projs else None def update(self, key): - if len(self) == 1 and ((key == 0) or (key == self.keys[0])) and not self.drawn: + if len(self) == 1 and key in (0, self.keys[0]) and not self.drawn: return self.initialize_plot() item = self.__getitem__(key) self.traverse(lambda x: setattr(x, '_updated', True)) @@ -1296,8 +1296,8 @@ def _execute_hooks(self, element): try: hook(self, element) except Exception as e: - self.param.warning("Plotting hook {!r} could not be " - "applied:\n\n {}".format(hook, e)) + self.param.warning(f"Plotting hook {hook!r} could not be " + f"applied:\n\n {e}") def get_aspect(self, xspan, yspan): """ @@ -1934,7 +1934,7 @@ def _get_subplot_extents(self, overlay, ranges, range_type, dimension=None): items = overlay.items() if self.batched and self.subplots: - subplot = list(self.subplots.values())[0] + subplot = next(iter(self.subplots.values())) subplots = [(k, subplot) for k in overlay.data.keys()] else: subplots = self.subplots.items() diff --git a/holoviews/plotting/plotly/element.py b/holoviews/plotting/plotly/element.py index dd476f4e27..9cdf9630ea 100644 --- a/holoviews/plotting/plotly/element.py +++ b/holoviews/plotting/plotly/element.py @@ -340,7 +340,7 @@ def _apply_transforms(self, element, ranges, style): elif v in element: v = dim(v) elif any(d==v for d in self.overlay_dims): - v = dim([d for d in self.overlay_dims if d==v][0]) + v = dim(next(d for d in self.overlay_dims if d==v)) if not isinstance(v, dim): continue @@ -609,7 +609,7 @@ def get_color_opts(self, eldim, element, ranges, style): if isinstance(eldim, dim): title = str(eldim) if eldim.ops: - title = title + pass elif title.startswith("dim('") and title.endswith("')"): title = title[5:-2] else: @@ -682,7 +682,7 @@ def initialize_plot(self, ranges=None, is_geo=False): Initializes a new plot object with the last available frame. """ # Get element key and ranges for frame - return self.generate_plot(list(self.hmap.data.keys())[0], ranges, is_geo=is_geo) + return self.generate_plot(next(iter(self.hmap.data.keys())), ranges, is_geo=is_geo) def generate_plot(self, key, ranges, element=None, is_geo=False): if element is None: diff --git a/holoviews/plotting/renderer.py b/holoviews/plotting/renderer.py index 12579e974b..7a2af71a1b 100644 --- a/holoviews/plotting/renderer.py +++ b/holoviews/plotting/renderer.py @@ -324,8 +324,8 @@ def _apply_post_render_hooks(self, data, obj, fmt): try: data = hook(data, obj) except Exception as e: - self.param.warning("The post_render_hook {!r} could not " - "be applied:\n\n {}".format(hook, e)) + self.param.warning(f"The post_render_hook {hook!r} could not " + f"be applied:\n\n {e}") return data def html(self, obj, fmt=None, css=None, resources='CDN', **kwargs): diff --git a/holoviews/plotting/util.py b/holoviews/plotting/util.py index a1c28dc5d8..48f82bef24 100644 --- a/holoviews/plotting/util.py +++ b/holoviews/plotting/util.py @@ -50,8 +50,8 @@ class Warning(param.Parameterized): pass def collate(obj): if isinstance(obj, Overlay): - nested_type = [type(o).__name__ for o in obj - if isinstance(o, (HoloMap, GridSpace, AdjointLayout))][0] + nested_type = next(type(o).__name__ for o in obj + if isinstance(o, (HoloMap, GridSpace, AdjointLayout))) display_warning.param.warning( "Nesting %ss within an Overlay makes it difficult to " "access your data or control how it appears; we recommend " @@ -419,8 +419,8 @@ def get_sideplot_ranges(plot, element, main, ranges): range_item = main if isinstance(main, HoloMap): if issubclass(main.type, CompositeOverlay): - range_item = [hm for hm in main._split_overlays()[1] - if dim in hm.dimensions('all')][0] + range_item = next(hm for hm in main._split_overlays()[1] + if dim in hm.dimensions('all')) else: range_item = HoloMap({0: main}, kdims=['Frame']) ranges = match_spec(range_item.last, ranges) @@ -438,8 +438,8 @@ def get_sideplot_ranges(plot, element, main, ranges): if isinstance(range_item, HoloMap): range_item = range_item.last if isinstance(range_item, CompositeOverlay): - range_item = [ov for ov in range_item - if dim in ov.dimensions('all')][0] + range_item = next(ov for ov in range_item + if dim in ov.dimensions('all')) return range_item, main_range, dim @@ -684,8 +684,8 @@ def _list_cmaps(provider=None, records=False): provider = providers elif isinstance(provider, str): if provider not in providers: - raise ValueError('Colormap provider {!r} not recognized, must ' - 'be one of {!r}'.format(provider, providers)) + raise ValueError(f'Colormap provider {provider!r} not recognized, must ' + f'be one of {providers!r}') provider = [provider] cmaps = [] diff --git a/holoviews/streams.py b/holoviews/streams.py index bd49ec8136..fb4877ff15 100644 --- a/holoviews/streams.py +++ b/holoviews/streams.py @@ -625,7 +625,7 @@ def _concat(self, data): elif data_length > self.length: data = data.iloc[-self.length:] elif isinstance(data, dict) and data: - data_length = len(list(data.values())[0]) + data_length = len(next(iter(data.values()))) new_data = {} for k, v in data.items(): if not self.length: @@ -735,7 +735,7 @@ def from_params(cls, params, **kwargs): streams = [] for _, group in groupby(sorted(params.items(), key=key_fn), key_fn): group = list(group) - inst = [p.owner for _, p in group][0] + inst = next(p.owner for _, p in group) if inst is None: continue names = [p.name for _, p in group] diff --git a/holoviews/tests/plotting/bokeh/test_curveplot.py b/holoviews/tests/plotting/bokeh/test_curveplot.py index 0b1a45d5dd..b84ffde639 100644 --- a/holoviews/tests/plotting/bokeh/test_curveplot.py +++ b/holoviews/tests/plotting/bokeh/test_curveplot.py @@ -28,7 +28,7 @@ def test_batched_curve_subscribers_correctly_attached(self): streams=[posx]) plot = bokeh_renderer.get_plot(overlay) self.assertIn(plot.refresh, posx.subscribers) - self.assertNotIn(list(plot.subplots.values())[0].refresh, posx.subscribers) + self.assertNotIn(next(iter(plot.subplots.values())).refresh, posx.subscribers) def test_batched_curve_subscribers_correctly_linked(self): # Checks if a stream callback is created to link batched plot @@ -41,7 +41,7 @@ def test_batched_curve_subscribers_correctly_linked(self): streams=[posx]) plot = bokeh_renderer.get_plot(overlay) self.assertEqual(len(Callback._callbacks), 1) - key = list(Callback._callbacks.keys())[0] + key = next(iter(Callback._callbacks.keys())) self.assertEqual(key, (id(plot.handles['plot']), id(PointerXCallback))) def test_cyclic_palette_curves(self): diff --git a/holoviews/tests/plotting/bokeh/test_histogramplot.py b/holoviews/tests/plotting/bokeh/test_histogramplot.py index ecb9cb0933..9273a7a0f2 100644 --- a/holoviews/tests/plotting/bokeh/test_histogramplot.py +++ b/holoviews/tests/plotting/bokeh/test_histogramplot.py @@ -19,7 +19,7 @@ def test_side_histogram_no_cmapper(self): points = Points(np.random.rand(100, 2)) plot = bokeh_renderer.get_plot(points.hist()) plot.initialize_plot() - adjoint_plot = list(plot.subplots.values())[0] + adjoint_plot = next(iter(plot.subplots.values())) main_plot = adjoint_plot.subplots['main'] right_plot = adjoint_plot.subplots['right'] self.assertTrue('color_mapper' not in main_plot.handles) @@ -31,7 +31,7 @@ def test_side_histogram_cmapper(self): img = Image(np.sin(x**2+y**2), bounds=(-1,-1,1,1)) plot = bokeh_renderer.get_plot(img.hist()) plot.initialize_plot() - adjoint_plot = list(plot.subplots.values())[0] + adjoint_plot = next(iter(plot.subplots.values())) main_plot = adjoint_plot.subplots['main'] right_plot = adjoint_plot.subplots['right'] self.assertIs(main_plot.handles['color_mapper'], @@ -46,7 +46,7 @@ def test_side_histogram_cmapper_weighted(self): mean_weighted=True) plot = bokeh_renderer.get_plot(adjoint) plot.initialize_plot() - adjoint_plot = list(plot.subplots.values())[0] + adjoint_plot = next(iter(plot.subplots.values())) main_plot = adjoint_plot.subplots['main'] right_plot = adjoint_plot.subplots['right'] top_plot = adjoint_plot.subplots['top'] diff --git a/holoviews/tests/plotting/bokeh/test_layoutplot.py b/holoviews/tests/plotting/bokeh/test_layoutplot.py index 173ad3e0bf..275c96417c 100644 --- a/holoviews/tests/plotting/bokeh/test_layoutplot.py +++ b/holoviews/tests/plotting/bokeh/test_layoutplot.py @@ -388,7 +388,7 @@ def test_layout_disable_toolbar(self): def test_layout_shared_inverted_yaxis(self): layout = (Curve([]) + Curve([])).opts('Curve', invert_yaxis=True) plot = bokeh_renderer.get_plot(layout) - subplot = list(plot.subplots.values())[0].subplots['main'] + subplot = next(iter(plot.subplots.values())).subplots['main'] self.assertEqual(subplot.handles['y_range'].start, 1) self.assertEqual(subplot.handles['y_range'].end, 0) diff --git a/holoviews/tests/plotting/bokeh/test_multiaxis.py b/holoviews/tests/plotting/bokeh/test_multiaxis.py index baa486d59d..4262e11d17 100644 --- a/holoviews/tests/plotting/bokeh/test_multiaxis.py +++ b/holoviews/tests/plotting/bokeh/test_multiaxis.py @@ -254,7 +254,7 @@ def test_only_x_axis_labels(self): assert plot.state.yaxis[0].axis_label is None assert plot.state.yaxis[1].axis_label is None - def test_only_x_axis_labels(self): + def test_none_x_axis_labels(self): overlay = ( Curve(range(10), vdims=['A']) * Curve(range(10), vdims=['B']) diff --git a/holoviews/tests/plotting/bokeh/test_pathplot.py b/holoviews/tests/plotting/bokeh/test_pathplot.py index ba08647105..a7ffb96dfc 100644 --- a/holoviews/tests/plotting/bokeh/test_pathplot.py +++ b/holoviews/tests/plotting/bokeh/test_pathplot.py @@ -219,7 +219,7 @@ def test_polygons_colored(self): def test_polygons_colored_batched(self): polygons = NdOverlay({j: Polygons([[(i**j, i, j) for i in range(10)]], vdims='Value') for j in range(5)}).opts(legend_limit=0) - plot = list(bokeh_renderer.get_plot(polygons).subplots.values())[0] + plot = next(iter(bokeh_renderer.get_plot(polygons).subplots.values())) cmapper = plot.handles['color_mapper'] self.assertEqual(cmapper.low, 0) self.assertEqual(cmapper.high, 4) @@ -231,7 +231,7 @@ def test_polygons_colored_batched_unsanitized(self): polygons = NdOverlay({j: Polygons([[(i**j, i, j) for i in range(10)] for i in range(2)], vdims=['some ? unescaped name']) for j in range(5)}).opts(legend_limit=0) - plot = list(bokeh_renderer.get_plot(polygons).subplots.values())[0] + plot = next(iter(bokeh_renderer.get_plot(polygons).subplots.values())) cmapper = plot.handles['color_mapper'] self.assertEqual(cmapper.low, 0) self.assertEqual(cmapper.high, 4) diff --git a/holoviews/tests/plotting/bokeh/test_tabular.py b/holoviews/tests/plotting/bokeh/test_tabular.py index e2b3a17c4c..1857736058 100644 --- a/holoviews/tests/plotting/bokeh/test_tabular.py +++ b/holoviews/tests/plotting/bokeh/test_tabular.py @@ -41,7 +41,7 @@ def test_table_plot_escaped_dimension(self): plot = bokeh_renderer.get_plot(table) source = plot.handles['source'] renderer = plot.handles['glyph_renderer'] - self.assertEqual(list(source.data.keys())[0], renderer.columns[0].field) + self.assertEqual(next(iter(source.data.keys())), renderer.columns[0].field) def test_table_plot_datetimes(self): table = Table([dt.now(), dt.now()], 'Date') diff --git a/holoviews/tests/plotting/plotly/test_dash.py b/holoviews/tests/plotting/plotly/test_dash.py index 6338747100..63f4174ee3 100644 --- a/holoviews/tests/plotting/plotly/test_dash.py +++ b/holoviews/tests/plotting/plotly/test_dash.py @@ -65,7 +65,7 @@ def test_boundsxy_dynamic_map(self): self.assertEqual(len(components.resets), 1) # Get arguments passed to @app.callback decorator - decorator_args = list(self.app.callback.call_args_list[0])[0] + decorator_args = next(iter(self.app.callback.call_args_list[0])) outputs, inputs, states = decorator_args # Check outputs @@ -206,7 +206,7 @@ def dmap_fn(x_range, y_range): self.assertEqual(len(components.resets), 1) # Get arguments passed to @app.callback decorator - decorator_args = list(self.app.callback.call_args_list[0])[0] + decorator_args = next(iter(self.app.callback.call_args_list[0])) outputs, inputs, states = decorator_args # Check outputs @@ -296,7 +296,7 @@ def test_selection1d_dynamic_map(self): self.assertEqual(len(components.resets), 1) # Get arguments passed to @app.callback decorator - decorator_args = list(self.app.callback.call_args_list[0])[0] + decorator_args = next(iter(self.app.callback.call_args_list[0])) outputs, inputs, states = decorator_args # Check outputs @@ -428,7 +428,7 @@ def test_kdims_dynamic_map(self): self.assertEqual(len(components.resets), 0) # Get arguments passed to @app.callback decorator - decorator_args = list(self.app.callback.call_args_list[0])[0] + decorator_args = next(iter(self.app.callback.call_args_list[0])) outputs, inputs, states = decorator_args # Check outputs @@ -444,7 +444,7 @@ def test_kdims_dynamic_map(self): (g.id, prop) for g in components.graphs for prop in ["selectedData", "relayoutData"] - ] + [(list(components.kdims.values())[0].children[1].id, 'value')] + ] + [(next(iter(components.kdims.values())).children[1].id, 'value')] self.assertEqual( [(ip.component_id, ip.component_property) for ip in inputs], diff --git a/holoviews/util/__init__.py b/holoviews/util/__init__.py index ec9f5f53d0..a22434a902 100644 --- a/holoviews/util/__init__.py +++ b/holoviews/util/__init__.py @@ -277,7 +277,7 @@ def defaults(cls, *options, **kwargs): *options: Option objects used to specify the defaults. backend: The plotting extension the options apply to """ - if kwargs and len(kwargs) != 1 and list(kwargs.keys())[0] != 'backend': + if kwargs and len(kwargs) != 1 and next(iter(kwargs.keys())) != 'backend': raise Exception('opts.defaults only accepts "backend" keyword argument') cls._linemagic(cls._expand_options(merge_options_to_dict(options)), backend=kwargs.get('backend')) @@ -376,13 +376,13 @@ def _options_error(cls, opt, objtype, backend, valid_options): matches = sorted(kws.fuzzy_match(opt)) if backend is not None: if matches: - raise ValueError('Unexpected option {!r} for {} type ' - 'when using the {!r} extension. Similar ' - 'options are: {}.'.format(opt, objtype, backend, matches)) + raise ValueError(f'Unexpected option {opt!r} for {objtype} type ' + f'when using the {backend!r} extension. Similar ' + f'options are: {matches}.') else: - raise ValueError('Unexpected option {!r} for {} type ' - 'when using the {!r} extension. No ' - 'similar options found.'.format(opt, objtype, backend)) + raise ValueError(f'Unexpected option {opt!r} for {objtype} type ' + f'when using the {backend!r} extension. No ' + 'similar options found.') # Check option is invalid for all backends found = [] @@ -395,9 +395,9 @@ def _options_error(cls, opt, objtype, backend, valid_options): found.append(lb) if found: param.main.param.warning( - 'Option {!r} for {} type not valid for selected ' - 'backend ({!r}). Option only applies to following ' - 'backends: {!r}'.format(opt, objtype, current_backend, found)) + f'Option {opt!r} for {objtype} type not valid for selected ' + f'backend ({current_backend!r}). Option only applies to following ' + f'backends: {found!r}') return if matches: @@ -405,9 +405,8 @@ def _options_error(cls, opt, objtype, backend, valid_options): 'across all extensions. Similar options ' 'for current extension ({!r}) are: {}.'.format(opt, objtype, current_backend, matches)) else: - raise ValueError('Unexpected option {!r} for {} type ' - 'across all extensions. No similar options ' - 'found.'.format(opt, objtype)) + raise ValueError(f'Unexpected option {opt!r} for {objtype} type ' + 'across all extensions. No similar options found.') @classmethod def _builder_reprs(cls, options, namespace=None, ns=None): @@ -475,7 +474,7 @@ def builder(cls, spec=None, **kws): reraise = False if invalid: try: - cls._options_error(list(invalid)[0], element, backend, allowed_kws) + cls._options_error(next(iter(invalid)), element, backend, allowed_kws) except ValueError as e: msg = str(e)[0].lower() + str(e)[1:] reraise = True @@ -692,13 +691,13 @@ def __call__(self, *args, **params): selected_backend = backend except util.VersionError as e: self.param.warning( - "HoloViews {} extension could not be loaded. " - "The installed {} version {} is less than " - "the required version {}.".format(backend, backend, e.version, e.min_version)) + f"HoloViews {backend} extension could not be loaded. " + f"The installed {backend} version {e.version} is less than " + f"the required version {e.min_version}.") except Exception as e: self.param.warning( - "Holoviews {} extension could not be imported, " - "it raised the following exception: {}('{}')".format(backend, type(e).__name__, e)) + f"Holoviews {backend} extension could not be imported, " + f"it raised the following exception: {type(e).__name__}('{e}')") finally: Store.output_settings.allowed['backend'] = list_backends() Store.output_settings.allowed['fig'] = list_formats('fig', backend) @@ -707,8 +706,8 @@ def __call__(self, *args, **params): try: hook() except Exception as e: - self.param.warning('{} backend hook {} failed with ' - 'following exception: {}'.format(backend, hook, e)) + self.param.warning(f'{backend} backend hook {hook} failed with ' + f'following exception: {e}') if selected_backend is None: raise ImportError('None of the backends could be imported') diff --git a/holoviews/util/_versions.py b/holoviews/util/_versions.py index aa7f469647..fcccbce25c 100644 --- a/holoviews/util/_versions.py +++ b/holoviews/util/_versions.py @@ -42,6 +42,8 @@ def show_versions(): print(f"Python : {sys.version}") print(f"Operating system : {platform.platform()}") + _panel_comms() + print() _package_version("holoviews") print() for p in sorted(PACKAGES, key=lambda x: x.lower()): @@ -54,3 +56,9 @@ def _package_version(p): print(f"{p:20}: {sys.modules[p].__version__}") except ImportError: print(f"{p:20}: -") + + +def _panel_comms(): + import panel as pn + + print(f"{'Panel comms':20}: {pn.config.comms}") diff --git a/holoviews/util/parser.py b/holoviews/util/parser.py index 59b7e3ca84..437289e8d6 100644 --- a/holoviews/util/parser.py +++ b/holoviews/util/parser.py @@ -239,7 +239,7 @@ def process_normalization(cls, parse_group): for pair in excluded: if all(exclude in opts for exclude in pair): raise SyntaxError("Normalization specification cannot" - " contain both {} and {}".format(pair[0], pair[1])) + f" contain both {pair[0]} and {pair[1]}") # If unspecified, default is -axiswise and -framewise if len(opts) == 1 and opts[0].endswith('framewise'): diff --git a/holoviews/util/settings.py b/holoviews/util/settings.py index 14dd3d87b1..d3ce63a3e4 100644 --- a/holoviews/util/settings.py +++ b/holoviews/util/settings.py @@ -317,10 +317,10 @@ def output(cls, line=None, cell=None, cell_runner=None, OutputSettings.options = prev_restore cls.set_backend(prev_backend) if backend not in Store.renderers: - raise ValueError("The selected plotting extension {ext} " + raise ValueError(f"The selected plotting extension {backend!r} " "has not been loaded, ensure you load it " - "with hv.extension({ext}) before using " - "hv.output.".format(ext=repr(backend))) + f"with hv.extension({backend!r}) before using " + "hv.output.") print(f'Error: {e}') if help_prompt: print(help_prompt) diff --git a/holoviews/util/transform.py b/holoviews/util/transform.py index adf4533146..9ab375bb7a 100644 --- a/holoviews/util/transform.py +++ b/holoviews/util/transform.py @@ -654,8 +654,8 @@ def _apply_fn(self, dataset, data, fn, fn_name, args, kwargs, accessor, drange): if method is None: mtype = 'attribute' if accessor else 'method' raise AttributeError( - "{!r} could not be applied to '{!r}', '{}' {} " - "does not exist on {} type.".format(self, dataset, fn, mtype, type(data).__name__) + f"{self!r} could not be applied to '{dataset!r}', '{fn}' {mtype} " + f"does not exist on {type(data).__name__} type." ) if accessor: data = method @@ -720,10 +720,10 @@ def apply(self, dataset, flat=False, expanded=None, ranges={}, all_values=False, (dataset.interface.multi and dataset.interface.isunique(dataset, dimension, True))) if not self.applies(dataset) and (not isinstance(dataset, Graph) or not self.applies(dataset.nodes)): - raise KeyError("One or more dimensions in the expression {!r} " - "could not resolve on '{}'. Ensure all " + raise KeyError(f"One or more dimensions in the expression {self!r} " + f"could not resolve on '{dataset}'. Ensure all " "dimensions referenced by the expression are " - "present on the supplied object.".format(self, dataset)) + "present on the supplied object.") if not self.interface_applies(dataset, coerce=self.coerce): if self.coerce: raise ValueError("The expression {!r} assumes a {}-like "