fix(sources): resolve arXiv IDs via arXiv DOI (OpenAlex /works/arxiv: 404s) #5
@@ -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:
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user