From 6cd8a4e929f967338dfa3538536a1a816891210f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Tue, 21 Nov 2023 10:53:26 +0100 Subject: [PATCH] Enable Bugbear 904 - Within an `except` clause, raise exceptions with `from err` or `from None` (#5992) --- .pre-commit-config.yaml | 2 +- holoviews/core/dimension.py | 2 +- holoviews/core/io.py | 8 ++++---- holoviews/core/options.py | 4 ++-- holoviews/element/comparison.py | 18 +++++++++--------- holoviews/element/graphs.py | 6 +++--- holoviews/element/raster.py | 2 +- holoviews/element/selection.py | 8 ++++---- holoviews/ipython/__init__.py | 4 ++-- holoviews/operation/datashader.py | 4 ++-- holoviews/operation/element.py | 2 +- holoviews/operation/normalization.py | 2 +- holoviews/operation/stats.py | 4 ++-- holoviews/plotting/plot.py | 4 ++-- holoviews/plotting/plotly/chart3d.py | 2 +- holoviews/plotting/plotly/images.py | 2 +- holoviews/plotting/renderer.py | 2 +- holoviews/plotting/util.py | 4 ++-- holoviews/selection.py | 4 ++-- holoviews/util/__init__.py | 6 +++--- holoviews/util/parser.py | 2 +- holoviews/util/settings.py | 4 ++-- holoviews/util/transform.py | 2 +- pyproject.toml | 2 +- 24 files changed, 50 insertions(+), 50 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 287615f2ea..6c59e8a313 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,6 +41,6 @@ repos: - id: rst-directive-colons - id: rst-inline-touching-normal - repo: https://github.com/shellcheck-py/shellcheck-py - rev: "v0.9.0.6" + rev: v0.9.0.6 hooks: - id: shellcheck diff --git a/holoviews/core/dimension.py b/holoviews/core/dimension.py index 200a3ed374..6ff601295c 100644 --- a/holoviews/core/dimension.py +++ b/holoviews/core/dimension.py @@ -998,7 +998,7 @@ def get_dimension_index(self, dimension): dimensions = self.kdims+self.vdims return next(i for i, d in enumerate(dimensions) if d == dim) except StopIteration: - raise Exception(f"Dimension {dim} not found in {self.__class__.__name__}.") + raise Exception(f"Dimension {dim} not found in {self.__class__.__name__}.") from None def get_dimension_type(self, dim): diff --git a/holoviews/core/io.py b/holoviews/core/io.py index e895c711c9..a3991aa85a 100644 --- a/holoviews/core/io.py +++ b/holoviews/core/io.py @@ -634,8 +634,8 @@ def parse_fields(cls, formatter): try: parse = list(string.Formatter().parse(formatter)) return {f for f in list(zip(*parse))[1] if f is not None} - except Exception: - raise SyntaxError(f"Could not parse formatter {formatter!r}") + except Exception as e: + raise SyntaxError(f"Could not parse formatter {formatter!r}") from e def __init__(self, **params): super().__init__(**params) @@ -673,8 +673,8 @@ def _validate_formatters(self): raise Exception(f"Valid export fields are: {','.join(sorted(self.efields))}") try: time.strftime(self.timestamp_format, tuple(time.localtime())) - except Exception: - raise Exception("Timestamp format invalid") + except Exception as e: + raise Exception("Timestamp format invalid") from e def add(self, obj=None, filename=None, data=None, info=None, **kwargs): diff --git a/holoviews/core/options.py b/holoviews/core/options.py index 18a87071d4..1ae4b1199b 100644 --- a/holoviews/core/options.py +++ b/holoviews/core/options.py @@ -78,7 +78,7 @@ def cleanup_custom_options(id, weakref=None): f"Cleanup of custom options tree with id '{id}' failed " f"with the following exception: {e}, an unreferenced " "orphan tree may persist in memory." - ) + ) from e def lookup_options(obj, group, backend): @@ -660,7 +660,7 @@ def _merge_options(self, identifier, group_name, options): raise OptionError(e.invalid_keyword, e.allowed_keywords, group_name=group_name, - path = self.path) + path = self.path) from e def __getitem__(self, item): if item in self.groups: diff --git a/holoviews/element/comparison.py b/holoviews/element/comparison.py index 94f8395255..1e40973552 100644 --- a/holoviews/element/comparison.py +++ b/holoviews/element/comparison.py @@ -241,8 +241,8 @@ def compare_lists(cls, l1, l2, msg=None): cls.assertEqual(len(l1), len(l2)) for v1, v2 in zip(l1, l2): cls.assertEqual(v1, v2) - except AssertionError: - raise AssertionError(msg or f'{l1!r} != {l2!r}') + except AssertionError as e: + raise AssertionError(msg or f'{l1!r} != {l2!r}') from e @classmethod @@ -251,8 +251,8 @@ def compare_tuples(cls, t1, t2, msg=None): cls.assertEqual(len(t1), len(t2)) for i1, i2 in zip(t1, t2): cls.assertEqual(i1, i2) - except AssertionError: - raise AssertionError(msg or f'{t1!r} != {t2!r}') + except AssertionError as e: + raise AssertionError(msg or f'{t1!r} != {t2!r}') from e #=====================# @@ -275,7 +275,7 @@ def compare_arrays(cls, arr1, arr2, msg='Arrays'): try: cls.assert_array_almost_equal_fn(arr1, arr2) except AssertionError as e: - raise cls.failureException(msg + str(e)[11:]) + raise cls.failureException(msg + str(e)[11:]) from e @classmethod def bounds_check(cls, el1, el2, msg=None): @@ -288,8 +288,8 @@ def bounds_check(cls, el1, el2, msg=None): if isinstance(v2, datetime_types): v2 = dt_to_int(v2) cls.assert_array_almost_equal_fn(v1, v2) - except AssertionError: - raise cls.failureException(f"BoundingBoxes are mismatched: {el1.bounds.lbrt()} != {el2.bounds.lbrt()}.") + except AssertionError as e: + raise cls.failureException(f"BoundingBoxes are mismatched: {el1.bounds.lbrt()} != {el2.bounds.lbrt()}.") from e #=======================================# @@ -321,7 +321,7 @@ def compare_dimensions(cls, dim1, dim2, msg=None): cls.assertEqual(dim1_params[k], dim2_params[k], msg=None) except AssertionError as e: msg = f'Dimension parameter {k!r} mismatched: ' - raise cls.failureException(f"{msg}{e!s}") + raise cls.failureException(f"{msg}{e!s}") from e @classmethod def compare_labelled_data(cls, obj1, obj2, msg=None): @@ -712,7 +712,7 @@ def compare_dataframe(cls, df1, df2, msg='DFrame'): try: assert_frame_equal(df1, df2) except AssertionError as e: - raise cls.failureException(msg+': '+str(e)) + raise cls.failureException(f'{msg}: {e}') from e #============# # Statistics # diff --git a/holoviews/element/graphs.py b/holoviews/element/graphs.py index d7082b4291..048d8b2103 100644 --- a/holoviews/element/graphs.py +++ b/holoviews/element/graphs.py @@ -528,11 +528,11 @@ def __init__(self, data, kdims=None, vdims=None, **params): points = self.point_type(nodes) ds = Dataset(points).add_dimension('index', 2, np.arange(len(points))) nodes = self.node_type(ds) - except Exception: + except Exception as e: raise ValueError( "Nodes argument could not be interpreted, expected " "data with two or three columns representing the " - "x/y positions and optionally the node indices.") + "x/y positions and optionally the node indices.") from e if edgepaths is not None and not isinstance(edgepaths, self.edge_type): edgepaths = self.edge_type(edgepaths) @@ -549,7 +549,7 @@ def from_vertices(cls, data): from scipy.spatial import Delaunay except ImportError: raise ImportError("Generating triangles from points requires " - "SciPy to be installed.") + "SciPy to be installed.") from None if not isinstance(data, Points): data = Points(data) if not len(data): diff --git a/holoviews/element/raster.py b/holoviews/element/raster.py index d8ac23e665..19cf97e28e 100644 --- a/holoviews/element/raster.py +++ b/holoviews/element/raster.py @@ -648,7 +648,7 @@ def load_image(cls, filename, height=1, array=False, bounds=None, bare=False, ** try: from PIL import Image except ImportError: - raise ImportError("RGB.load_image requires PIL (or Pillow).") + raise ImportError("RGB.load_image requires PIL (or Pillow).") from None with open(filename, 'rb') as f: data = np.array(Image.open(f)) diff --git a/holoviews/element/selection.py b/holoviews/element/selection.py index 367ab0ec0e..21c4311960 100644 --- a/holoviews/element/selection.py +++ b/holoviews/element/selection.py @@ -69,7 +69,7 @@ def spatial_select_gridded(xvals, yvals, geometry): from ..operation.datashader import rasterize except ImportError: raise ImportError("Lasso selection on gridded data requires " - "datashader to be available.") + "datashader to be available.") from None xs, ys = xvals[0], yvals[:, 0] target = Image((xs, ys, np.empty(ys.shape+xs.shape))) poly = Polygons([geometry]) @@ -132,7 +132,7 @@ def spatial_select_columnar(xvals, yvals, geometry): geom_mask = np.array([poly.contains(p) for p in points]) except ImportError: raise ImportError("Lasso selection on tabular data requires " - "either spatialpandas or shapely to be available.") + "either spatialpandas or shapely to be available.") from None if isinstance(xvals, pd.Series): sel_mask[sel_mask.index[np.where(sel_mask)[0]]] = geom_mask else: @@ -155,7 +155,7 @@ def spatial_geom_select(x0vals, y0vals, x1vals, y1vals, geometry): return np.array([poly.contains(p) for p in boxes]) except ImportError: raise ImportError("Lasso selection on geometry data requires " - "shapely to be available.") + "shapely to be available.") from None def spatial_poly_select(xvals, yvals, geometry): try: @@ -165,7 +165,7 @@ def spatial_poly_select(xvals, yvals, geometry): return np.array([poly.contains(p) for p in boxes]) except ImportError: raise ImportError("Lasso selection on geometry data requires " - "shapely to be available.") + "shapely to be available.") from None def spatial_bounds_select(xvals, yvals, bounds): x0, y0, x1, y1 = bounds diff --git a/holoviews/ipython/__init__.py b/holoviews/ipython/__init__.py index 840b6bb58f..cca6710393 100644 --- a/holoviews/ipython/__init__.py +++ b/holoviews/ipython/__init__.py @@ -41,8 +41,8 @@ def setUp(self): self.ip = IPython.InteractiveShell() if self.ip is None: raise TypeError() - except Exception: - raise SkipTest("IPython could not be started") + except Exception as e: + raise SkipTest("IPython could not be started") from e self.addTypeEqualityFunc(HTML, self.skip_comparison) self.addTypeEqualityFunc(SVG, self.skip_comparison) diff --git a/holoviews/operation/datashader.py b/holoviews/operation/datashader.py index d69d31838f..c6e3f312a2 100644 --- a/holoviews/operation/datashader.py +++ b/holoviews/operation/datashader.py @@ -1570,9 +1570,9 @@ def _process(self, overlay, key=None): try: imgs = xr.align(*imgs, join='exact') - except ValueError: + except ValueError as e: raise ValueError('RGB inputs to the stack operation could not be aligned; ' - 'ensure they share the same grid sampling.') + 'ensure they share the same grid sampling.') from e stacked = tf.stack(*imgs, how=self.p.compositor) arr = shade.uint32_to_uint8(stacked.data)[::-1] diff --git a/holoviews/operation/element.py b/holoviews/operation/element.py index 58fc53bef1..2bb914b55d 100644 --- a/holoviews/operation/element.py +++ b/holoviews/operation/element.py @@ -568,7 +568,7 @@ def _process(self, element, key=None): contour_generator, ) except ImportError: - raise ImportError("contours operation requires contourpy.") + raise ImportError("contours operation requires contourpy.") from None xs = element.dimension_values(0, True, flat=False) ys = element.dimension_values(1, True, flat=False) diff --git a/holoviews/operation/normalization.py b/holoviews/operation/normalization.py index 5e65f7994d..ffa6bac238 100644 --- a/holoviews/operation/normalization.py +++ b/holoviews/operation/normalization.py @@ -117,7 +117,7 @@ def get_ranges(self, element, key): index = keys.index(key) specs = ranges[index] except Exception: - raise KeyError("Could not match element key to defined keys") + raise KeyError("Could not match element key to defined keys") from None else: raise ValueError("Key list length must match length of supplied ranges") diff --git a/holoviews/operation/stats.py b/holoviews/operation/stats.py index dbe9c7b246..32b7585771 100644 --- a/holoviews/operation/stats.py +++ b/holoviews/operation/stats.py @@ -68,7 +68,7 @@ def _process(self, element, key=None): from scipy import stats from scipy.linalg import LinAlgError except ImportError: - raise ImportError(f'{type(self).__name__} operation requires SciPy to be installed.') + raise ImportError(f'{type(self).__name__} operation requires SciPy to be installed.') from None params = {} if isinstance(element, Distribution): @@ -172,7 +172,7 @@ def _process(self, element, key=None): try: from scipy import stats except ImportError: - raise ImportError(f'{type(self).__name__} operation requires SciPy to be installed.') + raise ImportError(f'{type(self).__name__} operation requires SciPy to be installed.') from None if len(element.dimensions()) < 2: raise ValueError("bivariate_kde can only be computed on elements " diff --git a/holoviews/plotting/plot.py b/holoviews/plotting/plot.py index 0bdd60fd05..c3ab171864 100644 --- a/holoviews/plotting/plot.py +++ b/holoviews/plotting/plot.py @@ -348,9 +348,9 @@ def get_plot_class(self, obj): def __setattr__(self, label, value): try: return super().__setattr__(label, value) - except Exception: + except Exception as e: raise Exception("Please set class parameters directly on classes %s" - % ', '.join(str(cls) for cls in self.__dict__['plot_classes'].values())) + % ', '.join(str(cls) for cls in self.__dict__['plot_classes'].values())) from e def params(self): return self.plot_options diff --git a/holoviews/plotting/plotly/chart3d.py b/holoviews/plotting/plotly/chart3d.py index e0ace1380d..ab314d8339 100644 --- a/holoviews/plotting/plotly/chart3d.py +++ b/holoviews/plotting/plotly/chart3d.py @@ -102,7 +102,7 @@ def get_data(self, element, ranges, style, **kwargs): try: from scipy.spatial import Delaunay except ImportError: - raise SkipRendering("SciPy not available, cannot plot TriSurface") + raise SkipRendering("SciPy not available, cannot plot TriSurface") from None x, y, z = (element.dimension_values(i) for i in range(3)) points2D = np.vstack([x, y]).T tri = Delaunay(points2D) diff --git a/holoviews/plotting/plotly/images.py b/holoviews/plotting/plotly/images.py index 53fb928e60..e0eaf3808e 100644 --- a/holoviews/plotting/plotly/images.py +++ b/holoviews/plotting/plotly/images.py @@ -55,7 +55,7 @@ def get_data(self, element, ranges, style, is_geo=False, **kwargs): import PIL.Image except ImportError: raise VersionError("""\ -Rendering RGB elements with the plotly backend requires the Pillow package""") +Rendering RGB elements with the plotly backend requires the Pillow package""") from None img = np.flip( np.dstack([element.dimension_values(d, flat=False) diff --git a/holoviews/plotting/renderer.py b/holoviews/plotting/renderer.py index 3b6c8a98ed..484be8b87d 100644 --- a/holoviews/plotting/renderer.py +++ b/holoviews/plotting/renderer.py @@ -541,7 +541,7 @@ class needed to render it with the current renderer. try: plotclass = Store.registry[cls.backend][element_type] except KeyError: - raise SkipRendering(f"No plotting class for {element_type.__name__} found.") + raise SkipRendering(f"No plotting class for {element_type.__name__} found.") from None return plotclass @classmethod diff --git a/holoviews/plotting/util.py b/holoviews/plotting/util.py index a4bc1e0d4f..d9157deca5 100644 --- a/holoviews/plotting/util.py +++ b/holoviews/plotting/util.py @@ -104,8 +104,8 @@ def collate(obj): collated_layout = Layout(el.collate()) expanded.extend(collated_layout.values()) return Layout(expanded) - except Exception: - raise Exception(undisplayable_info(obj)) + except Exception as e: + raise Exception(undisplayable_info(obj)) from e else: raise Exception(undisplayable_info(obj)) diff --git a/holoviews/selection.py b/holoviews/selection.py index a9c1a59388..cc7685b7e8 100644 --- a/holoviews/selection.py +++ b/holoviews/selection.py @@ -520,10 +520,10 @@ def _select(element, selection_expr, cache=None): except KeyError as e: key_error = str(e).replace('"', '').replace('.', '') raise CallbackError("linked_selection aborted because it could not " - f"display selection for all elements: {key_error} on '{element!r}'.") + f"display selection for all elements: {key_error} on '{element!r}'.") from e except Exception as e: raise CallbackError("linked_selection aborted because it could not " - "display selection for all elements: %s." % e) + "display selection for all elements: %s." % e) from e ds_cache[selection_expr] = mask else: selection = element diff --git a/holoviews/util/__init__.py b/holoviews/util/__init__.py index 3287dbab16..962496927c 100644 --- a/holoviews/util/__init__.py +++ b/holoviews/util/__init__.py @@ -341,7 +341,7 @@ def _expand_options(cls, options, backend=None): try: backend_options = Store.options(backend=backend or current_backend) except KeyError as e: - raise Exception(f'The {e} backend is not loaded. Please load the backend using hv.extension.') + raise Exception(f'The {e} backend is not loaded. Please load the backend using hv.extension.') from None expanded = {} if isinstance(options, list): options = merge_options_to_dict(options) @@ -645,10 +645,10 @@ def renderer(name): if prev_backend: Store.set_current_backend(prev_backend) return Store.renderers[name] - except ImportError: + except ImportError as e: msg = ('Could not find a {name!r} renderer, available renderers are: {available}.') available = ', '.join(repr(k) for k in Store.renderers) - raise ImportError(msg.format(name=name, available=available)) + raise ImportError(msg.format(name=name, available=available)) from e class extension(_pyviz_extension): diff --git a/holoviews/util/parser.py b/holoviews/util/parser.py index 750f831bf7..ec6af789d3 100644 --- a/holoviews/util/parser.py +++ b/holoviews/util/parser.py @@ -115,7 +115,7 @@ def todict(cls, parseresult, mode='parens', ns=None): dict(cls.namespace, **ns))) except Exception: if cls.abort_on_eval_failure: - raise SyntaxError(f"Could not evaluate keyword: {keyword!r}") + raise SyntaxError(f"Could not evaluate keyword: {keyword!r}") from None msg = "Ignoring keyword pair that fails to evaluate: '%s'" parsewarning.warning(msg % keyword) diff --git a/holoviews/util/settings.py b/holoviews/util/settings.py index 05065fb24e..547beefba3 100644 --- a/holoviews/util/settings.py +++ b/holoviews/util/settings.py @@ -95,7 +95,7 @@ def extract_keywords(cls, line, items): try: items.update(eval(f'dict({keyword})')) except Exception: - raise SyntaxError(f"Could not evaluate keyword: {keyword}") + raise SyntaxError(f"Could not evaluate keyword: {keyword}") from None return items @@ -321,7 +321,7 @@ def output(cls, line=None, cell=None, cell_runner=None, raise ValueError(f"The selected plotting extension {backend!r} " "has not been loaded, ensure you load it " f"with hv.extension({backend!r}) before using " - "hv.output.") + "hv.output.") from e print(f'Error: {e}') if help_prompt: print(help_prompt) diff --git a/holoviews/util/transform.py b/holoviews/util/transform.py index c4258237a2..9ecbd2470e 100644 --- a/holoviews/util/transform.py +++ b/holoviews/util/transform.py @@ -930,7 +930,7 @@ def __init__(self, obj, *args, **kwargs): import xarray as xr except ImportError: raise ImportError("XArray could not be imported, dim().xr " - "requires the xarray to be available.") + "requires the xarray to be available.") from None super().__init__(obj, *args, **kwargs) self._ns = xr.DataArray diff --git a/pyproject.toml b/pyproject.toml index 01d089481e..cf88cf6cc1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,6 @@ 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 @@ -99,6 +98,7 @@ unfixable = [ "holoviews/tests/*" = [ "RUF001", "RUF002", "RUF003", # Ambiguous unicode character "NPY002", # Replace legacy `np.random.rand` call with Generator + "B904", # Within an `except` clause, raise exceptions with from err or None ] [tool.ruff.lint.isort]