From 913d614228a74b1c6ebbe1e582fab4e6e0311750 Mon Sep 17 00:00:00 2001 From: Robbie Court Date: Thu, 16 Jul 2026 19:08:47 +0000 Subject: [PATCH] Also clean stray escaping in synonyms and relationship labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clean_label was only applied to MinimalEntityInfo (Name/Symbol). Synonyms (Synonym.label) and relationship edge labels (MinimalEdgeInfo.label) can carry the same over-escaped apostrophe (e.g. a synonym "y5β\'2a"), so add __post_init__ cleaning to both. Extends the regression test to cover a synonym + a relationship label. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01RQZ8eMr398cK1bnzN2XTqK --- src/test/term_info_queries_test.py | 16 ++++++++++++++-- src/vfbquery/term_info_queries.py | 8 ++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/test/term_info_queries_test.py b/src/test/term_info_queries_test.py index f494c1e..7ed5f61 100644 --- a/src/test/term_info_queries_test.py +++ b/src/test/term_info_queries_test.py @@ -29,8 +29,16 @@ def test_label_stray_backslash_cleaned(self): "short_form": "VFB_jrchk0e3", "unique_facets": ["Adult"], "label": "MBON01(y5β\\'2a)_L"}, "description": [], "comment": []}, "query": "Get JSON for Individual", "version": "t", "parents": [], - "relationships": [], "xrefs": [], "anatomy_channel_image": [], - "pub_syn": [], "def_pubs": []} + "relationships": [{"relation": {"iri": "i", "label": "is part\\'of", + "type": "part_of"}, "object": {"symbol": "", "iri": "i", + "types": ["Class"], "short_form": "FBbt_1", "unique_facets": [], + "label": "adult brain"}}], + "xrefs": [], "anatomy_channel_image": [], + "pub_syn": [{"synonym": {"scope": "has_exact_synonym", + "label": "MBON01(y5β\\'2a)_syn", "type": ""}, + "pub": {"core": {"symbol": "", "iri": "i", "types": ["pub"], + "short_form": "Unattributed", "unique_facets": ["pub"], "label": ""}, + "FlyBase": "", "PubMed": "", "DOI": ""}}], "def_pubs": []} ''' terminfo = deserialize_term_info(terminfo_json) # backslash stripped, apostrophe and Greek beta preserved @@ -38,6 +46,10 @@ def test_label_stray_backslash_cleaned(self): self.assertNotIn("\\", terminfo.term.core.label) # and it flows clean into the internal link (used for Name/Meta rendering) self.assertNotIn("\\", terminfo.term.core.get_int_link()) + # synonyms and relationship (edge) labels are cleaned too + self.assertEqual("MBON01(y5β'2a)_syn", terminfo.pub_syn[0].synonym.label) + self.assertNotIn("\\", terminfo.pub_syn[0].synonym.label) + self.assertNotIn("\\", terminfo.relationships[0].relation.label) def test_term_info_deserialization(self): terminfo_json = """ diff --git a/src/vfbquery/term_info_queries.py b/src/vfbquery/term_info_queries.py index 98dc517..376d5fc 100644 --- a/src/vfbquery/term_info_queries.py +++ b/src/vfbquery/term_info_queries.py @@ -133,6 +133,9 @@ class MinimalEdgeInfo: confidence_value: Optional[str] = "" database_cross_reference: Optional[List[str]] = None + def __post_init__(self): + self.label = clean_label(self.label) + @dataclass_json @dataclass @@ -141,6 +144,11 @@ class Synonym: scope: Optional[str] = "" type: Optional[str] = "" + def __post_init__(self): + # Synonyms carry the same over-escaping artifact as labels (e.g. a + # synonym "y5B\\'2a"); clean at ingestion so search/term-info render clean. + self.label = clean_label(self.label) + def __str__(self): if self.scope: return re.sub(r"([^_A-Z])([A-Z])", r"\1 \2", self.scope).replace("has ", "") + ": " + self.label