From d4d273da51cf6adc3c2dd80bc71c7a27877282f9 Mon Sep 17 00:00:00 2001 From: bsesic Date: Wed, 17 Jun 2026 14:59:28 +0200 Subject: [PATCH] fix(io/sefaria): use 'hebrew' language token instead of 'he' in API URLs Sefaria's v3 API expects the version-query language as a full name (e.g. 'hebrew', 'english', 'french'), not the ISO code. Passing 'he|' silently returns an empty 'versions' array with warning code 102, which then trips _select_version's 'response has no versions' ValueError when the importer is exercised against live Sefaria. The unit tests caught nothing because every existing test mocks _fetch_json and never exercises the URL construction path against the real API. Add two URL-builder tests that pin the language token to 'hebrew' and reject the ISO-code form. Discovered while building the Mishna Avot demo in tracealign-demos. --- src/tracealign/io/sefaria.py | 9 +++++++-- tests/io/test_sefaria_fetch.py | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/tracealign/io/sefaria.py b/src/tracealign/io/sefaria.py index 8443e03..8163df8 100644 --- a/src/tracealign/io/sefaria.py +++ b/src/tracealign/io/sefaria.py @@ -52,11 +52,16 @@ def _fetch_json(url: str) -> dict: def _build_url(ref: str, version: str | None) -> str: - """Build the v3 API URL for a reference, optionally pinning a version.""" + """Build the v3 API URL for a reference, optionally pinning a version. + + The Sefaria v3 API accepts ``version=<language>|<versionTitle>``. The + language token must be the full name (``hebrew``), not the ISO code + (``he``); the API silently returns an empty versions array otherwise. + """ encoded_ref = quote(ref.replace(" ", "_"), safe="._") url = f"{SEFARIA_API_BASE}/{encoded_ref}" if version is not None: - url += f"?version=he|{quote(version, safe='')}" + url += f"?version=hebrew|{quote(version, safe='')}" return url diff --git a/tests/io/test_sefaria_fetch.py b/tests/io/test_sefaria_fetch.py index e845bd9..e3586dc 100644 --- a/tests/io/test_sefaria_fetch.py +++ b/tests/io/test_sefaria_fetch.py @@ -30,3 +30,20 @@ def test_fetch_json_uses_fixture(): result = sefaria._fetch_json("https://example.invalid/") assert result["ref"] == "Pirkei Avot 1" assert result["versions"][0]["versionTitle"].startswith("William Davidson") + + +def test_build_url_uses_hebrew_language_token(): + """Pin: Sefaria API requires 'hebrew|' not 'he|' for the version language.""" + url = sefaria._build_url("Pirkei Avot 1", version="Vilna Edition") + assert "version=hebrew%7CVilna" in url or "version=hebrew|Vilna" in url + # Negative: the ISO-code form must not slip back in + assert "version=he|" not in url + assert "version=he%7C" not in url + + +def test_build_url_without_version_has_no_query(): + url = sefaria._build_url("Pirkei Avot 1", version=None) + assert "?" not in url + assert url.startswith("https://www.sefaria.org/api/v3/texts/") + # The reference must be URL-safe: no raw whitespace ends up in the URL. + assert " " not in url