This repository provides User Manual for setting up a Docker environment tailored for testing DGTD code.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

247 lines
9.1 KiB

"""
.. autoclass:: BibliographyKey
:members:
.. autoclass:: BibliographyValue
:members:
.. autoclass:: BibliographyDirective
.. automethod:: run
"""
import ast # parse(), used for filter
from typing import TYPE_CHECKING, Dict, List, NamedTuple, cast
import docutils.nodes
import docutils.parsers.rst.directives as directives
import sphinx.util
from docutils.parsers.rst import Directive
from .bibfile import _make_ids, normpath_filename
from .nodes import bibliography as bibliography_node
if TYPE_CHECKING:
from sphinx.environment import BuildEnvironment
from .domain import BibtexDomain
logger = sphinx.util.logging.getLogger(__name__)
class BibliographyKey(NamedTuple):
"""Unique key for each bibliography directive."""
docname: str #: Name of the document where the bibliography resides.
id_: str #: The id of the bibliography node in the document.
class BibliographyValue(NamedTuple):
"""Contains information about a bibliography directive."""
line: int #: Line number of the directive in the document.
bibfiles: List[str] #: List of bib files for this directive.
style: str #: The pybtex style.
list_: str #: The list type.
enumtype: str #: The sequence type (for enumerated lists).
start: int #: The start of the sequence (for enumerated lists).
labelprefix: str #: String prefix for pybtex generated labels.
keyprefix: str #: String prefix for citation keys.
filter_: ast.AST #: Parsed filter expression.
citation_nodes: Dict[str, docutils.nodes.Element] #: key -> citation node
keys: List[str] #: Keys listed as content of the directive.
class BibliographyDirective(Directive):
"""Class for processing the :rst:dir:`bibliography` directive.
Produces a
:class:`~sphinxcontrib.bibtex.nodes.bibliography` node,
along with (empty) citation nodes that will be formatted later in the
*env-updated* stage, and inserted into the document in a post-transform.
We cannot insert the citation nodes here because we do not yet know
which keys have been cited.
.. seealso::
Further processing of the resulting
:class:`~sphinxcontrib.bibtex.nodes.bibliography` node is done
by
:class:`~sphinxcontrib.bibtex.transforms.BibliographyTransform`.
"""
required_arguments = 0
optional_arguments = 1
final_argument_whitespace = True
has_content = True
option_spec = {
"cited": directives.flag,
"notcited": directives.flag,
"all": directives.flag,
"filter": directives.unchanged,
"style": directives.unchanged,
"list": directives.unchanged,
"enumtype": directives.unchanged,
"start": (
lambda value: directives.positive_int(value) if value != "continue" else -1
),
"labelprefix": directives.unchanged,
"keyprefix": directives.unchanged,
}
def _get_filter(self):
"""Get parsed filter from options."""
env = cast("BuildEnvironment", self.state.document.settings.env)
if "filter" in self.options:
if "all" in self.options:
logger.warning(
":filter: overrides :all:",
location=(env.docname, self.lineno),
type="bibtex",
subtype="filter_overrides",
)
if "notcited" in self.options:
logger.warning(
":filter: overrides :notcited:",
location=(env.docname, self.lineno),
type="bibtex",
subtype="filter_overrides",
)
if "cited" in self.options:
logger.warning(
":filter: overrides :cited:",
location=(env.docname, self.lineno),
type="bibtex",
subtype="filter_overrides",
)
try:
return ast.parse(self.options["filter"])
except SyntaxError:
logger.warning(
"syntax error in :filter: expression"
+ " ("
+ self.options["filter"]
+ "); "
"the option will be ignored",
location=(env.docname, self.lineno),
type="bibtex",
subtype="filter_syntax_error",
)
return ast.parse("cited")
elif "all" in self.options:
return ast.parse("True")
elif "notcited" in self.options:
return ast.parse("not cited")
else:
# the default filter: include only cited entries
return ast.parse("cited")
def run(self):
"""Process .bib files, set file dependencies, and create a
node that is to be transformed to the entries of the
bibliography.
"""
env = cast("BuildEnvironment", self.state.document.settings.env)
domain = cast("BibtexDomain", env.get_domain("cite"))
filter_ = self._get_filter()
if self.arguments:
bibfiles = []
for bibfile in self.arguments[0].split():
normbibfile = normpath_filename(env, bibfile)
if normbibfile not in domain.bibdata.bibfiles:
logger.warning(
"{0} not found or not configured"
" in bibtex_bibfiles".format(bibfile),
location=(env.docname, self.lineno),
type="bibtex",
subtype="bibfile_error",
)
else:
bibfiles.append(normbibfile)
else:
bibfiles = list(domain.bibdata.bibfiles.keys())
for bibfile in bibfiles:
env.note_dependency(bibfile)
# generate nodes and ids
keyprefix: str = self.options.get("keyprefix", "")
list_: str = self.options.get("list", "citation")
if list_ not in {"bullet", "enumerated", "citation"}:
logger.warning(
"unknown bibliography list type '{0}'.".format(list_),
location=(env.docname, self.lineno),
type="bibtex",
subtype="list_type_error",
)
list_ = "citation"
if list_ in {"bullet", "enumerated"}:
citation_node_class = docutils.nodes.list_item
else:
citation_node_class = docutils.nodes.citation
bibliography_count = env.temp_data["bibtex_bibliography_count"] = (
env.temp_data.get("bibtex_bibliography_count", 0) + 1
)
ids = set(self.state.document.ids.keys())
node = bibliography_node(
"",
docname=env.docname,
ids=_make_ids(
docname=env.docname,
lineno=self.lineno,
ids=ids,
raw_id=env.app.config.bibtex_bibliography_id.format(
bibliography_count=bibliography_count
),
),
)
self.state.document.note_explicit_target(node, node)
# we only know which citations to included at resolve stage
# but we need to know their ids before resolve stage
# so for now we generate a node, and thus, an id, for every entry
citation_nodes: Dict[str, docutils.nodes.Element] = {
keyprefix
+ entry.key: citation_node_class(
ids=_make_ids(
docname=env.docname,
lineno=self.lineno,
ids=ids,
raw_id=env.app.config.bibtex_cite_id.format(
bibliography_count=bibliography_count, key=keyprefix + entry.key
),
)
)
for entry in domain.get_entries(bibfiles)
}
for citation_node in citation_nodes.values():
self.state.document.note_explicit_target(citation_node, citation_node)
# check and get keys
keys = []
for key in self.content:
if keyprefix + key not in citation_nodes:
logger.warning(
'could not find bibtex key "%s"' % key,
location=(env.docname, self.lineno),
type="bibtex",
subtype="key_not_found",
)
else:
keys.append(key)
# create bibliography object
bibliography = BibliographyValue(
line=self.lineno,
list_=list_,
enumtype=self.options.get("enumtype", "arabic"),
start=self.options.get("start", 1),
style=self.options.get("style", env.app.config.bibtex_default_style),
filter_=filter_,
labelprefix=self.options.get("labelprefix", ""),
keyprefix=keyprefix,
bibfiles=bibfiles,
citation_nodes=citation_nodes,
keys=keys,
)
bib_key = BibliographyKey(docname=env.docname, id_=node["ids"][0])
assert bib_key not in domain.bibliographies
domain.bibliographies[bib_key] = bibliography
return [node]