Source code for bibble.metadata.key_locker

  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 copy import deepcopy
 21from uuid import UUID, uuid1
 22
 23# ##-- end stdlib imports
 24
 25# ##-- 3rd party imports
 26import bibtexparser
 27import bibtexparser.model as model
 28from bibtexparser import middlewares as ms
 29from bibtexparser.middlewares.middleware import (BlockMiddleware,
 30                                                 LibraryMiddleware)
 31from jgdv import Mixin, Proto
 32
 33# ##-- end 3rd party imports
 34
 35# ##-- 1st party imports
 36import bibble._interface as API
 37from . import _interface as MAPI
 38from bibble.util.middlecore import IdenBlockMiddleware
 39from bibble.util.mixins import ErrorRaiser_m
 40# ##-- end 1st party imports
 41
 42# ##-- types
 43# isort: off
 44import abc
 45import collections.abc
 46from typing import TYPE_CHECKING, cast, assert_type, assert_never
 47from typing import Generic, NewType
 48# Protocols:
 49from typing import Protocol, runtime_checkable
 50# Typing Decorators:
 51from typing import no_type_check, final, override, overload
 52
 53if TYPE_CHECKING:
 54    from jgdv import Maybe, Rx, RxStr
 55    from typing import Final
 56    from typing import ClassVar, Any, LiteralString
 57    from typing import Never, Self, Literal
 58    from typing import TypeGuard
 59    from collections.abc import Iterable, Iterator, Callable, Generator
 60    from collections.abc import Sequence, Mapping, MutableMapping, Hashable
 61
 62##--|
 63
 64# isort: on
 65# ##-- end types
 66
 67##-- logging
 68logging = logmod.getLogger(__name__)
 69##-- end logging
 70##--|
 71
[docs] 72@Proto(API.ReadTime_p) 73@Mixin(ErrorRaiser_m) 74class KeyLocker(IdenBlockMiddleware): 75 """ Ensure key/crossref consistency by: 76 removing unwanted chars in the key, 77 'locking' the key with a specific suffix (by default a '_'). 78 79 Also formats crossref values so they match. 80 Already locked keys are ignored. 81 82 __init__ takes: 83 - regex = the regex of chars to remove. 84 - sub = the substitute for removed chars 85 """ 86 87 def __init__(self, *, regex:Maybe[RxStr|Rx]=None, sub:Maybe[str]=None, lock_suffix:Maybe[str]=None, key_suffix:Maybe[RxStr|Rx]=None, **kwargs): 88 super().__init__(**kwargs) 89 self._remove_re : Rx = re.compile(regex or MAPI.KEY_CLEAN_RE) 90 self._key_suffix_re : Rx = re.compile(key_suffix or MAPI.KEY_SUFFIX_RE) 91 self._sub : str = sub or MAPI.KEY_SUB_CHAR 92 self._lock_suffix : str = MAPI.LOCK_SUFFIX 93 self._bad_lock : str = f"{self._lock_suffix}{self._lock_suffix}" 94
[docs] 95 def on_read(self): 96 Never()
97
[docs] 98 def transform_Entry(self, entry, library) -> list: 99 entry = deepcopy(entry) 100 entry.key = self.clean_key(entry.key) 101 match entry.get(MAPI.CROSSREF_K): 102 case None: 103 pass 104 case model.Field(value=value): 105 entry.set_field(model.Field(MAPI.CROSSREF_K, self.clean_key(value))) 106 107 return [entry]
108
[docs] 109 def clean_key(self, key:str) -> str: 110 """ Convert the entry key to a canonical form """ 111 if key.endswith(self._lock_suffix) and not key.endswith(self._bad_lock): 112 return key 113 114 # Remove bad chars 115 clean_key = self._remove_re.sub(self._sub, key) 116 # Enforce the correct suffix 117 clean_key = self._key_suffix_re.sub(self._lock_suffix, clean_key) 118 return clean_key