From c8b127bae0585f10bd1a9e0c95816e366bb181a5 Mon Sep 17 00:00:00 2001 From: Tarik Moussa Date: Sun, 7 Jun 2026 23:14:52 +0200 Subject: [PATCH] fix(sources): resolve arXiv IDs via arXiv DOI (OpenAlex /works/arxiv: 404s) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenAlex' /works/{id}-Pfad akzeptiert die arxiv:-Namespace-Form nicht (404). _resolve_id löst arXiv-IDs jetzt auf die arXiv-DOI 10.48550/arXiv. auf — empirisch verifiziert für modern (1005.2698) und legacy (math/0603097) IDs. Behebt D-03 (Erstingest: alle Metadaten leer, weil fetch_paper 404 gab). - _resolve_id: arxiv:-Präfix strippen, arXiv-DOI bauen - Tests: arxiv (modern/legacy/prefixed) erwarten jetzt doi:10.48550/arXiv.* - 19 Tests grün, ruff + mypy sauber Co-Authored-By: Claude Opus 4.8 --- codex/sources/openalex.py | 14 +++++++++----- tests/sources/test_openalex.py | 18 ++++++++++++++---- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/codex/sources/openalex.py b/codex/sources/openalex.py index a7f2961..80b5307 100644 --- a/codex/sources/openalex.py +++ b/codex/sources/openalex.py @@ -25,17 +25,21 @@ _BASE = "https://api.openalex.org" def _resolve_id(id: str) -> str: - """Prefix a bare DOI or arXiv ID for the OpenAlex /works/ endpoint. + """Resolve a bare DOI or arXiv ID for the OpenAlex /works/ endpoint. - OpenAlex accepts: doi:, arxiv:, W-IDs, or full https:// URLs. - Bare DOIs (starting with "10.") and bare arXiv IDs require a prefix. + OpenAlex resolves DOIs, W-IDs and full https:// URLs in the /works/ path, + but NOT the ``arxiv:`` namespace (that path 404s). arXiv papers are looked + up via their arXiv DOI ``10.48550/arXiv.`` instead — accepted by + OpenAlex for both modern (``2301.07041``) and legacy (``math/0603097``) IDs. """ s = id.strip() - if s.startswith(("W", "https://", "doi:", "arxiv:")): + if s.lower().startswith("arxiv:"): + s = s[len("arxiv:") :] + if s.startswith(("W", "https://", "doi:")): return s if s.startswith("10."): return f"doi:{s}" - return f"arxiv:{s}" + return f"doi:10.48550/arXiv.{s}" def _is_retryable(exc: BaseException) -> bool: diff --git a/tests/sources/test_openalex.py b/tests/sources/test_openalex.py index d9658c8..d481aa2 100644 --- a/tests/sources/test_openalex.py +++ b/tests/sources/test_openalex.py @@ -45,7 +45,18 @@ def test_resolve_doi() -> None: def test_resolve_arxiv() -> None: - assert _resolve_id("2301.07041") == "arxiv:2301.07041" + # arXiv IDs resolve to their arXiv DOI (the arxiv: path 404s on OpenAlex). + assert _resolve_id("2301.07041") == "doi:10.48550/arXiv.2301.07041" + + +def test_resolve_arxiv_legacy() -> None: + # Legacy category/number IDs (pre-2007) resolve the same way. + assert _resolve_id("math/0603097") == "doi:10.48550/arXiv.math/0603097" + + +def test_resolve_arxiv_prefix_stripped() -> None: + # An explicit arxiv: prefix is stripped before building the arXiv DOI. + assert _resolve_id("arxiv:2301.07041") == "doi:10.48550/arXiv.2301.07041" def test_resolve_w_id_unchanged() -> None: @@ -54,7 +65,6 @@ def test_resolve_w_id_unchanged() -> None: def test_resolve_prefixed_unchanged() -> None: assert _resolve_id("doi:10.1145/x") == "doi:10.1145/x" - assert _resolve_id("arxiv:2301.07041") == "arxiv:2301.07041" # --------------------------------------------------------------------------- @@ -83,7 +93,7 @@ def test_fetch_paper_success(monkeypatch: pytest.MonkeyPatch) -> None: def test_fetch_paper_arxiv_id_prefixed(monkeypatch: pytest.MonkeyPatch) -> None: - """Bare arXiv IDs are prefixed with 'arxiv:' in the URL.""" + """Bare arXiv IDs are resolved to their arXiv DOI in the URL.""" called_url: list[str] = [] def mock_get(url: str, params: dict[str, str] | None = None) -> httpx.Response: @@ -93,7 +103,7 @@ def test_fetch_paper_arxiv_id_prefixed(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(openalex, "_get", mock_get) openalex.fetch_paper("2301.07041") - assert called_url and "arxiv:2301.07041" in called_url[0] + assert called_url and "doi:10.48550/arXiv.2301.07041" in called_url[0] # --------------------------------------------------------------------------- -- 2.49.1