Source code for ipypublish.sphinx.gls.directives

""" define the bibglossary directive

Originally adapted from: https://github.com/mcmtroffaes/sphinxcontrib-bibtex
"""

import ast  # parse(), used for filter
import os.path  # getmtime()
import sphinx.util

from docutils.parsers.rst import Directive, directives
from sphinx.util.console import bold, standout

from ipypublish.bib2glossary import BibGlossDB
from ipypublish.sphinx.gls.nodes import BibGlossaryNode
from ipypublish.sphinx.gls.cache import BibliographyCache, BibfileCache


logger = sphinx.util.logging.getLogger(__name__)


[docs]class BibGlossaryDirective(Directive): """Class for processing the ``bibglossary`` directive. Parses the bibliography files, and produces a :class:`~ipypublish.sphinx.gls.nodes.BibGlossaryNode` node. .. seealso:: Further processing of the resulting :class:`~ipypublish.sphinx.gls.nodes.BibGlossaryNode` node is done by :class:`~ipypublish.sphinx.gls.transforms.BibGlossaryTransform`. """ required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True has_content = False option_spec = { "cited": directives.flag, "notcited": directives.flag, "all": directives.flag, "unsorted": directives.flag, "filter": directives.unchanged, "style": directives.unchanged, "encoding": directives.encoding, "keyprefix": directives.unchanged, } _allowed_styles = "list" _default_style = "list"
[docs] def run(self): """Process .bib files, set file dependencies, and create a node that is to be transformed to the entries of the glossary. """ env = self.state.document.settings.env # create id and cache for this node # this id will be stored with the node # and is used to look up additional data in env.bibgloss_cache # (implementation note: new_serialno only guarantees unique # ids within a single document, but we need the id to be # unique across all documents, so we also include the docname # in the id) id_ = "bibtex-bibglossary-%s-%s" % (env.docname, env.new_serialno("bibgloss")) # set filter option if "filter" in self.options: if "all" in self.options: logger.warning(standout(":filter: overrides :all:")) if "notcited" in self.options: logger.warning(standout(":filter: overrides :notcited:")) if "cited" in self.options: logger.warning(standout(":filter: overrides :cited:")) try: filter_ = ast.parse(self.options["filter"]) except SyntaxError: logger.warning( standout("syntax error in :filter: expression") + " (" + self.options["filter"] + "); " "the option will be ignored" ) filter_ = ast.parse("cited") elif "all" in self.options: filter_ = ast.parse("True") elif "notcited" in self.options: filter_ = ast.parse("not cited") else: # the default filter: include only cited entries filter_ = ast.parse("cited") style = self.options.get("style", env.app.config.bibgloss_default_style) if style not in self._allowed_styles: logger.warning( "style '{}' not in allowed styles, defaulting to '{}'".format( style, self._default_style ) ) style = self._default_style bibcache = BibliographyCache( style=style, unsorted=("unsorted" in self.options), filter_=filter_, encoding=self.options.get( "encoding", self.state.document.settings.input_encoding ), keyprefix=self.options.get("keyprefix", ""), labels={}, plurals={}, bibfiles=[], ) for bibfile in self.arguments[0].split(): # convert to normalized absolute path to ensure that the same file # only occurs once in the cache bibfile = os.path.normpath(env.relfn2path(bibfile.strip())[1]) # if the bibfile has been supplied with no extension, guess path bibfile = BibGlossDB().guess_path(bibfile) or bibfile self.process_bibfile(bibfile, bibcache.encoding) env.note_dependency(bibfile) bibcache.bibfiles.append(bibfile) env.bibgloss_cache.set_bibliography_cache(env.docname, id_, bibcache) return [BibGlossaryNode("", ids=[id_])]
[docs] def update_bibfile_cache(self, bibfile, mtime, encoding): """Parse *bibfile*, and store the parsed data, along with modification time *mtime*, in the bibtex cache. Parameters ---------- bibfile: str The bib file name. mtime: float The bib file's modification time. """ logger.info(bold("parsing bibtex file {0}... ".format(bibfile)), nonl=True) bibglossdb = BibGlossDB() bibglossdb.load(path=bibfile, encoding=encoding) logger.info("parsed {0} entries".format(len(bibglossdb))) env = self.state.document.settings.env env.bibgloss_cache.bibfiles[bibfile] = BibfileCache( mtime=mtime, data=bibglossdb )
[docs] def process_bibfile(self, bibfile, encoding): """Check if ``env.bibgloss_cache.bibfiles[bibfile]`` is still up to date. If not, parse the *bibfile*, and store parsed data in the bibtex cache. Parameters ---------- bibfile: str The bib file name. """ env = self.state.document.settings.env cache = env.bibgloss_cache.bibfiles # get modification time of bibfile try: mtime = os.path.getmtime(bibfile) except OSError: logger.warning(standout("could not open bibtex file {0}.".format(bibfile))) cache[bibfile] = BibfileCache( # dummy cache mtime=-float("inf"), data=BibGlossDB() ) 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( bold("checking for {0} in bibtex cache... ".format(bibfile)), nonl=True ) try: bibfile_cache = cache[bibfile] except KeyError: logger.info("not found") self.update_bibfile_cache(bibfile, mtime, encoding) else: if mtime != bibfile_cache.mtime: logger.info("out of date") self.update_bibfile_cache(bibfile, mtime, encoding) else: logger.info("up to date")