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 None: 102 loaders = [DEFAULT_LOADER] 103 case pl.Path() as x: 104 loaders = [jinja2.FileSystemLoader(x), DEFAULT_LOADER] 105 self._env = jinja2.Environment( 106 loader=jinja2.ChoiceLoader(loaders), 107 autoescape=jinja2.select_autoescape(), 108 trim_blocks=True, 109 lstrip_blocks=True, 110 ) 111 self._env.filters['wrap'] = self._wrap_braces 112 self._templates = {} 113 self.update_templates(DEFAULT_TEMPLATES) 114
[docs] 115 def update_templates(self, templates:dict[str, Maybe[str]]) -> None: 116 for key, name in templates.items(): 117 match name: 118 case None: 119 self._templates[key] = None 120 case str() as x: 121 self._templates[key] = self._env.get_template(name)
122
[docs] 123 def make_header(self, library:Library, title:Maybe[str]=None) -> list[str]: 124 if self._templates['header'] is None: 125 return [] 126 return [self._templates['header'].render(title=title)]
127 132
[docs] 133 def make_lib(self, *, header:list[str], body:list[str], footer:list[str]) -> str: 134 if self._templates['lib'] is None: 135 return self._join_char.join([*header, *body, *footer]).strip() 136 137 return self._templates['lib'].render( 138 header=self._join_char.join(header), 139 body=self._join_char.join(body), 140 footer=self._join_char.join(footer), 141 ).strip()
142
[docs] 143 def visit_entry(self, block:Block) -> list[str]: 144 return [self._templates['entry'].render(entry_type=block.entry_type, 145 key=block.key, 146 available_keys=block.fields_dict.keys(), 147 entry=block.fields_dict, 148 )]
149 150
[docs] 151 def write(self, library, *, templates:Maybe[dict]=None, **kwargs) -> str: 152 x : Any 153 match templates: 154 case None: 155 pass 156 case dict() as x: 157 self.update_templates(templates) 158 return super().write(library, **kwargs)
159 160
[docs] 161 def _wrap_braces(self, val:str) -> str: 162 return "".join(["{", str(val), "}"])