Coverage for liitos/__init__.py: 86.81%
81 statements
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-11 15:51:33 +00:00
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-11 15:51:33 +00:00
1"""Splice (Finnish liitos) contributions."""
3import datetime as dti
4import logging
5import os
6import pathlib
7import shellingham # type: ignore
8from typing import Union, no_type_check
10# [[[fill git_describe()]]]
11__version__ = '2025.8.11+parent.gd451cf84'
12# [[[end]]] (checksum: 1f8e192582df6747d71dfe4747e7f6f9)
13__version_info__ = tuple(
14 e if '-' not in e else e.split('-')[0] for part in __version__.split('+') for e in part.split('.') if e != 'parent'
15)
17APP_ALIAS = str(pathlib.Path(__file__).parent.name)
18APP_ENV = APP_ALIAS.upper()
19APP_NAME = locals()['__doc__']
20DEBUG = bool(os.getenv(f'{APP_ENV}_DEBUG', ''))
21VERBOSE = bool(os.getenv(f'{APP_ENV}_VERBOSE', ''))
22QUIET = True # HACK A DID ACK - the new normal
23STRICT = bool(os.getenv(f'{APP_ENV}_STRICT', ''))
24ENCODING = 'utf-8'
25ENCODING_ERRORS_POLICY = 'ignore'
26DEFAULT_CONFIG_NAME = f'.{APP_ALIAS}.json'
28APP_VERSION = __version__
29COMMA = ','
30DEFAULT_LF_ONLY = 'YES'
31FILTER_CS_LIST = 'mermaid-filter'
32FROM_FORMAT_SPEC = 'markdown'
33LATEX_PAYLOAD_NAME = 'document.tex'
34log = logging.getLogger() # Module level logger is sufficient
35LOG_FOLDER = pathlib.Path('logs')
36LOG_FILE = f'{APP_ALIAS}.log'
37LOG_PATH = pathlib.Path(LOG_FOLDER, LOG_FILE) if LOG_FOLDER.is_dir() else pathlib.Path(LOG_FILE)
38LOG_LEVEL = logging.INFO
39LOG_SEPARATOR = '- ' * 80
41DEFAULT_STRUCTURE_NAME = 'structure.yml'
42KEY_APPROVALS = 'approvals'
43KEY_BIND = 'bind'
44KEY_CHANGES = 'changes'
45KEY_LAYOUT = 'layout'
46KEY_META = 'meta'
47KEYS_REQUIRED = (KEY_APPROVALS, KEY_BIND, KEY_CHANGES, KEY_META)
49CONTEXT: dict[str, str] = {}
50KNOWN_APPROVALS_STRATEGIES = ('south', 'east')
51APPROVALS_STRATEGY = os.getenv('LIITOS_APPROVALS_STRATEGY', '').lower()
53OptionsType = dict[str, Union[bool, str, None]]
54PathLike = Union[str, pathlib.Path]
55PathLikeOrBool = Union[PathLike, bool]
56ExternalsType = dict[str, dict[str, PathLikeOrBool]]
58try:
59 SHELL = shellingham.detect_shell()
60except shellingham.ShellDetectionFailure:
61 SHELL = ('', 'echo')
63TOOL_VERSION_COMMAND_MAP = {
64 'etiketti': {
65 'command': 'etiketti --version',
66 'banner': 'Label and document the pdf file (data protection and identity)',
67 },
68 'exiftool': {
69 'command': 'exiftool -ver',
70 'banner': 'Change and list EXIF attributes',
71 },
72 'foran': {
73 'command': 'foran version',
74 'banner': 'Inspect local git status (or detect that there is no repository)',
75 },
76 'git': {
77 'command': 'git --version',
78 'banner': 'Version control system (git)',
79 },
80 'liitos': {
81 'command': 'liitos version',
82 'banner': 'Process the markdown documentation to produce PDF',
83 },
84 'lualatex': {
85 'command': 'lualatex --version',
86 'banner': 'Process LaTeX to produce PDF',
87 },
88 'mermaid': {
89 'command': 'npm view mermaid',
90 'banner': 'Mermaid for rendering diagrams from textual representations',
91 },
92 'mermaid-filter': {
93 'command': 'npm view mermaid-filter',
94 'banner': 'Pandoc filter for mermaid diagrams (rasterized version for PDF)',
95 },
96 'navigaattori': {
97 'command': 'navigaattori version',
98 'banner': 'Discover publication structural information from tree',
99 },
100 'node': {
101 'command': 'node --version',
102 'banner': 'Node server for executing some tools',
103 },
104 'npm': {
105 'command': 'npm --version',
106 'banner': 'Node package manager for inspecting versions of some node based tools',
107 },
108 'pandoc': {
109 'command': 'pandoc --version',
110 'banner': 'Pandoc for transforming markdown to LaTeX',
111 },
112 'pandoc-filter': {
113 'command': 'npm view pandoc-filter',
114 'banner': 'Pandoc base filter for mermaid diagram tools (interface module to pandoc filtering)',
115 },
116 'pdfinfo': {
117 'command': 'pdfinfo -v',
118 'banner': 'Show PDF file information',
119 },
120 'puppeteer': {
121 'command': 'npm view puppeteer',
122 'banner': 'Headless browser driver used by svgexport',
123 },
124 'python': {
125 'command': 'python -V',
126 'banner': 'Python driving it all',
127 },
128 'shell': {
129 'command': f'{SHELL[1]} --version',
130 'banner': 'The shell under which this process executes',
131 },
132 'svgexport': {
133 'command': 'npm view svgexport',
134 'banner': 'Export SVG to PNG (rasterized version for inclusion in PDF)',
135 },
136 'taksonomia': {
137 'command': 'taksonomia --version',
138 'banner': 'Assess and document the inventory of folders and files',
139 },
140}
142ToolKey = str
144EXTERNALS: ExternalsType = {
145 'bookmatter': {
146 'id': 'templates/bookmatter.tex.in',
147 'is_custom': False,
148 },
149 'driver': {
150 'id': 'templates/driver.tex.in',
151 'is_custom': False,
152 },
153 'publisher': {
154 'id': 'templates/publisher.tex.in',
155 'is_custom': False,
156 },
157 'metadata': {
158 'id': 'templates/metadata.tex.in',
159 'is_custom': False,
160 },
161 'setup': {
162 'id': 'templates/setup.tex.in',
163 'is_custom': False,
164 },
165}
167BOOKMATTER_TEMPLATE = os.getenv('LIITOS_BOOKMATTER_TEMPLATE', '')
168if BOOKMATTER_TEMPLATE: 168 ↛ 169line 168 didn't jump to line 169 because the condition on line 168 was never true
169 EXTERNALS['bookmatter'] = {'id': BOOKMATTER_TEMPLATE, 'is_custom': True}
171DRIVER_TEMPLATE = os.getenv('LIITOS_DRIVER_TEMPLATE', '')
172if DRIVER_TEMPLATE: 172 ↛ 173line 172 didn't jump to line 173 because the condition on line 172 was never true
173 EXTERNALS['driver'] = {'id': DRIVER_TEMPLATE, 'is_custom': True}
175METADATA_TEMPLATE = os.getenv('LIITOS_METADATA_TEMPLATE', '')
176if METADATA_TEMPLATE: 176 ↛ 177line 176 didn't jump to line 177 because the condition on line 176 was never true
177 EXTERNALS['metadata'] = {'id': METADATA_TEMPLATE, 'is_custom': True}
179PUBLISHER_TEMPLATE = os.getenv('LIITOS_PUBLISHER_TEMPLATE', '')
180if PUBLISHER_TEMPLATE: 180 ↛ 181line 180 didn't jump to line 181 because the condition on line 180 was never true
181 EXTERNALS['publisher'] = {'id': PUBLISHER_TEMPLATE, 'is_custom': True}
183SETUP_TEMPLATE = os.getenv('LIITOS_SETUP_TEMPLATE', '')
184if SETUP_TEMPLATE: 184 ↛ 185line 184 didn't jump to line 185 because the condition on line 184 was never true
185 EXTERNALS['setup'] = {'id': SETUP_TEMPLATE, 'is_custom': True}
187TS_FORMAT_LOG = '%Y-%m-%dT%H:%M:%S'
188TS_FORMAT_PAYLOADS = '%Y-%m-%d %H:%M:%S.%f UTC'
190__all__: list[str] = [
191 'APP_ALIAS',
192 'APP_ENV',
193 'APP_VERSION',
194 'APPROVALS_STRATEGY',
195 'DEBUG',
196 'DEFAULT_STRUCTURE_NAME',
197 'ENCODING',
198 'EXTERNALS',
199 'ExternalsType',
200 'CONTEXT',
201 'FILTER_CS_LIST',
202 'FROM_FORMAT_SPEC',
203 'KEY_APPROVALS',
204 'KEY_BIND',
205 'KEY_CHANGES',
206 'KEY_LAYOUT',
207 'KEY_META',
208 'KEYS_REQUIRED',
209 'KNOWN_APPROVALS_STRATEGIES',
210 'LATEX_PAYLOAD_NAME',
211 'LOG_SEPARATOR',
212 'OptionsType',
213 'PathLike',
214 'QUIET',
215 'TOOL_VERSION_COMMAND_MAP',
216 'ToolKey',
217 'TS_FORMAT_PAYLOADS',
218 'log',
219 'parse_csl',
220]
223def parse_csl(csl: str) -> list[str]:
224 """DRY."""
225 return [fmt.strip() for fmt in csl.split(COMMA) if fmt.strip()]
228@no_type_check
229def formatTime_RFC3339(self, record, datefmt=None): # noqa
230 """HACK A DID ACK we could inject .astimezone() to localize ..."""
231 return dti.datetime.fromtimestamp(record.created, dti.timezone.utc).isoformat() # pragma: no cover
234@no_type_check
235def init_logger(name=None, level=None):
236 """Initialize module level logger"""
237 global log # pylint: disable=global-statement
239 log_format = {
240 'format': '%(asctime)s %(levelname)s [%(name)s]: %(message)s',
241 'datefmt': TS_FORMAT_LOG,
242 # 'filename': LOG_PATH,
243 'level': LOG_LEVEL if level is None else level,
244 }
245 logging.Formatter.formatTime = formatTime_RFC3339
246 logging.basicConfig(**log_format)
247 log = logging.getLogger(APP_ENV if name is None else name)
248 log.propagate = True
251init_logger(name=APP_ENV, level=logging.DEBUG if DEBUG else None)