From c2e5e950e560ea6698c70dedee02d155da3accad Mon Sep 17 00:00:00 2001 From: Xylar Asay-Davis Date: Wed, 11 Jun 2025 14:59:02 -0500 Subject: [PATCH 1/3] Switch `download_data.py` to use `importlib.resources` --- mpas_analysis/download_data.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mpas_analysis/download_data.py b/mpas_analysis/download_data.py index 3c70aeea6..ce850ef9d 100755 --- a/mpas_analysis/download_data.py +++ b/mpas_analysis/download_data.py @@ -19,7 +19,7 @@ import argparse -import pkg_resources +import importlib.resources as resources import os from mpas_analysis.shared.io.download import download_files @@ -49,9 +49,10 @@ def download_analysis_data(): pass urlBase = 'https://web.lcrc.anl.gov/public/e3sm/diagnostics' - analysisFileList = pkg_resources.resource_string( - 'mpas_analysis', - 'obs/{}_input_files'.format(args.dataset)).decode('utf-8') + resource = resources.files('mpas_analysis.obs').joinpath( + f'{args.dataset}_input_files') + with resource.open('r') as f: + analysisFileList = f.read() # remove any empty strings from the list analysisFileList = list(filter(None, analysisFileList.split('\n'))) From 9c2e7b1148c63dbbcfb1773f3efd8203301f8325 Mon Sep 17 00:00:00 2001 From: Xylar Asay-Davis Date: Wed, 11 Jun 2025 14:59:36 -0500 Subject: [PATCH 2/3] Switch html generation to use `importlib.resources` --- mpas_analysis/shared/html/pages.py | 97 ++++++++----------- .../shared/html/templates/__init__.py | 0 2 files changed, 38 insertions(+), 59 deletions(-) create mode 100644 mpas_analysis/shared/html/templates/__init__.py diff --git a/mpas_analysis/shared/html/pages.py b/mpas_analysis/shared/html/pages.py index 883940dec..302ce7dd0 100644 --- a/mpas_analysis/shared/html/pages.py +++ b/mpas_analysis/shared/html/pages.py @@ -16,7 +16,7 @@ from os import makedirs from pathlib import Path -import pkg_resources +import importlib.resources as resources from lxml import etree import mpas_analysis.version @@ -66,13 +66,13 @@ def generate_html(config, analyses, controlConfig, customConfigFiles): try: ComponentPage.add_image(fileName, config, components, controlConfig) - except IOError: - print(' missing file {}'.format(fileName)) + except IOError as e: + print(f'Error reading {fileName}: {e}') missingCount += 1 if missingCount > 0: - print('Warning: {} XML files were missing and the analysis website' - ' will be incomplete.'.format(missingCount)) + print(f'Warning: {missingCount} XML files could not be read and the ' + f'analysis website will be incomplete.') # generate the page for each component and add the component to the main # page for componentName, component in components.items(): @@ -155,24 +155,15 @@ def __init__(self, config, controlConfig, customConfigFiles): self.customConfigFiles = customConfigFiles # get template text - fileName = \ - pkg_resources.resource_filename(__name__, - "templates/main_page.html") - - with open(fileName, 'r') as templateFile: - self.pageTemplate = templateFile.read() - - fileName = \ - pkg_resources.resource_filename(__name__, - "templates/main_component.html") - with open(fileName, 'r') as templateFile: - self.componentTemplate = templateFile.read() - - fileName = \ - pkg_resources.resource_filename(__name__, - "templates/config.html") - with open(fileName, 'r') as templateFile: - self.configTemplate = templateFile.read() + package = 'mpas_analysis.shared.html.templates' + templates = {} + for name in ['main_page', 'main_component', 'config']: + resource = resources.files(package).joinpath(f'{name}.html') + with resource.open('r') as templateFile: + templates[name] = templateFile.read() + self.pageTemplate = templates['main_page'] + self.componentTemplate = templates['main_component'] + self.configTemplate = templates['config'] # start with no components self.components = OrderedDict() @@ -273,45 +264,34 @@ def generate(self): pageText = _replace_tempate_text(self.pageTemplate, replacements) - htmlBaseDirectory = build_config_full_path(self.config, 'output', - 'htmlSubdirectory') + htmlBaseDirectory = build_config_full_path( + self.config, 'output', 'htmlSubdirectory' + ) for subdir in ['css', 'js']: - try: - makedirs('{}/{}'.format(htmlBaseDirectory, subdir)) - except OSError: - pass + makedirs(f'{htmlBaseDirectory}/{subdir}', exist_ok=True) - outFileName = '{}/index.html'.format(htmlBaseDirectory) + outFileName = f'{htmlBaseDirectory}/index.html' with open(outFileName, mode='w') as mainFile: mainFile.write( - pageText.encode('ascii', - 'xmlcharrefreplace').decode('ascii')) + pageText.encode('ascii', 'xmlcharrefreplace').decode('ascii') + ) # copy the css and js files as well as general images - fileName = \ - pkg_resources.resource_filename(__name__, - "templates/style.css") - copyfile(fileName, '{}/css/style.css'.format(htmlBaseDirectory)) - - fileName = \ - pkg_resources.resource_filename(__name__, - "templates/index.js") - copyfile(fileName, '{}/js/index.js'.format(htmlBaseDirectory)) - - fileName = \ - pkg_resources.resource_filename(__name__, - "templates/mpas_logo.png") - copyfile(fileName, '{}/mpas_logo.png'.format(htmlBaseDirectory)) - - fileName = \ - pkg_resources.resource_filename(__name__, - "templates/config.png") - copyfile(fileName, '{}/config.png'.format(htmlBaseDirectory)) - - with open('{}/complete.{}.cfg'.format(htmlBaseDirectory, - runName), 'w') as configFile: + resource_targets = [ + ("style.css", "css/style.css"), + ("index.js", "js/index.js"), + ("mpas_logo.png", "mpas_logo.png"), + ("config.png", "config.png"), + ] + for resource_name, target_path in resource_targets: + package = 'mpas_analysis.shared.html.templates' + fileName = resources.files(package).joinpath(resource_name) + copyfile(str(fileName), f'{htmlBaseDirectory}/{target_path}') + + outFileName = f'{htmlBaseDirectory}/complete.{runName}.cfg' + with open(outFileName, 'w') as configFile: self.config.write(configFile) @@ -386,11 +366,10 @@ def __init__(self, config, name, subdirectory, controlConfig=None): for templateName in ['page', 'quicklink', 'group', 'gallery', 'image', 'subtitle']: # get template text - fileName = pkg_resources.resource_filename( - __name__, - "templates/component_{}.html".format(templateName)) - - with open(fileName, 'r') as templateFile: + package = 'mpas_analysis.shared.html.templates' + resource = resources.files(package).joinpath( + f'component_{templateName}.html') + with resource.open('r') as templateFile: self.templates[templateName] = templateFile.read() # start with no groups diff --git a/mpas_analysis/shared/html/templates/__init__.py b/mpas_analysis/shared/html/templates/__init__.py new file mode 100644 index 000000000..e69de29bb From 49635b47f7f712c9ef09ca282d0c982cf64ca206 Mon Sep 17 00:00:00 2001 From: Xylar Asay-Davis Date: Thu, 12 Jun 2025 04:06:15 -0500 Subject: [PATCH 3/3] Fix docstring for ClimatologyMapCustom --- mpas_analysis/ocean/climatology_map_custom.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mpas_analysis/ocean/climatology_map_custom.py b/mpas_analysis/ocean/climatology_map_custom.py index 7b6aa248c..f2c705268 100644 --- a/mpas_analysis/ocean/climatology_map_custom.py +++ b/mpas_analysis/ocean/climatology_map_custom.py @@ -23,12 +23,14 @@ class ClimatologyMapCustom(AnalysisTask): """ - A felxible analysis task for plotting climatologies of any MPAS-Ocean field + A flexible analysis task for plotting climatologies of any MPAS-Ocean field on cells from timeSeriesStatsMonthly at various depths (if the field has - vertical levels) and for various seasons. Various derived fields are also - supported: - * velocity magnitude - * thermal forcing (temperature - freezing temperature) + vertical levels) and for various seasons. + + Various derived fields are also supported: + + * velocity magnitude + * thermal forcing (temperature - freezing temperature) """ def __init__(self, config, mpasClimatologyTask, controlConfig=None):