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"}),