Skip to content

Commit df3b722

Browse files
committed
rimport can now take BOTH --file and --filelist.
1 parent c90114f commit df3b722

4 files changed

Lines changed: 80 additions & 33 deletions

File tree

rimport

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,15 @@ def build_parser() -> argparse.ArgumentParser:
5252
add_help=False, # Disable automatic help to add custom -help flag
5353
)
5454

55-
# Mutually exclusive: -file or -list (one required)
56-
group = parser.add_mutually_exclusive_group(required=True)
57-
group.add_argument(
55+
parser.add_argument(
5856
"--file",
5957
"-file",
6058
dest="file",
6159
metavar="filename",
6260
help="Provide a file to import. Must be in the CESM inputdata directory.",
6361
)
64-
group.add_argument(
62+
63+
parser.add_argument(
6564
"--list",
6665
"-list",
6766
dest="filelist",
@@ -309,12 +308,11 @@ def print_can_file_be_downloaded(file_can_be_downloaded: bool):
309308
def get_files_to_process(file: str, filelist: str):
310309
"""Get list of files to process.
311310
312-
Uses either --file or --filelist. "Prefers" --file, but they're actually mutually exclusive, so
313-
don't rely on that.
311+
Uses --file and/or --filelist arguments.
314312
315313
Args:
316314
file (str): Single file to process.
317-
filelist (str): File containing list of files to process
315+
filelist (str): File containing list of files to process.
318316
319317
Returns:
320318
list: List of files to process
@@ -323,14 +321,22 @@ def get_files_to_process(file: str, filelist: str):
323321
if file is not None:
324322
files_to_process = [file]
325323
else:
324+
files_to_process = []
325+
326+
if filelist is not None:
326327
list_path = Path(filelist).expanduser().resolve()
327328
if not list_path.exists():
328329
logger.error("rimport: list file not found: %s", list_path)
329330
return None, 2
330-
files_to_process = read_filelist(list_path)
331-
if not files_to_process:
331+
files_in_list = read_filelist(list_path)
332+
if not files_in_list:
332333
logger.error("rimport: no filenames found in list: %s", list_path)
333334
return None, 2
335+
files_to_process.extend(files_in_list)
336+
337+
if not files_to_process:
338+
logger.error("rimport: At least one of --file or --filelist is required")
339+
return None, 2
334340

335341
return files_to_process, 0
336342

tests/rimport/test_build_parser.py

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -60,27 +60,14 @@ def test_inputdata_arguments_accepted(self, temp_dirs, inputdata_flag):
6060
)
6161
assert args.inputdata_root == inputdata_root
6262

63-
def test_file_and_list_mutually_exclusive(self, capsys):
64-
"""Test that -file and -list cannot be used together."""
65-
parser = rimport.build_parser()
66-
with pytest.raises(SystemExit):
67-
parser.parse_args(["-file", "test.txt", "-list", "files.txt"])
68-
69-
# Check that the error message explains the problem
70-
captured = capsys.readouterr()
71-
stderr_lines = captured.err.strip().split("\n")
72-
assert "not allowed with argument" in stderr_lines[-1]
73-
74-
def test_file_or_list_required(self, capsys):
75-
"""Test that either -file or -list is required."""
76-
parser = rimport.build_parser()
77-
with pytest.raises(SystemExit):
78-
parser.parse_args([])
79-
80-
# Check that the error message explains the problem
81-
captured = capsys.readouterr()
82-
stderr_lines = captured.err.strip().split("\n")
83-
assert "error: one of the arguments" in stderr_lines[-1]
63+
def test_file_and_list_not_mutually_exclusive(self, capsys):
64+
"""Test that -file and -list can be used together."""
65+
parser = rimport.build_parser()
66+
file = "test.txt"
67+
filelist = "files.txt"
68+
args = parser.parse_args(["-file", file, "-list", filelist])
69+
assert args.file == file
70+
assert args.filelist == filelist
8471

8572
def test_inputdata_default(self):
8673
"""Test that -inputdata has correct default value."""

tests/rimport/test_get_relnames_to_process.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
import os
99
import importlib.util
1010
from importlib.machinery import SourceFileLoader
11-
from pathlib import Path
12-
from unittest.mock import patch, call
13-
import pytest
1411

1512
# pylint: disable=too-many-arguments,too-many-positional-arguments
1613

@@ -198,3 +195,44 @@ def test_filelist_empty(self, tmp_path):
198195
)
199196
assert result == 2
200197
assert files_to_process is None
198+
199+
def test_single_file_and_list(self, tmp_path):
200+
"""Test giving it a single file by its relative path"""
201+
# Setup
202+
inputdata_root = tmp_path / "inputdata"
203+
inputdata_root.mkdir()
204+
staging_root = tmp_path / "staging"
205+
staging_root.mkdir()
206+
207+
filename = "test.nc"
208+
test_file = inputdata_root / filename
209+
test_file.write_text("abc123")
210+
211+
filenames = []
212+
for i in range(2):
213+
f = f"test{i}.txt"
214+
filenames.append(f)
215+
(inputdata_root / f).write_text("def567")
216+
217+
filelist = tmp_path / "file_list.txt"
218+
filelist.write_text("\n".join(filenames), encoding="utf8")
219+
220+
# Run
221+
files_to_process, result = rimport.get_files_to_process(
222+
file=filename, filelist=filelist
223+
)
224+
225+
# Verify
226+
assert result == 0
227+
assert files_to_process == [filename] + filenames
228+
229+
def test_single_or_list_required(self):
230+
"""Test that either file or filelist is required"""
231+
# Run
232+
files_to_process, result = rimport.get_files_to_process(
233+
file=None, filelist=None
234+
)
235+
236+
# Verify
237+
assert result == 2
238+
assert files_to_process is None

tests/rimport/test_main.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,22 @@ def test_empty_filelist(
203203
captured = capsys.readouterr()
204204
assert "no filenames found in list" in captured.err
205205

206+
@patch.object(rimport, "ensure_running_as")
207+
def test_requires_file_or_filelist(
208+
self, _mock_ensure_running_as, tmp_path, capsys
209+
):
210+
"""Test that main() returns error code 2 if neither file nor filelist provided."""
211+
inputdata_root = tmp_path / "inputdata"
212+
inputdata_root.mkdir()
213+
214+
result = rimport.main(
215+
["-inputdata", str(inputdata_root)]
216+
)
217+
218+
assert result == 2
219+
captured = capsys.readouterr()
220+
assert "At least one of --file or --filelist is required" in captured.err
221+
206222
@patch.object(rimport, "stage_data")
207223
@patch.object(rimport, "get_staging_root")
208224
@patch.object(rimport, "normalize_paths")

0 commit comments

Comments
 (0)