Source code for bibble.io.jinja_writer

  1#!/usr/bin/env python3
  2"""
  3
  4"""
  5from __future__ import annotations
  6# Imports:
  7
  8# ##-- stdlib imports
  9import datetime
 10import enum
 11import functools as ftz
 12import itertools as itz
 13import logging as logmod
 14import pathlib as pl
 15import re
 16import time
 17import collections
 18import contextlib
 19import hashlib
 20from copy import deepcopy
 21from uuid import UUID, uuid1
 22from weakref import ref
 23import atexit # for @atexit.register
 24import faulthandler
 25# ##-- end stdlib imports
 26
 27import jinja2
 28from .writer import BibbleWriter
 29from bibble import _interface as API
 30from . import _interface as API_W
 31from bibble.util.mixins import MiddlewareValidator_m
 32from bibble.model import MetaBlock
 33from bibble.util import PairStack
 34from bibtexparser.writer import BibtexFormat
 35
 36from ._util import Runner_m
 37
 38# ##-- types
 39# isort: off
 40# General
 41import abc
 42import collections.abc
 43import typing
 44import types
 45from typing import cast, assert_type, assert_never
 46from typing import Generic, NewType, Never
 47from typing import no_type_check, final, override, overload
 48# Protocols and Interfaces:
 49from typing import Protocol, runtime_checkable
 50# isort: on
 51# ##-- end types
 52
 53# ##-- type checking
 54# isort: off
 55if typing.TYPE_CHECKING:
 56    from typing import Final, ClassVar, Any, Self
 57    from typing import Literal, LiteralString
 58    from typing import TypeGuard
 59    from collections.abc import Iterable, Iterator, Callable, Generator
 60    from collections.abc import Sequence, Mapping, MutableMapping, Hashable
 61    from logmod import Logger
 62    from bibtexparser.model import Block
 63
 64    from jgdv import Maybe
 65
 66    from bibtexparser.library import Library
 67    from bibtexparser.writer import BibtexFormat
 68
 69    type Middleware = API.Middleware_p | API.BidirectionalMiddleware_p
 70## isort: on
 71# ##-- end type checking
 72
 73##-- logging
 74logging = logmod.getLogger(__name__)
 75##-- end logging
 76
 77# Vars:
 78NEWLINE            : Final[str]   = "\n"
 79DEFAULT_TEMPLATES  : Final[dict]  = {
 80    "lib"          : "lib.bib.jinja",
 81    "header"       : "header.bib.jinja",
 82    "entry"        : "entry.bib.jinja",
 83    "footer"       : "footer.bib.jinja",
 84}
 85DEFAULT_LOADER     : Final[jinja2.BaseLoader]  = jinja2.PackageLoader("bibble", "_templates")
 86
 87# Body:
 88
[docs] 89class JinjaWriter(BibbleWriter): 90 """ 91 Use jinja templates to write out bibtex 92 """ 93 _env : jinja2.Environment 94 _templates : dict[str, Maybe[jinja2.Template]] 95 96 def __init__(self, stack:PairStack, *, format:Maybe[BibtexFormat]=None, logger:Maybe[Logger]=None, templates:Maybe[pl.Path]=None) -> None: 97 x : Any 98 super().__init__(stack, format=format, logger=logger) 99 self._join_char = NEWLINE 100 match templates: 101 case str()|pl.Path() as x: 102 loaders = [jinja2.FileSystemLoader(pl.Path(x)), DEFAULT_LOADER] 103 case [*xs]: 104 loaders = [jinja2.FileSystemLoader(pl.Path(x)) for x in xs] 105 loaders.append(DEFAULT_LOADER) 106 case None: 107 loaders = [DEFAULT_LOADER] 108 self._env = jinja2.Environment( 109 loader=jinja2.ChoiceLoader(loaders), 110 autoescape=jinja2.select_autoescape(), 111 trim_blocks=True, 112 lstrip_blocks=True, 113 ) 114 self._env.filters['wrap'] = self._wrap_braces 115 self._templates = {} 116 self.update_templates(DEFAULT_TEMPLATES) 117
[docs] 118 def update_templates(self, templates:dict[str, Maybe[str]]) -> None: 119 for key, name in templates.items(): 120 match name: 121 case None: 122 self._templates[key] = None 123 case str() as x: 124 self._templates[key] = self._env.get_template(name)
125
[docs] 126 def make_header(self, library:Library, title:Maybe[str]=None) -> list[str]: 127 if self._templates['header'] is None: 128 return [] 129 return [self._templates['header'].render(title=title)]
130 135
[docs] 136 def make_lib(self, *, header:list[str], body:list[str], footer:list[str]) -> str: 137 if self._templates['lib'] is None: 138 return self._join_char.join([*header, *body, *footer]).strip() 139 140 return self._templates['lib'].render( 141 header=self._join_char.join(header), 142 body=self._join_char.join(body), 143 footer=self._join_char.join(footer), 144 ).strip()
145
[docs] 146 def visit_entry(self, block:Block) -> list[str]: 147 return [self._templates['entry'].render(entry_type=block.entry_type, 148 key=block.key, 149 available_keys=block.fields_dict.keys(), 150 entry=block.fields_dict, 151 )]
152 153
[docs] 154 def write(self, library, *, templates:Maybe[dict]=None, **kwargs) -> str: 155 x : Any 156 match templates: 157 case None: 158 pass 159 case dict() as x: 160 self.update_templates(templates) 161 return super().write(library, **kwargs)
162 163
[docs] 164 def _wrap_braces(self, val:str) -> str: 165 return "".join(["{", str(val), "}"])