Librarian: stop warning about missing netrc when mailto is set via env
CI / compile (pull_request) Successful in 9s
CI / unit (pull_request) Successful in 18s
CI / integration (pull_request) Successful in 25s

Librarian.__init__ reads the Crossref contact from CONJURER_CROSSREF_MAILTO,
then tries to override it from a 'crossref' netrc entry. When no netrc is
mounted (the normal container setup - default /root/.netrc) the read raises
FileNotFoundError and it logged 'Crossref credentials missing in netrc ...'
on EVERY search, even though the env var was set and used. Pure noise.

Only warn when there is genuinely no contact from either source (env unset
AND netrc unreadable) - which is also the case that then raises. When the
env var is set, a missing netrc is expected and logged at debug.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 17:42:05 +02:00
parent 44b7298a15
commit 711ce8c0c1
2 changed files with 71 additions and 4 deletions
+19 -4
View File
@@ -212,6 +212,9 @@ class Librarian(object):
- search_result_from_cr: A dictionary to store the search results from Crossref.
- done: A flag indicating if the search is done.
"""
# Crossref only needs a contact mailto. It can come from
# CONJURER_CROSSREF_MAILTO (the usual container setup) OR from a
# "crossref" entry in the netrc; netrc takes precedence when present.
mailto_contact: Optional[str] = os.getenv("CONJURER_CROSSREF_MAILTO")
if netrc:
try:
@@ -219,10 +222,22 @@ class Librarian(object):
auth_tokens = netrc_mod.authenticators("crossref")
if auth_tokens:
mailto_contact = auth_tokens[0]
except (FileNotFoundError, netrc.NetrcParseError):
logging.getLogger("conjurer_librarian").warning(
"Crossref credentials missing in netrc %s", NETRC_FILE
)
except (FileNotFoundError, netrc.NetrcParseError) as exc:
# A missing/unreadable netrc is NORMAL when the mailto is set via
# env - don't cry wolf on every single search. Only warn when we
# genuinely have no contact from either source.
_log = logging.getLogger("conjurer_librarian")
if mailto_contact:
_log.debug(
"netrc %s not used (%s) - using CONJURER_CROSSREF_MAILTO",
NETRC_FILE, exc,
)
else:
_log.warning(
"Crossref contact not configured: netrc %s unreadable (%s) "
"and CONJURER_CROSSREF_MAILTO unset",
NETRC_FILE, exc,
)
if not mailto_contact:
raise RuntimeError(
"Crossref credentials not configured. Set CONJURER_CROSSREF_MAILTO or add to netrc."
@@ -0,0 +1,52 @@
"""Integration: the librarian's Crossref-contact resolution.
CONJURER_CROSSREF_MAILTO alone is a valid, complete configuration. A missing
netrc must NOT produce a "credentials missing" warning in that case - the old
code warned on every single search even though the env var was set and used.
Only a genuine absence of any contact should warn (and then raise).
"""
import logging
import sys
import types
import pytest
if "habanero" not in sys.modules:
_habanero = types.ModuleType("habanero")
_habanero.Crossref = object
sys.modules["habanero"] = _habanero
import conjurer_librarian as lib # noqa: E402
class _DummyCrossref:
"""Accepts the kwargs the real Crossref does, so Librarian() can construct."""
def __init__(self, **kwargs):
self.kwargs = kwargs
@pytest.fixture(autouse=True)
def _crossref_and_missing_netrc(monkeypatch):
# Build with a harmless Crossref, and force the netrc read to miss so the
# env-var path is what's exercised.
monkeypatch.setattr(lib, "Crossref", _DummyCrossref)
monkeypatch.setattr(lib, "NETRC_FILE", "/nonexistent/conjurer/.netrc")
def test_env_mailto_alone_does_not_warn(monkeypatch, caplog):
monkeypatch.setenv("CONJURER_CROSSREF_MAILTO", "mtuszowski@example.com")
with caplog.at_level(logging.WARNING, logger="conjurer_librarian"):
librarian = lib.Librarian(lib.app, "kwas foliowy", "uuid-1", False)
assert librarian.uuid == "uuid-1" # constructed fine
assert not any(
"credentials missing" in r.getMessage().lower()
or "not configured" in r.getMessage().lower()
for r in caplog.records
), "a missing netrc must not warn when CONJURER_CROSSREF_MAILTO is set"
def test_no_contact_anywhere_raises(monkeypatch):
monkeypatch.delenv("CONJURER_CROSSREF_MAILTO", raising=False)
with pytest.raises(RuntimeError):
lib.Librarian(lib.app, "kwas foliowy", "uuid-2", False)