Skip to content

Issue #1009: Retrieve single dataset - #1047

Open
Ed (mo-gill) wants to merge 38 commits into
v4.0_releasefrom
1009_cdds_retrieve_dataset_for_CREPP
Open

Issue #1009: Retrieve single dataset#1047
Ed (mo-gill) wants to merge 38 commits into
v4.0_releasefrom
1009_cdds_retrieve_dataset_for_CREPP

Conversation

@mo-gill

@mo-gill Ed (mo-gill) commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Closes #1009

Edit: Ended up opting for making a totally different script for this tool rather than adding the functionality to the pre-existing retrieve data tool as was originally planned. This was due to the CLI interfaces for the two intended uses becoming quite complicated and the code got quite convoluted when i attempted it. Also considering this tool is only intended for external CREPP users, i decided that was another reason to keep the code separate.

@mo-gill Ed (mo-gill) added this to the CDDS v4.0.2 milestone Aug 11, 2026
@mo-gill Ed (mo-gill) self-assigned this Aug 11, 2026
@mo-gill Ed (mo-gill) added the enhancement New feature or request label Aug 11, 2026
@mo-gill
Ed (mo-gill) changed the base branch from main to v4.0_release August 11, 2026 08:04
Comment thread cdds/cdds/common/mass.py
Comment on lines 237 to +251
@@ -246,9 +246,9 @@ def mass_list_files_recursively(mass_path, simulation):
'files': []
}
datasets[dataset_id]['files'].append({
'filesize': elems[4],
'filesize': elems[2],
'filename': filename,
'mass_path': elems[8]
'mass_path': elems[6]

@mo-gill Ed (mo-gill) Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data retrieval tool appeared to be broken when i tested it at the start of this issue. It was throwing this error:

  File "CDDS/cdds/cdds/misc/retrieve_archived_data.py", line 333, in main_cdds_retrieve_archived_data
    mass_file_list = mass_list_files_recursively(
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "CDDS/cdds/cdds/common/mass.py", line 238, in mass_list_files_recursively
    timestamp, filename) = elems[8].split('/')[-11:]
                           ~~~~~^^^
IndexError: list index out of range

I can only assume that the 'moo', 'ls', '-Rl' command used here now has slightly different stdout formatting after the move to new MASS.

This change fixes the error so the tool works again.

@mo-gill

Ed (mo-gill) commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

Undertook a rename of the cdds specific tool in this commit from cdds_retrieve_datato cdds_retrieve_archived_data to be a bit more specific as to its usage.

@mo-gill

Ed (mo-gill) commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator Author

Edit: this comment is stale. See #1047 (comment)

In this commit i created a replacement (called mass_list_files_recursively_with_checksums) very similar to mass_list_files_recursively which uses moo ls -Rlxm instead of moo ls -Rl, allowing the checksum to be retrieved from the returned xml.

I considered modifying mass_list_files_recursively instead, but walk_mass_dir uses that and seems reliant on it's current structure. The creation of an alternative function seemed like the safer solution (potentially walk_mass_dir could be refactored to use mass_list_files_recursively_with_checksums in future?)

I've dropped this new function as a replacement for mass_list_files_recursively that was being used in main_cdds_retrieve_archived_data and it seems to be working as expected.

@mo-gill
Ed (mo-gill) force-pushed the 1009_cdds_retrieve_dataset_for_CREPP branch from ae7a0a3 to 5c77cde Compare August 18, 2026 12:20
"dataset_id",
help="Full CMIP6 dataset_id, e.g. CMIP6.CMIP.MOHC.UKESM1-0-LL.piControl.r1i1p1f2.Amon.tas.gn",
)
if len(sys.argv) > 1 and sys.argv[1] == "get":

@mo-gill Ed (mo-gill) Aug 19, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This guards against IndexError when no command is supplied - provides helpful output instead:

$ cdds/bin/crepp_retrieve_archived_dataset get     
usage: crepp_retrieve_archived_dataset [-h] [--create-directories-false] [--mass-root MASS_ROOT]
                                       [--dry-run] [--chunk-size CHUNK_SIZE]
                                       {get,ls} dataset_id destination
crepp_retrieve_archived_dataset: error: the following arguments are required: dataset_id, destination

Arguably a bit brittle but the alternative was to use subparsers, which don't appear to be used elswhere in cdds - and they look a bit odd to me at first glance. So leaving this for ease of review unless that change is requested.

Comment thread cdds/cdds/common/mass.py
"""The :mod:`mass` module interact with the MASS archiving system."""
import logging
import subprocess
import re

@mo-gill Ed (mo-gill) Aug 20, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't appear to be used

Pylance: "re" is not accessed

def __init__(self, mass_failure, command):
super(MassError, self).__init__(mass_failure.get_message(command))
self.msg = mass_failure.get_message(command)
self.mass_failure = mass_failure

@mo-gill Ed (mo-gill) Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this so callers can inspect which MassFailure caused an error, not just its rendered message.

@mo-gill Ed (mo-gill) linked an issue Sep 9, 2026 that may be closed by this pull request
TMPDIR: str = tmpdir


def list_mass_files_with_checksums(mass_path: str) -> List[Dict[str, Any]]:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function was inspired by mass_list_files_recursively in https://github.com/MetOffice/CDDS/blob/main/cdds/cdds/common/mass.py#L202

I was considering modifying that pre-existing one, but i think that would've had knock on effects if i messed around with it's interface.

Instead i decided to keep this logic in the module where it's used as i'm guessing the checksum functionality is something that will be exclusive to CREPP.

# Please see LICENSE.md for license details.
import sys
from cdds.misc.retrieve_mass_data import main_cdds_retrieve_data
from cdds.misc.retrieve_archived_variables import main_cdds_retrieve_archived_variables

@mo-gill Ed (mo-gill) Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I undertook this rename of the previous tool to make the difference clearer to users that one is more for retrieving specific variables, whilst the other is for retrieving an entire dataset (also that the tools are specific to archived data as this has caused confusion for a user before).
So at command line they are now:
cdds_retrieve_archived_variables
cdds_retrieve_archived_dataset

Comment on lines +62 to +76
root = ET.fromstring(stdout_str)
for item in root.findall('node'):
# Skip directories and other non-file entries
if item.get('kind') != 'F':
continue
# Three following asserts largely exist to satisfy type checker as MASS should always provide them.
mass_file_path = item.get('url')
assert mass_file_path is not None
size_elem = item.find('size')
assert size_elem is not None
filesize = size_elem.text
checksum_elem = item.find('checksum/value')
assert checksum_elem is not None
checksum_value = checksum_elem.text
checksum = f"md5:{checksum_value}"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went for element tree for parsing the XML. I did see sax used for a similar purpose in other areas of CDDS. From what i could see the only advantage sax has is when dealing with much larger amounts of XML than needed for this issue. Personally i find elementtree more readable, but don't mind refactoring this to use sax if you'd prefer.

More info on elementtree here: https://docs.python.org/3/library/xml.etree.elementtree.html

# 1st: moo ls -Rlxm, 2nd: moo get -I -n (ls command is used to build paths the get uses for retrieval/dry run).

@patch(f"{_MODULE}.query_files_by_version")
def test_invalid_source_filepath_missing_status_returns_3(self, mock_query_files_by_version, tmp_path, caplog):

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll find use of caplog and capsys in this test file. I can't see them used previously in CDDS. They're convenient built in pytest fixtures for capturing logger output or normal printed output.

More info:
https://docs.pytest.org/en/6.2.x/logging.html#caplog-fixture
https://docs.pytest.org/en/6.2.x/capture.html

def test_success_returns_0_and_prints_json(self, _mock_run_mass_command, capsys):
result = run_ls_action(_CMIP6_FULL_DATASET_ID, _MASS_ROOT)
assert result == 0
captured = capsys.readouterr()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I like capsys, but i'm not a fan of their naming of this method.

At first i thought it meant it only read error output. It's actually supposed to mean it reads stdout AND stderr.

@mo-gill

Copy link
Copy Markdown
Collaborator Author

Both these tools have now been tested on MO and JASMIN and both seem to work for me:
cdds_retrieve_archived_variables
cdds_retrieve_archived_dataset

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add cdds_retrieve_dataset as interface for CREPP

2 participants