Skip to content

Commit ef456ed

Browse files
authored
Write definition and version directly independently from the reader (#312)
* Emit a sensible error message if the nxdl does not exist * Fix test for non existing nxdl * Write definition and version automatically * Don't overwrite version * Move get entry names to template
1 parent df8001b commit ef456ed

File tree

5 files changed

+91
-12
lines changed

5 files changed

+91
-12
lines changed

pynxtools/dataconverter/convert.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ def transfer_data_into_template(
207207
data = data_reader().read( # type: ignore[operator]
208208
template=Template(template), file_paths=input_file, **kwargs
209209
)
210+
entry_names = data.get_all_entry_names()
211+
for entry_name in entry_names:
212+
helpers.write_nexus_def_to_entry(data, entry_name, nxdl_name)
210213
if not skip_verify:
211214
helpers.validate_data_dict(template, data, nxdl_root)
212215
return data
@@ -423,15 +426,20 @@ def convert_cli(
423426
"The --input-file option is deprecated. Please use the positional arguments instead."
424427
)
425428

426-
convert(
427-
tuple(file_list) + input_file,
428-
reader,
429-
nxdl,
430-
output,
431-
fair,
432-
undocumented,
433-
skip_verify,
434-
)
429+
try:
430+
convert(
431+
tuple(file_list) + input_file,
432+
reader,
433+
nxdl,
434+
output,
435+
fair,
436+
undocumented,
437+
skip_verify,
438+
)
439+
except FileNotFoundError as exc:
440+
raise click.BadParameter(
441+
f"{nxdl} is not a valid application definition", param_hint="--nxdl"
442+
) from exc
435443

436444

437445
@main_cli.command()

pynxtools/dataconverter/helpers.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ def add_default_root_attributes(data, filename):
686686
def update_and_warn(key: str, value: str):
687687
if key in data and data[key] != value:
688688
logger.warning(
689-
f"The NXroot entry '{key}' (value: {data[key]}) should not be populated by "
689+
f"The NXroot entry '{key}' (value: {data[key]}) should not be changed by "
690690
f"the reader. This is overwritten by the actually used value '{value}'"
691691
)
692692
data[key] = value
@@ -705,6 +705,34 @@ def update_and_warn(key: str, value: str):
705705
update_and_warn("/@h5py_version", h5py.__version__)
706706

707707

708+
def write_nexus_def_to_entry(data, entry_name: str, nxdl_def: str):
709+
"""
710+
Writes the used nexus definition and version to /ENTRY/definition
711+
"""
712+
713+
def update_and_warn(key: str, value: str, overwrite=False):
714+
if key in data and data[key] is not None and data[key] != value:
715+
report = (
716+
f"This is overwritten by the actually used value '{value}'"
717+
if overwrite
718+
else f"The provided version '{value}' is kept. We assume you know what you are doing."
719+
)
720+
logger.log(
721+
logging.WARNING if overwrite else logging.INFO,
722+
f"The entry '{key}' (value: {data[key]}) should not be changed by "
723+
f"the reader. {report}",
724+
)
725+
if overwrite or data.get(key) is None:
726+
data[key] = value
727+
728+
update_and_warn(f"/ENTRY[{entry_name}]/definition", nxdl_def, overwrite=True)
729+
update_and_warn(
730+
f"/ENTRY[{entry_name}]/definition/@version",
731+
get_nexus_version(),
732+
overwrite=False,
733+
)
734+
735+
708736
def extract_atom_types(formula, mode="hill"):
709737
"""Extract atom types form chemical formula."""
710738
atom_types: set = set()

pynxtools/dataconverter/template.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
import copy
2121
import json
22+
import re
23+
from typing import Set
2224

2325
from pynxtools.dataconverter import helpers
2426

@@ -197,6 +199,20 @@ def rename_entry(self, old_name: str, new_name: str, deepcopy=True):
197199
internal_dict[f"/ENTRY[{new_name}]{rest_of_path}"] = value
198200
del internal_dict[key]
199201

202+
def get_all_entry_names(self) -> Set[str]:
203+
"""
204+
Get all entry names in the template.
205+
206+
Returns:
207+
Set[str]: A set of entry names.
208+
"""
209+
entry_names = set()
210+
for key in self:
211+
entry_name_match = re.search(r"\/ENTRY\[([a-zA-Z0-9_\.]+)\]", key)
212+
if entry_name_match is not None:
213+
entry_names.add(entry_name_match.group(1))
214+
return entry_names
215+
200216
def update(self, template):
201217
"""Merges second template to original
202218
or updates values from a dictionary if the type of :code:`template` is dict"""

tests/dataconverter/test_convert.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import os
2222
from pathlib import Path
2323

24+
import click
2425
import h5py
2526
import pytest
2627
from click.testing import CliRunner
@@ -83,7 +84,11 @@ def test_find_nxdl(cli_inputs):
8384
runner = CliRunner()
8485
result = runner.invoke(dataconverter.convert_cli, cli_inputs)
8586
if "NXdoesnotexist" in cli_inputs:
86-
assert isinstance(result.exception, FileNotFoundError)
87+
assert result.exit_code == 2
88+
assert result.output.endswith(
89+
"Error: Invalid value for --nxdl: "
90+
"NXdoesnotexist is not a valid application definition\n"
91+
)
8792
else:
8893
assert isinstance(result.exception, Exception)
8994
assert "The chosen NXDL isn't supported by the selected reader." in str(

tests/dataconverter/test_helpers.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,8 @@ def test_writing_of_root_attributes(caplog):
483483
filename = "my_nexus_file.nxs"
484484
with caplog.at_level(logging.WARNING):
485485
helpers.add_default_root_attributes(template, filename)
486+
helpers.write_nexus_def_to_entry(template, "entry", "NXtest")
487+
helpers.write_nexus_def_to_entry(template, "entry1", "NXtest")
486488

487489
assert "" == caplog.text
488490

@@ -497,6 +499,10 @@ def test_writing_of_root_attributes(caplog):
497499
assert "/@NeXus_version" in keys_added
498500
assert "/@HDF5_version" in keys_added
499501
assert "/@h5py_version" in keys_added
502+
assert "/ENTRY[entry]/definition" in keys_added
503+
assert "/ENTRY[entry]/definition/@version" in keys_added
504+
assert "/ENTRY[entry1]/definition" in keys_added
505+
assert "/ENTRY[entry1]/definition/@version" in keys_added
500506

501507

502508
def test_warning_on_root_attribute_overwrite(caplog):
@@ -510,10 +516,26 @@ def test_warning_on_root_attribute_overwrite(caplog):
510516
with caplog.at_level(logging.WARNING):
511517
helpers.add_default_root_attributes(template, filname)
512518
error_text = (
513-
"The NXroot entry '/@NX_class' (value: NXwrong) should not be populated by the reader. "
519+
"The NXroot entry '/@NX_class' (value: NXwrong) should not be changed by the reader. "
514520
"This is overwritten by the actually used value 'NXroot'"
515521
)
516522
assert error_text in caplog.text
517523

518524
assert "/@NX_class" in template.keys()
519525
assert template["/@NX_class"] == "NXroot"
526+
527+
528+
def test_warning_on_definition_changed_by_reader(caplog):
529+
template = Template()
530+
template["/ENTRY[entry]/definition"] = "NXwrong"
531+
with caplog.at_level(logging.WARNING):
532+
helpers.write_nexus_def_to_entry(template, "entry", "NXtest")
533+
534+
error_text = (
535+
"The entry '/ENTRY[entry]/definition' (value: NXwrong) should not be changed by the reader. "
536+
"This is overwritten by the actually used value 'NXtest'"
537+
)
538+
assert error_text in caplog.text
539+
540+
assert "/ENTRY[entry]/definition" in template.keys()
541+
assert template["/ENTRY[entry]/definition"] == "NXtest"

0 commit comments

Comments
 (0)