Source code for bibble.fields.field_substitutor

  1#!/usr/bin/env python3
  2"""
  3
  4"""
  5
  6# Imports:
  7from __future__ import annotations
  8
  9# ##-- stdlib imports
 10import datetime
 11import enum
 12import functools as ftz
 13import itertools as itz
 14import logging as logmod
 15import pathlib as pl
 16import re
 17import time
 18import types
 19import weakref
 20from uuid import UUID, uuid1
 21
 22# ##-- end stdlib imports
 23
 24# ##-- 3rd party imports
 25from jgdv import Mixin
 26import bibtexparser
 27import bibtexparser.model as model
 28from bibtexparser import middlewares as ms
 29from bibtexparser.middlewares.middleware import (BlockMiddleware,
 30                                                 LibraryMiddleware)
 31from jgdv.files.tags import SubstitutionFile
 32
 33# ##-- end 3rd party imports
 34
 35# ##-- 1st party imports
 36from bibble.util.mixins import ErrorRaiser_m, FieldMatcher_m
 37from bibble.util.middlecore import IdenBlockMiddleware
 38
 39# ##-- end 1st party imports
 40
 41# ##-- types
 42# isort: off
 43import abc
 44import collections.abc
 45from typing import TYPE_CHECKING, cast, assert_type, assert_never
 46from typing import Generic, NewType
 47# Protocols:
 48from typing import Protocol, runtime_checkable
 49# Typing Decorators:
 50from typing import no_type_check, final, override, overload
 51
 52if TYPE_CHECKING:
 53    from jgdv import Maybe
 54    from typing import Final
 55    from typing import ClassVar, Any, LiteralString
 56    from typing import Never, Self, Literal
 57    from typing import TypeGuard
 58    from collections.abc import Iterable, Iterator, Callable, Generator
 59    from collections.abc import Sequence, Mapping, MutableMapping, Hashable
 60
 61##--|
 62
 63# isort: on
 64# ##-- end types
 65
 66##-- logging
 67logging = logmod.getLogger(__name__)
 68##-- end logging
 69
[docs] 70@Mixin(ErrorRaiser_m, FieldMatcher_m) 71class FieldSubstitutor(IdenBlockMiddleware): 72 """ 73 For a given field(s), and a given jgdv.SubstitutionFile, 74 replace the field value as necessary in each entry. 75 76 If force_single_value is True, only the first replacement will be used, 77 others will be discarded 78 79 eg: for target=['tags'], 80 and subs({'AI': ['artificial_intelligence', 'agents', 'machine_learning']) 81 and entry(fields={'tags': 'ai'}) 82 will give: entry(fields={''tags': ['artificial_intelligence', 'agents', 'machine_learning']}) 83 84 """ 85 86 def __init__(self, *, fields:list[str], subs:SubstitutionFile, force_single_value:bool=False, **kwargs): 87 super().__init__(**kwargs) 88 match fields: 89 case list(): 90 self._target_fields = fields 91 case x: 92 raise TypeError(type(x)) 93 94 self._subs = subs 95 self._force_single_value = force_single_value 96 self.set_field_matchers(white=self._target_fields, black=[]) 97
[docs] 98 def transform_Entry(self, entry, library): 99 if self._subs is None or not bool(self._subs): 100 return [entry] 101 102 match self.match_on_fields(entry, library): 103 case model.Entry() as x: 104 return [x] 105 case Exception() as err: 106 return [self.make_error_block(entry, err)] 107 case x: 108 raise TypeError(type(x))
109
[docs] 110 def field_h(self, field, entry): 111 """ """ 112 match field.value: 113 case str() as value if self._force_single_value: 114 head, *_ = list(self._subs.sub(value)) 115 return [model.Field(field.key, head)] 116 case str() as value: 117 subs = list(self._subs.sub(value)) 118 return [model.Field(field.key, subs)] 119 case list() | set() as value: 120 result = self._subs.sub_many(*value) 121 return [model.Field(field.key, result)] 122 case value: 123 return ValueError("Unsupported replacement field value type", entry.key, type(value)) 124 125 return []