From 12247ab2a21623497013be323f4e80011cee4722 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 17:02:00 +0000 Subject: [PATCH] lookup(): trim the prefix and remainder as MATLAB does getOntologyNameFromPrefix (ontology.m:493) strtrims both halves of the lookup string before mapping the prefix and calling lookupTermOrID. Python matched the untrimmed prefix and passed the untrimmed remainder, so 'CL: 0000540' and ' CL:0000540' failed in Python and resolved in MATLAB. The cache stays keyed on the string as typed, as MATLAB's is. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UaMQ8AiBeK9Bj7tZ7U1CS2 --- src/ndi_ontology/__init__.py | 7 +++++-- src/ndi_ontology/ndi_matlab_python_bridge.yaml | 8 ++++++++ tests/test_ontology.py | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/ndi_ontology/__init__.py b/src/ndi_ontology/__init__.py index 9d09800..81cca1c 100644 --- a/src/ndi_ontology/__init__.py +++ b/src/ndi_ontology/__init__.py @@ -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() @@ -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 diff --git a/src/ndi_ontology/ndi_matlab_python_bridge.yaml b/src/ndi_ontology/ndi_matlab_python_bridge.yaml index 4219709..4c695b9 100644 --- a/src/ndi_ontology/ndi_matlab_python_bridge.yaml +++ b/src/ndi_ontology/ndi_matlab_python_bridge.yaml @@ -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 diff --git a/tests/test_ontology.py b/tests/test_ontology.py index 00c9241..e17bc46 100644 --- a/tests/test_ontology.py +++ b/tests/test_ontology.py @@ -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.