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/.github/workflows/checks.yaml b/.github/workflows/checks.yaml new file mode 100644 index 00000000..889e0384 --- /dev/null +++ b/.github/workflows/checks.yaml @@ -0,0 +1,48 @@ +--- +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: + + mule-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: CPPLINT + # 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/.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 new file mode 100644 index 00000000..809eb27c --- /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-docs: + 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-docs + 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/.gitignore b/.gitignore new file mode 100644 index 00000000..fcbaa4bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,224 @@ +# Gitignore created from github template + +_build/ + +# Virtual environment +.venv/ +uv.lock + +# 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/.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/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/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/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) 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 d4f57421..c6dbddd5 100755 --- a/admin/meto_install_mule.sh +++ b/admin/meto_install_mule.sh @@ -31,12 +31,10 @@ 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 +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) @@ -51,26 +49,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 +80,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 +94,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 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 6edf7cda..3d43d159 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 @@ -29,35 +27,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 = "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 # 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. @@ -89,7 +87,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 +103,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 +132,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 +149,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 +183,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 +200,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 +228,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 +240,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/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/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_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/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/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 f80a4383..7d8685e4 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="2025.10.1", + description="Unified Model Fields File interface", + author="UM Systems Team", + url="https://github.com/metoffice/mule", + 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/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..f9110d0f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[project] +name = "mule" +version = "2025.10.1" +description = "API for interacting with UM output files" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "cpplint == 2.0.2", + "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_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/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 ae03cabf..06d33e33 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="2025.10.1", + description="Unified Model packing library extension", + author="UM Systems Team", + url="https://github.com/metoffice/mule", + 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/README b/um_ppibm/README.rst similarity index 100% rename from um_ppibm/README rename to um_ppibm/README.rst 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/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 58c06bf8..73122dc2 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="2025.10.1", + description="Unified Model pp conversion utility with IBM number format", + author="UM Systems Team", + url="https://github.com/metoffice/mule", + 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/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_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/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 b9aefce9..8a5df3eb 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="2025.10.1", + description="Unified Model Spiral Search extension", + author="UM Systems Team", + url="https://github.com/metoffice/mule", + 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/README b/um_sstpert/README.rst similarity index 100% rename from um_sstpert/README rename to um_sstpert/README.rst 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/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 e52d4b79..15225238 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="2025.10.1", + description="Unified Model SST-perturbation extension and utility", + author="UM Systems Team", + url="https://github.com/metoffice/mule", + 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/README b/um_utils/README.rst similarity index 100% rename from um_utils/README rename to um_utils/README.rst diff --git a/um_utils/docs/source/conf.py b/um_utils/docs/source/conf.py index 2cacfca5..44a03848 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 @@ -29,35 +27,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 = "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. @@ -89,7 +87,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 +103,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 +132,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 +149,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 +183,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 +191,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 +201,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 +236,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 +249,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/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/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 80ab856f..e3a76698 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="2025.10.1", + description="Unified Model Fields File utilities", + author="UM Systems Team", + url="https://github.com/metoffice/mule", + 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/README b/um_wafccb/README.rst similarity index 100% rename from um_wafccb/README rename to um_wafccb/README.rst 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/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 450d3421..7fcf59d0 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="2025.10.1", + description="Unified Model WAFC CB extension", + author="UM Systems Team", + url="https://github.com/metoffice/mule", + 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"], + ), + ], +)