From e4d19ecb32998df5757bcc78b997f02e5d3514f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 18 Aug 2023 08:22:31 +0200 Subject: [PATCH 01/11] Fix bare self.assertRaises --- holoviews/tests/plotting/matplotlib/test_graphplot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/holoviews/tests/plotting/matplotlib/test_graphplot.py b/holoviews/tests/plotting/matplotlib/test_graphplot.py index 327754fb11..b370b95ebb 100644 --- a/holoviews/tests/plotting/matplotlib/test_graphplot.py +++ b/holoviews/tests/plotting/matplotlib/test_graphplot.py @@ -206,7 +206,8 @@ def test_graph_op_node_alpha(self): if Version(mpl.__version__) < Version("3.4.0"): # Python 3.6 only support up to matplotlib 3.3 - with self.assertRaises(Exception): + msg = 'TypeError: alpha must be a float or None' + with pytest.raises(AbbreviatedException, match=msg): mpl_renderer.get_plot(graph) else: plot = mpl_renderer.get_plot(graph) From 8069931f4b2f58e0a5c660754233e508f42b4f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 18 Aug 2023 08:50:34 +0200 Subject: [PATCH 02/11] B006 - Autofix mutable argument --- holoviews/annotators.py | 4 ++- holoviews/core/accessors.py | 16 ++++++--- holoviews/core/boundingregion.py | 4 ++- holoviews/core/data/__init__.py | 16 ++++++--- holoviews/core/data/array.py | 8 +++-- holoviews/core/data/cudf.py | 4 ++- holoviews/core/data/dask.py | 8 +++-- holoviews/core/data/dictionary.py | 8 +++-- holoviews/core/data/grid.py | 12 +++++-- holoviews/core/data/ibis.py | 8 +++-- holoviews/core/data/image.py | 4 ++- holoviews/core/data/multipath.py | 8 +++-- holoviews/core/data/pandas.py | 8 +++-- holoviews/core/data/spatialpandas.py | 12 +++++-- holoviews/core/data/xarray.py | 8 +++-- holoviews/core/element.py | 8 +++-- holoviews/core/io.py | 40 ++++++++++++++++----- holoviews/core/ndmapping.py | 4 ++- holoviews/core/options.py | 16 ++++++--- holoviews/core/pprint.py | 10 ++++-- holoviews/core/spaces.py | 8 +++-- holoviews/core/util.py | 20 ++++++++--- holoviews/element/__init__.py | 4 ++- holoviews/element/raster.py | 8 +++-- holoviews/element/tabular.py | 4 ++- holoviews/ipython/archive.py | 4 ++- holoviews/operation/element.py | 4 ++- holoviews/operation/normalization.py | 8 +++-- holoviews/plotting/bokeh/chart.py | 4 ++- holoviews/plotting/bokeh/element.py | 16 ++++++--- holoviews/plotting/bokeh/plot.py | 8 +++-- holoviews/plotting/bokeh/selection.py | 4 ++- holoviews/plotting/bokeh/util.py | 4 ++- holoviews/plotting/mpl/plot.py | 4 ++- holoviews/plotting/mpl/util.py | 12 +++++-- holoviews/plotting/plot.py | 6 +++- holoviews/plotting/renderer.py | 6 +++- holoviews/plotting/util.py | 4 ++- holoviews/selection.py | 12 +++++-- holoviews/streams.py | 32 +++++++++++++---- holoviews/tests/core/test_dimensioned.py | 6 +++- holoviews/tests/plotting/bokeh/test_plot.py | 4 ++- holoviews/util/__init__.py | 4 ++- holoviews/util/parser.py | 16 ++++++--- holoviews/util/transform.py | 4 ++- 45 files changed, 315 insertions(+), 97 deletions(-) diff --git a/holoviews/annotators.py b/holoviews/annotators.py index 9daf124816..15acd36f36 100644 --- a/holoviews/annotators.py +++ b/holoviews/annotators.py @@ -16,7 +16,7 @@ from .streams import BoxEdit, PolyDraw, PolyEdit, Selection1D, PointDraw, CurveEdit -def preprocess(function, current=[]): +def preprocess(function, current=None): """ Turns a param.depends watch call into a preprocessor method, i.e. skips all downstream events triggered by it. @@ -25,6 +25,8 @@ def preprocess(function, current=[]): method which depends on a particular parameter. (see https://github.com/pyviz/param/issues/332) """ + if current is None: + current = [] def inner(*args, **kwargs): self = args[0] self.param._BATCH_WATCH = True diff --git a/holoviews/core/accessors.py b/holoviews/core/accessors.py index 63ff65d4c3..32c3a91e2a 100644 --- a/holoviews/core/accessors.py +++ b/holoviews/core/accessors.py @@ -92,7 +92,7 @@ class Apply(metaclass=AccessorPipelineMeta): def __init__(self, obj, mode=None): self._obj = obj - def __call__(self, apply_function, streams=[], link_inputs=True, + def __call__(self, apply_function, streams=None, link_inputs=True, link_dataset=True, dynamic=None, per_element=False, **kwargs): """Applies a function to all (Nd)Overlay or Element objects. @@ -132,6 +132,8 @@ def __call__(self, apply_function, streams=[], link_inputs=True, A new object where the function was applied to all contained (Nd)Overlay or Element objects. """ + if streams is None: + streams = [] from .data import Dataset from .dimension import ViewableElement from .element import Element @@ -238,22 +240,26 @@ def opts(self, *args, **kwargs): kwargs['_method_args'] = args return self.__call__('opts', **kwargs) - def reduce(self, dimensions=[], function=None, spreadfn=None, **kwargs): + def reduce(self, dimensions=None, function=None, spreadfn=None, **kwargs): """Applies a reduce function to all ViewableElement objects. See :py:meth:`Dimensioned.opts` and :py:meth:`Apply.__call__` for more information. """ + if dimensions is None: + dimensions = [] kwargs['_method_args'] = (dimensions, function, spreadfn) kwargs['per_element'] = True return self.__call__('reduce', **kwargs) - def sample(self, samples=[], bounds=None, **kwargs): + def sample(self, samples=None, bounds=None, **kwargs): """Samples element values at supplied coordinates. See :py:meth:`Dataset.sample` and :py:meth:`Apply.__call__` for more information. """ + if samples is None: + samples = [] kwargs['_method_args'] = (samples, bounds) kwargs['per_element'] = True return self.__call__('sample', **kwargs) @@ -357,7 +363,9 @@ def _transform_dimension(self, kdims, vdims, dimension): dimension = self._obj.vdims[idx] return dimension - def _create_expression_transform(self, kdims, vdims, exclude=[]): + def _create_expression_transform(self, kdims, vdims, exclude=None): + if exclude is None: + exclude = [] from .dimension import dimension_name from ..util.transform import dim diff --git a/holoviews/core/boundingregion.py b/holoviews/core/boundingregion.py index 942a962c85..35f94c6c84 100644 --- a/holoviews/core/boundingregion.py +++ b/holoviews/core/boundingregion.py @@ -103,8 +103,10 @@ def __repr__(self): return self.__str__() - def script_repr(self, imports=[], prefix=" "): + def script_repr(self, imports=None, prefix=" "): # Generate import statement + if imports is None: + imports = [] cls = self.__class__.__name__ mod = self.__module__ imports.append(f"from {mod} import {cls}") diff --git a/holoviews/core/data/__init__.py b/holoviews/core/data/__init__.py index 4ff02a87a6..ba82c8e7a0 100644 --- a/holoviews/core/data/__init__.py +++ b/holoviews/core/data/__init__.py @@ -431,7 +431,7 @@ def persist(self): self._cached = persisted return self - def closest(self, coords=[], **kwargs): + def closest(self, coords=None, **kwargs): """Snaps coordinate(s) to closest coordinate in Dataset Args: @@ -444,6 +444,8 @@ def closest(self, coords=[], **kwargs): Raises: NotImplementedError: Raised if snapping is not supported """ + if coords is None: + coords = [] if self.ndims > 1: raise NotImplementedError("Closest method currently only " "implemented for 1D Elements") @@ -717,7 +719,7 @@ def __getitem__(self, slices): return data - def sample(self, samples=[], bounds=None, closest=True, **kwargs): + def sample(self, samples=None, bounds=None, closest=True, **kwargs): """Samples values at supplied coordinates. Allows sampling of element with a list of coordinates matching @@ -749,6 +751,8 @@ def sample(self, samples=[], bounds=None, closest=True, **kwargs): Returns: Element containing the sampled coordinates """ + if samples is None: + samples = [] if kwargs and samples != []: raise Exception('Supply explicit list of samples or kwargs, not both.') elif kwargs: @@ -824,7 +828,7 @@ def sample(self, samples=[], bounds=None, closest=True, **kwargs): return self.clone(sampled, new_type=Table, datatype=datatype) - def reduce(self, dimensions=[], function=None, spreadfn=None, **reductions): + def reduce(self, dimensions=None, function=None, spreadfn=None, **reductions): """Applies reduction along the specified dimension(s). Allows reducing the values along one or more key dimension @@ -852,6 +856,8 @@ def reduce(self, dimensions=[], function=None, spreadfn=None, **reductions): Returns: The Dataset after reductions have been applied. """ + if dimensions is None: + dimensions = [] if any(dim in self.vdims for dim in dimensions): raise Exception("Reduce cannot be applied to value dimensions") function, dims = self._reduce_map(dimensions, function, reductions) @@ -941,7 +947,7 @@ def aggregate(self, dimensions=None, function=None, spreadfn=None, **kwargs): new_type=new_type, datatype=datatype) - def groupby(self, dimensions=[], container_type=HoloMap, group_type=None, + def groupby(self, dimensions=None, container_type=HoloMap, group_type=None, dynamic=False, **kwargs): """Groups object by one or more dimensions @@ -960,6 +966,8 @@ def groupby(self, dimensions=[], container_type=HoloMap, group_type=None, Returns object of supplied container_type containing the groups. If dynamic=True returns a DynamicMap instead. """ + if dimensions is None: + dimensions = [] if not isinstance(dimensions, list): dimensions = [dimensions] if not len(dimensions): dimensions = self.dimensions('key', True) if group_type is None: group_type = type(self) diff --git a/holoviews/core/data/array.py b/holoviews/core/data/array.py index d96e07d71f..c2ab3d97ea 100644 --- a/holoviews/core/data/array.py +++ b/holoviews/core/data/array.py @@ -106,7 +106,9 @@ def add_dimension(cls, dataset, dimension, dim_pos, values, vdim): @classmethod - def sort(cls, dataset, by=[], reverse=False): + def sort(cls, dataset, by=None, reverse=False): + if by is None: + by = [] data = dataset.data if len(by) == 1: sorting = cls.values(dataset, by[0]).argsort() @@ -210,7 +212,9 @@ def select(cls, dataset, selection_mask=None, **selection): @classmethod - def sample(cls, dataset, samples=[]): + def sample(cls, dataset, samples=None): + if samples is None: + samples = [] data = dataset.data mask = False for sample in samples: diff --git a/holoviews/core/data/cudf.py b/holoviews/core/data/cudf.py index 4a608a4fd6..42cb8b2c24 100644 --- a/holoviews/core/data/cudf.py +++ b/holoviews/core/data/cudf.py @@ -326,7 +326,9 @@ def iloc(cls, dataset, index): @classmethod - def sort(cls, dataset, by=[], reverse=False): + def sort(cls, dataset, by=None, reverse=False): + if by is None: + by = [] cols = [dataset.get_dimension(d, strict=True).name for d in by] return dataset.data.sort_values(by=cols, ascending=not reverse) diff --git a/holoviews/core/data/dask.py b/holoviews/core/data/dask.py index 5952a84b10..3fcff7cb22 100644 --- a/holoviews/core/data/dask.py +++ b/holoviews/core/data/dask.py @@ -90,7 +90,9 @@ def range(cls, dataset, dimension): return dd.compute(column.min(), column.max()) @classmethod - def sort(cls, dataset, by=[], reverse=False): + def sort(cls, dataset, by=None, reverse=False): + if by is None: + by = [] dataset.param.warning('Dask dataframes do not support sorting') return dataset.data @@ -249,7 +251,9 @@ def unpack_scalar(cls, dataset, data): return data.iat[0,0] @classmethod - def sample(cls, dataset, samples=[]): + def sample(cls, dataset, samples=None): + if samples is None: + samples = [] data = dataset.data dims = dataset.dimensions('key', label='name') mask = None diff --git a/holoviews/core/data/dictionary.py b/holoviews/core/data/dictionary.py index 53dcd1f838..07e8afa619 100644 --- a/holoviews/core/data/dictionary.py +++ b/holoviews/core/data/dictionary.py @@ -229,7 +229,9 @@ def mask(cls, dataset, mask, mask_value=np.nan): @classmethod - def sort(cls, dataset, by=[], reverse=False): + def sort(cls, dataset, by=None, reverse=False): + if by is None: + by = [] by = [dataset.get_dimension(d).name for d in by] if len(by) == 1: sorting = cls.values(dataset, by[0]).argsort() @@ -338,7 +340,9 @@ def select(cls, dataset, selection_mask=None, **selection): @classmethod - def sample(cls, dataset, samples=[]): + def sample(cls, dataset, samples=None): + if samples is None: + samples = [] mask = False for sample in samples: sample_mask = True diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index a92c779142..5501b6034b 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -298,7 +298,7 @@ def coords(cls, dataset, dim, ordered=False, expanded=False, edges=False): @classmethod - def canonicalize(cls, dataset, data, data_coords=None, virtual_coords=[]): + def canonicalize(cls, dataset, data, data_coords=None, virtual_coords=None): """ Canonicalize takes an array of values as input and reorients and transposes it to match the canonical format expected by @@ -313,6 +313,8 @@ def canonicalize(cls, dataset, data, data_coords=None, virtual_coords=[]): with a virtual integer index. This ensures these coordinates are not simply dropped. """ + if virtual_coords is None: + virtual_coords = [] if data_coords is None: data_coords = dataset.dimensions('key', label='name')[::-1] @@ -641,10 +643,12 @@ def mask(cls, dataset, mask, mask_val=np.nan): @classmethod - def sample(cls, dataset, samples=[]): + def sample(cls, dataset, samples=None): """ Samples the gridded data into dataset of samples. """ + if samples is None: + samples = [] ndims = dataset.ndims dimensions = dataset.dimensions(label='name') arrays = [dataset.data[vdim.name] for vdim in dataset.vdims] @@ -742,7 +746,9 @@ def add_dimension(cls, dataset, dimension, dim_pos, values, vdim): @classmethod - def sort(cls, dataset, by=[], reverse=False): + def sort(cls, dataset, by=None, reverse=False): + if by is None: + by = [] if not by or by in [dataset.kdims, dataset.dimensions()]: return dataset.data else: diff --git a/holoviews/core/data/ibis.py b/holoviews/core/data/ibis.py index 2041a57d2d..1b62701e09 100644 --- a/holoviews/core/data/ibis.py +++ b/holoviews/core/data/ibis.py @@ -189,7 +189,9 @@ def dtype(cls, dataset, dimension): dimension_type = dtype @classmethod - def sort(cls, dataset, by=[], reverse=False): + def sort(cls, dataset, by=None, reverse=False): + if by is None: + by = [] if ibis4(): return dataset.data.order_by([(dataset.get_dimension(x).name, not reverse) for x in by]) else: @@ -414,7 +416,9 @@ def select_mask(cls, dataset, selection): return predicates @classmethod - def sample(cls, dataset, samples=[]): + def sample(cls, dataset, samples=None): + if samples is None: + samples = [] import ibis dims = dataset.dimensions() data = dataset.data diff --git a/holoviews/core/data/image.py b/holoviews/core/data/image.py index 5e556af243..885aee9a54 100644 --- a/holoviews/core/data/image.py +++ b/holoviews/core/data/image.py @@ -239,7 +239,7 @@ def select(cls, dataset, selection_mask=None, **selection): @classmethod - def sample(cls, dataset, samples=[]): + def sample(cls, dataset, samples=None): """ Sample the Raster along one or both of its dimensions, returning a reduced dimensionality type, which is either @@ -248,6 +248,8 @@ def sample(cls, dataset, samples=[]): of the sampled unit indexed by the value in the new_xaxis tuple. """ + if samples is None: + samples = [] if len(samples[0]) == 1: select = {dataset.kdims[0].name: [s[0] for s in samples]} return tuple(dataset.select(**select).columns().values()) diff --git a/holoviews/core/data/multipath.py b/holoviews/core/data/multipath.py index aea4bf4901..e328af2e50 100644 --- a/holoviews/core/data/multipath.py +++ b/holoviews/core/data/multipath.py @@ -300,7 +300,9 @@ def groupby(cls, dataset, dimensions, container_type, group_type, **kwargs): return container_type(grouped_data) @classmethod - def sample(cls, dataset, samples=[]): + def sample(cls, dataset, samples=None): + if samples is None: + samples = [] raise NotImplementedError('Sampling operation on subpaths not supported') @classmethod @@ -348,7 +350,9 @@ def dtype(cls, dataset, dimension): return ds.interface.dtype(ds, dimension) @classmethod - def sort(cls, dataset, by=[], reverse=False): + def sort(cls, dataset, by=None, reverse=False): + if by is None: + by = [] by = [dataset.get_dimension(d).name for d in by] if len(by) == 1: sorting = cls.values(dataset, by[0], False).argsort() diff --git a/holoviews/core/data/pandas.py b/holoviews/core/data/pandas.py index da5cf4403d..8b2fb7300e 100644 --- a/holoviews/core/data/pandas.py +++ b/holoviews/core/data/pandas.py @@ -319,7 +319,9 @@ def redim(cls, dataset, dimensions): @classmethod - def sort(cls, dataset, by=[], reverse=False): + def sort(cls, dataset, by=None, reverse=False): + if by is None: + by = [] cols = [dataset.get_dimension(d, strict=True).name for d in by] if (not isinstance(dataset.data, pd.DataFrame) or @@ -367,7 +369,9 @@ def values( @classmethod - def sample(cls, dataset, samples=[]): + def sample(cls, dataset, samples=None): + if samples is None: + samples = [] data = dataset.data mask = None for sample in samples: diff --git a/holoviews/core/data/spatialpandas.py b/holoviews/core/data/spatialpandas.py index 81d3df03b4..88bc202565 100644 --- a/holoviews/core/data/spatialpandas.py +++ b/holoviews/core/data/spatialpandas.py @@ -238,7 +238,9 @@ def aggregate(cls, columns, dimensions, function, **kwargs): raise NotImplementedError @classmethod - def sample(cls, columns, samples=[]): + def sample(cls, columns, samples=None): + if samples is None: + samples = [] raise NotImplementedError @classmethod @@ -250,7 +252,9 @@ def shape(cls, dataset): return (cls.length(dataset), len(dataset.dimensions())) @classmethod - def sort(cls, dataset, by=[], reverse=False): + def sort(cls, dataset, by=None, reverse=False): + if by is None: + by = [] geo_dims = cls.geom_dims(dataset) if any(d in geo_dims for d in by): raise DataError("SpatialPandasInterface does not allow sorting " @@ -670,7 +674,7 @@ def geom_to_holes(geom): return [[]] -def to_spatialpandas(data, xdim, ydim, columns=[], geom='point'): +def to_spatialpandas(data, xdim, ydim, columns=None, geom='point'): """Converts list of dictionary format geometries to spatialpandas line geometries. Args: @@ -683,6 +687,8 @@ def to_spatialpandas(data, xdim, ydim, columns=[], geom='point'): Returns: A spatialpandas.GeoDataFrame version of the data """ + if columns is None: + columns = [] from spatialpandas import GeoSeries, GeoDataFrame from spatialpandas.geometry import ( Point, Line, Polygon, Ring, LineArray, PolygonArray, PointArray, diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index 8b124e5384..cb9a1ac196 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -533,7 +533,9 @@ def reindex(cls, dataset, kdims=None, vdims=None): return dataset.data @classmethod - def sort(cls, dataset, by=[], reverse=False): + def sort(cls, dataset, by=None, reverse=False): + if by is None: + by = [] return dataset @classmethod @@ -643,7 +645,9 @@ def dframe(cls, dataset, dimensions): return data @classmethod - def sample(cls, dataset, samples=[]): + def sample(cls, dataset, samples=None): + if samples is None: + samples = [] names = [kd.name for kd in dataset.kdims] samples = [dataset.data.sel(**{k: [v] for k, v in zip(names, s)}).to_dataframe().reset_index() for s in samples] diff --git a/holoviews/core/element.py b/holoviews/core/element.py index 537d09c7e9..f0ab22e7c8 100644 --- a/holoviews/core/element.py +++ b/holoviews/core/element.py @@ -109,7 +109,7 @@ def closest(self, coords, **kwargs): """ raise NotImplementedError - def sample(self, samples=[], bounds=None, closest=False, **sample_values): + def sample(self, samples=None, bounds=None, closest=False, **sample_values): """Samples values at supplied coordinates. Allows sampling of element with a list of coordinates matching @@ -141,10 +141,12 @@ def sample(self, samples=[], bounds=None, closest=False, **sample_values): Returns: Element containing the sampled coordinates """ + if samples is None: + samples = [] raise NotImplementedError - def reduce(self, dimensions=[], function=None, spreadfn=None, **reduction): + def reduce(self, dimensions=None, function=None, spreadfn=None, **reduction): """Applies reduction along the specified dimension(s). Allows reducing the values along one or more key dimension @@ -172,6 +174,8 @@ def reduce(self, dimensions=[], function=None, spreadfn=None, **reduction): Returns: The element after reductions have been applied. """ + if dimensions is None: + dimensions = [] raise NotImplementedError diff --git a/holoviews/core/io.py b/holoviews/core/io.py index c6a4af5b39..e1298d1c89 100644 --- a/holoviews/core/io.py +++ b/holoviews/core/io.py @@ -37,10 +37,12 @@ from .util import unique_iterator, group_sanitizer, label_sanitizer -def sanitizer(name, replacements=[(':','_'), ('/','_'), ('\\','_')]): +def sanitizer(name, replacements=None): """ String sanitizer to avoid problematic characters in filenames. """ + if replacements is None: + replacements = [(':', '_'), ('/', '_'), ('\\', '_')] for old,new in replacements: name = name.replace(old,new) return name @@ -160,7 +162,7 @@ def __call__(self, obj, fmt=None): @bothmethod - def save(self_or_cls, obj, basename, fmt=None, key={}, info={}, **kwargs): + def save(self_or_cls, obj, basename, fmt=None, key=None, info=None, **kwargs): """ Similar to the call method except saves exporter data to disk into a file with specified basename. For exporters that @@ -171,6 +173,10 @@ def save(self_or_cls, obj, basename, fmt=None, key={}, info={}, **kwargs): to update the output of the relevant key and info functions which is then saved (if supported). """ + if info is None: + info = {} + if key is None: + key = {} raise NotImplementedError("Exporter save method not implemented.") @@ -250,7 +256,11 @@ def __call__(self, obj, **kwargs): return data, {'file-ext': self.file_ext, 'mime_type':self.mime_type} @bothmethod - def save(self_or_cls, obj, filename, info={}, key={}, **kwargs): + def save(self_or_cls, obj, filename, info=None, key=None, **kwargs): + if key is None: + key = {} + if info is None: + info = {} data, base_info = self_or_cls(obj, **kwargs) key = self_or_cls._merge_metadata(obj, self_or_cls.key_fn, key) info = self_or_cls._merge_metadata(obj, self_or_cls.info_fn, info, base_info) @@ -331,14 +341,22 @@ class Pickler(Exporter): file_ext = 'hvz' - def __call__(self, obj, key={}, info={}, **kwargs): + def __call__(self, obj, key=None, info=None, **kwargs): + if info is None: + info = {} + if key is None: + key = {} buff = BytesIO() self.save(obj, buff, key=key, info=info, **kwargs) buff.seek(0) return buff.read(), {'file-ext': 'hvz', 'mime_type':self.mime_type} @bothmethod - def save(self_or_cls, obj, filename, key={}, info={}, **kwargs): + def save(self_or_cls, obj, filename, key=None, info=None, **kwargs): + if info is None: + info = {} + if key is None: + key = {} base_info = {'file-ext': 'hvz', 'mime_type':self_or_cls.mime_type} key = self_or_cls._merge_metadata(obj, self_or_cls.key_fn, key) info = self_or_cls._merge_metadata(obj, self_or_cls.info_fn, info, base_info) @@ -421,7 +439,7 @@ def entries(self_or_cls, filename): return [el for el in f.namelist() if el != 'metadata'] @bothmethod - def collect(self_or_cls, files, drop=[], metadata=True): + def collect(self_or_cls, files, drop=None, metadata=True): """ Given a list or NdMapping type containing file paths return a Layout of Collators, which can be called to load a given set @@ -433,6 +451,8 @@ def collect(self_or_cls, files, drop=[], metadata=True): they do not clash with the file metadata. Any key dimension may be dropped by name by supplying a drop argument. """ + if drop is None: + drop = [] aslist = not isinstance(files, (NdMapping, Element)) if isinstance(files, Element): files = Collator(files) @@ -658,7 +678,7 @@ def _validate_formatters(self): raise Exception("Timestamp format invalid") - def add(self, obj=None, filename=None, data=None, info={}, **kwargs): + def add(self, obj=None, filename=None, data=None, info=None, **kwargs): """ If a filename is supplied, it will be used. Otherwise, a filename will be generated from the supplied object. Note that @@ -668,6 +688,8 @@ def add(self, obj=None, filename=None, data=None, info={}, **kwargs): The data to be archived is either supplied explicitly as 'data' or automatically rendered from the object. """ + if info is None: + info = {} if [filename, obj] == [None, None]: raise Exception("Either filename or a HoloViews object is " "needed to create an entry in the archive.") @@ -810,10 +832,12 @@ def _normalize_name(self, basename): return basename.replace(' ', '_') - def export(self, timestamp=None, info={}): + def export(self, timestamp=None, info=None): """ Export the archive, directory or file. """ + if info is None: + info = {} tval = tuple(time.localtime()) if timestamp is None else timestamp tstamp = time.strftime(self.timestamp_format, tval) diff --git a/holoviews/core/ndmapping.py b/holoviews/core/ndmapping.py index 4cc8c2392d..90ffcc6090 100644 --- a/holoviews/core/ndmapping.py +++ b/holoviews/core/ndmapping.py @@ -406,7 +406,7 @@ def dimension_values(self, dimension, expanded=True, flat=True): return super().dimension_values(dimension, expanded, flat) - def reindex(self, kdims=[], force=False): + def reindex(self, kdims=None, force=False): """Reindexes object dropping static or supplied kdims Creates a new object with a reordered or reduced set of key @@ -424,6 +424,8 @@ def reindex(self, kdims=[], force=False): Returns: Reindexed object """ + if kdims is None: + kdims = [] old_kdims = [d.name for d in self.kdims] if not isinstance(kdims, list): kdims = [kdims] diff --git a/holoviews/core/options.py b/holoviews/core/options.py index cf341e2936..97b26901d8 100644 --- a/holoviews/core/options.py +++ b/holoviews/core/options.py @@ -258,8 +258,10 @@ class Keywords(param.Parameterized): target = param.String(allow_None=True, doc=""" Optional string description of what the keywords apply to.""") - def __init__(self, values=[], target=None): + def __init__(self, values=None, target=None): + if values is None: + values = [] strings = [isinstance(v, str) for v in values] if False in strings: raise ValueError(f'All keywords must be strings: {values}') @@ -459,9 +461,11 @@ class Options: _output_allowed_kws = ['backend'] - def __init__(self, key=None, allowed_keywords=[], merge_keywords=True, + def __init__(self, key=None, allowed_keywords=None, merge_keywords=True, max_cycles=None, **kwargs): + if allowed_keywords is None: + allowed_keywords = [] invalid_kws = [] for kwarg in sorted(kwargs.keys()): if allowed_keywords and kwarg not in allowed_keywords: @@ -1240,12 +1244,14 @@ def dumps(cls, obj, protocol=0): @classmethod def info(cls, obj, ansi=True, backend='matplotlib', visualization=True, - recursive=False, pattern=None, elements=[]): + recursive=False, pattern=None, elements=None): """ Show information about a particular object or component class including the applicable style and plot options. Returns None if the object is not parameterized. """ + if elements is None: + elements = [] parameterized_object = isinstance(obj, param.Parameterized) parameterized_class = (isinstance(obj,type) and issubclass(obj,param.Parameterized)) @@ -1352,11 +1358,13 @@ def add_style_opts(cls, component, new_options, backend=None): ) @classmethod - def register(cls, associations, backend, style_aliases={}): + def register(cls, associations, backend, style_aliases=None): """ Register the supplied dictionary of associations between elements and plotting classes to the specified backend. """ + if style_aliases is None: + style_aliases = {} if backend not in cls.registry: cls.registry[backend] = {} cls.registry[backend].update(associations) diff --git a/holoviews/core/pprint.py b/holoviews/core/pprint.py index 7ca8dd600b..279bec2967 100644 --- a/holoviews/core/pprint.py +++ b/holoviews/core/pprint.py @@ -132,11 +132,13 @@ def highlight(cls, pattern, string): @classmethod def info(cls, obj, ansi=False, backend='matplotlib', visualization=True, - pattern=None, elements=[]): + pattern=None, elements=None): """ Show information about an object in the given category. ANSI color codes may be enabled or disabled. """ + if elements is None: + elements = [] cls.elements = elements ansi_escape = re.compile(r'\x1b[^m]*m') @@ -296,11 +298,15 @@ def component_type(cls_or_slf, node): return cls_or_slf.type_formatter.format(type=str(type(node).__name__)) @bothmethod - def recurse(cls_or_slf, node, attrpath=None, attrpaths=[], siblings=[], level=0, value_dims=True): + def recurse(cls_or_slf, node, attrpath=None, attrpaths=None, siblings=None, level=0, value_dims=True): """ Recursive function that builds up an ASCII tree given an AttrTree node. """ + if siblings is None: + siblings = [] + if attrpaths is None: + attrpaths = [] level, lines = cls_or_slf.node_info(node, attrpath, attrpaths, siblings, level, value_dims) attrpaths = ['.'.join(k) for k in node.keys()] if hasattr(node, 'children') else [] siblings = [node.get(child) for child in attrpaths] diff --git a/holoviews/core/spaces.py b/holoviews/core/spaces.py index 22298db4b8..13d0419752 100644 --- a/holoviews/core/spaces.py +++ b/holoviews/core/spaces.py @@ -304,7 +304,7 @@ def __lshift__(self, other): raise TypeError(f'Cannot append {type(other).__name__} to a AdjointLayout') - def collate(self, merge_type=None, drop=[], drop_constant=False): + def collate(self, merge_type=None, drop=None, drop_constant=False): """Collate allows reordering nested containers Collation allows collapsing nested mapping types by merging @@ -328,6 +328,8 @@ def collate(self, merge_type=None, drop=[], drop_constant=False): Returns: Collated Layout or HoloMap """ + if drop is None: + drop = [] from .element import Collator merge_type=merge_type if merge_type else self.__class__ return Collator(self, merge_type=merge_type, drop=drop, @@ -1681,7 +1683,7 @@ def dynamic_hist(obj, **dynkwargs): return hist - def reindex(self, kdims=[], force=False): + def reindex(self, kdims=None, force=False): """Reorders key dimensions on DynamicMap Create a new object with a reordered set of key dimensions. @@ -1694,6 +1696,8 @@ def reindex(self, kdims=[], force=False): Returns: Reindexed DynamicMap """ + if kdims is None: + kdims = [] if not isinstance(kdims, list): kdims = [kdims] kdims = [self.get_dimension(kd, strict=True) for kd in kdims] diff --git a/holoviews/core/util.py b/holoviews/core/util.py index 8fbfc1bea9..e5881a22a5 100644 --- a/holoviews/core/util.py +++ b/holoviews/core/util.py @@ -695,12 +695,18 @@ def remove_diacritics(self_or_cls, identifier): return chars @param.parameterized.bothmethod - def shortened_character_name(self_or_cls, c, eliminations=[], substitutions={}, transforms=[]): + def shortened_character_name(self_or_cls, c, eliminations=None, substitutions=None, transforms=None): """ Given a unicode character c, return the shortened unicode name (as a list of tokens) by applying the eliminations, substitutions and transforms. """ + if transforms is None: + transforms = [] + if substitutions is None: + substitutions = {} + if eliminations is None: + eliminations = [] name = unicodedata.name(c).lower() # Filtering for elim in eliminations: @@ -922,12 +928,14 @@ def find_minmax(lims, olims): return limits -def find_range(values, soft_range=[]): +def find_range(values, soft_range=None): """ Safely finds either the numerical min and max of a set of values, falling back to the first and the last value in the sorted list of values. """ + if soft_range is None: + soft_range = [] try: values = np.array(values) values = np.squeeze(values) if len(values.shape) > 1 else values @@ -1658,7 +1666,7 @@ def wrap_tuple(unwrapped): return (unwrapped if isinstance(unwrapped, tuple) else (unwrapped,)) -def stream_name_mapping(stream, exclude_params=['name'], reverse=False): +def stream_name_mapping(stream, exclude_params=None, reverse=False): """ Return a complete dictionary mapping between stream parameter names to their applicable renames, excluding parameters listed in @@ -1667,6 +1675,8 @@ def stream_name_mapping(stream, exclude_params=['name'], reverse=False): If reverse is True, the mapping is from the renamed strings to the original stream parameter names. """ + if exclude_params is None: + exclude_params = ['name'] from ..streams import Params if isinstance(stream, Params): mapping = {} @@ -1703,7 +1713,7 @@ def rename_stream_kwargs(stream, kwargs, reverse=False): return mapped_kwargs -def stream_parameters(streams, no_duplicates=True, exclude=['name', '_memoize_key']): +def stream_parameters(streams, no_duplicates=True, exclude=None): """ Given a list of streams, return a flat list of parameter name, excluding those listed in the exclude list. @@ -1711,6 +1721,8 @@ def stream_parameters(streams, no_duplicates=True, exclude=['name', '_memoize_ke If no_duplicates is enabled, a KeyError will be raised if there are parameter name clashes across the streams. """ + if exclude is None: + exclude = ['name', '_memoize_key'] from ..streams import Params param_groups = {} for s in streams: diff --git a/holoviews/element/__init__.py b/holoviews/element/__init__.py index 43482ff454..3d05f594df 100644 --- a/holoviews/element/__init__.py +++ b/holoviews/element/__init__.py @@ -35,7 +35,9 @@ def curve(self, kdims=None, vdims=None, groupby=None, **kwargs): def errorbars(self, kdims=None, vdims=None, groupby=None, **kwargs): return self(ErrorBars, kdims, vdims, groupby, **kwargs) - def distribution(self, dim=None, groupby=[], **kwargs): + def distribution(self, dim=None, groupby=None, **kwargs): + if groupby is None: + groupby = [] if dim is None: if self._element.vdims: dim = self._element.vdims[0] diff --git a/holoviews/element/raster.py b/holoviews/element/raster.py index 7850ba3437..0d5eaa8242 100644 --- a/holoviews/element/raster.py +++ b/holoviews/element/raster.py @@ -101,7 +101,7 @@ def dimension_values(self, dim, expanded=True, flat=True): else: return super().dimension_values(dim) - def sample(self, samples=[], bounds=None, **sample_values): + def sample(self, samples=None, bounds=None, **sample_values): """ Sample the Raster along one or both of its dimensions, returning a reduced dimensionality type, which is either @@ -110,6 +110,8 @@ def sample(self, samples=[], bounds=None, **sample_values): of the sampled unit indexed by the value in the new_xaxis tuple. """ + if samples is None: + samples = [] if isinstance(samples, tuple): X, Y = samples samples = zip(X, Y) @@ -452,13 +454,15 @@ def select(self, selection_specs=None, **selection): ydensity=self.ydensity, bounds=bounds) - def closest(self, coords=[], **kwargs): + def closest(self, coords=None, **kwargs): """ Given a single coordinate or multiple coordinates as a tuple or list of tuples or keyword arguments matching the dimension closest will find the closest actual x/y coordinates. """ + if coords is None: + coords = [] if kwargs and coords: raise ValueError("Specify coordinate using as either a list " "keyword arguments not both") diff --git a/holoviews/element/tabular.py b/holoviews/element/tabular.py index b0c333e0ed..f2b82a46ab 100644 --- a/holoviews/element/tabular.py +++ b/holoviews/element/tabular.py @@ -71,7 +71,9 @@ def dimension_values(self, dimension, expanded=True, flat=True): else: return super().dimension_values(dimension) - def sample(self, samples=[]): + def sample(self, samples=None): + if samples is None: + samples = [] if callable(samples): sampled_data = OrderedDict(item for item in self.data.items() if samples(item)) diff --git a/holoviews/ipython/archive.py b/holoviews/ipython/archive.py index b506a772ad..e607db9d00 100644 --- a/holoviews/ipython/archive.py +++ b/holoviews/ipython/archive.py @@ -156,8 +156,10 @@ def export(self, timestamp=None): display(Javascript(cmd)) - def add(self, obj=None, filename=None, data=None, info={}, html=None): + def add(self, obj=None, filename=None, data=None, info=None, html=None): "Similar to FileArchive.add but accepts html strings for substitution" + if info is None: + info = {} initial_last_key = list(self._files.keys())[-1] if len(self) else None if self._auto: exporters = self.exporters[:] diff --git a/holoviews/operation/element.py b/holoviews/operation/element.py index 58cde6da18..f4f45ea555 100644 --- a/holoviews/operation/element.py +++ b/holoviews/operation/element.py @@ -1069,9 +1069,11 @@ def __call__(self, data, **params): return GridMatrix(data) - def _process(self, p, element, ranges={}): + def _process(self, p, element, ranges=None): # Creates a unified Dataset.data attribute # to draw the data from + if ranges is None: + ranges = {} if isinstance(element.data, np.ndarray): el_data = element.table(default_datatype) else: diff --git a/holoviews/operation/normalization.py b/holoviews/operation/normalization.py index 16d94b92e9..1a46e44105 100644 --- a/holoviews/operation/normalization.py +++ b/holoviews/operation/normalization.py @@ -79,12 +79,16 @@ class Normalization(Operation): normalization.""") - def __call__(self, element, ranges={}, keys=None, **params): + def __call__(self, element, ranges=None, keys=None, **params): + if ranges is None: + ranges = {} params = dict(params,ranges=ranges, keys=keys) return super().__call__(element, **params) - def process_element(self, element, key, ranges={}, keys=None, **params): + def process_element(self, element, key, ranges=None, keys=None, **params): + if ranges is None: + ranges = {} params = dict(params,ranges=ranges, keys=keys) self.p = param.ParamOverrides(self, params) return self._process(element, key) diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py index 6165b17c12..b484a7feef 100644 --- a/holoviews/plotting/bokeh/chart.py +++ b/holoviews/plotting/bokeh/chart.py @@ -786,7 +786,9 @@ class BarPlot(BarsMixin, ColorbarPlot, LegendPlot): _y_range_type = Range1d def _axis_properties(self, axis, key, plot, dimension=None, - ax_mapping={'x': 0, 'y': 1}): + ax_mapping=None): + if ax_mapping is None: + ax_mapping = {"x": 0, "y": 1} props = super()._axis_properties(axis, key, plot, dimension, ax_mapping) if (not self.multi_level and not self.stacked and self.current_frame.ndims > 1 and ((not self.invert_axes and axis == 'x') or (self.invert_axes and axis =='y'))): diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index 9ef835d5a7..66ff9e5779 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -255,10 +255,12 @@ def _hover_opts(self, element): dims += element.dimensions() return list(util.unique_iterator(dims)), {} - def _init_tools(self, element, callbacks=[]): + def _init_tools(self, element, callbacks=None): """ Processes the list of tools to be supplied to the plot. """ + if callbacks is None: + callbacks = [] tooltips, hover_opts = self._hover_opts(element) tooltips = [(ttp.pprint_label, '@{%s}' % util.dimension_sanitizer(ttp.name)) if isinstance(ttp, Dimension) else ttp for ttp in tooltips] @@ -402,8 +404,10 @@ def _shared_axis_range(self, plots, specs, range_type, axis_type, pos): return dim_range def _axis_props(self, plots, subplots, element, ranges, pos, *, dim=None, - range_tags_extras=[], extra_range_name=None): + range_tags_extras=None, extra_range_name=None): + if range_tags_extras is None: + range_tags_extras = [] el = element.traverse(lambda x: x, [lambda el: isinstance(el, Element) and not isinstance(el, (Annotation, Tiles))]) el = el[0] if el else element if isinstance(el, Graph): @@ -755,12 +759,14 @@ def _populate_axis_handles(self, plot): self.handles['extra_y_scales'] = plot.extra_y_scales def _axis_properties(self, axis, key, plot, dimension=None, - ax_mapping={'x': 0, 'y': 1}): + ax_mapping=None): """ Returns a dictionary of axis properties depending on the specified axis. """ # need to copy dictionary by calling dict() on it + if ax_mapping is None: + ax_mapping = {'x': 0, 'y': 1} axis_props = dict(theme_attr_json(self.renderer.theme, 'Axis')) if ((axis == 'x' and self.xaxis in ['bottom-bare', 'top-bare', 'bare']) or @@ -2662,10 +2668,12 @@ def _process_legend(self, overlay): r.muted = self.legend_muted or r.muted - def _init_tools(self, element, callbacks=[]): + def _init_tools(self, element, callbacks=None): """ Processes the list of tools to be supplied to the plot. """ + if callbacks is None: + callbacks = [] hover_tools = {} init_tools, tool_types = [], [] for key, subplot in self.subplots.items(): diff --git a/holoviews/plotting/bokeh/plot.py b/holoviews/plotting/bokeh/plot.py index 9c936b901d..a1e1be1959 100644 --- a/holoviews/plotting/bokeh/plot.py +++ b/holoviews/plotting/bokeh/plot.py @@ -570,7 +570,9 @@ def _create_subplots(self, layout, ranges): return subplots, collapsed_layout - def initialize_plot(self, ranges=None, plots=[]): + def initialize_plot(self, ranges=None, plots=None): + if plots is None: + plots = [] ranges = self.compute_ranges(self.layout, self.keys[-1], None) passed_plots = list(plots) plots = [[None for c in range(self.cols)] for r in range(self.rows)] @@ -1045,7 +1047,7 @@ def __init__(self, layout, layout_type, subplots, **params): # The supplied (axes, view) objects as indexed by position super().__init__(subplots=subplots, **params) - def initialize_plot(self, ranges=None, plots=[]): + def initialize_plot(self, ranges=None, plots=None): """ Plot all the views contained in the AdjointLayout Object using axes appropriate to the layout configuration. All the axes are @@ -1053,6 +1055,8 @@ def initialize_plot(self, ranges=None, plots=[]): invoke subplots with correct options and styles and hide any empty axes as necessary. """ + if plots is None: + plots = [] if plots is None: plots = [] adjoined_plots = [] for pos in self.view_positions: diff --git a/holoviews/plotting/bokeh/selection.py b/holoviews/plotting/bokeh/selection.py index 5c2b6bd1bc..436c99a914 100644 --- a/holoviews/plotting/bokeh/selection.py +++ b/holoviews/plotting/bokeh/selection.py @@ -14,7 +14,9 @@ def _build_selection(self, el, exprs, **kwargs): opts['selected'] = list(np.where(mask)[0]) return el.opts(clone=True, backend='bokeh', **opts) - def build_selection(self, selection_streams, hvobj, operations, region_stream=None, cache={}): + def build_selection(self, selection_streams, hvobj, operations, region_stream=None, cache=None): + if cache is None: + cache = {} sel_streams = [selection_streams.exprs_stream] hvobj = hvobj.apply(self._build_selection, streams=sel_streams, per_element=True) for op in operations: diff --git a/holoviews/plotting/bokeh/util.py b/holoviews/plotting/bokeh/util.py index 3d5557c9aa..40775bbdae 100644 --- a/holoviews/plotting/bokeh/util.py +++ b/holoviews/plotting/bokeh/util.py @@ -998,7 +998,7 @@ def date_to_integer(date): return dt_int -def glyph_order(keys, draw_order=[]): +def glyph_order(keys, draw_order=None): """ Orders a set of glyph handles using regular sort and an explicit sort order. The explicit draw order must take the form of a list @@ -1006,6 +1006,8 @@ def glyph_order(keys, draw_order=[]): suffix. The draw order may only match subset of the keys and any matched items will take precedence over other entries. """ + if draw_order is None: + draw_order = [] keys = sorted(keys) def order_fn(glyph): matches = [item for item in draw_order if glyph.startswith(item)] diff --git a/holoviews/plotting/mpl/plot.py b/holoviews/plotting/mpl/plot.py index f313e34a05..ba586dbdd3 100644 --- a/holoviews/plotting/mpl/plot.py +++ b/holoviews/plotting/mpl/plot.py @@ -1019,7 +1019,7 @@ def grid_situate(self, current_idx, layout_type, subgrid_width): return start, inds - def _create_subplots(self, layout, positions, layout_dimensions, ranges, axes={}, num=1, create=True): + def _create_subplots(self, layout, positions, layout_dimensions, ranges, axes=None, num=1, create=True): """ Plot all the views contained in the AdjointLayout Object using axes appropriate to the layout configuration. All the axes are @@ -1027,6 +1027,8 @@ def _create_subplots(self, layout, positions, layout_dimensions, ranges, axes={} invoke subplots with correct options and styles and hide any empty axes as necessary. """ + if axes is None: + axes = {} subplots = {} projections = [] adjoint_clone = layout.clone(shared_data=False, id=layout.id) diff --git a/holoviews/plotting/mpl/util.py b/holoviews/plotting/mpl/util.py index 6fd5662705..be7eb09f39 100644 --- a/holoviews/plotting/mpl/util.py +++ b/holoviews/plotting/mpl/util.py @@ -128,7 +128,7 @@ def validate(style, value, vectorized=True): return False -def filter_styles(style, group, other_groups, blacklist=[]): +def filter_styles(style, group, other_groups, blacklist=None): """ Filters styles which are specific to a particular artist, e.g. for a GraphPlot this will filter options specific to the nodes and @@ -150,6 +150,8 @@ def filter_styles(style, group, other_groups, blacklist=[]): filtered: dict Filtered dictionary of styles """ + if blacklist is None: + blacklist = [] group = group+'_' filtered = {} for k, v in style.items(): @@ -248,12 +250,14 @@ def resolve_rows(rows): return resolve_rows(merged_rows) -def fix_aspect(fig, nrows, ncols, title=None, extra_artists=[], +def fix_aspect(fig, nrows, ncols, title=None, extra_artists=None, vspace=0.2, hspace=0.2): """ Calculate heights and widths of axes and adjust the size of the figure to match the aspect. """ + if extra_artists is None: + extra_artists = [] fig.canvas.draw() w, h = fig.get_size_inches() @@ -288,10 +292,12 @@ def fix_aspect(fig, nrows, ncols, title=None, extra_artists=[], title.set_y(top/(w*aspect)) -def get_tight_bbox(fig, bbox_extra_artists=[], pad=None): +def get_tight_bbox(fig, bbox_extra_artists=None, pad=None): """ Compute a tight bounding box around all the artists in the figure. """ + if bbox_extra_artists is None: + bbox_extra_artists = [] renderer = fig.canvas.get_renderer() bbox_inches = fig.get_tightbbox(renderer) bbox_artists = bbox_extra_artists[:] diff --git a/holoviews/plotting/plot.py b/holoviews/plotting/plot.py index 29d60f51b0..9bf5831825 100644 --- a/holoviews/plotting/plot.py +++ b/holoviews/plotting/plot.py @@ -1170,7 +1170,11 @@ class GenericElementPlot(DimensionedPlot): def __init__(self, element, keys=None, ranges=None, dimensions=None, batched=False, overlaid=0, cyclic_index=0, zorder=0, style=None, - overlay_dims={}, stream_sources={}, streams=None, **params): + overlay_dims=None, stream_sources=None, streams=None, **params): + if stream_sources is None: + stream_sources = {} + if overlay_dims is None: + overlay_dims = {} self.zorder = zorder self.cyclic_index = cyclic_index self.overlaid = overlaid diff --git a/holoviews/plotting/renderer.py b/holoviews/plotting/renderer.py index 7a2af71a1b..30da306a7c 100644 --- a/holoviews/plotting/renderer.py +++ b/holoviews/plotting/renderer.py @@ -558,12 +558,16 @@ def plot_options(cls, obj, percent_size): raise NotImplementedError @bothmethod - def save(self_or_cls, obj, basename, fmt='auto', key={}, info={}, + def save(self_or_cls, obj, basename, fmt='auto', key=None, info=None, options=None, resources='inline', title=None, **kwargs): """ Save a HoloViews object to file, either using an explicitly supplied format or to the appropriate default. """ + if info is None: + info = {} + if key is None: + key = {} if info or key: raise Exception('Renderer does not support saving metadata to file.') diff --git a/holoviews/plotting/util.py b/holoviews/plotting/util.py index 48f82bef24..02bcab317a 100644 --- a/holoviews/plotting/util.py +++ b/holoviews/plotting/util.py @@ -120,7 +120,7 @@ def overlay_depth(obj): return 1 -def compute_overlayable_zorders(obj, path=[]): +def compute_overlayable_zorders(obj, path=None): """ Traverses an overlayable composite container to determine which objects are associated with specific (Nd)Overlay layers by @@ -131,6 +131,8 @@ def compute_overlayable_zorders(obj, path=[]): Used to determine which overlaid subplots should be linked with Stream callbacks. """ + if path is None: + path = [] path = path+[obj] zorder_map = defaultdict(list) diff --git a/holoviews/selection.py b/holoviews/selection.py index 628ead0d33..ea5ae96bbb 100644 --- a/holoviews/selection.py +++ b/holoviews/selection.py @@ -475,11 +475,15 @@ class SelectionDisplay: def __call__(self, element): return self - def build_selection(self, selection_streams, hvobj, operations, region_stream=None, cache={}): + def build_selection(self, selection_streams, hvobj, operations, region_stream=None, cache=None): + if cache is None: + cache = {} raise NotImplementedError() @staticmethod - def _select(element, selection_expr, cache={}): + def _select(element, selection_expr, cache=None): + if cache is None: + cache = {} from .element import Curve, Spread from .util.transform import dim if isinstance(selection_expr, dim): @@ -646,7 +650,9 @@ def __init__(self, color_prop='color', alpha_prop='alpha', backend=None): self.alpha_props = [alpha_prop] self.backend = backend - def build_selection(self, selection_streams, hvobj, operations, region_stream=None, cache={}): + def build_selection(self, selection_streams, hvobj, operations, region_stream=None, cache=None): + if cache is None: + cache = {} def _build_selection(el, colors, alpha, exprs, **kwargs): from .plotting.util import linear_gradient ds = el.dataset diff --git a/holoviews/streams.py b/holoviews/streams.py index fb4877ff15..69ff448609 100644 --- a/holoviews/streams.py +++ b/holoviews/streams.py @@ -241,7 +241,7 @@ def _process_streams(cls, streams): return valid, invalid - def __init__(self, rename={}, source=None, subscribers=[], linked=False, + def __init__(self, rename=None, source=None, subscribers=None, linked=False, transient=False, **params): """ The rename argument allows multiple streams with similar event @@ -256,6 +256,10 @@ def __init__(self, rename={}, source=None, subscribers=[], linked=False, """ # Source is stored as a weakref to allow it to be garbage collected + if subscribers is None: + subscribers = [] + if rename is None: + rename = {} self._source = None if source is None else weakref.ref(source) self._subscribers = [] @@ -1603,7 +1607,9 @@ class PointDraw(CDSStream): path-like data).""") def __init__(self, empty_value=None, add=True, drag=True, num_objects=0, - styles={}, tooltip=None, **params): + styles=None, tooltip=None, **params): + if styles is None: + styles = {} self.add = add self.drag = drag self.empty_value = empty_value @@ -1646,7 +1652,9 @@ class CurveEdit(PointDraw): (for point-like data) or list of lists of values (for path-like data).""") - def __init__(self, style={}, tooltip=None, **params): + def __init__(self, style=None, tooltip=None, **params): + if style is None: + style = {} self.style = style or {'size': 10} self.tooltip = tooltip super(PointDraw, self).__init__(**params) @@ -1690,8 +1698,12 @@ class PolyDraw(CDSStream): path-like data).""") def __init__(self, empty_value=None, drag=True, num_objects=0, - show_vertices=False, vertex_style={}, styles={}, + show_vertices=False, vertex_style=None, styles=None, tooltip=None, **params): + if styles is None: + styles = {} + if vertex_style is None: + vertex_style = {} self.drag = drag self.empty_value = empty_value self.num_objects = num_objects @@ -1748,7 +1760,9 @@ class FreehandDraw(CDSStream): (for point-like data) or list of lists of values (for path-like data).""") - def __init__(self, empty_value=None, num_objects=0, styles={}, tooltip=None, **params): + def __init__(self, empty_value=None, num_objects=0, styles=None, tooltip=None, **params): + if styles is None: + styles = {} self.empty_value = empty_value self.num_objects = num_objects self.styles = styles @@ -1802,7 +1816,9 @@ class BoxEdit(CDSStream): (for point-like data) or list of lists of values (for path-like data).""") - def __init__(self, empty_value=None, num_objects=0, styles={}, tooltip=None, **params): + def __init__(self, empty_value=None, num_objects=0, styles=None, tooltip=None, **params): + if styles is None: + styles = {} self.empty_value = empty_value self.num_objects = num_objects self.styles = styles @@ -1864,6 +1880,8 @@ class PolyEdit(PolyDraw): (for point-like data) or list of lists of values (for path-like data).""") - def __init__(self, vertex_style={}, shared=True, **params): + def __init__(self, vertex_style=None, shared=True, **params): + if vertex_style is None: + vertex_style = {} self.shared = shared super().__init__(vertex_style=vertex_style, **params) diff --git a/holoviews/tests/core/test_dimensioned.py b/holoviews/tests/core/test_dimensioned.py index dbd4743bc9..de4688b5ae 100644 --- a/holoviews/tests/core/test_dimensioned.py +++ b/holoviews/tests/core/test_dimensioned.py @@ -40,7 +40,11 @@ def tearDown(self): Store.renderers.pop('backend_2') @classmethod - def register_custom(cls, objtype, backend, custom_plot=[], custom_style=[]): + def register_custom(cls, objtype, backend, custom_plot=None, custom_style=None): + if custom_style is None: + custom_style = [] + if custom_plot is None: + custom_plot = [] groups = Options._option_groups if backend not in Store._options: Store._options[backend] = OptionTree([], groups=groups) diff --git a/holoviews/tests/plotting/bokeh/test_plot.py b/holoviews/tests/plotting/bokeh/test_plot.py index aa0e04fa84..d7496bdae6 100644 --- a/holoviews/tests/plotting/bokeh/test_plot.py +++ b/holoviews/tests/plotting/bokeh/test_plot.py @@ -59,7 +59,9 @@ def _test_colormapping(self, element, dim, log=False): mapper_type = LogColorMapper if log else LinearColorMapper self.assertTrue(isinstance(cmapper, mapper_type)) - def _test_hover_info(self, element, tooltips, line_policy='nearest', formatters={}): + def _test_hover_info(self, element, tooltips, line_policy='nearest', formatters=None): + if formatters is None: + formatters = {} plot = bokeh_renderer.get_plot(element) plot.initialize_plot() fig = plot.state diff --git a/holoviews/util/__init__.py b/holoviews/util/__init__.py index a22434a902..3961ade353 100644 --- a/holoviews/util/__init__.py +++ b/holoviews/util/__init__.py @@ -991,7 +991,9 @@ def _get_streams(self, map_obj, watch=True): raise TypeError(msg.format(objs = ', '.join(f'{el!r}' for el in invalid))) return valid - def _process(self, element, key=None, kwargs={}): + def _process(self, element, key=None, kwargs=None): + if kwargs is None: + kwargs = {} if util.is_param_method(self.p.operation) and util.get_method_owner(self.p.operation) is element: return self.p.operation(**kwargs) elif isinstance(self.p.operation, Operation): diff --git a/holoviews/util/parser.py b/holoviews/util/parser.py index 437289e8d6..b2bab3c6f5 100644 --- a/holoviews/util/parser.py +++ b/holoviews/util/parser.py @@ -78,7 +78,7 @@ def collect_tokens(cls, parseresult, mode): return tokens @classmethod - def todict(cls, parseresult, mode='parens', ns={}): + def todict(cls, parseresult, mode='parens', ns=None): """ Helper function to return dictionary given the parse results from a pyparsing.nestedExpr object (containing keywords). @@ -86,6 +86,8 @@ def todict(cls, parseresult, mode='parens', ns={}): The ns is a dynamic namespace (typically the IPython Notebook namespace) used to update the class-level namespace. """ + if ns is None: + ns = {} grouped, kwargs = [], {} tokens = cls.collect_tokens(parseresult, mode) # Group tokens without '=' and append to last token containing '=' @@ -299,11 +301,13 @@ def apply_deprecations(cls, path): @classmethod - def parse(cls, line, ns={}): + def parse(cls, line, ns=None): """ Parse an options specification, returning a dictionary with path keys and {'plot':, 'style':} values. """ + if ns is None: + ns = {} parses = [p for p in cls.opts_spec.scanString(line)] if len(parses) != 1: raise SyntaxError("Invalid specification syntax.") @@ -344,11 +348,13 @@ def parse(cls, line, ns={}): } @classmethod - def parse_options(cls, line, ns={}): + def parse_options(cls, line, ns=None): """ Similar to parse but returns a list of Options objects instead of the dictionary format. """ + if ns is None: + ns = {} parsed = cls.parse(line, ns=ns) options_list = [] for spec in sorted(parsed.keys()): @@ -400,10 +406,12 @@ class CompositorSpec(Parser): @classmethod - def parse(cls, line, ns={}): + def parse(cls, line, ns=None): """ Parse compositor specifications, returning a list Compositors """ + if ns is None: + ns = {} definitions = [] parses = [p for p in cls.compositor_spec.scanString(line)] if len(parses) != 1: diff --git a/holoviews/util/transform.py b/holoviews/util/transform.py index 9ab375bb7a..5681517dcc 100644 --- a/holoviews/util/transform.py +++ b/holoviews/util/transform.py @@ -689,7 +689,7 @@ def _coerce(self, data): """ return data - def apply(self, dataset, flat=False, expanded=None, ranges={}, all_values=False, + def apply(self, dataset, flat=False, expanded=None, ranges=None, all_values=False, keep_index=False, compute=True, strict=False): """Evaluates the transform on the supplied dataset. @@ -712,6 +712,8 @@ def apply(self, dataset, flat=False, expanded=None, ranges={}, all_values=False, Returns: values: NumPy array computed by evaluating the expression """ + if ranges is None: + ranges = {} from ..element import Graph dimension = self.dimension From bdb519a26ab1f3bcca311c8c3542f4853b0cc468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 18 Aug 2023 08:50:51 +0200 Subject: [PATCH 03/11] B006 - Manual fix mutable argument --- holoviews/tests/core/test_dynamic.py | 21 ++++++++++++------- .../tests/plotting/bokeh/test_callbacks.py | 3 ++- .../plotting/matplotlib/test_callbacks.py | 3 ++- .../tests/plotting/plotly/test_elementplot.py | 3 ++- holoviews/util/command.py | 9 ++++---- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/holoviews/tests/core/test_dynamic.py b/holoviews/tests/core/test_dynamic.py index 8d043b0fa5..8363f99453 100644 --- a/holoviews/tests/core/test_dynamic.py +++ b/holoviews/tests/core/test_dynamic.py @@ -389,7 +389,8 @@ def test_deep_map_apply_dmap_function_no_clone(self): self.assertIs(mapped, layout) def test_dynamic_reindex_reorder(self): - def history_callback(x, y, history=deque(maxlen=10)): + history = deque(maxlen=10) + def history_callback(x, y): history.append((x, y)) return Points(list(history)) dmap = DynamicMap(history_callback, kdims=['x', 'y']) @@ -398,7 +399,8 @@ def history_callback(x, y, history=deque(maxlen=10)): self.assertEqual(points, Points([(1, 2)])) def test_dynamic_reindex_drop_raises_exception(self): - def history_callback(x, y, history=deque(maxlen=10)): + history = deque(maxlen=10) + def history_callback(x, y): history.append((x, y)) return Points(list(history)) dmap = DynamicMap(history_callback, kdims=['x', 'y']) @@ -790,7 +792,8 @@ def test_dynamic_keydim_memoize_disable(self): def test_dynamic_callable_memoize(self): # Always memoized only one of each held - def history_callback(x, history=deque(maxlen=10)): + history = deque(maxlen=10) + def history_callback(x): history.append(x) return Curve(list(history)) @@ -812,7 +815,8 @@ def history_callback(x, history=deque(maxlen=10)): def test_dynamic_callable_disable_callable_memoize(self): # Disabling Callable.memoize means no memoization is applied, # every access to DynamicMap calls callback and adds sample - def history_callback(x, history=deque(maxlen=10)): + history = deque(maxlen=10) + def history_callback(x): history.append(x) return Curve(list(history)) @@ -878,7 +882,8 @@ def test_dynamic_callable_stream_transient(self): # Enable transient stream meaning memoization only happens when # stream is inactive, should have sample for each call to # stream.update - def history_callback(x, history=deque(maxlen=10)): + history = deque(maxlen=10) + def history_callback(x): if x is not None: history.append(x) return Curve(list(history)) @@ -900,7 +905,8 @@ def history_callback(x, history=deque(maxlen=10)): def test_dynamic_stream_transients(self): # Ensure Stream reset option resets streams to default value # when not triggering - def history_callback(x, y, history=deque(maxlen=10)): + history = deque(maxlen=10) + def history_callback(x, y): if x is None: history_callback.xresets += 1 else: @@ -933,7 +939,8 @@ def test_dynamic_callable_stream_hashkey(self): # Enable transient stream meaning memoization only happens when # stream is inactive, should have sample for each call to # stream.update - def history_callback(x, history=deque(maxlen=10)): + history = deque(maxlen=10) + def history_callback(x): if x is not None: history.append(x) return Curve(list(history)) diff --git a/holoviews/tests/plotting/bokeh/test_callbacks.py b/holoviews/tests/plotting/bokeh/test_callbacks.py index 68e868a1b7..bb0810ab67 100644 --- a/holoviews/tests/plotting/bokeh/test_callbacks.py +++ b/holoviews/tests/plotting/bokeh/test_callbacks.py @@ -98,7 +98,8 @@ def test_stream_callback_with_ids(self): self.assertEqual(data['y'], np.array([0.4])) def test_stream_callback_single_call(self): - def history_callback(x, history=deque(maxlen=10)): + history = deque(maxlen=10) + def history_callback(x): history.append(x) return Curve(list(history)) stream = PointerX(x=0) diff --git a/holoviews/tests/plotting/matplotlib/test_callbacks.py b/holoviews/tests/plotting/matplotlib/test_callbacks.py index 7214704aba..2c7fe2cddf 100644 --- a/holoviews/tests/plotting/matplotlib/test_callbacks.py +++ b/holoviews/tests/plotting/matplotlib/test_callbacks.py @@ -24,7 +24,8 @@ def test_dynamic_streams_refresh(self): self.assertNotEqual(pre, post) def test_stream_callback_single_call(self): - def history_callback(x, history=deque(maxlen=10)): + history = deque(maxlen=10) + def history_callback(x): history.append(x) return Curve(list(history)) stream = PointerX(x=0) diff --git a/holoviews/tests/plotting/plotly/test_elementplot.py b/holoviews/tests/plotting/plotly/test_elementplot.py index 5f1460c6b1..e3b589fd26 100644 --- a/holoviews/tests/plotting/plotly/test_elementplot.py +++ b/holoviews/tests/plotting/plotly/test_elementplot.py @@ -13,7 +13,8 @@ class TestElementPlot(TestPlotlyPlot): def test_stream_callback_single_call(self): - def history_callback(x, history=deque(maxlen=10)): + history = deque(maxlen=10) + def history_callback(x): history.append(x) return Curve(list(history)) stream = PointerX(x=0) diff --git a/holoviews/util/command.py b/holoviews/util/command.py index 6751142bcc..da73b4165a 100755 --- a/holoviews/util/command.py +++ b/holoviews/util/command.py @@ -25,6 +25,7 @@ from . import examples +_PREPROCESSORS = [OptsMagicProcessor(), OutputMagicProcessor(), StripMagicsProcessor()] def main(): if len(sys.argv) < 2: @@ -56,11 +57,9 @@ def main(): examples(path=examples_dir, root=root) -def export_to_python(filename=None, - preprocessors=[OptsMagicProcessor(), - OutputMagicProcessor(), - StripMagicsProcessor()]): - +def export_to_python(filename=None, preprocessors=None): + if preprocessors is None: + preprocessors = _PREPROCESSORS.copy() filename = filename if filename else sys.argv[1] with open(filename) as f: nb = nbformat.read(f, nbformat.NO_CONVERT) From a0d601d9f52c692aeb0fb921d096c5cfa93dcc08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 18 Aug 2023 08:59:39 +0200 Subject: [PATCH 04/11] Fix self.assertRaises(Exception) --- holoviews/tests/plotting/matplotlib/test_graphplot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/holoviews/tests/plotting/matplotlib/test_graphplot.py b/holoviews/tests/plotting/matplotlib/test_graphplot.py index b370b95ebb..22aa1bf85b 100644 --- a/holoviews/tests/plotting/matplotlib/test_graphplot.py +++ b/holoviews/tests/plotting/matplotlib/test_graphplot.py @@ -401,7 +401,8 @@ def test_trimesh_op_node_alpha(self): if Version(mpl.__version__) < Version("3.4.0"): # Python 3.6 only support up to matplotlib 3.3 - with self.assertRaises(Exception): + msg = "TypeError: alpha must be a float or None" + with pytest.raises(AbbreviatedException, match=msg): mpl_renderer.get_plot(trimesh) else: plot = mpl_renderer.get_plot(trimesh) From 3a5fe49fd77cb6363b14310c0cbb8ab8f6e806aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 18 Aug 2023 09:07:52 +0200 Subject: [PATCH 05/11] B018 - Fix useless expression, by making it a function call to be more explicit --- holoviews/element/graphs.py | 13 +++++++++---- holoviews/plotting/bokeh/graphs.py | 2 +- holoviews/plotting/mpl/graphs.py | 3 +-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/holoviews/element/graphs.py b/holoviews/element/graphs.py index 781100a873..2935ff93e7 100644 --- a/holoviews/element/graphs.py +++ b/holoviews/element/graphs.py @@ -552,8 +552,7 @@ def from_vertices(cls, data): tris = Delaunay(data.array([0, 1])) return cls((tris.simplices, data)) - @property - def edgepaths(self): + def _initialize_edgepaths(self): """ Returns the EdgePaths by generating a triangle for each simplex. """ @@ -576,6 +575,13 @@ def edgepaths(self): self._edgepaths = edgepaths return edgepaths + @property + def edgepaths(self): + """ + Returns the EdgePaths by generating a triangle for each simplex. + """ + return self._initialize_edgepaths() + def select(self, selection_specs=None, **selection): """ Allows selecting data by the slices, sets and scalar values @@ -586,8 +592,7 @@ def select(self, selection_specs=None, **selection): supplied, which will ensure the selection is only applied if the specs match the selected object. """ - # Ensure that edgepaths are initialized so they can be selected on - self.edgepaths + self._initialize_edgepaths() return super().select(selection_specs=None, selection_mode='nodes', **selection) diff --git a/holoviews/plotting/bokeh/graphs.py b/holoviews/plotting/bokeh/graphs.py index 4f6924b07e..bf9bfd898e 100644 --- a/holoviews/plotting/bokeh/graphs.py +++ b/holoviews/plotting/bokeh/graphs.py @@ -507,7 +507,7 @@ def _process_vertices(self, element): z = element.nodes.dimension_values(vertex_dim) z = z[simplices].mean(axis=1) element = element.add_dimension(vertex_dim, len(element.vdims), z, vdim=True) - element.edgepaths + element._initialize_edgepaths() return element def _init_glyphs(self, plot, element, ranges, source): diff --git a/holoviews/plotting/mpl/graphs.py b/holoviews/plotting/mpl/graphs.py index cab4288583..1609428e2a 100644 --- a/holoviews/plotting/mpl/graphs.py +++ b/holoviews/plotting/mpl/graphs.py @@ -245,8 +245,7 @@ def get_data(self, element, ranges, style): z = element.nodes.dimension_values(vertex_dim) z = z[simplices].mean(axis=1) element = element.add_dimension(vertex_dim, len(element.vdims), z, vdim=True) - # Ensure the edgepaths for the triangles are generated before plotting - element.edgepaths + element._initialize_edgepaths() return super().get_data(element, ranges, style) From e51198faebb153440d5941ce6ca8bc14eb7cfefc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 18 Aug 2023 09:13:26 +0200 Subject: [PATCH 06/11] Add Bugbear to Ruff --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index e6307aa6da..783f30af4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ run.concurrency = ["greenlet"] target-version = "py38" select = [ + "B", "E", "F", "FLY", @@ -51,6 +52,7 @@ select = [ ] ignore = [ + "B904", # Within an `except` clause, raise exceptions with from err or None "E402", # Module level import not at top of file "E501", # Line too long "E701", # Multiple statements on one line From c1dc5b596e544882d256b2eaddb4dd6302eb9d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 18 Aug 2023 09:14:49 +0200 Subject: [PATCH 07/11] Ignore valid code --- holoviews/core/util.py | 2 +- holoviews/tests/core/test_options.py | 7 +++---- holoviews/tests/core/test_tree.py | 7 +++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/holoviews/core/util.py b/holoviews/core/util.py index e5881a22a5..d78aa52ff0 100644 --- a/holoviews/core/util.py +++ b/holoviews/core/util.py @@ -1209,7 +1209,7 @@ def python2sort(x,key=None): try: item_precedence = item if key is None else key(item) group_precedence = group[0] if key is None else key(group[0]) - item_precedence < group_precedence # exception if not comparable + item_precedence < group_precedence # noqa: B015, TypeError if not comparable group.append(item) break except TypeError: diff --git a/holoviews/tests/core/test_options.py b/holoviews/tests/core/test_options.py index 06ecbf5558..ceac1ce5e9 100644 --- a/holoviews/tests/core/test_options.py +++ b/holoviews/tests/core/test_options.py @@ -182,10 +182,9 @@ def test_cyclic_property_false(self): def test_options_property_disabled(self): cycle1 = Cycle(values=['a', 'b', 'c']) opts = Options('test', one=cycle1) - try: - opts.options - except Exception as e: - self.assertEqual(str(e), "The options property may only be used with non-cyclic Options.") + msg = r"The options property may only be used with non-cyclic Options\." + with pytest.raises(Exception, match=msg): + opts.options # noqa: B018 diff --git a/holoviews/tests/core/test_tree.py b/holoviews/tests/core/test_tree.py index 57f5c0a610..7b2c707944 100644 --- a/holoviews/tests/core/test_tree.py +++ b/holoviews/tests/core/test_tree.py @@ -1,3 +1,5 @@ +import pytest + from holoviews.core.element import AttrTree from holoviews.element.comparison import ComparisonTestCase @@ -24,8 +26,9 @@ def test_deep_setattr(self): self.assertEqual(self.tree.C.I, 3) def test_lowercase_attribute_error(self): - with self.assertRaises(AttributeError): - self.tree.c + msg = r"'AttrTree' object has no attribute c\." + with pytest.raises(AttributeError, match=msg): + self.tree.c # noqa: B018 def test_number_getitem_key_error(self): with self.assertRaises(KeyError): From 31136af8e77053df013071d937c5f58e9e55e600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Mon, 28 Aug 2023 11:54:58 +0200 Subject: [PATCH 08/11] Move import to top --- holoviews/core/accessors.py | 5 +++-- holoviews/core/data/ibis.py | 2 +- holoviews/util/transform.py | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/holoviews/core/accessors.py b/holoviews/core/accessors.py index 32c3a91e2a..7e742394f7 100644 --- a/holoviews/core/accessors.py +++ b/holoviews/core/accessors.py @@ -132,14 +132,15 @@ def __call__(self, apply_function, streams=None, link_inputs=True, A new object where the function was applied to all contained (Nd)Overlay or Element objects. """ - if streams is None: - streams = [] from .data import Dataset from .dimension import ViewableElement from .element import Element from .spaces import HoloMap, DynamicMap from ..util import Dynamic + if streams is None: + streams = [] + if isinstance(self._obj, DynamicMap) and dynamic == False: samples = tuple(d.values for d in self._obj.kdims) if not all(samples): diff --git a/holoviews/core/data/ibis.py b/holoviews/core/data/ibis.py index 1b62701e09..0a7022f8c8 100644 --- a/holoviews/core/data/ibis.py +++ b/holoviews/core/data/ibis.py @@ -417,9 +417,9 @@ def select_mask(cls, dataset, selection): @classmethod def sample(cls, dataset, samples=None): + import ibis if samples is None: samples = [] - import ibis dims = dataset.dimensions() data = dataset.data if all(util.isscalar(s) or len(s) == 1 for s in samples): diff --git a/holoviews/util/transform.py b/holoviews/util/transform.py index 5681517dcc..c7b9bd1d7f 100644 --- a/holoviews/util/transform.py +++ b/holoviews/util/transform.py @@ -712,9 +712,10 @@ def apply(self, dataset, flat=False, expanded=None, ranges=None, all_values=Fals Returns: values: NumPy array computed by evaluating the expression """ + from ..element import Graph + if ranges is None: ranges = {} - from ..element import Graph dimension = self.dimension if expanded is None: From 1690af93bbf955bf659ade2d9cf9f47d6fb4a033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Tue, 29 Aug 2023 08:53:33 +0200 Subject: [PATCH 09/11] Convert v to _ in split_dmap_overlay --- holoviews/plotting/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/holoviews/plotting/util.py b/holoviews/plotting/util.py index 02bcab317a..a78994b54c 100644 --- a/holoviews/plotting/util.py +++ b/holoviews/plotting/util.py @@ -225,14 +225,14 @@ def split_dmap_overlay(obj, depth=0): if isinstance(obj, DynamicMap): initialize_dynamic(obj) if issubclass(obj.type, NdOverlay) and not depth: - for v in obj.last.values(): + for _ in obj.last.values(): layers.append(obj) elif issubclass(obj.type, Overlay): if obj.callback.inputs and is_dynamic_overlay(obj): for inp in obj.callback.inputs: layers += split_dmap_overlay(inp, depth+1) else: - for v in obj.last.values(): + for _ in obj.last.values(): layers.append(obj) else: layers.append(obj) From 79254680e5d20fe3e0da38454c592cbf3e869efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 8 Sep 2023 13:59:56 +0200 Subject: [PATCH 10/11] Move import to top --- holoviews/core/accessors.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/holoviews/core/accessors.py b/holoviews/core/accessors.py index 7e742394f7..ea167e7930 100644 --- a/holoviews/core/accessors.py +++ b/holoviews/core/accessors.py @@ -365,11 +365,12 @@ def _transform_dimension(self, kdims, vdims, dimension): return dimension def _create_expression_transform(self, kdims, vdims, exclude=None): - if exclude is None: - exclude = [] from .dimension import dimension_name from ..util.transform import dim + if exclude is None: + exclude = [] + def _transform_expression(expression): if dimension_name(expression.dimension) in exclude: dimension = expression.dimension From 3163a6acdfaf5d881b620e1eaa2a7145b2630391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 22 Sep 2023 12:16:38 +0200 Subject: [PATCH 11/11] Remove star unpack after keyword argument --- holoviews/core/overlay.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/holoviews/core/overlay.py b/holoviews/core/overlay.py index 39eebb244e..050b14a4cb 100644 --- a/holoviews/core/overlay.py +++ b/holoviews/core/overlay.py @@ -287,11 +287,10 @@ def ddims(self): def shape(self): raise NotImplementedError - def clone(self, data=None, shared_data=True, new_type=None, link=True, - *args, **overrides): + def clone(self, data=None, shared_data=True, new_type=None, link=True, **overrides): if data is None and link: overrides['plot_id'] = self._plot_id - return super().clone(data, shared_data=shared_data, new_type=new_type, link=link, *args, **overrides) + return super().clone(data, shared_data=shared_data, new_type=new_type, link=link, **overrides) class NdOverlay(Overlayable, UniformNdMapping, CompositeOverlay):