Skip to content

Commit ee1078b

Browse files
authored
Merge pull request #1491 from FAIRmat-NFDI/name-type-partial-in-dev-tools
`name_type="partial"` in dev tools
2 parents 979ec79 + bdf0ed8 commit ee1078b

4 files changed

Lines changed: 371 additions & 89 deletions

File tree

dev_tools/docs/anchor_list.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ def write(self):
114114
return
115115
contents = dict(
116116
_metadata=dict(
117+
# datetime=datetime.datetime.now(datetime.UTC).isoformat(),
118+
# the next line is the py3.9 supported way of getting the datetime
119+
# this will become deprecated however in py3.12 for which the
120+
# line above-mentioned is a fix, which however does not work in py3.9
117121
datetime=datetime.datetime.utcnow().isoformat(),
118122
title="NeXus NXDL vocabulary.",
119123
subtitle="Anchors for all NeXus fields, groups, "

dev_tools/docs/nxdl.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,13 +311,38 @@ def _get_doc_blocks(ns, node):
311311
out_blocks.append("\n".join(out_lines))
312312
return out_blocks
313313

314+
def _handle_multiline_docstring(self, blocks):
315+
link_pattern = re.compile(r"\.\. _([^:]+):(.*)")
316+
317+
links = []
318+
docstring = ""
319+
expanded_blocks = []
320+
321+
for block in blocks:
322+
expanded_blocks += block.split("\n")
323+
324+
for block in expanded_blocks:
325+
if not block:
326+
continue
327+
328+
link_match = link_pattern.search(block)
329+
if link_match is not None:
330+
links.append((link_match.group(1), link_match.group(2).strip()))
331+
else:
332+
docstring += " " + block.strip().replace("\n", " ")
333+
334+
for name, target in links:
335+
docstring = docstring.replace(f"`{name}`_", f"`{name} <{target}>`_")
336+
337+
return docstring
338+
314339
def _get_doc_line(self, ns, node):
315340
blocks = self._get_doc_blocks(ns, node)
316341
if len(blocks) == 0:
317342
return ""
318343
if len(blocks) > 1:
319-
raise Exception(f"Unexpected multi-paragraph doc [{'|'.join(blocks)}]")
320-
return re.sub(r"\n", " ", blocks[0])
344+
return self._handle_multiline_docstring(blocks)
345+
return blocks[0].replace("\n", " ")
321346

322347
def _get_minOccurs(self, node):
323348
"""

dev_tools/tests/test_nxdl_utils.py

Lines changed: 139 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
33
"""
44

5-
import os
5+
from pathlib import Path
66

77
import lxml.etree as ET
8+
import pytest
89

910
from ..utils import nxdl_utils as nexus
1011

@@ -32,8 +33,8 @@ def test_get_nexus_classes_units_attributes():
3233

3334
def test_get_node_at_nxdl_path():
3435
"""Test to verify if we receive the right XML element for a given NXDL path"""
35-
local_dir = os.path.abspath(os.path.dirname(__file__))
36-
nxdl_file_path = os.path.join(local_dir, "./NXtest.nxdl.xml")
36+
local_dir = Path(__file__).resolve().parent
37+
nxdl_file_path = local_dir / "NXtest.nxdl.xml"
3738
elem = ET.parse(nxdl_file_path).getroot()
3839
node = nexus.get_node_at_nxdl_path("/ENTRY/NXODD_name", elem=elem)
3940
assert node.attrib["type"] == "NXdata"
@@ -48,11 +49,144 @@ def test_get_node_at_nxdl_path():
4849
)
4950
assert node.attrib["name"] == "long_name"
5051

52+
nxdl_file_path = (
53+
local_dir.parent.parent / "contributed_definitions" / "NXiv_temp.nxdl.xml"
54+
)
55+
elem = ET.parse(nxdl_file_path).getroot()
56+
node = nexus.get_node_at_nxdl_path(
57+
"/ENTRY/INSTRUMENT/ENVIRONMENT/voltage_controller", elem=elem
58+
)
59+
assert node.attrib["name"] == "voltage_controller"
60+
61+
node = nexus.get_node_at_nxdl_path(
62+
"/ENTRY/INSTRUMENT/ENVIRONMENT/voltage_controller/calibration_time", elem=elem
63+
)
64+
assert node.attrib["name"] == "calibration_time"
65+
5166

5267
def test_get_inherited_nodes():
5368
"""Test to verify if we receive the right XML element list for a given NXDL path."""
54-
local_dir = os.path.abspath(os.path.dirname(__file__))
55-
nxdl_file_path = os.path.join(local_dir, "./NXtest.nxdl.xml")
69+
local_dir = Path(__file__).resolve().parent
70+
nxdl_file_path = local_dir / "NXtest.nxdl.xml"
71+
5672
elem = ET.parse(nxdl_file_path).getroot()
5773
(_, _, elist) = nexus.get_inherited_nodes(nxdl_path="/ENTRY/NXODD_name", elem=elem)
5874
assert len(elist) == 3
75+
76+
nxdl_file_path = (
77+
local_dir.parent.parent / "contributed_definitions" / "NXiv_temp.nxdl.xml"
78+
)
79+
80+
elem = ET.parse(nxdl_file_path).getroot()
81+
(_, _, elist) = nexus.get_inherited_nodes(
82+
nxdl_path="/ENTRY/INSTRUMENT/ENVIRONMENT", elem=elem
83+
)
84+
assert len(elist) == 3
85+
86+
(_, _, elist) = nexus.get_inherited_nodes(
87+
nxdl_path="/ENTRY/INSTRUMENT/ENVIRONMENT/voltage_controller", elem=elem
88+
)
89+
assert len(elist) == 4
90+
91+
(_, _, elist) = nexus.get_inherited_nodes(
92+
nxdl_path="/ENTRY/INSTRUMENT/ENVIRONMENT/voltage_controller",
93+
nx_name="NXiv_temp",
94+
)
95+
assert len(elist) == 4
96+
97+
98+
@pytest.mark.parametrize(
99+
"hdf_name,concept_name,should_fit",
100+
[
101+
("source_pump", "sourceType", False),
102+
("source_pump", "sourceTYPE", True),
103+
("source pump", "sourceTYPE", False),
104+
("source", "sourceTYPE", False),
105+
("source123", "SOURCE", True),
106+
("1source", "SOURCE", True),
107+
("_source", "SOURCE", True),
108+
("same_name", "same_name", True),
109+
("angular_energy_resolution", "angularNresolution", True),
110+
("angularresolution", "angularNresolution", False),
111+
("Name with some whitespaces in it", "ENTRY", False),
112+
("simple_name", "TEST", True),
113+
(".test", "TEST", False),
114+
],
115+
)
116+
def test_namefitting(hdf_name, concept_name, should_fit):
117+
"""Test namefitting of nexus concept names"""
118+
if should_fit:
119+
assert nexus.get_nx_namefit(hdf_name, concept_name, name_partial=True) > -1
120+
else:
121+
assert nexus.get_nx_namefit(hdf_name, concept_name, name_partial=True) == -1
122+
123+
124+
@pytest.mark.parametrize(
125+
"hdf_name,concept_name, score",
126+
[
127+
("test_name", "TEST_name", 9),
128+
("te_name", "TEST_name", 7),
129+
("my_other_name", "TEST_name", 5),
130+
("test_name", "test_name", 18),
131+
("test_other", "test_name", -1),
132+
("my_fancy_yet_long_name", "my_SOME_name", 8),
133+
("something", "XXXX", 0),
134+
("something", "OTHER", 1),
135+
],
136+
)
137+
def test_namefitting_scores(hdf_name, concept_name, score):
138+
"""Test namefitting of nexus concept names"""
139+
assert nexus.get_nx_namefit(hdf_name, concept_name, name_partial=True) == score
140+
141+
142+
@pytest.mark.parametrize(
143+
"better_fit,better_ref,worse_fit,worse_ref",
144+
[
145+
("sourcetype", "sourceTYPE", "source_pump", "sourceTYPE"),
146+
("source_pump", "sourceTYPE", "source_pump", "TEST"),
147+
],
148+
)
149+
def test_namefitting_precedence(better_fit, better_ref, worse_fit, worse_ref):
150+
"""Test if namefitting follows proper precedence rules"""
151+
152+
assert nexus.get_nx_namefit(
153+
better_fit, better_ref, name_partial=True
154+
) > nexus.get_nx_namefit(worse_fit, worse_ref)
155+
156+
157+
@pytest.mark.parametrize(
158+
"string_obj, decode, expected",
159+
[
160+
# Test with lists of bytes and strings
161+
([b"bytes", "string"], True, ["bytes", "string"]),
162+
([b"bytes", "string"], False, [b"bytes", "string"]),
163+
([b"bytes", b"more_bytes", "string"], True, ["bytes", "more_bytes", "string"]),
164+
(
165+
[b"bytes", b"more_bytes", "string"],
166+
False,
167+
[b"bytes", b"more_bytes", "string"],
168+
),
169+
([b"fixed", b"length", b"strings"], True, ["fixed", "length", "strings"]),
170+
([b"fixed", b"length", b"strings"], False, [b"fixed", b"length", b"strings"]),
171+
# Test with nested lists
172+
([[b"nested1"], [b"nested2"]], True, [["nested1"], ["nested2"]]),
173+
([[b"nested1"], [b"nested2"]], False, [[b"nested1"], [b"nested2"]]),
174+
# Test with bytes
175+
(b"single", True, "single"),
176+
(b"single", False, b"single"),
177+
# Test with str
178+
("single", True, "single"),
179+
("single", False, "single"),
180+
# Test with int
181+
(123, True, 123),
182+
(123, False, 123),
183+
],
184+
)
185+
def test_decode_or_not(string_obj, decode, expected):
186+
# Handle normal cases
187+
result = nexus.decode_or_not(elem=string_obj, decode=decode)
188+
if isinstance(expected, list):
189+
assert isinstance(result, list), f"Expected list, but got {type(result)}"
190+
# Handle all other cases
191+
else:
192+
assert result == expected, f"Failed for {string_obj} with decode={decode}"

0 commit comments

Comments
 (0)