Skip to content

Commit b33a092

Browse files
committed
Improve error handling, tests of "tdc check" CLI
1 parent 4408dc4 commit b33a092

File tree

2 files changed

+65
-24
lines changed

2 files changed

+65
-24
lines changed

transport_data/cli/__init__.py

+18-11
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,19 @@ def check(structure_urn: str, path: Path, sheets, verbose, **options): # noqa:
6969
from transport_data import STORE
7070
from transport_data.util.sdmx import read_csv
7171

72+
# Pieces of any error message
73+
message = []
74+
7275
# Handle `structure_urn`: retrieve a data structure that describes the data
73-
structure = STORE.get(structure_urn)
76+
try:
77+
structure = STORE.get(structure_urn)
78+
except Exception:
79+
message.append(f"Structure {structure_urn!r} could not be loaded")
80+
structure = structure_cls = structure_id = None
81+
else:
82+
structure_cls = type(structure).__name__.lower().replace("definition", "")
83+
structure_id = sdmx.urn.shorten(structure.urn).split("=")[-1]
84+
7485
if isinstance(structure, common.BaseDataflow):
7586
# Also retrieve the data structure definition
7687
STORE.resolve(structure, "structure")
@@ -79,10 +90,8 @@ def check(structure_urn: str, path: Path, sheets, verbose, **options): # noqa:
7990
# Construct keyword arguments for CSVAdapter
8091
# TODO Check if this works for full SDMX-CSV
8192
adapt = {
82-
"structure": options.pop("structure")
83-
or type(structure).__name__.lower().replace("definition", ""),
84-
"structure_id": options.pop("structure_id")
85-
or sdmx.urn.shorten(structure.urn).split("=")[-1],
93+
"structure": options.pop("structure") or structure_cls,
94+
"structure_id": options.pop("structure_id") or structure_id,
8695
"action": options.pop("action"),
8796
}
8897

@@ -125,18 +134,16 @@ def check(structure_urn: str, path: Path, sheets, verbose, **options): # noqa:
125134
try:
126135
dm = read_csv(p, structure, adapt)
127136
except Exception as e:
128-
message = [f"read failed with\n{type(e).__name__}: {' '.join(e.args)}"]
137+
message.append(f"read failed with\n{type(e).__name__}: {' '.join(e.args)}")
129138

130139
if len(e.args) and "line 1" in e.args[0]:
131140
message.append(
132141
"Hint: try giving --structure= or --structure-id argument(s) to "
133142
"adapt to SDMX-CSV."
134143
)
135-
elif isinstance(e, KeyError):
136-
message.append(
137-
"Hint: try giving --action argument to adapt to SDMX-CSV."
138-
)
139-
else:
144+
elif structure is None:
145+
pass
146+
else: # pragma: no cover
140147
message.append("\n".join(format_exception(e)))
141148

142149
print("")

transport_data/tests/test_cli.py

+47-13
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,26 @@ def test_cli(tdc_cli, command):
3030
tdc_cli.invoke(command)
3131

3232

33-
def test_check(tdc_cli, test_data_path, tmp_store):
33+
CHECK_ARGS = [
34+
"--structure=dataflow",
35+
"--structure-id=FOO",
36+
"--action=I",
37+
"--sheets=sheet_2",
38+
"Dataflow=TDCI:EMBER_001(1.0.0)",
39+
]
40+
41+
42+
def test_check0(tdc_cli, test_data_path, tmp_store):
43+
"""Check a successful read of a .xlsx file."""
3444
ember_dfd(tmp_store)
3545

36-
result = tdc_cli.invoke(
37-
[
38-
"check",
39-
"--sheets=sheet_2",
40-
"--structure=dataflow",
41-
"--structure-id=FOO",
42-
"--action=I",
43-
"Dataflow=TDCI:EMBER_001(1.0.0)",
44-
str(test_data_path.joinpath("read-csv-2.xlsx")),
45-
]
46-
)
46+
path = test_data_path.joinpath("read-csv-2.xlsx")
47+
result = tdc_cli.invoke(["check"] + CHECK_ARGS + [str(path)])
4748

4849
# Command runs without error
4950
assert 0 == result.exit_code, result.output
5051

5152
# Data was located, converted to CSV, read, and summary displayed
52-
print(result.output)
5353
assert re.fullmatch(
5454
r"""
5555
File: .*read-csv-2.xlsx
@@ -64,6 +64,40 @@ def test_check(tdc_cli, test_data_path, tmp_store):
6464
)
6565

6666

67+
@pytest.mark.parametrize(
68+
"args, exit_code, text",
69+
(
70+
(["X"], 1, "line 1, field 1"), # Omit all options and URN
71+
(CHECK_ARGS[:1] + ["X"], 1, "line 1, field 2"), # Only --structure=
72+
(CHECK_ARGS[:2] + ["X"], 1, "'X' could not be loaded"), # Invalid structure URN
73+
(CHECK_ARGS + ["-v"], 0, "..."), # Show pd.DataFrame abbreviated string repr
74+
(CHECK_ARGS + ["-vv"], 0, ""), # Show pd.DataFrame full string repr
75+
),
76+
)
77+
def test_check1(tdc_cli, test_data_path, tmp_store, args, exit_code, text):
78+
"""Check various other argument combinations."""
79+
ember_dfd(tmp_store)
80+
81+
path = test_data_path.joinpath("read-csv-1.csv")
82+
result = tdc_cli.invoke(["check"] + args + [str(path)])
83+
84+
# Command gives the expected exit code
85+
assert exit_code == result.exit_code, result.output
86+
87+
# Expected text is contained in the output
88+
if text:
89+
assert text in result.output
90+
91+
92+
def test_check2(tdc_cli, tmp_path):
93+
path = tmp_path.joinpath("foo.txt")
94+
path.touch()
95+
result = tdc_cli.invoke(["check", "X", str(path)])
96+
97+
assert 2 == result.exit_code, result.output
98+
assert "Unsupported file extension" in result.output
99+
100+
67101
# ‘Script’ of CLI input to produce a data structure definition
68102
SCRIPT = [
69103
# Maintainer ID: accept the default (TDCI)

0 commit comments

Comments
 (0)