fix(sources): resolve arXiv IDs via arXiv DOI (OpenAlex /works/arxiv: 404s) #5

Merged
user2595 merged 1 commits from fix/openalex-arxiv-doi into main 2026-06-14 12:31:34 +00:00
2 changed files with 23 additions and 9 deletions
Showing only changes of commit c8b127bae0 - Show all commits

View File

@@ -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:<doi>, arxiv:<id>, 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.<id>`` 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:

View File

@@ -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]
# ---------------------------------------------------------------------------