Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/ndi_ontology/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,10 @@ def lookup(lookup_string: str) -> OntologyResult:
f"{lookup_string!r} has no ontology prefix; expected 'PREFIX:term'"
)

prefix, remainder = lookup_string.split(":", 1)
# MATLAB trims both halves before mapping the prefix or calling the
# provider (getOntologyNameFromPrefix, ontology.m:493), so ' CL : neuron'
# resolves there. The cache stays keyed on the string as typed, as MATLAB's is.
prefix, remainder = (part.strip() for part in lookup_string.split(":", 1))

prefix_map = _load_prefix_map()

Expand Down Expand Up @@ -274,7 +277,7 @@ def lookup(lookup_string: str) -> OntologyResult:
# NDI's subject tables name treatment columns with it, so a provider
# returning OLS's short_form or '' gave Python different columns from
# MATLAB for the same dataset.
result.prefix = prefix.strip()
result.prefix = prefix
result.synonyms = [
s
for item in result.synonyms
Expand Down
8 changes: 8 additions & 0 deletions src/ndi_ontology/ndi_matlab_python_bridge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ classes:
'taxonomy:9606'). NDI-python names treatment columns in its subject
tables from shortName, so the same dataset got different columns
in each language. The shared-case runner now asserts both fields.
FIXED 2026-10-21: lookup() now trims the prefix and the remainder
before mapping the prefix and calling the provider, as
getOntologyNameFromPrefix does (ontology.m:493, read at 44efcec).
Python used to match the untrimmed prefix and pass the untrimmed
remainder, so 'CL: 0000540' and ' CL:0000540' failed in Python and
resolved in MATLAB. The cache is still keyed on the string as typed,
as MATLAB's is. Python's str.strip() stands in for strtrim; the two
differ only on NUL characters, which strtrim also removes.

# --- Instance: lookupTermOrID ---
- name: lookupTermOrID
Expand Down
18 changes: 18 additions & 0 deletions tests/test_ontology.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,24 @@ def test_case_insensitive_prefix(self):
lookup("cl:1") # lowercase
assert mock_lt.called

def test_remainder_is_trimmed_before_the_provider_sees_it(self):
"""ontology.m:493 -- remainder = strtrim(...) before lookupTermOrID."""
from ndi_ontology import OntologyResult, lookup

with patch("ndi_ontology.providers.OLSProvider.lookup_term") as mock_lt:
mock_lt.return_value = OntologyResult(id="CL:0000540", name="neuron")
lookup("CL: 0000540 \t")
mock_lt.assert_called_once_with("0000540", "CL")

@pytest.mark.parametrize("typed", ["NDIC: 8", " NDIC:8", "NDIC :8", " ndic : 8 "])
def test_whitespace_around_prefix_or_term_resolves(self, typed):
"""ontology.m:493 trims both halves; the prefix before it is mapped."""
from ndi_ontology import lookup

result = lookup(typed)
assert result.name == "Experienced cricket hunting"
assert result.prefix == typed.split(":", 1)[0].strip()

def test_provider_exception_is_chained(self):
"""A provider error surfaces as NDIOntologyLookupError, cause intact.

Expand Down
Loading