From 99b83b5cefad6ebd1fcc62e24a1791d0844d7a1d Mon Sep 17 00:00:00 2001 From: Andreas Eknes Lie <114403625+andreas-el@users.noreply.github.com> Date: Thu, 2 May 2024 13:58:33 +0200 Subject: [PATCH] Unpin pylint (#1277) * Unpin pylint * Avoid using non-existant variable * Remove useless pylint control * Fix warning string * Replace range calculation with quadratic calculation * Actually raise exceptions if failures occur * Replace badly chained expression * Formatting with black * Lower pylint standards slightly * Use bash exit codes to determine pylint status --- .github/workflows/subsurface.yml | 13 +++- .pylintrc | 3 +- setup.py | 2 +- webviz_subsurface/_components/color_picker.py | 4 +- webviz_subsurface/_figures/px_figure.py | 66 +++++++++++-------- .../plugins/_prod_misfit/_plugin.py | 4 +- .../controllers/tornado_controllers.py | 63 ++++++++++-------- .../utils/table_and_figure_utils.py | 8 +-- .../plugins/_well_cross_section_fmu.py | 16 +++-- 9 files changed, 108 insertions(+), 71 deletions(-) diff --git a/.github/workflows/subsurface.yml b/.github/workflows/subsurface.yml index 711da80e3..2c4af67d4 100644 --- a/.github/workflows/subsurface.yml +++ b/.github/workflows/subsurface.yml @@ -72,7 +72,18 @@ jobs: if: github.event_name != 'release' run: | black --check webviz_subsurface tests setup.py - pylint webviz_subsurface tests setup.py + + pylint_exit_code=0 + pylint webviz_subsurface tests setup.py || pylint_exit_code=$? + + error_bitmask=$((1 | 2 | 4)) # Fatal, Error, Warning + result=$(($pylint_exit_code & $error_bitmask)) + + if [[ $result != 0 ]]; then + echo "Error: Pylint returned exit code $pylint_exit_code" + exit $pylint_exit_code + fi + bandit -r -c ./bandit.yml webviz_subsurface tests setup.py isort --check-only webviz_subsurface tests setup.py mypy --package webviz_subsurface diff --git a/.pylintrc b/.pylintrc index 29b802eb4..33e394749 100644 --- a/.pylintrc +++ b/.pylintrc @@ -6,7 +6,7 @@ init-hook = "import astroid; astroid.context.InferenceContext.max_inferred = 500 [MESSAGES CONTROL] disable = bad-continuation, missing-docstring, duplicate-code, logging-fstring-interpolation, unspecified-encoding -enable = useless-suppression, no-print +enable = useless-suppression [DESIGN] @@ -27,6 +27,7 @@ good-names = i, df, _ + [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. diff --git a/setup.py b/setup.py index ffdd0f270..d0c92ae83 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ "dash[testing]", "isort", "mypy", - "pylint<=2.13.9", # Locked due to https://github.com/equinor/webviz-subsurface/issues/1052 + "pylint", "pytest-mock", "pytest-xdist", "pytest-forked", diff --git a/webviz_subsurface/_components/color_picker.py b/webviz_subsurface/_components/color_picker.py index 2769d5db4..acd632898 100644 --- a/webviz_subsurface/_components/color_picker.py +++ b/webviz_subsurface/_components/color_picker.py @@ -73,7 +73,9 @@ def get_color(self, color_list: List, filter_query: Dict[str, str]) -> str: warnings.warn("No color found for filter!") return "#ffffff" if len(df["COLOR"].unique()) > 1: - warnings.warn("Multiple colors found for filter. " "Return first color.") + warnings.warn( + f"Multiple colors found for filter, using first color: {color_list[df.index[0]]}" + ) return color_list[df.index[0]] @property diff --git a/webviz_subsurface/_figures/px_figure.py b/webviz_subsurface/_figures/px_figure.py index 29b5f4cfd..80fe4fa89 100644 --- a/webviz_subsurface/_figures/px_figure.py +++ b/webviz_subsurface/_figures/px_figure.py @@ -1,4 +1,5 @@ import inspect +import math from typing import Any, Callable, List, Optional, Union import pandas as pd @@ -40,11 +41,12 @@ def set_default_args(**plotargs: Any) -> dict: if plotargs.get("facet_col") is not None: facet_cols = plotargs["data_frame"][plotargs["facet_col"]].nunique() + + x = math.ceil((math.sqrt(1 + 4 * facet_cols) - 1) / 2) + facet_col_wrap = min(x, 20) + plotargs.update( - facet_col_wrap=min( - min([x for x in range(100) if (x * (x + 1)) >= facet_cols]), - 20, - ), + facet_col_wrap=facet_col_wrap, facet_row_spacing=max((0.08 - (0.00071 * facet_cols)), 0.03), facet_col_spacing=max((0.06 - (0.00071 * facet_cols)), 0.03), ) @@ -77,16 +79,20 @@ def update_xaxes(figure: go.Figure, plot_type: str, **kwargs: Any) -> go.Figure: linewidth=2, linecolor="black", mirror=True, - title=None - if facet_col is not None or not isinstance(kwargs.get("x"), str) - else kwargs.get("x"), - showticklabels=(data_frame[facet_col].nunique() <= 100) - if facet_col is not None - else None, + title=( + None + if facet_col is not None or not isinstance(kwargs.get("x"), str) + else kwargs.get("x") + ), + showticklabels=( + (data_frame[facet_col].nunique() <= 100) if facet_col is not None else None + ), tickangle=0, - tickfont_size=max((20 - (0.4 * data_frame[facet_col].nunique())), 10) - if facet_col is not None - else None, + tickfont_size=( + max((20 - (0.4 * data_frame[facet_col].nunique())), 10) + if facet_col is not None + else None + ), fixedrange=plot_type == "distribution", ).update_xaxes(**kwargs.get("xaxis", {})) @@ -116,19 +122,23 @@ def update_traces(figure: go.Figure, **kwargs: Any) -> go.Figure: facet_col = kwargs.get("facet_col") return ( figure.update_traces( - marker_size=max((20 - (1.5 * data_frame[facet_col].nunique())), 5) - if facet_col is not None - else 20, + marker_size=( + max((20 - (1.5 * data_frame[facet_col].nunique())), 5) + if facet_col is not None + else 20 + ), selector=lambda t: t["type"] in ["scatter", "scattergl"], ) .update_traces(textposition="inside", selector=dict(type="pie")) .for_each_trace(lambda t: set_marker_color(t)) .for_each_trace( - lambda t: t.update( - xbins_size=(t["x"].max() - t["x"].min()) / kwargs.get("nbins", 15) - ) - if is_numeric_dtype(t["x"]) - else None, + lambda t: ( + t.update( + xbins_size=(t["x"].max() - t["x"].min()) / kwargs.get("nbins", 15) + ) + if is_numeric_dtype(t["x"]) + else None + ), selector=dict(type="histogram"), ) ) @@ -156,12 +166,14 @@ def for_each_annotation(figure: go.Figure, **kwargs: Any) -> go.Figure: return figure.for_each_annotation( lambda a: a.update( text=(a.text.split("=")[-1]), - visible=data_frame[facet_col].nunique() <= 42 - if facet_col is not None - else None, - font_size=max((18 - (0.4 * data_frame[facet_col].nunique())), 10) - if facet_col is not None - else None, + visible=( + data_frame[facet_col].nunique() <= 42 if facet_col is not None else None + ), + font_size=( + max((18 - (0.4 * data_frame[facet_col].nunique())), 10) + if facet_col is not None + else None + ), ) ) diff --git a/webviz_subsurface/plugins/_prod_misfit/_plugin.py b/webviz_subsurface/plugins/_prod_misfit/_plugin.py index b9afa8e57..c71fbf8e3 100644 --- a/webviz_subsurface/plugins/_prod_misfit/_plugin.py +++ b/webviz_subsurface/plugins/_prod_misfit/_plugin.py @@ -439,7 +439,7 @@ def _get_wells_vectors_phases( wells, vectors = sorted(wells), sorted(vectors) if not vectors: - RuntimeError("No WOPT, WWPT or WGPT vectors found.") + raise RuntimeError("No WOPT, WWPT or WGPT vectors found.") if drop_list: logging.debug( @@ -496,7 +496,7 @@ def _get_well_collections_from_attr( df_well_groups = well_attributes.dataframe_melted.dropna() df_cols = df_well_groups.columns if "WELL" not in df_cols or "VALUE" not in df_cols: - RuntimeError( + raise RuntimeError( f"The {well_attributes.file_name} file must contain the columns" " 'WELL' and 'VALUE'" ) diff --git a/webviz_subsurface/plugins/_volumetric_analysis/controllers/tornado_controllers.py b/webviz_subsurface/plugins/_volumetric_analysis/controllers/tornado_controllers.py index 5263616d8..3fa1660cf 100644 --- a/webviz_subsurface/plugins/_volumetric_analysis/controllers/tornado_controllers.py +++ b/webviz_subsurface/plugins/_volumetric_analysis/controllers/tornado_controllers.py @@ -73,6 +73,7 @@ def _update_tornado_pages( if page_selected == "torn_bulk_inplace" else [selections["Response"]] ) + realplot = None for response in responses: if not (response == "BULK" and page_selected == "torn_bulk_inplace"): if selections["Reference"] not in selections["Sensitivities"]: @@ -132,29 +133,33 @@ def _update_tornado_pages( height="39vh", table_id={"table_id": f"{page_selected}-torntable"}, ) - elif selections["bottom_viz"] == "realplot" and figures: + elif realplot and selections["bottom_viz"] == "realplot" and figures: bottom_display = [ - wcc.Graph( - config={"displayModeBar": False}, - style={"height": "40vh"}, - figure=realplot, + ( + wcc.Graph( + config={"displayModeBar": False}, + style={"height": "40vh"}, + figure=realplot, + ) + if not subplots + else "Realization plot not available when `Subplots` is active" ) - if not subplots - else "Realization plot not available when `Subplots` is active" ] return update_relevant_components( id_list=id_list, update_info=[ { - "new_value": tornado_plots_layout( - figures=figures, bottom_display=bottom_display - ) - if figures - else tornado_error_layout( - "No data left after filtering" - if dframe.empty - else f"Reference sensitivity '{selections['Reference']}' not in input data" + "new_value": ( + tornado_plots_layout( + figures=figures, bottom_display=bottom_display + ) + if figures + else tornado_error_layout( + "No data left after filtering" + if dframe.empty + else f"Reference sensitivity '{selections['Reference']}' not in input data" + ) ), "conditions": {"page": page_selected}, } @@ -209,9 +214,9 @@ def _update_tornado_selections( ] settings["Response"] = { "options": [{"label": i, "value": i} for i in volume_options], - "value": volume_options[0] - if initial_page_load - else selections["Response"], + "value": ( + volume_options[0] if initial_page_load else selections["Response"] + ), "disabled": len(volume_options) == 1, } else: @@ -288,10 +293,12 @@ def tornado_figure_and_table( ).figure figure.update_xaxes(side="bottom", title=None).update_layout( - title_text=f"Tornadoplot for {response}
" - + f"Fluid zone: {(' + ').join(selections['filters']['FLUID_ZONE'])}" - if group is None - else f"{response} {group}", + title_text=( + f"Tornadoplot for {response}
" + + f"Fluid zone: {(' + ').join(selections['filters']['FLUID_ZONE'])}" + if group is None + else f"{response} {group}" + ), title_font_size=font_size, margin={"t": 70}, ) @@ -328,12 +335,14 @@ def create_realplot(df: pd.DataFrame, sensitivity_colors: dict) -> go.Figure: .update_layout(legend_title_text="") .for_each_trace( lambda t: ( - t.update(marker_line_color="black") - if t["customdata"][0][0] == "high" - else t.update(marker_line_color="white", marker_line_width=2) + ( + t.update(marker_line_color="black") + if t["customdata"][0][0] == "high" + else t.update(marker_line_color="white", marker_line_width=2) + ) + if t["customdata"][0][0] != "mc" + else None ) - if t["customdata"][0][0] != "mc" - else None ) ) diff --git a/webviz_subsurface/plugins/_volumetric_analysis/utils/table_and_figure_utils.py b/webviz_subsurface/plugins/_volumetric_analysis/utils/table_and_figure_utils.py index e1ebb4aba..0d6555bd8 100644 --- a/webviz_subsurface/plugins/_volumetric_analysis/utils/table_and_figure_utils.py +++ b/webviz_subsurface/plugins/_volumetric_analysis/utils/table_and_figure_utils.py @@ -141,10 +141,10 @@ def add_correlation_line(figure: go.Figure, xy_min: float, xy_max: float) -> go. def create_figure_matrix(figures: List[go.Figure]) -> List[List[go.Figure]]: """Convert a list of figures into a matrix for display""" - figs_in_row = min( - min([x for x in range(100) if (x * (x + 1)) > len(figures)]), - 20, - ) + + x = math.ceil((math.sqrt(1 + 4 * len(figures)) - 1) / 2) + figs_in_row = min(x, 20) + len_of_matrix = figs_in_row * math.ceil(len(figures) / figs_in_row) # extend figure list with None to fit size of matrix figures.extend([None] * (len_of_matrix - len(figures))) diff --git a/webviz_subsurface/plugins/_well_cross_section_fmu.py b/webviz_subsurface/plugins/_well_cross_section_fmu.py index 99b5817e4..040b6da88 100644 --- a/webviz_subsurface/plugins/_well_cross_section_fmu.py +++ b/webviz_subsurface/plugins/_well_cross_section_fmu.py @@ -104,10 +104,6 @@ def __init__( ): super().__init__() - if wellfiles is not None == wellfolder is not None: - raise ValueError( - 'Incorrent arguments. Either provide "wellfiles" or "wellfolder"' - ) self.wellfolder = wellfolder self.wellsuffix = wellsuffix self.wellfiles: List[str] @@ -115,6 +111,10 @@ def __init__( self.wellfiles = json.load(find_files(wellfolder, wellsuffix)) elif wellfiles is not None: self.wellfiles = [str(well) for well in wellfiles] + else: + raise ValueError( + "Incorrect arguments, either provide wellfiles or wellfolder" + ) self.surfacefolder = surfacefolder self.surfacefiles = surfacefiles @@ -243,9 +243,11 @@ def ensemble_layout(self) -> html.Div: def seismic_layout(self) -> html.Div: if self.segyfiles: return html.Div( - style=self.set_style(marginTop="20px") - if self.segyfiles - else {"display": "none"}, + style=( + self.set_style(marginTop="20px") + if self.segyfiles + else {"display": "none"} + ), children=html.Label( children=[ html.Span("Seismic:", style={"font-weight": "bold"}),