"""
Classes and methods to work with bib files.
.. autoclass:: BibFile
:members:
.. autofunction:: normpath_filename
.. autofunction:: parse_bibfile
.. autofunction:: process_bibfile
.. autofunction:: get_bibliography_entry
"""
import os.path
from typing import NamedTuple, Dict, Optional
from pybtex.database.input import bibtex
from pybtex.database import BibliographyData, Entry
import sphinx.util
from sphinx.environment import BuildEnvironment
logger = sphinx.util.logging.getLogger(__name__)
[docs]class BibFile(NamedTuple):
"""Contains information about a parsed bib file."""
mtime: float #: modification time of bib file when last parsed
data: BibliographyData #: parsed data from pybtex
[docs]def normpath_filename(env: BuildEnvironment, filename: str) -> str:
"""Return normalised path to *filename* for the given environment *env*."""
return os.path.normpath(env.relfn2path(filename.strip())[1])
[docs]def parse_bibfile(bibfilename: str, encoding: str) -> BibliographyData:
"""Parse *bibfilename* with given *encoding*, and return parsed data."""
parser = bibtex.Parser(encoding)
logger.info("parsing bibtex file {0}... ".format(bibfilename), nonl=True)
parser.parse_file(bibfilename)
logger.info("parsed {0} entries"
.format(len(parser.data.entries)))
return parser.data
[docs]def process_bibfile(bibfiles: Dict[str, BibFile],
bibfilename: str, encoding: str) -> None:
"""Check if *bibfiles* is still up to date. If not, parse
*bibfilename* and store parsed data in *bibfiles*.
"""
try:
mtime = os.path.getmtime(bibfilename)
except OSError:
logger.warning(
"could not open bibtex file {0}.".format(bibfilename))
return
# get cache and check if it is still up to date
# if it is not up to date, parse the bibtex file
# and store it in the cache
logger.info("checking for {0} in bibtex cache... ".format(bibfilename),
nonl=True)
try:
bibfile = bibfiles[bibfilename]
except KeyError:
logger.info("not found")
bibfiles[bibfilename] = BibFile(
mtime=mtime, data=parse_bibfile(bibfilename, encoding))
else:
if mtime != bibfile.mtime:
logger.info("out of date")
bibfiles[bibfilename] = BibFile(
mtime=mtime, data=parse_bibfile(bibfilename, encoding))
else:
logger.info('up to date')
[docs]def get_bibliography_entry(
bibfiles: Dict[str, BibFile], key: str) -> Optional[Entry]:
"""Return bibliography entry from *bibfiles* for the given *key*."""
for bibfile in bibfiles.values():
try:
return bibfile.data.entries[key]
except KeyError:
pass
else:
logger.warning("could not find bibtex key {0}.".format(key))
return None