adsfolio.pdf.identity

Verify that a PDF is the paper that metadata claims it is.

Independent verification requires the PDF’s own identifiers to agree with the provider record. A unique ADS bibcode in a filename can also associate a PDF with a record. That association is not independent verification.

exception adsfolio.pdf.identity.IdentityConflictError[source]

Bases: adsfolio.pdf.identity.IdentityValidationError

The PDF’s own identifiers contradict the proposed metadata.

exception adsfolio.pdf.identity.IdentityValidationError[source]

Bases: ValueError

The PDF may not be associated with the proposed metadata.

class adsfolio.pdf.identity.PdfOwnIdentity(sha256, size, mtime_ns, arxiv_ids, dois, errors=(), warnings=(), article_dois=frozenset({}), subject_dois=frozenset({}))[source]

Bases: object

Identifiers a PDF states about itself, with the file state they came from.

Notes

‘sha256’ is the hash of the bytes that were parsed, and ‘size’ and ‘mtime_ns’ are the file state observed before and after reading (a change between the two raises instead of returning). ‘arxiv_ids’ holds version-free, lower-case arXiv identifiers, including arXiv DOIs. ‘dois’ holds lower-case DOIs from document metadata, never from body text. article_dois and subject_dois hold unverified metadata claims. They can reject a conflict. Page and provider evidence must corroborate them before they establish identity. ‘errors’ lists all parser failures. ‘warnings’ identifies unreadable optional metadata sources. These do not invalidate readable identifiers from other sources. ‘blocking_errors’ excludes those warnings.

Parameters:
article_dois: frozenset[str] = frozenset({})
arxiv_ids: frozenset[str]
property blocking_errors: tuple[str, ...][source]

Return failures that prevent automatic identity verification.

dois: frozenset[str]
errors: tuple[str, ...] = ()
property has_identifier: bool[source]

Whether the PDF states at least one identifier.

property identifiers: frozenset[tuple[str, str]][source]

Own identifiers as (‘arxiv’ | ‘doi’, value) pairs.

mtime_ns: int
sha256: str
size: int
subject_dois: frozenset[str] = frozenset({})
warnings: tuple[str, ...] = ()
class adsfolio.pdf.identity.TargetIdentity(primary, bibcodes, dois, arxiv_ids)[source]

Bases: object

Canonical aliases and primary identity of a metadata record.

‘primary’ is ‘bibcode:<bibcode>’ for ADS records, ‘ids:<sorted DOIs and arXiv ids>’ for other records with stable identifiers, and ‘bib:<title>|<authors>|<year>’ for hand-entered records with none. Only the first two are stable identities; the last identifies manual input.

Parameters:
arxiv_ids: frozenset[str]
bibcodes: frozenset[str]
claims_outside(authority)[source]

List claimed identifiers (and the primary) that ‘authority’ does not list.

Parameters:

authority (adsfolio.pdf.identity.TargetIdentity)

Return type:

list[str]

dois: frozenset[str]
property fingerprint: str[source]

Digest of the primary identity and every claimed identifier.

Any change to a claimed bibcode, DOI or arXiv id changes the digest, so a stored proof cannot be reused for an edited record. ‘’ if the record has no identity at all.

property has_stable_identity: bool[source]

Whether the record names a bibcode, DOI or arXiv identifier.

primary: str
exception adsfolio.pdf.identity.UnverifiedIdentityError(message, *, manually_confirmable=False, candidate_bibcode=None, candidate_sha256=None, candidate_fingerprint=None)[source]

Bases: adsfolio.pdf.identity.IdentityValidationError

The PDF’s own identifiers do not confirm the proposed metadata.

Parameters:
  • message (str) – Failed identity check and recovery instructions.

  • manually_confirmable (bool) – Whether explicit review can establish the association.

  • candidate_bibcode (str or None) – Proposed ADS record for review. This does not establish PDF identity.

  • candidate_sha256 (str or None) – Content hash of the PDF inspected for this candidate.

  • candidate_fingerprint (str or None) – Identifier fingerprint of the exact proposed record.

Return type:

None

Notes

The ‘manually_confirmable’ attribute is True only when a person can settle the question: the PDF states no identifier of its own, or the metadata claims no stable identifier. It is False when the PDF names itself and the provider does not corroborate that identifier. Manual confirmation cannot override that.

adsfolio.pdf.identity.assert_mutation_allowed(pdf_path, metadata, *, expected_sha256=None, manual_confirmation=False, manual_conflict_override=False, provider_document=None, filename_bibcode=None, allow_filename_association=False)[source]

Allow associating a PDF with ‘metadata’ only if the PDF vouches for it.

The current file is always hashed and parsed. A new ‘verified’ result needs an authoritative ‘provider_document’ (the ADS or external-provider record) that explicitly lists every identifier ‘metadata’ claims (its primary bibcode may be an alternate bibcode of the provider record) and whose aliases include every identifier the PDF states. Aliases that appear only in ‘metadata’ (for example secondary fields merged into a legacy record) never verify anything. Without a provider document, a stored ‘verified’ proof bound to this file hash and every identifier this record claims is accepted; otherwise the result is unverified.

Unreadable optional XMP produces a recorded warning. Readable matching identifiers or corroborated document DOI evidence can still verify the record. Every readable own identifier must agree, including identifiers recovered from XMP.

Ordinary manual confirmation admits an otherwise unverified identity. A separate conflict override records explicit review of a provider result against the PDF. Its proof applies only to the same PDF and identifiers.

Parameters:
  • pdf_path (pathlib.Path) – PDF to check.

  • metadata (collections.abc.Mapping[str, Any]) – Proposed record (ADS document, external record, or library entry).

  • expected_sha256 (str | None) – Hash the caller believes the file has; a different current hash raises.

  • manual_confirmation (bool) – The user explicitly confirmed an otherwise unverified association.

  • manual_conflict_override (bool) – The user reviewed a conflict with the provider record. Requires the expected PDF hash and a provider document for the chosen record.

  • provider_document (collections.abc.Mapping[str, Any] | None) – Fresh record from the authoritative provider for ‘metadata’.

  • filename_bibcode (str | None) – Exact bibcode to check against the filename and provider record.

  • allow_filename_association (bool) – Permit a filename association after a complete, unique ADS lookup.

Returns:

Provenance for ‘IDENTITY_KEY’, including its status, file hash, record fingerprint, and PDF identifiers. A filename association is not an independently verified identity.

Return type:

dict

Raises:
  • IdentityConflictError – A PDF identifier contradicts the provider (or, without a provider, the metadata).

  • UnverifiedIdentityError – The PDF does not confirm the record; see ‘manually_confirmable’.

  • IdentityValidationError – The file is unreadable, changed, differs from ‘expected_sha256’, or ‘provider_document’ is not the record named by ‘metadata’.

adsfolio.pdf.identity.extract_pdf_own_identity(path)[source]

Read the identifiers a PDF states about itself.

The file is read once; the hash and every parsed identifier come from the same bytes. Sources are the arXiv margin stamp on page 1, DocInfo DOI keys, a complete labeled DOI in DocInfo Title, and XMP prism:doi / dc:identifier. Publisher article DOI claims are kept separately until corroborated. DOIs printed in the body (often references) are never used alone. Parser failures remain in errors. Unreadable optional XMP is also listed in warnings. It does not invalidate readable identifiers or corroborated document DOI evidence.

Raises:

IdentityValidationError – If the file cannot be read or changed while it was read.

Parameters:

path (pathlib.Path)

Return type:

adsfolio.pdf.identity.PdfOwnIdentity

adsfolio.pdf.identity.target_identity(metadata)[source]

Collect canonical aliases from the identifier fields of a metadata record.

Reads bibcode, alternate_bibcode, identifier, doi, arxiv and arxiv_id. Nothing else (title, authors, file name) is consulted.

Parameters:

metadata (collections.abc.Mapping[str, Any])

Return type:

adsfolio.pdf.identity.TargetIdentity