From 5828abb7cf28fad363cc1cd2905e84eb2c864ce9 Mon Sep 17 00:00:00 2001 From: James Bruten Date: Thu, 18 Sep 2025 09:34:19 +0100 Subject: [PATCH 01/15] update install script for git --- admin/meto_install_mule.sh | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/admin/meto_install_mule.sh b/admin/meto_install_mule.sh index d4f57421..5ca24af2 100755 --- a/admin/meto_install_mule.sh +++ b/admin/meto_install_mule.sh @@ -51,26 +51,28 @@ elif [[ $hostname == uan01 ]] || [[ $hostname == login* ]] || [[ $hostname == ca wafc=$UMDIR/$um_ver/ex1a/wafccb_cce fi -# Get a copy of the mule trunk at the required version - if it has already been -# checked out, re-use the copy already present. Note that this leaves a working -# copy in the current directory, which you may want to clean up once finished -if [ ! -d mule_trunk_$mule_ver ] ; then - fcm co fcm:mule.xm_tr@$mule_ver mule_trunk_$mule_ver -fi -cd mule_trunk_$mule_ver +# Run from git clone containing this script +clone_location="$(dirname "$0")/.." +cd $clone_location # Find out the versions of Python and Numpy the environment has pythonver=$(python -c "from platform import python_version ; print(python_version())") numpyver=$(python -c "import numpy; print(numpy.__version__)") # Construct the name of the install directory for this Mule install -dest_dir=python-${pythonver}_numpy-${numpyver} +dest_dir=$(realpath ../python-${pythonver}_numpy-${numpyver}) # Don't overwrite/rebuild an existing install if [ -d $dest_dir ] ; then echo "$dest_dir already exists..." exit 1 fi +echo "Installing mule for Python ${pythonver} and Numpy ${numpyver}" +echo "Insalling from clone at $(realpath $clone_location)" +echo "Installing to ${dest_dir}" +echo "" +echo "" + # Run the build twice - once with and once without openmp for omp in openmp no-openmp ; do @@ -80,11 +82,11 @@ for omp in openmp no-openmp ; do --library_lock --ppibm_lib --spiral_lib --packing_lib\ --sstpert_lib $sst --wafccb_lib $wafc \ --shumlib_path $shum/$omp \ - ../$dest_dir/$omp/lib ../$dest_dir/$omp/bin + $dest_dir/$omp/lib $dest_dir/$omp/bin # Check the build works by running the unit-tests for mod in um_packing mule um_utils um_spiral_search ; do - PYTHONPATH=../$dest_dir/$omp/lib python -m unittest discover -v $mod.tests + PYTHONPATH=$dest_dir/$omp/lib python -m unittest discover -v $mod.tests done # By default the entry-point scripts created this way will be tied to @@ -94,6 +96,6 @@ for omp in openmp no-openmp ; do # to be called when a compatible environment is loaded; which the # modules we setup make sure of). So we'll replace the first line of # the entry point scripts with the generic environment python invocation - sed -i "s:^#!.*:#!/usr/bin/env python:g" ../$dest_dir/$omp/bin/mule-* + sed -i "s:^#!.*:#!/usr/bin/env python:g" $dest_dir/$omp/bin/mule-* done From 1204643060b6386121f4c100c140a72ac76b6f96 Mon Sep 17 00:00:00 2001 From: James Bruten Date: Tue, 30 Sep 2025 11:20:09 +0100 Subject: [PATCH 02/15] mule 2025.10.1 changes --- admin/meto_install_mule.sh | 6 +++--- mule/docs/source/conf.py | 4 ++-- mule/lib/mule/__init__.py | 2 +- mule/setup.py | 2 +- um_packing/lib/um_packing/__init__.py | 2 +- um_packing/setup.py | 2 +- um_ppibm/lib/um_ppibm/__init__.py | 2 +- um_ppibm/setup.py | 2 +- um_spiral_search/lib/um_spiral_search/__init__.py | 2 +- um_spiral_search/setup.py | 2 +- um_sstpert/lib/um_sstpert/__init__.py | 2 +- um_sstpert/setup.py | 2 +- um_utils/docs/source/conf.py | 4 ++-- um_utils/lib/um_utils/__init__.py | 2 +- um_utils/setup.py | 2 +- um_wafccb/lib/um_wafccb/__init__.py | 2 +- um_wafccb/setup.py | 2 +- 17 files changed, 21 insertions(+), 21 deletions(-) diff --git a/admin/meto_install_mule.sh b/admin/meto_install_mule.sh index 5ca24af2..dde3ce21 100755 --- a/admin/meto_install_mule.sh +++ b/admin/meto_install_mule.sh @@ -32,11 +32,11 @@ set -eu # Setup what version of things should be used # Mule version for build (will be checked out from SRS) -mule_ver=2024.11.1 +mule_ver=2025.10.1 # UM version for sstpert and wafccb libraries (will be looked up in $UMDIR) -um_ver=vn13.7 +um_ver=vn13.9 # Shumlib version (will be looked up in $UMDIR) -shum_ver=2024.11.1 +shum_ver=2025.10.1 # Set library locations and which specific builds to use on each platform hostname=$(hostname) diff --git a/mule/docs/source/conf.py b/mule/docs/source/conf.py index 6edf7cda..d3ba8bea 100644 --- a/mule/docs/source/conf.py +++ b/mule/docs/source/conf.py @@ -55,9 +55,9 @@ # built documents. # # The short X.Y version. -version = '2024.11.1' +version = '2025.10.1' # The full version, including alpha/beta/rc tags. -release = '2024.11.1' +release = '2025.10.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/mule/lib/mule/__init__.py b/mule/lib/mule/__init__.py index 1adb7a0f..ec414e2c 100644 --- a/mule/lib/mule/__init__.py +++ b/mule/lib/mule/__init__.py @@ -57,7 +57,7 @@ from contextlib import contextmanager from mule.stashmaster import STASHmaster -__version__ = "2024.11.1" +__version__ = "2025.10.1" # UM fixed length header names and positions _UM_FIXED_LENGTH_HEADER = [ diff --git a/mule/setup.py b/mule/setup.py index f80a4383..34709c12 100644 --- a/mule/setup.py +++ b/mule/setup.py @@ -47,7 +47,7 @@ def run(self): setuptools.setup( name='mule', - version='2024.11.1', + version='2025.10.1', description='Unified Model Fields File interface', author='UM Systems Team', url='https://code.metoffice.gov.uk/trac/um', diff --git a/um_packing/lib/um_packing/__init__.py b/um_packing/lib/um_packing/__init__.py index dac15eac..57bab6d3 100644 --- a/um_packing/lib/um_packing/__init__.py +++ b/um_packing/lib/um_packing/__init__.py @@ -20,4 +20,4 @@ from .um_packing import wgdos_pack, wgdos_unpack, get_shumlib_version -__version__ = "2024.11.1" +__version__ = "2025.10.1" diff --git a/um_packing/setup.py b/um_packing/setup.py index ae03cabf..b4ecdaf9 100644 --- a/um_packing/setup.py +++ b/um_packing/setup.py @@ -52,7 +52,7 @@ def run(self): setuptools.setup( name='um_packing', - version='2024.11.1', + version='2025.10.1', description='Unified Model packing library extension', author='UM Systems Team', url='https://code.metoffice.gov.uk/trac/um', diff --git a/um_ppibm/lib/um_ppibm/__init__.py b/um_ppibm/lib/um_ppibm/__init__.py index 156e4b2f..3bbbf934 100644 --- a/um_ppibm/lib/um_ppibm/__init__.py +++ b/um_ppibm/lib/um_ppibm/__init__.py @@ -40,7 +40,7 @@ msg = "Failed to import ieee2ibm32 extension" raise ImportError(err.args + (msg,)) -__version__ = "2024.11.1" +__version__ = "2025.10.1" # Custom write operator for "unpacked" fields which passes them through the diff --git a/um_ppibm/setup.py b/um_ppibm/setup.py index 58c06bf8..fc6e6055 100644 --- a/um_ppibm/setup.py +++ b/um_ppibm/setup.py @@ -52,7 +52,7 @@ def run(self): setuptools.setup( name='um_ppibm', - version='2024.11.1', + version='2025.10.1', description='Unified Model pp conversion utility with IBM number format', author='UM Systems Team', url='https://code.metoffice.gov.uk/trac/um', diff --git a/um_spiral_search/lib/um_spiral_search/__init__.py b/um_spiral_search/lib/um_spiral_search/__init__.py index 9f26eed7..4253a7f7 100644 --- a/um_spiral_search/lib/um_spiral_search/__init__.py +++ b/um_spiral_search/lib/um_spiral_search/__init__.py @@ -21,4 +21,4 @@ from .um_spiral_search import spiral_search -__version__ = "2024.11.1" +__version__ = "2025.10.1" diff --git a/um_spiral_search/setup.py b/um_spiral_search/setup.py index b9aefce9..b4c7b777 100644 --- a/um_spiral_search/setup.py +++ b/um_spiral_search/setup.py @@ -52,7 +52,7 @@ def run(self): setuptools.setup( name='um_spiral_search', - version='2024.11.1', + version='2025.10.1', description='Unified Model Spiral Search extension', author='UM Systems Team', url='https://code.metoffice.gov.uk/trac/um', diff --git a/um_sstpert/lib/um_sstpert/__init__.py b/um_sstpert/lib/um_sstpert/__init__.py index bd18a31d..5aba6eb6 100644 --- a/um_sstpert/lib/um_sstpert/__init__.py +++ b/um_sstpert/lib/um_sstpert/__init__.py @@ -48,7 +48,7 @@ from um_utils.version import report_modules from um_utils.pumf import _banner -__version__ = "2024.11.1" +__version__ = "2025.10.1" def gen_pert_field(clim_fields, alpha, ens_member, date): diff --git a/um_sstpert/setup.py b/um_sstpert/setup.py index e52d4b79..55a48837 100644 --- a/um_sstpert/setup.py +++ b/um_sstpert/setup.py @@ -52,7 +52,7 @@ def run(self): setuptools.setup( name='um_sstpert', - version='2024.11.1', + version='2025.10.1', description='Unified Model SST-perturbation extension and utility', author='UM Systems Team', url='https://code.metoffice.gov.uk/trac/um', diff --git a/um_utils/docs/source/conf.py b/um_utils/docs/source/conf.py index 2cacfca5..15f412a4 100644 --- a/um_utils/docs/source/conf.py +++ b/um_utils/docs/source/conf.py @@ -55,9 +55,9 @@ # built documents. # # The short X.Y version. -version = '2024.11.1' +version = '2025.10.1' # The full version, including alpha/beta/rc tags. -release = '2024.11.1' +release = '2025.10.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/um_utils/lib/um_utils/__init__.py b/um_utils/lib/um_utils/__init__.py index 28712948..165cd7eb 100644 --- a/um_utils/lib/um_utils/__init__.py +++ b/um_utils/lib/um_utils/__init__.py @@ -19,4 +19,4 @@ # along with these utilities. # If not, see . -__version__ = "2024.11.1" +__version__ = "2025.10.1" diff --git a/um_utils/setup.py b/um_utils/setup.py index 80ab856f..28fb09b4 100644 --- a/um_utils/setup.py +++ b/um_utils/setup.py @@ -47,7 +47,7 @@ def run(self): setuptools.setup( name='um_utils', - version='2024.11.1', + version='2025.10.1', description='Unified Model Fields File utilities', author='UM Systems Team', url='https://code.metoffice.gov.uk/trac/um', diff --git a/um_wafccb/lib/um_wafccb/__init__.py b/um_wafccb/lib/um_wafccb/__init__.py index f1efc7fa..79e34be7 100644 --- a/um_wafccb/lib/um_wafccb/__init__.py +++ b/um_wafccb/lib/um_wafccb/__init__.py @@ -20,4 +20,4 @@ from .um_wafccb import wafccb -__version__ = "2024.11.1" +__version__ = "2025.10.1" diff --git a/um_wafccb/setup.py b/um_wafccb/setup.py index 450d3421..01cf52bf 100644 --- a/um_wafccb/setup.py +++ b/um_wafccb/setup.py @@ -52,7 +52,7 @@ def run(self): setuptools.setup( name='um_wafccb', - version='2024.11.1', + version='2025.10.1', description='Unified Model WAFC CB extension', author='UM Systems Team', url='https://code.metoffice.gov.uk/trac/um', From d49934e720f3cb14ad4fdac04a440d450744f98d Mon Sep 17 00:00:00 2001 From: James Bruten <109733895+james-bruten-mo@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:20:10 +0100 Subject: [PATCH 03/15] update install script for git (#1) Co-authored-by: Jenny Hickson <61183013+jennyhickson@users.noreply.github.com> --- admin/meto_install_mule.sh | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/admin/meto_install_mule.sh b/admin/meto_install_mule.sh index d4f57421..0b0bd747 100755 --- a/admin/meto_install_mule.sh +++ b/admin/meto_install_mule.sh @@ -51,26 +51,28 @@ elif [[ $hostname == uan01 ]] || [[ $hostname == login* ]] || [[ $hostname == ca wafc=$UMDIR/$um_ver/ex1a/wafccb_cce fi -# Get a copy of the mule trunk at the required version - if it has already been -# checked out, re-use the copy already present. Note that this leaves a working -# copy in the current directory, which you may want to clean up once finished -if [ ! -d mule_trunk_$mule_ver ] ; then - fcm co fcm:mule.xm_tr@$mule_ver mule_trunk_$mule_ver -fi -cd mule_trunk_$mule_ver +# Run from git clone containing this script +clone_location="$(dirname "$0")/.." +cd $clone_location # Find out the versions of Python and Numpy the environment has pythonver=$(python -c "from platform import python_version ; print(python_version())") numpyver=$(python -c "import numpy; print(numpy.__version__)") # Construct the name of the install directory for this Mule install -dest_dir=python-${pythonver}_numpy-${numpyver} +dest_dir=$(realpath ../python-${pythonver}_numpy-${numpyver}) # Don't overwrite/rebuild an existing install if [ -d $dest_dir ] ; then echo "$dest_dir already exists..." exit 1 fi +echo "Installing mule for Python ${pythonver} and Numpy ${numpyver}" +echo "Installing from clone at $(realpath $clone_location)" +echo "Installing to ${dest_dir}" +echo "" +echo "" + # Run the build twice - once with and once without openmp for omp in openmp no-openmp ; do @@ -80,11 +82,11 @@ for omp in openmp no-openmp ; do --library_lock --ppibm_lib --spiral_lib --packing_lib\ --sstpert_lib $sst --wafccb_lib $wafc \ --shumlib_path $shum/$omp \ - ../$dest_dir/$omp/lib ../$dest_dir/$omp/bin + $dest_dir/$omp/lib $dest_dir/$omp/bin # Check the build works by running the unit-tests for mod in um_packing mule um_utils um_spiral_search ; do - PYTHONPATH=../$dest_dir/$omp/lib python -m unittest discover -v $mod.tests + PYTHONPATH=$dest_dir/$omp/lib python -m unittest discover -v $mod.tests done # By default the entry-point scripts created this way will be tied to @@ -94,6 +96,6 @@ for omp in openmp no-openmp ; do # to be called when a compatible environment is loaded; which the # modules we setup make sure of). So we'll replace the first line of # the entry point scripts with the generic environment python invocation - sed -i "s:^#!.*:#!/usr/bin/env python:g" ../$dest_dir/$omp/bin/mule-* + sed -i "s:^#!.*:#!/usr/bin/env python:g" $dest_dir/$omp/bin/mule-* done From c85232d96e00ce79b9e21c8d2da7e891ac099ecc Mon Sep 17 00:00:00 2001 From: James Bruten <109733895+james-bruten-mo@users.noreply.github.com> Date: Wed, 1 Oct 2025 10:00:06 +0100 Subject: [PATCH 04/15] Git migration (#3) Co-authored-by: Yaswant Pradhan <2984440+yaswant@users.noreply.github.com> --- .github/pull_request_template.md | 87 ++++++++++++ .gitignore | 218 +++++++++++++++++++++++++++++++ CONTRIBUTING.md | 13 ++ LICENCE | 28 ++++ README.md | 7 + 5 files changed, 353 insertions(+) create mode 100644 .github/pull_request_template.md create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 LICENCE create mode 100644 README.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..c431f6aa --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,87 @@ +# PR Summary + +Code Reviewer: + + + + + + + + + +## Code Quality Checklist + +(_Some checks are automatically carried out via the CI pipeline_) + +- [ ] I have performed a self-review of my own code +- [ ] My code follows the project's style guidelines +- [ ] Comments have been included that aid undertanding and enhance the + readability of the code +- [ ] My changes generate no new warnings + +## Testing + +- [ ] I have tested this change locally, using the rose-stem suite +- [ ] If any tests fail (rose-stem or CI) the reason is understood and + acceptable (eg. kgo changes) +- [ ] I have added tests to cover new functionality as appropriate (eg. system + tests, unit tests, etc.) + + + +### trac.log + + + +## Security Considerations + +- [ ] This change does not introduce security vulnerabilities +- [ ] I have reviewed the code for potential security issues +- [ ] Sensitive data is properly handled (if applicable) +- [ ] Authentication and authorisation are properly implemented (if applicable) + +## Performance Impact + +- [ ] Performance of the code has been considered and, if applicable, suitable + performance measurements have been conducted + +## Contributor License Agreement (CLA) + +- [ ] **Required** - I confirm that I have read and agree to the project's + [Contributor License Agreement](todo-enter-link-to-cla) + +## AI Assistance and Attribution + +- [ ] Some of the content of this change has been produced with the assistance + of _Generative AI tool name_ (e.g., Met Office Github Copilot Enterprise, + Github Copilot Personal, ChatGPT GPT-4, etc) and I have followed the + [Simulation Systems AI policy](todo-enter-link-to-policy-page) (including + attribution labels) + +## Documentation + +- [ ] Where appropriate I have updated documentation related to this change and + confirmed that it builds correctly + +# Code Review + + + +- [ ] All dependencies have been resolved +- [ ] Related Issues are properly linked and addressed +- [ ] CLA compliance is confirmed +- [ ] Code quality standards are met +- [ ] Tests are adequate and passing +- [ ] Documentation is complete and accurate +- [ ] Security considerations have been addressed +- [ ] Performance impact is acceptable diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5b81ac1b --- /dev/null +++ b/.gitignore @@ -0,0 +1,218 @@ +# Gitignore created from github template + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[codz] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py.cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +# Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +# poetry.lock +# poetry.toml + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +# pdm.lock +# pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +# pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# Redis +*.rdb +*.aof +*.pid + +# RabbitMQ +mnesia/ +rabbitmq/ +rabbitmq-data/ + +# ActiveMQ +activemq-data/ + +# SageMath parsed files +*.sage.py + +# Environments +.env +.envrc +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +# .idea/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..4823ba9e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing Guidelines + +Please follow the +[simulation-systems working practices](https://metoffice.github.io/simulation-systems/index.html) + +By contributing you agree to the +[simulation-systems Contributor Licence Agreement](https://metoffice.github.io/simulation-systems/FurtherDetails/contributing.html) + +Please be aware of and follow the +[simulation-systems Code of Conduct](https://metoffice.github.io/simulation-systems/FurtherDetails/code_of_conduct.html) + +Please be aware of and follow the +[simulation-systems AI Policy](https://metoffice.github.io/simulation-systems/FurtherDetails/ai.html) diff --git a/LICENCE b/LICENCE new file mode 100644 index 00000000..f3865e0d --- /dev/null +++ b/LICENCE @@ -0,0 +1,28 @@ +BSD 3-Clause Licence + +Crown Copyright (c) Met Office + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..73dd9215 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Mule + +Mule is an API written in Python which allows you to access and manipulate files +produced by the UM (Unified Model, of the Met Office (UK)). + +Please follow the +[simulation-systems working practices](https://metoffice.github.io/simulation-systems/index.html) From 87a301ebd02c03ebd5b4f018211ae6599d5b47eb Mon Sep 17 00:00:00 2001 From: James Bruten Date: Wed, 1 Oct 2025 13:02:50 +0100 Subject: [PATCH 05/15] run ruff --- mule/docs/source/conf.py | 54 ++++++++++++------------- mule/setup.py | 41 +++++++++++-------- um_packing/setup.py | 38 ++++++++++-------- um_ppibm/setup.py | 33 ++++++++------- um_spiral_search/setup.py | 33 ++++++++------- um_sstpert/setup.py | 38 +++++++++--------- um_utils/docs/source/conf.py | 58 +++++++++++++++------------ um_utils/setup.py | 78 ++++++++++++++++++++---------------- um_wafccb/setup.py | 30 +++++++------- 9 files changed, 220 insertions(+), 183 deletions(-) diff --git a/mule/docs/source/conf.py b/mule/docs/source/conf.py index 6edf7cda..fef0ec91 100644 --- a/mule/docs/source/conf.py +++ b/mule/docs/source/conf.py @@ -29,35 +29,35 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.viewcode', + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.viewcode", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'Mule' -copyright = u'2022, UM Systems Team' +project = "Mule" +copyright = "2022, UM Systems Team" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '2024.11.1' +version = "2024.11.1" # The full version, including alpha/beta/rc tags. -release = '2024.11.1' +release = "2024.11.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -89,7 +89,7 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -105,7 +105,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinxdoc' +html_theme = "sphinxdoc" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -134,7 +134,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -151,8 +151,8 @@ # Custom sidebar templates, maps document names to template names. html_sidebars = { - '**': ['localtoc.html', 'sourcelink.html', 'searchbox.html'], - } + "**": ["localtoc.html", "sourcelink.html", "searchbox.html"], +} # Additional templates that should be rendered to pages, maps page names to # template names. @@ -185,17 +185,15 @@ # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'Muledoc' +htmlhelp_basename = "Muledoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # 'preamble': '', } @@ -204,8 +202,7 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'Mule.tex', u'Mule Documentation', - u'UM Systems Team', 'manual'), + ("index", "Mule.tex", "Mule Documentation", "UM Systems Team", "manual"), ] # The name of an image file (relative to this directory) to place at the top of @@ -233,10 +230,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'mule', u'Mule Documentation', - [u'UM Systems Team'], 1) -] +man_pages = [("index", "mule", "Mule Documentation", ["UM Systems Team"], 1)] # If true, show URL addresses after external links. # man_show_urls = False @@ -248,9 +242,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'Mule', u'Mule Documentation', - u'UM Systems Team', 'Mule', '', - 'Miscellaneous'), + ( + "index", + "Mule", + "Mule Documentation", + "UM Systems Team", + "Mule", + "", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. diff --git a/mule/setup.py b/mule/setup.py index f80a4383..1ae430c1 100644 --- a/mule/setup.py +++ b/mule/setup.py @@ -29,6 +29,7 @@ class CleanCommand(setuptools.Command): Custom clean which gets rid of build files that the standard clean command does not """ + user_options = [] def initialize_options(self): @@ -46,19 +47,27 @@ def run(self): setuptools.setup( - name='mule', - version='2024.11.1', - description='Unified Model Fields File interface', - author='UM Systems Team', - url='https://code.metoffice.gov.uk/trac/um', - cmdclass={'clean': CleanCommand}, - package_dir={'': 'lib'}, - packages=['mule', - 'mule.tests', - 'mule.tests.unit', - 'mule.tests.integration', - 'mule.example_code'], - package_data={'mule': - [os.path.relpath(path, "lib/mule") - for path in (glob('lib/mule/tests/test_datafiles/*') + - ['lib/mule/tests/test_stashmaster'])]}) + name="mule", + version="2024.11.1", + description="Unified Model Fields File interface", + author="UM Systems Team", + url="https://code.metoffice.gov.uk/trac/um", + cmdclass={"clean": CleanCommand}, + package_dir={"": "lib"}, + packages=[ + "mule", + "mule.tests", + "mule.tests.unit", + "mule.tests.integration", + "mule.example_code", + ], + package_data={ + "mule": [ + os.path.relpath(path, "lib/mule") + for path in ( + glob("lib/mule/tests/test_datafiles/*") + + ["lib/mule/tests/test_stashmaster"] + ) + ] + }, +) diff --git a/um_packing/setup.py b/um_packing/setup.py index ae03cabf..9dc9cdb9 100644 --- a/um_packing/setup.py +++ b/um_packing/setup.py @@ -30,6 +30,7 @@ class CleanCommand(setuptools.Command): Custom clean which gets rid of build files that the standard clean command does not """ + user_options = [] def initialize_options(self): @@ -39,8 +40,7 @@ def finalize_options(self): pass def run(self): - for cleanpath in ["./build", "./dist", "./lib/*.egg-info", - "./lib/*/*.so"]: + for cleanpath in ["./build", "./dist", "./lib/*.egg-info", "./lib/*/*.so"]: print("Removing: {0}...".format(cleanpath)) cleanpath = glob(cleanpath) if cleanpath: @@ -51,21 +51,25 @@ def run(self): setuptools.setup( - name='um_packing', - version='2024.11.1', - description='Unified Model packing library extension', - author='UM Systems Team', - url='https://code.metoffice.gov.uk/trac/um', - cmdclass={'clean': CleanCommand}, - package_dir={'': 'lib'}, - packages=['um_packing', - 'um_packing.tests'], + name="um_packing", + version="2024.11.1", + description="Unified Model packing library extension", + author="UM Systems Team", + url="https://code.metoffice.gov.uk/trac/um", + cmdclass={"clean": CleanCommand}, + package_dir={"": "lib"}, + packages=["um_packing", "um_packing.tests"], ext_modules=[ setuptools.Extension( - 'um_packing.um_packing', - ['lib/um_packing/um_packing.c'], + "um_packing.um_packing", + ["lib/um_packing/um_packing.c"], include_dirs=[np.get_include()], - libraries=["shum_byteswap", - "shum_wgdos_packing", - "shum_string_conv", - "shum_constants"])]) + libraries=[ + "shum_byteswap", + "shum_wgdos_packing", + "shum_string_conv", + "shum_constants", + ], + ) + ], +) diff --git a/um_ppibm/setup.py b/um_ppibm/setup.py index 58c06bf8..be62a7cd 100644 --- a/um_ppibm/setup.py +++ b/um_ppibm/setup.py @@ -30,6 +30,7 @@ class CleanCommand(setuptools.Command): Custom clean which gets rid of build files that the standard clean command does not """ + user_options = [] def initialize_options(self): @@ -39,8 +40,7 @@ def finalize_options(self): pass def run(self): - for cleanpath in ["./build", "./dist", "./lib/*.egg-info", - "./lib/*/*.so"]: + for cleanpath in ["./build", "./dist", "./lib/*.egg-info", "./lib/*/*.so"]: print("Removing: {0}...".format(cleanpath)) cleanpath = glob(cleanpath) if cleanpath: @@ -51,19 +51,22 @@ def run(self): setuptools.setup( - name='um_ppibm', - version='2024.11.1', - description='Unified Model pp conversion utility with IBM number format', - author='UM Systems Team', - url='https://code.metoffice.gov.uk/trac/um', - cmdclass={'clean': CleanCommand}, - package_dir={'': 'lib'}, - packages=['um_ppibm', ], + name="um_ppibm", + version="2024.11.1", + description="Unified Model pp conversion utility with IBM number format", + author="UM Systems Team", + url="https://code.metoffice.gov.uk/trac/um", + cmdclass={"clean": CleanCommand}, + package_dir={"": "lib"}, + packages=[ + "um_ppibm", + ], ext_modules=[ setuptools.Extension( - 'um_ppibm.um_ieee2ibm32', - ['lib/um_ppibm/um_ieee2ibm32.c'], + "um_ppibm.um_ieee2ibm32", + ["lib/um_ppibm/um_ieee2ibm32.c"], include_dirs=[np.get_include()], - libraries=["shum_string_conv", - "shum_byteswap", - "shum_data_conv"])]) + libraries=["shum_string_conv", "shum_byteswap", "shum_data_conv"], + ) + ], +) diff --git a/um_spiral_search/setup.py b/um_spiral_search/setup.py index b9aefce9..fe4f1076 100644 --- a/um_spiral_search/setup.py +++ b/um_spiral_search/setup.py @@ -30,6 +30,7 @@ class CleanCommand(setuptools.Command): Custom clean which gets rid of build files that the standard clean command does not """ + user_options = [] def initialize_options(self): @@ -39,8 +40,7 @@ def finalize_options(self): pass def run(self): - for cleanpath in ["./build", "./dist", "./lib/*.egg-info", - "./lib/*/*.so"]: + for cleanpath in ["./build", "./dist", "./lib/*.egg-info", "./lib/*/*.so"]: print("Removing: {0}...".format(cleanpath)) cleanpath = glob(cleanpath) if cleanpath: @@ -51,21 +51,20 @@ def run(self): setuptools.setup( - name='um_spiral_search', - version='2024.11.1', - description='Unified Model Spiral Search extension', - author='UM Systems Team', - url='https://code.metoffice.gov.uk/trac/um', - cmdclass={'clean': CleanCommand}, - package_dir={'': 'lib'}, - packages=['um_spiral_search', - 'um_spiral_search.tests'], + name="um_spiral_search", + version="2024.11.1", + description="Unified Model Spiral Search extension", + author="UM Systems Team", + url="https://code.metoffice.gov.uk/trac/um", + cmdclass={"clean": CleanCommand}, + package_dir={"": "lib"}, + packages=["um_spiral_search", "um_spiral_search.tests"], ext_modules=[ setuptools.Extension( - 'um_spiral_search.um_spiral_search', - ['lib/um_spiral_search/um_spiral_search.c'], + "um_spiral_search.um_spiral_search", + ["lib/um_spiral_search/um_spiral_search.c"], include_dirs=[np.get_include()], - libraries=["shum_spiral_search", - "shum_string_conv", - "shum_constants"]) - ]) + libraries=["shum_spiral_search", "shum_string_conv", "shum_constants"], + ) + ], +) diff --git a/um_sstpert/setup.py b/um_sstpert/setup.py index e52d4b79..6d9e7d47 100644 --- a/um_sstpert/setup.py +++ b/um_sstpert/setup.py @@ -30,6 +30,7 @@ class CleanCommand(setuptools.Command): Custom clean which gets rid of build files that the standard clean command does not """ + user_options = [] def initialize_options(self): @@ -39,8 +40,7 @@ def finalize_options(self): pass def run(self): - for cleanpath in ["./build", "./dist", "./lib/*.egg-info", - "./lib/*/*.so"]: + for cleanpath in ["./build", "./dist", "./lib/*.egg-info", "./lib/*/*.so"]: print("Removing: {0}...".format(cleanpath)) cleanpath = glob(cleanpath) if cleanpath: @@ -51,23 +51,25 @@ def run(self): setuptools.setup( - name='um_sstpert', - version='2024.11.1', - description='Unified Model SST-perturbation extension and utility', - author='UM Systems Team', - url='https://code.metoffice.gov.uk/trac/um', - cmdclass={'clean': CleanCommand}, - package_dir={'': 'lib'}, - packages=['um_sstpert'], + name="um_sstpert", + version="2024.11.1", + description="Unified Model SST-perturbation extension and utility", + author="UM Systems Team", + url="https://code.metoffice.gov.uk/trac/um", + cmdclass={"clean": CleanCommand}, + package_dir={"": "lib"}, + packages=["um_sstpert"], ext_modules=[ setuptools.Extension( - 'um_sstpert.um_sstpert', - ['lib/um_sstpert/um_sstpert.c'], + "um_sstpert.um_sstpert", + ["lib/um_sstpert/um_sstpert.c"], include_dirs=[np.get_include()], - libraries=["um_sstpert", - "shum_string_conv", - "shum_constants"])], + libraries=["um_sstpert", "shum_string_conv", "shum_constants"], + ) + ], entry_points={ - 'console_scripts': [ - 'mule-sstpert = um_sstpert:_main', - ]}) + "console_scripts": [ + "mule-sstpert = um_sstpert:_main", + ] + }, +) diff --git a/um_utils/docs/source/conf.py b/um_utils/docs/source/conf.py index 2cacfca5..3ceaff3d 100644 --- a/um_utils/docs/source/conf.py +++ b/um_utils/docs/source/conf.py @@ -29,35 +29,35 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.viewcode', + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.viewcode", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'UM Utilities' -copyright = u'2022, UM Systems Team' +project = "UM Utilities" +copyright = "2022, UM Systems Team" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '2024.11.1' +version = "2024.11.1" # The full version, including alpha/beta/rc tags. -release = '2024.11.1' +release = "2024.11.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -89,7 +89,7 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -105,7 +105,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinxdoc' +html_theme = "sphinxdoc" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -134,7 +134,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -151,8 +151,8 @@ # Custom sidebar templates, maps document names to template names. html_sidebars = { - '**': ['localtoc.html', 'sourcelink.html', 'searchbox.html'], - } + "**": ["localtoc.html", "sourcelink.html", "searchbox.html"], +} # Additional templates that should be rendered to pages, maps page names to # template names. @@ -185,7 +185,7 @@ # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'UMUtilsdoc' +htmlhelp_basename = "UMUtilsdoc" # -- Options for LaTeX output --------------------------------------------- @@ -193,10 +193,8 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # 'preamble': '', } @@ -205,8 +203,13 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'UM_utils.tex', u'UM Utilities Documentation', - u'UM Systems Team', 'manual'), + ( + "index", + "UM_utils.tex", + "UM Utilities Documentation", + "UM Systems Team", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -235,8 +238,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'um_utils', u'UM Utilities Documentation', - [u'UM Systems Team'], 1) + ("index", "um_utils", "UM Utilities Documentation", ["UM Systems Team"], 1) ] # If true, show URL addresses after external links. @@ -249,9 +251,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'UM_Utils', u'UM Utilities Documentation', - u'UM Systems Team', 'UM_Utils', '', - 'Miscellaneous'), + ( + "index", + "UM_Utils", + "UM Utilities Documentation", + "UM Systems Team", + "UM_Utils", + "", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. diff --git a/um_utils/setup.py b/um_utils/setup.py index 80ab856f..0052b0ef 100644 --- a/um_utils/setup.py +++ b/um_utils/setup.py @@ -29,6 +29,7 @@ class CleanCommand(setuptools.Command): Custom clean which gets rid of build files that the standard clean command does not """ + user_options = [] def initialize_options(self): @@ -46,38 +47,47 @@ def run(self): setuptools.setup( - name='um_utils', - version='2024.11.1', - description='Unified Model Fields File utilities', - author='UM Systems Team', - url='https://code.metoffice.gov.uk/trac/um', - cmdclass={'clean': CleanCommand}, - package_dir={'': 'lib'}, - packages=['um_utils', - 'um_utils.tests', - 'um_utils.tests.pumf', - 'um_utils.tests.select', - 'um_utils.tests.summary', - 'um_utils.tests.cumf', - 'um_utils.tests.cutout', - 'um_utils.tests.fixframe' - ], - package_data={'um_utils': - [os.path.relpath(path, "lib/um_utils") - for path in (glob('lib/um_utils/tests/cumf/output/*') - + glob('lib/um_utils/tests/pumf/output/*') - + glob('lib/um_utils/tests/summary/output/*') - + ['lib/um_utils/tests/test_stashmaster'])]}, + name="um_utils", + version="2024.11.1", + description="Unified Model Fields File utilities", + author="UM Systems Team", + url="https://code.metoffice.gov.uk/trac/um", + cmdclass={"clean": CleanCommand}, + package_dir={"": "lib"}, + packages=[ + "um_utils", + "um_utils.tests", + "um_utils.tests.pumf", + "um_utils.tests.select", + "um_utils.tests.summary", + "um_utils.tests.cumf", + "um_utils.tests.cutout", + "um_utils.tests.fixframe", + ], + package_data={ + "um_utils": [ + os.path.relpath(path, "lib/um_utils") + for path in ( + glob("lib/um_utils/tests/cumf/output/*") + + glob("lib/um_utils/tests/pumf/output/*") + + glob("lib/um_utils/tests/summary/output/*") + + ["lib/um_utils/tests/test_stashmaster"] + ) + ] + }, entry_points={ - 'console_scripts': [ - 'mule-pumf = um_utils.pumf:_main', - 'mule-summary = um_utils.summary:_main', - 'mule-cumf = um_utils.cumf:_main', - 'mule-cutout = um_utils.cutout:_main', - 'mule-trim = um_utils.trim:_main', - 'mule-version = um_utils.version:_main', - 'mule-fixframe = um_utils.fixframe:_main', - 'mule-unpack = um_utils.unpack:_main', - 'mule-select = um_utils.select:_main', - 'mule-convpp = um_utils.convpp:_main', - 'mule-editmask = um_utils.editmask:_main']}) + "console_scripts": [ + "mule-pumf = um_utils.pumf:_main", + "mule-summary = um_utils.summary:_main", + "mule-cumf = um_utils.cumf:_main", + "mule-cutout = um_utils.cutout:_main", + "mule-trim = um_utils.trim:_main", + "mule-version = um_utils.version:_main", + "mule-fixframe = um_utils.fixframe:_main", + "mule-unpack = um_utils.unpack:_main", + "mule-select = um_utils.select:_main", + "mule-convpp = um_utils.convpp:_main", + "mule-editmask = um_utils.editmask:_main", + ] + }, +) diff --git a/um_wafccb/setup.py b/um_wafccb/setup.py index 450d3421..d1fd17ec 100644 --- a/um_wafccb/setup.py +++ b/um_wafccb/setup.py @@ -30,6 +30,7 @@ class CleanCommand(setuptools.Command): Custom clean which gets rid of build files that the standard clean command does not """ + user_options = [] def initialize_options(self): @@ -39,8 +40,7 @@ def finalize_options(self): pass def run(self): - for cleanpath in ["./build", "./dist", "./lib/*.egg-info", - "./lib/*/*.so"]: + for cleanpath in ["./build", "./dist", "./lib/*.egg-info", "./lib/*/*.so"]: print("Removing: {0}...".format(cleanpath)) cleanpath = glob(cleanpath) if cleanpath: @@ -51,18 +51,20 @@ def run(self): setuptools.setup( - name='um_wafccb', - version='2024.11.1', - description='Unified Model WAFC CB extension', - author='UM Systems Team', - url='https://code.metoffice.gov.uk/trac/um', - cmdclass={'clean': CleanCommand}, - package_dir={'': 'lib'}, - packages=['um_wafccb'], + name="um_wafccb", + version="2024.11.1", + description="Unified Model WAFC CB extension", + author="UM Systems Team", + url="https://code.metoffice.gov.uk/trac/um", + cmdclass={"clean": CleanCommand}, + package_dir={"": "lib"}, + packages=["um_wafccb"], ext_modules=[ setuptools.Extension( - 'um_wafccb.um_wafccb', - ['lib/um_wafccb/um_wafccb.c'], + "um_wafccb.um_wafccb", + ["lib/um_wafccb/um_wafccb.c"], include_dirs=[np.get_include()], - libraries=["um_wafccb"]), - ]) + libraries=["um_wafccb"], + ), + ], +) From 6726eec2acf97434879f401216bb2866e1554767 Mon Sep 17 00:00:00 2001 From: James Bruten Date: Wed, 1 Oct 2025 13:08:13 +0100 Subject: [PATCH 06/15] run ruff check --fix --- mule/docs/source/conf.py | 2 -- um_utils/docs/source/conf.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/mule/docs/source/conf.py b/mule/docs/source/conf.py index fef0ec91..5e425381 100644 --- a/mule/docs/source/conf.py +++ b/mule/docs/source/conf.py @@ -12,8 +12,6 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys -import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the diff --git a/um_utils/docs/source/conf.py b/um_utils/docs/source/conf.py index 3ceaff3d..ee5ea83a 100644 --- a/um_utils/docs/source/conf.py +++ b/um_utils/docs/source/conf.py @@ -12,8 +12,6 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys -import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the From 544366142f501f8b0a03bfb6800ac2e3e40581e4 Mon Sep 17 00:00:00 2001 From: James Bruten Date: Wed, 1 Oct 2025 13:17:00 +0100 Subject: [PATCH 07/15] run shellcheck --- admin/install_mule.sh | 2 +- admin/meto_install_mule.sh | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/admin/install_mule.sh b/admin/install_mule.sh index f0d81dfd..15a149ab 100755 --- a/admin/install_mule.sh +++ b/admin/install_mule.sh @@ -358,7 +358,7 @@ function unpack_and_copy(){ # The egg might be zipped - if it is unzip it in place if [ ! -d $egg ] ; then - egg=$SCRATCHLIB/$module*.egg + egg="$SCRATCHLIB/$module*.egg" fi if [ ! -d $egg ] ; then echo "[INFO] Unpacking zipped egg..." diff --git a/admin/meto_install_mule.sh b/admin/meto_install_mule.sh index 0b0bd747..54899d86 100755 --- a/admin/meto_install_mule.sh +++ b/admin/meto_install_mule.sh @@ -31,8 +31,6 @@ set -eu # Setup what version of things should be used -# Mule version for build (will be checked out from SRS) -mule_ver=2024.11.1 # UM version for sstpert and wafccb libraries (will be looked up in $UMDIR) um_ver=vn13.7 # Shumlib version (will be looked up in $UMDIR) From 0850fc69fc389e3d1d42d8786174eb040bc949c1 Mon Sep 17 00:00:00 2001 From: James Bruten Date: Wed, 1 Oct 2025 13:18:47 +0100 Subject: [PATCH 08/15] add ci checks --- .github/workflows/checks.yaml | 42 +++++++++++++++++++++++++++++++++++ .gitignore | 4 ++++ pyproject.toml | 10 +++++++++ 3 files changed, 56 insertions(+) create mode 100644 .github/workflows/checks.yaml create mode 100644 pyproject.toml diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml new file mode 100644 index 00000000..72ad1e84 --- /dev/null +++ b/.github/workflows/checks.yaml @@ -0,0 +1,42 @@ +--- +name: Quality + +on: # yamllint disable-line rule:truthy + pull_request: + types: [opened, synchronize, reopened] + workflow_dispatch: + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +permissions: read-all + +jobs: + + checks: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup uv + uses: astral-sh/setup-uv@v6 + - name: Install dependencies + run: uv sync + - name: Python Lint + run: uv run ruff check . + - name: Python Format Check + if: always() + run: uv run ruff format --check --diff -s --respect-gitignore . + - name: Shell Check + if: always() + run: | + find . -type f \( -name "*.*sh" -o ! -name "*.*" \) \ + -not -path "*.git/*" -not -path "*.venv/*" -print0 \ + | xargs -0 file | grep "shell script" | cut -d: -f1 \ + | xargs -r uv run shellcheck -S warning \ + && echo "All checks passed!" + - name: Minimize uv cache + run: uv cache prune --ci diff --git a/.gitignore b/.gitignore index 5b81ac1b..8be4a881 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ # Gitignore created from github template +# Virtual environment +.venv/ +uv.lock + # Byte-compiled / optimized / DLL files __pycache__/ *.py[codz] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..d948d6bb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,10 @@ +[project] +name = "mule" +version = "2025.10.1" +description = "API for interacting with UM output files" +readme = "README.md" +requires-python = ">=3.12.9" +dependencies = [ + "ruff==0.12.1", + "shellcheck-py==0.10.0.1", +] From 333fca622e867077220063435270cdb6b5448f97 Mon Sep 17 00:00:00 2001 From: James Bruten Date: Wed, 1 Oct 2025 13:35:50 +0100 Subject: [PATCH 09/15] add cpplint --- .github/workflows/checks.yaml | 6 ++++++ CPPLINT.cfg | 6 ++++++ pyproject.toml | 1 + 3 files changed, 13 insertions(+) create mode 100644 CPPLINT.cfg diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 72ad1e84..cfae188d 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -38,5 +38,11 @@ jobs: | xargs -0 file | grep "shell script" | cut -d: -f1 \ | xargs -r uv run shellcheck -S warning \ && echo "All checks passed!" + - name: C + # See: CPPLINT.cfg + if: always() + run: | + uv run cpplint --recursive --extensions=c,h \ + --exclude=.git --exclude=.venv . - name: Minimize uv cache run: uv cache prune --ci diff --git a/CPPLINT.cfg b/CPPLINT.cfg new file mode 100644 index 00000000..a59fd797 --- /dev/null +++ b/CPPLINT.cfg @@ -0,0 +1,6 @@ +set noparent +linelength=120 +filter=-whitespace +filter=-build/include_subdir,-build/include,-build/header_guard +filter=-readability/braces,-readability/multiline_comment +filter=-runtime/arrays,-runtime/int diff --git a/pyproject.toml b/pyproject.toml index d948d6bb..31d57c7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ description = "API for interacting with UM output files" readme = "README.md" requires-python = ">=3.12.9" dependencies = [ + "cpplint == 2.0.2", "ruff==0.12.1", "shellcheck-py==0.10.0.1", ] From d47aebc45f38e2132090203fbac51f0e6b6330e6 Mon Sep 17 00:00:00 2001 From: James Bruten Date: Wed, 1 Oct 2025 13:49:31 +0100 Subject: [PATCH 10/15] add space --- .github/workflows/checks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index cfae188d..c3312cc3 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -1,7 +1,7 @@ --- name: Quality -on: # yamllint disable-line rule:truthy +on: # yamllint disable-line rule:truthy pull_request: types: [opened, synchronize, reopened] workflow_dispatch: From f1a2a3fac763374033725f78f675f2b289533514 Mon Sep 17 00:00:00 2001 From: James Bruten Date: Wed, 1 Oct 2025 14:15:51 +0100 Subject: [PATCH 11/15] change cpplint name --- .github/workflows/checks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index c3312cc3..d1cf75b3 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -38,7 +38,7 @@ jobs: | xargs -0 file | grep "shell script" | cut -d: -f1 \ | xargs -r uv run shellcheck -S warning \ && echo "All checks passed!" - - name: C + - name: CPPLINT # See: CPPLINT.cfg if: always() run: | From 5cac73267fbe5b0f75074af338d63d38165d2d7b Mon Sep 17 00:00:00 2001 From: James Bruten Date: Wed, 1 Oct 2025 14:32:22 +0100 Subject: [PATCH 12/15] fix shell --- admin/meto_install_mule.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/admin/meto_install_mule.sh b/admin/meto_install_mule.sh index 58f73888..c6dbddd5 100755 --- a/admin/meto_install_mule.sh +++ b/admin/meto_install_mule.sh @@ -66,11 +66,7 @@ if [ -d $dest_dir ] ; then fi echo "Installing mule for Python ${pythonver} and Numpy ${numpyver}" -<<<<<<< HEAD -echo "Insalling from clone at $(realpath $clone_location)" -======= echo "Installing from clone at $(realpath $clone_location)" ->>>>>>> main echo "Installing to ${dest_dir}" echo "" echo "" From 4c2e0a9122888de4aa7d2c344dd3ca4c7549a92c Mon Sep 17 00:00:00 2001 From: Yaswant Pradhan <2984440+yaswant@users.noreply.github.com> Date: Thu, 2 Oct 2025 11:51:25 +0100 Subject: [PATCH 13/15] Initial docgen action (#6) --- .github/workflows/docs.yaml | 67 +++++++++++++++++++++ .python-version | 1 + mule/{README => README.rst} | 0 mule/docs/source/conf.py | 2 +- mule/lib/mule/tests/unit/test_AncilFile.py | 8 +-- mule/lib/mule/tests/unit/test_DumpFile.py | 8 +-- mule/lib/mule/tests/unit/test_FieldsFile.py | 8 +-- mule/lib/mule/tests/unit/test_LBCFile.py | 8 +-- mule/pyproject.toml | 2 +- mule/setup.py | 2 +- pyproject.toml | 17 +++++- um_packing/{README => README.rst} | 0 um_ppibm/{README => README.rst} | 0 um_spiral_search/{README => README.rst} | 0 um_sstpert/{README => README.rst} | 0 um_utils/{README => README.rst} | 0 um_wafccb/{README => README.rst} | 0 17 files changed, 101 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/docs.yaml create mode 100644 .python-version rename mule/{README => README.rst} (100%) rename um_packing/{README => README.rst} (100%) rename um_ppibm/{README => README.rst} (100%) rename um_spiral_search/{README => README.rst} (100%) rename um_sstpert/{README => README.rst} (100%) rename um_utils/{README => README.rst} (100%) rename um_wafccb/{README => README.rst} (100%) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 00000000..bfb77303 --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,67 @@ +--- +name: Docs + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - 'releases/**' + pull_request: + types: [opened, synchronize, reopened] + paths: + - 'mule/docs/**' + - '.github/workflows/docs.yaml' + workflow_dispatch: + +permissions: read-all + +jobs: + + build: + runs-on: ubuntu-latest + timeout-minutes: 3 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup uv + uses: astral-sh/setup-uv@v6 + - name: Install dependencies + run: uv sync && uv pip install ./mule ./um_utils + - name: Build mule docs + run: uv run make clean html --directory ./mule/docs + working-directory: ${{ github.workspace }} + - name: Build um_utils docs + run: uv run make clean html --directory ./um_utils/docs + working-directory: ${{ github.workspace }} + # - id: permissions + # name: Set permissions + # run: | + # chmod -c -R +rX "./doc/_build/html/" + - name: Upload artifacts + uses: actions/upload-pages-artifact@v4 + with: + name: github-pages + path: ./mule/docs/build/html + - name: Minimize uv cache + if: always() + run: uv cache prune --ci + + # Deploy job + deploy: + if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + # Add a dependency to the build job + needs: build + permissions: + pages: write # to deploy to Pages + id-token: write # to verify the deployment originates from an appropriate source + # Deploy to the github-pages environment + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + # Specify runner + deployment step + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..f3fe474a --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12.9 diff --git a/mule/README b/mule/README.rst similarity index 100% rename from mule/README rename to mule/README.rst diff --git a/mule/docs/source/conf.py b/mule/docs/source/conf.py index 5e425381..a71dff32 100644 --- a/mule/docs/source/conf.py +++ b/mule/docs/source/conf.py @@ -46,7 +46,7 @@ # General information about the project. project = "Mule" -copyright = "2022, UM Systems Team" +copyright = "2025, Met Office" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/mule/lib/mule/tests/unit/test_AncilFile.py b/mule/lib/mule/tests/unit/test_AncilFile.py index 0809fd0d..585fe19c 100644 --- a/mule/lib/mule/tests/unit/test_AncilFile.py +++ b/mule/lib/mule/tests/unit/test_AncilFile.py @@ -319,7 +319,7 @@ def test_basic_varres_field_bzx_fail(self): self.anc.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field start longitude \(bzx\) not RMDI"): + r"Field start longitude \(bzx\) not RMDI"): self.anc.validate() # Test a variable resolution field with non RMDI bzy fails @@ -335,7 +335,7 @@ def test_basic_varres_field_bzy_fail(self): self.anc.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field start latitude \(bzy\) not RMDI"): + r"Field start latitude \(bzy\) not RMDI"): self.anc.validate() # Test a variable resolution field with non RMDI bdx fails @@ -351,7 +351,7 @@ def test_basic_varres_field_bdx_fail(self): self.anc.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field longitude interval \(bdx\) not RMDI"): + r"Field longitude interval \(bdx\) not RMDI"): self.anc.validate() # Test a variable resolution field with non RMDI bdy fails @@ -367,7 +367,7 @@ def test_basic_varres_field_bdy_fail(self): self.anc.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field latitude interval \(bdy\) not RMDI"): + r"Field latitude interval \(bdy\) not RMDI"): self.anc.validate() # Test lower boundary x value just within tolerance passes diff --git a/mule/lib/mule/tests/unit/test_DumpFile.py b/mule/lib/mule/tests/unit/test_DumpFile.py index beb8cb45..47863692 100644 --- a/mule/lib/mule/tests/unit/test_DumpFile.py +++ b/mule/lib/mule/tests/unit/test_DumpFile.py @@ -437,7 +437,7 @@ def test_basic_varres_field_bzx_fail(self): self.ff.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field start longitude \(bzx\) not RMDI"): + r"Field start longitude \(bzx\) not RMDI"): self.ff.validate() # Test a variable resolution field with non RMDI bzy fails @@ -453,7 +453,7 @@ def test_basic_varres_field_bzy_fail(self): self.ff.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field start latitude \(bzy\) not RMDI"): + r"Field start latitude \(bzy\) not RMDI"): self.ff.validate() # Test a variable resolution field with non RMDI bdx fails @@ -469,7 +469,7 @@ def test_basic_varres_field_bdx_fail(self): self.ff.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field longitude interval \(bdx\) not RMDI"): + r"Field longitude interval \(bdx\) not RMDI"): self.ff.validate() # Test a variable resolution field with non RMDI bdy fails @@ -485,7 +485,7 @@ def test_basic_varres_field_bdy_fail(self): self.ff.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field latitude interval \(bdy\) not RMDI"): + r"Field latitude interval \(bdy\) not RMDI"): self.ff.validate() # Test lower boundary x value just within tolerance passes diff --git a/mule/lib/mule/tests/unit/test_FieldsFile.py b/mule/lib/mule/tests/unit/test_FieldsFile.py index 3c315255..43d9e501 100644 --- a/mule/lib/mule/tests/unit/test_FieldsFile.py +++ b/mule/lib/mule/tests/unit/test_FieldsFile.py @@ -418,7 +418,7 @@ def test_basic_varres_field_bzx_fail(self): self.ff.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field start longitude \(bzx\) not RMDI"): + r"Field start longitude \(bzx\) not RMDI"): self.ff.validate() # Test a variable resolution field with non RMDI bzy fails @@ -433,7 +433,7 @@ def test_basic_varres_field_bzy_fail(self): self.ff.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field start latitude \(bzy\) not RMDI"): + r"Field start latitude \(bzy\) not RMDI"): self.ff.validate() # Test a variable resolution field with non RMDI bdx fails @@ -448,7 +448,7 @@ def test_basic_varres_field_bdx_fail(self): self.ff.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field longitude interval \(bdx\) not RMDI"): + r"Field longitude interval \(bdx\) not RMDI"): self.ff.validate() # Test a variable resolution field with non RMDI bdy fails @@ -463,7 +463,7 @@ def test_basic_varres_field_bdy_fail(self): self.ff.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field latitude interval \(bdy\) not RMDI"): + r"Field latitude interval \(bdy\) not RMDI"): self.ff.validate() # Test lower boundary x value just within tolerance passes diff --git a/mule/lib/mule/tests/unit/test_LBCFile.py b/mule/lib/mule/tests/unit/test_LBCFile.py index e537facc..cb00f8c9 100644 --- a/mule/lib/mule/tests/unit/test_LBCFile.py +++ b/mule/lib/mule/tests/unit/test_LBCFile.py @@ -316,7 +316,7 @@ def test_basic_varres_field_bzx_fail(self): self.lbc.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field start longitude \(bzx\) not RMDI"): + r"Field start longitude \(bzx\) not RMDI"): self.lbc.validate() # Test a variable resolution field with non RMDI bzy fails @@ -332,7 +332,7 @@ def test_basic_varres_field_bzy_fail(self): self.lbc.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field start latitude \(bzy\) not RMDI"): + r"Field start latitude \(bzy\) not RMDI"): self.lbc.validate() # Test a variable resolution field with non RMDI bdx fails @@ -348,7 +348,7 @@ def test_basic_varres_field_bdx_fail(self): self.lbc.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field longitude interval \(bdx\) not RMDI"): + r"Field longitude interval \(bdx\) not RMDI"): self.lbc.validate() # Test a variable resolution field with non RMDI bdy fails @@ -364,7 +364,7 @@ def test_basic_varres_field_bdy_fail(self): self.lbc.fields = [self.fld] with six.assertRaisesRegex( self, ValidateError, - "Field latitude interval \(bdy\) not RMDI"): + r"Field latitude interval \(bdy\) not RMDI"): self.lbc.validate() # Test lower boundary x value just within tolerance passes diff --git a/mule/pyproject.toml b/mule/pyproject.toml index 0abe9ac0..8e5ff691 100644 --- a/mule/pyproject.toml +++ b/mule/pyproject.toml @@ -1,4 +1,4 @@ # pyproject.toml [build-system] requires = ["setuptools >= 42.0.0", "numpy"] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" diff --git a/mule/setup.py b/mule/setup.py index 1ae430c1..ca3f0d5e 100644 --- a/mule/setup.py +++ b/mule/setup.py @@ -51,7 +51,7 @@ def run(self): version="2024.11.1", description="Unified Model Fields File interface", author="UM Systems Team", - url="https://code.metoffice.gov.uk/trac/um", + url="https://github.com/metoffice/mule", cmdclass={"clean": CleanCommand}, package_dir={"": "lib"}, packages=[ diff --git a/pyproject.toml b/pyproject.toml index 31d57c7a..f9110d0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,9 +3,20 @@ name = "mule" version = "2025.10.1" description = "API for interacting with UM output files" readme = "README.md" -requires-python = ">=3.12.9" +requires-python = ">=3.11" dependencies = [ "cpplint == 2.0.2", - "ruff==0.12.1", - "shellcheck-py==0.10.0.1", + "ruff == 0.12.1", + "shellcheck-py == 0.10.0.1", + "sphinx == 8.2.3", + "sphinx-lint == 1.0.0", + "pydata-sphinx-theme == 0.16.1", + "sphinx-design == 0.6.1", + "sphinx-copybutton == 0.5.2", + "sphinx-lint == 1.0.0", + "sphinx-sitemap == 2.8.0", + "sphinxcontrib-svg2pdfconverter == 1.3.0", + "numpy == 2.3.3", + "six == 1.17.0", + "pillow == 11.3.0", ] diff --git a/um_packing/README b/um_packing/README.rst similarity index 100% rename from um_packing/README rename to um_packing/README.rst diff --git a/um_ppibm/README b/um_ppibm/README.rst similarity index 100% rename from um_ppibm/README rename to um_ppibm/README.rst diff --git a/um_spiral_search/README b/um_spiral_search/README.rst similarity index 100% rename from um_spiral_search/README rename to um_spiral_search/README.rst diff --git a/um_sstpert/README b/um_sstpert/README.rst similarity index 100% rename from um_sstpert/README rename to um_sstpert/README.rst diff --git a/um_utils/README b/um_utils/README.rst similarity index 100% rename from um_utils/README rename to um_utils/README.rst diff --git a/um_wafccb/README b/um_wafccb/README.rst similarity index 100% rename from um_wafccb/README rename to um_wafccb/README.rst From 5131ebc4bd248d15a52a16518723791be1f63f1a Mon Sep 17 00:00:00 2001 From: James Bruten <109733895+james-bruten-mo@users.noreply.github.com> Date: Thu, 2 Oct 2025 16:11:42 +0100 Subject: [PATCH 14/15] add build and test ci (#5) Co-authored-by: Yaswant Pradhan <2984440+yaswant@users.noreply.github.com> --- .github/workflows/checks.yaml | 2 +- .github/workflows/ci.yaml | 48 +++++++++++++++++++ .github/workflows/docs.yaml | 4 +- .gitignore | 2 + admin/install_mule.sh | 16 ++++++- .../tests/integration/test_DataOperator.py | 21 ++++---- mule/lib/mule/tests/unit/test_pp.py | 18 ++++--- um_packing/pyproject.toml | 2 +- um_packing/setup.py | 2 +- um_ppibm/pyproject.toml | 2 +- um_ppibm/setup.py | 2 +- um_spiral_search/pyproject.toml | 2 +- um_spiral_search/setup.py | 2 +- um_sstpert/pyproject.toml | 2 +- um_sstpert/setup.py | 2 +- um_utils/pyproject.toml | 2 +- um_utils/setup.py | 2 +- um_wafccb/pyproject.toml | 2 +- um_wafccb/setup.py | 2 +- 19 files changed, 103 insertions(+), 32 deletions(-) create mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index d1cf75b3..889e0384 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -14,7 +14,7 @@ permissions: read-all jobs: - checks: + mule-checks: runs-on: ubuntu-latest timeout-minutes: 5 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..91fdf7c5 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,48 @@ +--- +name: CI +on: # yamllint disable-line rule:truthy + push: + branches: + - main + - 'releases/**' + pull_request: + types: [opened, synchronize, reopened] + workflow_dispatch: + +permissions: read-all + +jobs: + build-test-mule: + runs-on: ubuntu-24.04 + timeout-minutes: 5 + strategy: + max-parallel: 4 + matrix: + python-version: ['3.12', '3.13'] + numpy-version: ['1.26.4', '2.2.6'] + exclude: + - python-version: '3.13' + numpy-version: '1.26.4' + + steps: + - name: Checkout Mule + uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + - name: Install Python Dependencies ${{ matrix.numpy-version }} + run: | + pip install setuptools + pip install six + pip install numpy==${{ matrix.numpy-version }} + - name: Build python-${{ matrix.python-version }}_numpy-${{ matrix.numpy-version }} + run: | + ./admin/install_mule.sh ./_build/lib ./_build/bin + - name: Test python-${{ matrix.python-version }}_numpy-${{ matrix.numpy-version }} + run: | + cd _build/lib + for name in ./*; do + python -m unittest discover -v ${name##*/}.tests + done diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index bfb77303..809eb27c 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -17,7 +17,7 @@ permissions: read-all jobs: - build: + build-docs: runs-on: ubuntu-latest timeout-minutes: 3 @@ -51,7 +51,7 @@ jobs: deploy: if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' # Add a dependency to the build job - needs: build + needs: build-docs permissions: pages: write # to deploy to Pages id-token: write # to verify the deployment originates from an appropriate source diff --git a/.gitignore b/.gitignore index 8be4a881..fcbaa4bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ # Gitignore created from github template +_build/ + # Virtual environment .venv/ uv.lock diff --git a/admin/install_mule.sh b/admin/install_mule.sh index 15a149ab..1fb313cf 100755 --- a/admin/install_mule.sh +++ b/admin/install_mule.sh @@ -140,7 +140,15 @@ echo "[INFO] Installing against Python $PYTHONVER" # Setup a temporary directory where the install will be initially created SCRATCHDIR=$(mktemp -d) -SCRATCHLIB=$SCRATCHDIR/lib/$PYTHONEXEC/site-packages +pkg_loc=$($mule_python_exec -c "import site ; print(site.getsitepackages()[0])") +if [[ $pkg_loc == *"dist-packages"* ]]; then + debian_like=true + SCRATCHLIB=$SCRATCHDIR/local/lib/$PYTHONEXEC/dist-packages +else + debian_like=false + SCRATCHLIB=$SCRATCHDIR/lib/$PYTHONEXEC/site-packages +fi +echo "SCRATCHLIB $SCRATCHLIB" # Make relative paths absolute if [ ! ${LIB_DEST:0:1} == "/" ] ; then @@ -384,7 +392,11 @@ function unpack_and_copy(){ else cp -vr $egg*.*-info $BIN_DEST fi - cp -vr $SCRATCHDIR/bin/* $BIN_DEST/ + if $debian_like; then + cp -vr $SCRATCHDIR/local/bin/* $BIN_DEST/ + else + cp -vr $SCRATCHDIR/bin/* $BIN_DEST/ + fi fi } diff --git a/mule/lib/mule/tests/integration/test_DataOperator.py b/mule/lib/mule/tests/integration/test_DataOperator.py index d65c01f6..c97907ab 100644 --- a/mule/lib/mule/tests/integration/test_DataOperator.py +++ b/mule/lib/mule/tests/integration/test_DataOperator.py @@ -59,15 +59,18 @@ def test(self): ff.integer_constants.num_cols = num_cols // 4 ff.real_constants.col_spacing = col_spacing*4 with self.temp_filename() as temp_path: - ff.to_file(temp_path) - ff_back = FieldsFile.from_file(temp_path) - self.assertEqual(ff_back.integer_constants.num_cols, num_cols // 4) - self.assertEqual(ff_back.real_constants.col_spacing, col_spacing*4) - self.assertEqual(ff_back.fields[0].lbnpt, num_cols // 4) - self.assertEqual(ff_back.fields[0].bdx, col_spacing*4) - self.assertEqual(ff_back.fields[1].lbnpt, num_cols//4) - self.assertEqual(ff_back.fields[1].bdx, col_spacing*4) - self.assertEqual(ff_back.fields[1].get_data().shape, (73, 24)) + try: + ff.to_file(temp_path) + ff_back = FieldsFile.from_file(temp_path) + self.assertEqual(ff_back.integer_constants.num_cols, num_cols // 4) + self.assertEqual(ff_back.real_constants.col_spacing, col_spacing*4) + self.assertEqual(ff_back.fields[0].lbnpt, num_cols // 4) + self.assertEqual(ff_back.fields[0].bdx, col_spacing*4) + self.assertEqual(ff_back.fields[1].lbnpt, num_cols//4) + self.assertEqual(ff_back.fields[1].bdx, col_spacing*4) + self.assertEqual(ff_back.fields[1].get_data().shape, (73, 24)) + except NotImplementedError: + self.skipTest("Skipping tests as WGDOS packing library unavailable") if __name__ == '__main__': diff --git a/mule/lib/mule/tests/unit/test_pp.py b/mule/lib/mule/tests/unit/test_pp.py index 376f2440..97440213 100644 --- a/mule/lib/mule/tests/unit/test_pp.py +++ b/mule/lib/mule/tests/unit/test_pp.py @@ -67,9 +67,12 @@ def test_read_ppfile_fix_grid(self, fname=None): self.assertEqual(field.lbrel, rel) self.assertEqual(field.lbvc, vc) - data = field.get_data() - self.assertEqual(data.shape[0], field.lbrow) - self.assertEqual(data.shape[1], field.lbnpt) + try: + data = field.get_data() + self.assertEqual(data.shape[0], field.lbrow) + self.assertEqual(data.shape[1], field.lbnpt) + except NotImplementedError: + self.skipTest("Skipping tests as WGDOS packing library unavailable") def test_read_ppfile_var_grid(self, fname=None): @@ -85,9 +88,12 @@ def test_read_ppfile_var_grid(self, fname=None): self.assertEqual(field.lbrel, 3) self.assertEqual(field.lbvc, 65) - data = field.get_data() - self.assertEqual(data.shape[0], field.lbrow) - self.assertEqual(data.shape[1], field.lbnpt) + try: + data = field.get_data() + self.assertEqual(data.shape[0], field.lbrow) + self.assertEqual(data.shape[1], field.lbnpt) + except NotImplementedError: + print("Skipping tests as WGDOS packing library unavailable") expected_extra = [1, 2, 12, 13, 14, 15] self.assertEqual(len(field.pp_extra_data), 6) diff --git a/um_packing/pyproject.toml b/um_packing/pyproject.toml index 0abe9ac0..8e5ff691 100644 --- a/um_packing/pyproject.toml +++ b/um_packing/pyproject.toml @@ -1,4 +1,4 @@ # pyproject.toml [build-system] requires = ["setuptools >= 42.0.0", "numpy"] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" diff --git a/um_packing/setup.py b/um_packing/setup.py index 9dc9cdb9..5b4229c0 100644 --- a/um_packing/setup.py +++ b/um_packing/setup.py @@ -55,7 +55,7 @@ def run(self): version="2024.11.1", description="Unified Model packing library extension", author="UM Systems Team", - url="https://code.metoffice.gov.uk/trac/um", + url="https://github.com/metoffice/mule", cmdclass={"clean": CleanCommand}, package_dir={"": "lib"}, packages=["um_packing", "um_packing.tests"], diff --git a/um_ppibm/pyproject.toml b/um_ppibm/pyproject.toml index 0abe9ac0..8e5ff691 100644 --- a/um_ppibm/pyproject.toml +++ b/um_ppibm/pyproject.toml @@ -1,4 +1,4 @@ # pyproject.toml [build-system] requires = ["setuptools >= 42.0.0", "numpy"] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" diff --git a/um_ppibm/setup.py b/um_ppibm/setup.py index be62a7cd..919daf2f 100644 --- a/um_ppibm/setup.py +++ b/um_ppibm/setup.py @@ -55,7 +55,7 @@ def run(self): version="2024.11.1", description="Unified Model pp conversion utility with IBM number format", author="UM Systems Team", - url="https://code.metoffice.gov.uk/trac/um", + url="https://github.com/metoffice/mule", cmdclass={"clean": CleanCommand}, package_dir={"": "lib"}, packages=[ diff --git a/um_spiral_search/pyproject.toml b/um_spiral_search/pyproject.toml index 0abe9ac0..8e5ff691 100644 --- a/um_spiral_search/pyproject.toml +++ b/um_spiral_search/pyproject.toml @@ -1,4 +1,4 @@ # pyproject.toml [build-system] requires = ["setuptools >= 42.0.0", "numpy"] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" diff --git a/um_spiral_search/setup.py b/um_spiral_search/setup.py index fe4f1076..21594847 100644 --- a/um_spiral_search/setup.py +++ b/um_spiral_search/setup.py @@ -55,7 +55,7 @@ def run(self): version="2024.11.1", description="Unified Model Spiral Search extension", author="UM Systems Team", - url="https://code.metoffice.gov.uk/trac/um", + url="https://github.com/metoffice/mule", cmdclass={"clean": CleanCommand}, package_dir={"": "lib"}, packages=["um_spiral_search", "um_spiral_search.tests"], diff --git a/um_sstpert/pyproject.toml b/um_sstpert/pyproject.toml index 0abe9ac0..8e5ff691 100644 --- a/um_sstpert/pyproject.toml +++ b/um_sstpert/pyproject.toml @@ -1,4 +1,4 @@ # pyproject.toml [build-system] requires = ["setuptools >= 42.0.0", "numpy"] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" diff --git a/um_sstpert/setup.py b/um_sstpert/setup.py index 6d9e7d47..3ad04e7a 100644 --- a/um_sstpert/setup.py +++ b/um_sstpert/setup.py @@ -55,7 +55,7 @@ def run(self): version="2024.11.1", description="Unified Model SST-perturbation extension and utility", author="UM Systems Team", - url="https://code.metoffice.gov.uk/trac/um", + url="https://github.com/metoffice/mule", cmdclass={"clean": CleanCommand}, package_dir={"": "lib"}, packages=["um_sstpert"], diff --git a/um_utils/pyproject.toml b/um_utils/pyproject.toml index 0abe9ac0..8e5ff691 100644 --- a/um_utils/pyproject.toml +++ b/um_utils/pyproject.toml @@ -1,4 +1,4 @@ # pyproject.toml [build-system] requires = ["setuptools >= 42.0.0", "numpy"] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" diff --git a/um_utils/setup.py b/um_utils/setup.py index 0052b0ef..65dd4ce5 100644 --- a/um_utils/setup.py +++ b/um_utils/setup.py @@ -51,7 +51,7 @@ def run(self): version="2024.11.1", description="Unified Model Fields File utilities", author="UM Systems Team", - url="https://code.metoffice.gov.uk/trac/um", + url="https://github.com/metoffice/mule", cmdclass={"clean": CleanCommand}, package_dir={"": "lib"}, packages=[ diff --git a/um_wafccb/pyproject.toml b/um_wafccb/pyproject.toml index 0abe9ac0..8e5ff691 100644 --- a/um_wafccb/pyproject.toml +++ b/um_wafccb/pyproject.toml @@ -1,4 +1,4 @@ # pyproject.toml [build-system] requires = ["setuptools >= 42.0.0", "numpy"] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" diff --git a/um_wafccb/setup.py b/um_wafccb/setup.py index d1fd17ec..a0f51b64 100644 --- a/um_wafccb/setup.py +++ b/um_wafccb/setup.py @@ -55,7 +55,7 @@ def run(self): version="2024.11.1", description="Unified Model WAFC CB extension", author="UM Systems Team", - url="https://code.metoffice.gov.uk/trac/um", + url="https://github.com/metoffice/mule", cmdclass={"clean": CleanCommand}, package_dir={"": "lib"}, packages=["um_wafccb"], From aaf8c1f2bfeeaa9bae02da45db448f84d52a7e4e Mon Sep 17 00:00:00 2001 From: James Bruten <109733895+james-bruten-mo@users.noreply.github.com> Date: Fri, 3 Oct 2025 08:35:09 +0100 Subject: [PATCH 15/15] Revert to previous install script (#7) --- admin/install_mule.sh | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/admin/install_mule.sh b/admin/install_mule.sh index 1fb313cf..15a149ab 100755 --- a/admin/install_mule.sh +++ b/admin/install_mule.sh @@ -140,15 +140,7 @@ echo "[INFO] Installing against Python $PYTHONVER" # Setup a temporary directory where the install will be initially created SCRATCHDIR=$(mktemp -d) -pkg_loc=$($mule_python_exec -c "import site ; print(site.getsitepackages()[0])") -if [[ $pkg_loc == *"dist-packages"* ]]; then - debian_like=true - SCRATCHLIB=$SCRATCHDIR/local/lib/$PYTHONEXEC/dist-packages -else - debian_like=false - SCRATCHLIB=$SCRATCHDIR/lib/$PYTHONEXEC/site-packages -fi -echo "SCRATCHLIB $SCRATCHLIB" +SCRATCHLIB=$SCRATCHDIR/lib/$PYTHONEXEC/site-packages # Make relative paths absolute if [ ! ${LIB_DEST:0:1} == "/" ] ; then @@ -392,11 +384,7 @@ function unpack_and_copy(){ else cp -vr $egg*.*-info $BIN_DEST fi - if $debian_like; then - cp -vr $SCRATCHDIR/local/bin/* $BIN_DEST/ - else - cp -vr $SCRATCHDIR/bin/* $BIN_DEST/ - fi + cp -vr $SCRATCHDIR/bin/* $BIN_DEST/ fi }