1#!/usr/bin/env python3
2"""
3
4"""
5# Imports:
6from __future__ import annotations
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 types
18import collections
19import contextlib
20import hashlib
21from copy import deepcopy
22from uuid import UUID, uuid1
23from weakref import ref
24import atexit # for @atexit.register
25import faulthandler
26# ##-- end stdlib imports
27
28from pylatexenc.latexencode import (RULE_REGEX, UnicodeToLatexConversionRule,
29 UnicodeToLatexEncoder)
30from pylatexenc.latex2text import (LatexNodes2Text, MacroTextSpec,
31 get_default_latex_context_db)
32from pylatexenc.macrospec import LatexContextDb
33
34from bibtexparser import model
35
36from . import _interface as LAPI
37from jgdv import Mixin
38# ##-- types
39# isort: off
40import abc
41import collections.abc
42from typing import TYPE_CHECKING, cast, assert_type, assert_never
43from typing import Generic, NewType
44# Protocols:
45from typing import Protocol, runtime_checkable
46# Typing Decorators:
47from typing import no_type_check, final, override, overload
48# from dataclasses import InitVar, dataclass, field
49# from pydantic import BaseModel, Field, model_validator, field_validator, ValidationError
50
51if TYPE_CHECKING:
52 from jgdv import Maybe, VList
53 from typing import Final
54 from typing import ClassVar, Any, LiteralString
55 from typing import Never, Self, Literal
56 from typing import TypeGuard
57 from collections.abc import Iterable, Iterator, Callable, Generator
58 from collections.abc import Sequence, Mapping, MutableMapping, Hashable
59
60 type U2LRule = UnicodeToLatexConversionRule
61 type Entry = model.Field
62 type Block = model.Block
63 from bibtexparser.library import Library
64##--|
65
66# isort: on
67# ##-- end types
68
69##-- logging
70logging = logmod.getLogger(__name__)
71##-- end logging
72
73# Vars:
74
75# Body:
76
[docs]
77class UnicodeHelper_m:
78 """
79 A Helper for using pylatexenc
80
81 Builds Encoders using lists of str pairs
82 and Decoders using dicts
83 """
84
[docs]
85 @staticmethod
86 def prep_encode_tuples(tuples:list) -> list:
87 """ prep tuples of str,str for conversion rules,
88 as re.Pattern,str
89 """
90 result = []
91 for x,y in tuples:
92 assert(isinstance(y, str))
93 match x:
94 case str():
95 result.append((re.compile(x), y))
96 case re.Pattern():
97 result.append((x, y))
98 case x:
99 raise TypeError(type(x))
100 else:
101 return result
102
[docs]
103 @staticmethod
104 def build_encode_rule(tuples:list) -> U2LRule:
105 compiled = UnicodeHelper_m.prep_encode_tuples(tuples)
106 return UnicodeToLatexConversionRule(rule_type=RULE_REGEX, rule=compiled)
107
[docs]
108 @staticmethod
109 def build_decode_rule(pair:tuple) -> MacroTextSpec:
110 name, replacement = pair
111 return MacroTextSpec("url", simplify_repl="%s")
112
[docs]
113 @staticmethod
114 def build_encoder(*, rules:list[str|U2LRule], kwargs:dict) -> UnicodeToLatexEncoder:
115 if any(not isinstance(x, str|UnicodeToLatexConversionRule) for x in rules):
116 raise TypeError("Encoding Rules need to be UniicodeToLatexConversionRules, use LatexWriter.build_encode_rule")
117
118 rules.append(LAPI.DEFAULT_RULES_K)
119 logging.debug("Building Latex Encoder: %s", rules)
120 return UnicodeToLatexEncoder(conversion_rules=rules, **kwargs)
121
[docs]
122 @staticmethod
123 def build_decoder(*, rules:dict[str,VList[MacroTextSpec]], kwargs:dict) -> LatexNodes2Text:
124 context_db : LatexContextDb = get_default_latex_context_db()
125 logging.debug("Building Latex Decoder: %s", rules)
126 for cat, y in rules.items():
127 match y:
128 case list() if all(isinstance(y2, MacroTextSpec) for y2 in y):
129 context_db.add_context_category(cat, prepend=True, macros=y)
130 case MacroTextSpec():
131 context_db.add_context_category(cat, prepend=True, macros=[y])
132 case _:
133 raise TypeError("Bad Decode Rules Specified", cat, y)
134
135 return LatexNodes2Text(latex_context=context_db, **kwargs)
136
[docs]
137 def rebuild_encoder(self, *, rules:Maybe[list[U2LRule]]=None, **kwargs) -> None:
138 """ Accumulates rules and rebuilds the encoder """
139 self._total_rules += [x for x in (rules or []) if x not in self._total_rules]
140 self._total_options.update(kwargs)
141 self._encoder = self.build_encoder(rules=self._total_rules[:], kwargs=self._total_options)
142
[docs]
143 def rebuild_decoder(self, *, rules:dict=None, **kwargs) -> None:
144 self._total_rules.update(rules or {})
145 self._total_options.update(kwargs)
146 self._decoder = self.build_decoder(rules=self._total_rules, kwargs=self._total_options)
147
[docs]
148 def _test_encode(self, text) -> str:
149 """ utility to test latex encoding """
150 return self._encoder.unicode_to_latex(text)
151
[docs]
152 def _test_decode(self, text:str) -> str:
153 """ utility to test decoding """
154 return self._decoder.latex_to_text(text)