Source code for sphinxcontrib.bibtex.style.referencing

import dataclasses
from abc import ABC

import pybtex.plugin
from pybtex.richtext import Text, Tag
from sphinxcontrib.bibtex.richtext import ReferenceInfo, BaseReferenceText
from sphinxcontrib.bibtex.style.template import names, sentence, join
from typing import (
    TYPE_CHECKING, Tuple, List, Union, Iterable, Type, Optional, Dict
)

if TYPE_CHECKING:
    from pybtex.database import Entry
    from pybtex.richtext import BaseText
    from pybtex.style import FormattedEntry
    from pybtex.style.names import BaseNameStyle
    from pybtex.style.template import Node


[docs]@dataclasses.dataclass class BaseReferenceStyle(ABC): """Base class for citation reference styles. For consistency, all subclasses of this class must be decorated as a :class:`dataclasses.dataclass`, and must provide a type annotation and default value for all attributes (unless ``init=False`` is used, in which case they can be initialized in :meth:`~dataclasses.dataclass.__post_init__`).). This allows client code to instantiate any reference style without needing to specify any arguments through the constructor. """ # see https://stackoverflow.com/a/59987363 as to why this is here def __post_init__(self): pass
[docs] def role_names(self) -> Iterable[str]: """Get list of role names supported by this style.""" raise NotImplementedError
[docs] def outer( self, role_name: str, children: List["BaseText"]) -> "Node": """Returns outer template for formatting the references.""" raise NotImplementedError
[docs] def inner(self, role_name: str) -> "Node": """Returns inner template for formatting the references.""" raise NotImplementedError
[docs]def format_references( style: BaseReferenceStyle, reference_text_class: Type[BaseReferenceText[ReferenceInfo]], role_name: str, references: Iterable[Tuple[ "Entry", "FormattedEntry", ReferenceInfo]], ) -> "BaseText": """Format the list of references according to the given role. First formats each reference using the style's :meth:`~BaseReferenceStyle.get_inner` method, then joins all these formatted references together using the style's :meth:`~BaseReferenceStyle.get_outer` method. """ children = [ style.inner(role_name).format_data( data=dict( entry=entry, formatted_entry=formatted_entry, reference_info=info, reference_text_class=reference_text_class, style=style)) for entry, formatted_entry, info in references] return style.outer(role_name, children).format()
[docs]@dataclasses.dataclass class BracketStyle: """A class which provides brackets, as well as separators and a function to facilitate formatting of the outer template. """ #: Left bracket. left: Union["BaseText", str] = '[' #: Right bracket. right: Union["BaseText", str] = ']' #: Separators used for outer template (i.e. in between references #: if multiple keys are referenced in a single citation). sep: Union["BaseText", str] = ', ' #: Separator for outer template, if only two items. sep2: Optional[Union["BaseText", str]] = None #: Separator for outer template, for last item if three or more items. last_sep: Optional[Union["BaseText", str]] = None
[docs] def outer( self, children: List["BaseText"], brackets=False, capfirst=False) -> "Node": """Creates an outer template with separators, adding brackets if requested, and capitalizing the first word if requested. """ return join[ self.left if brackets else '', sentence( capfirst=capfirst, add_period=False, sep=self.sep, sep2=self.sep2, last_sep=self.last_sep, )[children], self.right if brackets else '', ]
[docs]@dataclasses.dataclass class PersonStyle: """A class providing additional data and helper functions to facilitate formatting of person names. """ #: Plugin name of the style used for formatting person names. style: str = 'last' #: Plugin class instance used for formatting person names. #: Automatically initialised from :attr:`style`. style_plugin: "BaseNameStyle" = dataclasses.field(init=False) #: Whether or not to abbreviate first names. abbreviate: bool = True #: Separator between persons. sep: Union["BaseText", str] = ', ' #: Separator between persons, if only two persons. sep2: Optional[Union["BaseText", str]] = ' and ' #: Separator between persons, for last person if three or more persons. last_sep: Optional[Union["BaseText", str]] = ', and ' #: Abbreviation text if three or more persons. other: Optional[Union["BaseText", str]] = \ Text(' ', Tag('em', 'et al.')) def __post_init__(self): self.style_plugin = pybtex.plugin.find_plugin( 'pybtex.style.names', name=self.style)()
[docs] def names(self, role: str, full: bool) -> "Node": """Returns a template formatting the persons with correct separators and using the full person list if so requested. """ return names( role=role, sep=self.sep, sep2=self.sep2, last_sep=self.last_sep, other=None if full else self.other, )
[docs]@dataclasses.dataclass class GroupReferenceStyle(BaseReferenceStyle): """Composes a group of reference styles into a single consistent style.""" #: List of style types. styles: List[BaseReferenceStyle] \ = dataclasses.field(default_factory=list) #: Dictionary from role names to styles. #: Automatically initialized from :attr:`styles`. role_style: Dict[str, BaseReferenceStyle] \ = dataclasses.field(default_factory=dict) def __post_init__(self): super().__post_init__() self.role_style.update( (role_name, style) for style in self.styles for role_name in style.role_names() )
[docs] def role_names(self): return self.role_style.keys()
[docs] def outer(self, role_name: str, children: List["BaseText"]) -> "Node": """Gets the outer template associated with *role_name* in one of the :attr:`styles`. """ style = self.role_style[role_name] return style.outer(role_name, children)
[docs] def inner(self, role_name: str) -> "Node": """Gets the inner template associated with *role_name* in one of the :attr:`styles`. """ style = self.role_style[role_name] return style.inner(role_name)