Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
ed7d180
Feature METplus2857 main v3.1 support (#512)
jprestop Jun 9, 2025
69c28d1
Per #515, remove deprecated import (#516)
georgemccabe Jun 9, 2025
6239d5a
Update base_plot.py
bikegeek Jun 27, 2025
4dedb9f
Update base_plot.py
bikegeek Jun 27, 2025
0b165a2
Update test_scatter.py
bikegeek Jun 28, 2025
e272a1e
Remove soon to be deprecated call to plotly.io.kaleido.scope
bikegeek Jun 30, 2025
6a0e262
Comment out soon-to-be deprecated call to plolty.io.kaleido.scope
bikegeek Jun 30, 2025
ea2f281
add call to install chrome
bikegeek Jul 1, 2025
9cbe7cb
Add test_* to trigger and setup test to use test_requirements.txt
bikegeek Jul 1, 2025
d623116
To test plotly/kaleido updates
bikegeek Jul 1, 2025
0b6f185
Try using kaleido_get_chrome_sync
bikegeek Jul 1, 2025
dd03b3f
Update import to use ploty.io._kaleido
bikegeek Jul 1, 2025
2422aa8
Using latest import, invoke the get_chrome() method instead of get_ch…
bikegeek Jul 1, 2025
1833159
explicitly pip install kaleido 1.0
bikegeek Jul 1, 2025
96d33fa
Add Google Chrome action
bikegeek Jul 1, 2025
0a37fce
fix syntax error
bikegeek Jul 1, 2025
fec2961
remove - from the run command
bikegeek Jul 1, 2025
e00bfff
Explicitly get the Chrome environment, bypass the calls to plotly.io.…
bikegeek Jul 1, 2025
141ec5e
Set up a CHROME_INSTALL path and save it as an environment
bikegeek Jul 1, 2025
077517d
remove no-sudo
bikegeek Jul 1, 2025
6119238
removed extra line at top of file
bikegeek Jul 1, 2025
c49572b
move env definition by indenting
bikegeek Jul 1, 2025
ceab0dc
Removed all imports that didn't work and the soon to be deprecated re…
bikegeek Jul 1, 2025
659ffae
Remove any attempt to set an env
bikegeek Jul 1, 2025
71c7df8
Remove the --version to the path under the chrome installation
bikegeek Jul 1, 2025
89f1ff6
Remove skip
bikegeek Jul 1, 2025
fb2ec73
Update test_requirements.txt
bikegeek Jul 1, 2025
41791d4
Attempt to get chrome via kaleido.get_chrome_sync()
bikegeek Jul 3, 2025
01032a5
Merge branch 'test_plotly' of https://github.com/dtcenter/METplotpy i…
bikegeek Jul 3, 2025
afb2d38
Update unit_tests.yaml
bikegeek Jul 3, 2025
9a32b75
Update base_plot.py
bikegeek Jul 3, 2025
e080cc2
Replace test_requirements.txt with requirements.txt
bikegeek Jul 3, 2025
1869154
Update Plotly from 6.0.0 to 6.1.1 and kaleido from 0.2.1 to 1.0.0
bikegeek Jul 3, 2025
3998c64
Adding modulefile for new machine ursa
jprestop Jul 7, 2025
eac0c30
Merge branch 'develop' into feature_525_update_plotly_kaleido
bikegeek Jul 8, 2025
e2e52b7
Feature 525 update plotly kaleido (#526)
bikegeek Jul 8, 2025
42fb2a9
Merge branch 'main_v3.1' of https://github.com/dtcenter/METplotpy int…
bikegeek Jul 8, 2025
45e3e3f
Added support for env variable to turn on/off invoking the get_chrome…
bikegeek Jul 8, 2025
83fe527
test truthiness for booleans rather than string
bikegeek Jul 8, 2025
cf93967
Update logic for checking PRE_LOAD_CHROME environment variable that i…
bikegeek Jul 8, 2025
127b488
Remove print statement
bikegeek Jul 8, 2025
3333c1a
Merge branch 'develop' of https://github.com/dtcenter/METplotpy into …
bikegeek Jul 9, 2025
32eeb0f
Add check for PRE_LOAD_CHROME env variable set in Dockerfile to turn …
bikegeek Jul 9, 2025
379a2de
Updated versions for Plotly and kaleido
bikegeek Jul 16, 2025
dd6d057
Allow logging to stdout and/or file to indicate whether Chrome is get…
bikegeek Jul 16, 2025
212789f
Update the runtime loading of Chrome by allowing logging to monitor i…
bikegeek Jul 16, 2025
917fde7
Implement our own version of the deprecated (in Python 3.12+) of dist…
bikegeek Jul 16, 2025
61d8e6c
Add testing for the strtobool implementation in util.py
bikegeek Jul 16, 2025
61c72e3
Fix import for util strtobool
bikegeek Jul 16, 2025
ed47597
Forgot to change the args to strtobool to match our implementation
bikegeek Jul 16, 2025
ca8c3e1
Include the information about impacts of kaleido 1.0.0 changes under …
bikegeek Jul 16, 2025
354ae4a
Still one more place to fix the input to strtobool to match our imple…
bikegeek Jul 16, 2025
292cf38
Refactor to eliminate sonarqube code smell
bikegeek Jul 17, 2025
9dd8c49
Remove test code
bikegeek Jul 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/Users_Guide/release-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,20 @@ METplotpy Upgrade Instructions
development with Python 3.12. View the requirements.txt/nco_requirements.txt file at the top
level of the repository for version numbers for the corresponding third-party packages.


In June 2025, Plotly made significant updates to the kaleido package with the 1.0.0
release by removing Google Chrome code. Now, users will need to have Google Chrome
installed in directories specified in this Plotly documentation (based on operating
system):

https://plotly.com/python/static-image-export/

The METplotpy code downloads Chrome at runtime by invoking the kaleido.get_chrome_sync()
method call.

If users do not wish to have Chrome downloaded at run time and already have Chrome installed
in one of the expected locations (specified in the Plotly link above), then the PRE_LOAD_CHROME environment variable
will need to be set to 'True' (case insensitive string).

Refer to the Kaleido README for more information on the changes:
https://github.com/plotly/Kaleido
25 changes: 25 additions & 0 deletions internal/scripts/installation/modulefiles/3.1.0_ursa
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#%Module######################################################################
##
## METplotpy
##
proc ModulesHelp { } {
puts stderr "Sets up the paths and environment variables to use the METplotpy-3.1.0.
*** For help see the official MET webpage at http://www.dtcenter.org/met/users ***"
}

prereq intel-oneapi-compilers/2025.1.1

setenv METPLOTPY_SOURCE /contrib/METplotpy/METplotpy-3.1.0
setenv METPLOTPY_BASE /contrib/METplotpy/METplotpy-3.1.0

prepend-path PATH /scratch3/BMC/dtc/METplus/miniconda/miniconda3/envs/metplus_v6.1_py3.12/bin
prepend-path PATH /contrib/METplotpy/METplotpy-3.1.0/metplotpy/contributed
prepend-path PATH /contrib/METplotpy/METplotpy-3.1.0/metplotpy/plots/performance_diagram
prepend-path PATH /contrib/METplotpy/METplotpy-3.1.0/metplotpy/plots
prepend-path PATH /contrib/METplotpy/METplotpy-3.1.0/metplotpy
prepend-path PATH /contrib/METplotpy/METplotpy-3.1.0
prepend-path PYTHONPATH /contrib/METplotpy/METplotpy-3.1.0/metplotpy/contributed
prepend-path PYTHONPATH /contrib/METplotpy/METplotpy-3.1.0/metplotpy/plots/performance_diagram
prepend-path PYTHONPATH /contrib/METplotpy/METplotpy-3.1.0/metplotpy/plots
prepend-path PYTHONPATH /contrib/METplotpy/METplotpy-3.1.0/metplotpy
prepend-path PYTHONPATH /contrib/METplotpy/METplotpy-3.1.0
45 changes: 40 additions & 5 deletions metplotpy/plots/base_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,53 @@
import numpy as np
import yaml
from typing import Union

import plotly.io as pio

import kaleido
import metplotpy.plots.util
from metplotpy.plots.util import strtobool
from .config import Config
from metplotpy.plots.context_filter import ContextFilter

# kaleido 0.x will be deprecated after September 2025 and Chrome will no longer
# be included with kaleido from version 1.0.0. Explicitly get Chrome via call to kaleido.
import kaleido
kaleido.get_chrome_sync()

# In some instances, we do NOT want Chrome to be installed at run-time. If the
# PRE_LOAD_CHROME environment variable exists, or set to TRUE,
# then Chrome will be assumed to have been pre-loaded. Otherwise,
# invoke get_chrome_sync() to install Chrome in the
# /path-to-python-libs/pythonx.yz/site-packages/... directory

# Check if the PRE_LOAD_CHROME env variable exists
aquire_chrome = False

turn_on_logging = strtobool('LOG_BASE_PLOT')
# Log when Chrome is downloaded at runtime
if turn_on_logging is True:
log = logging.getLogger("base_plot")
log.setLevel(logging.INFO)

formatter = logging.Formatter("%(asctime)s [%(levelname)s] | %(name)s | %(message)s")

# set the WRITE_LOG env var to True to save the log message to a
# separate log file
write_log = strtobool('WRITE_LOG')
if write_log is True:
file_handler = logging.FileHandler("./base_plot.log")
file_handler.setFormatter(formatter)
log.addHandler(file_handler)

# Only load Chrome at run-time if PRE_LOAD_CHROME is False or not defined.
# Some applications may not want to load Chrome at runtime and
# will set the PRE_LOAD_CHROME to True to indicate that it is already
# loaded/downloaded prior to runtime.
chrome_env =strtobool ('PRE_LOAD_CHROME')
if ('PRE_LOAD_CHROME' not in os.environ)or (chrome_env is False):
aquire_chrome=True
kaleido.get_chrome_sync()


# Log when kaleido is downloading Chrome
if aquire_chrome is True and turn_on_logging is True:
log.info("Plotly kaleido is loading Chrome at run time")

class BasePlot:
"""A class that provides methods for building Plotly plot's common features
Expand Down
47 changes: 47 additions & 0 deletions metplotpy/plots/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,3 +623,50 @@ def prepare_ctc_roc(subset_df, is_ascending):
thresh = pd.concat([thresh, pd.Series([''])], ignore_index=True)

return pody, pofd, thresh


def strtobool(env_var:str)->bool:
"""
Since distutils.util.strtobool was deprecated in Python 3.12, implement
our own version.

In the distutils.util.strtobool, a simple one line command was used to determine
whether an environment variable was set to True or False. In this
example, the default value is set to False in the event that the environment
variable is not defined:

turn_on_logging = strtobool(os.getenv('LOG_BASE_PLOT', 'False') )

Environment variables can be set as string or bool. Evaluate whether a string
value for true or false (support case-insensitive text) is True/False and
set the default value.

Args:
@parm env_vars: string name of the environment variable to evaluate

turn_on_logging = strtobool(os.getenv('LOG_BASE_PLOT') )
"""

true_list = ['true', 't', '1',]
false_list = ['false', 'f', '0' ]
# if the environment variable does not exist, then return False
try:
val = os.environ[env_var]
except KeyError:
return False

# If the environment variable is None, return false
if val is None:
return False
else:
# Check for variations of truth values
lower = val.lower()
if lower in true_list:
return True
elif lower in false_list:
return False
else:
msg = "Value does not represent a truth value (i.e. true or false)"
raise ValueError(msg)


4 changes: 2 additions & 2 deletions nco_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
kaleido>=0.2.1
kaleido>=1.0.0
matplotlib>=3.10.0
metpy>=1.6.3
netcdf4>=1.7.2
numpy==2.2.2
scipy>=1.15.1
pandas>=2.2.3
pint>=0.24.4
plotly>=6.0.0
plotly>=6.1.1
pytest>=8.3.4
pyyaml>=6.0.2
xarray>=2025.1.2
47 changes: 46 additions & 1 deletion test/util/test_util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import pandas as pd

import pytest
import metplotpy.plots.util as util
import gc

Expand Down Expand Up @@ -170,3 +170,48 @@ def test_filter_by_fixed_vars():

for filtered in filtered_list:
assert filtered in expected_list


def test_strtobool():
"""
Test that strtobool is returning the corresponding bool value to an environment

"""

# Non-existent env var should return boolean False
assert (util.strtobool('NON_EXISTENT') is False)

# Variations of false should return boolean False
os.environ['FALSE_1'] = 'f'
os.environ['FALSE_2'] = 'FaLSE'
os.environ['FALSE_3'] = '0'
os.environ['FALSE_4'] = 'F'
false_1 = 'FALSE_1'
false_2 = 'FALSE_2'
false_3 = 'FALSE_3'
false_4 = 'FALSE_4'
assert(util.strtobool(false_1) is False)
assert(util.strtobool(false_2) is False)
assert(util.strtobool(false_3) is False)
assert(util.strtobool(false_4) is False)


# Variations of true should return boolean True
os.environ['TRUE_1'] = 't'
os.environ['TRUE_2'] = 'T'
os.environ['TRUE_3'] = 'tRuE'
os.environ['TRUE_4'] = '1'
true_1 = 'TRUE_1'
true_2 = 'TRUE_2'
true_3 = 'TRUE_3'
true_4 = 'TRUE_4'
assert (util.strtobool(true_1) is True)
assert (util.strtobool(true_2) is True)
assert (util.strtobool(true_3) is True)
assert (util.strtobool(true_4) is True)

# non-truth values should raise a ValueError
os.environ['BOGUS'] = 'Whatever'
var = 'BOGUS'
with pytest.raises(ValueError):
util.strtobool(var)
Loading