Coverage for liitos/__init__.py: 86.81%
81 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-25 15:36:16 +00:00
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-25 15:36:16 +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__ = '2024.11.25+parent.g7f0ffa27'
12# [[[end]]] (checksum: 983e3af0fbce1ef8cd388a26028e5ce0)
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 = False
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 'pdfinfo': {
113 'command': 'pdfinfo -v',
114 'banner': 'Show PDF file information',
115 },
116 'python': {
117 'command': 'python -V',
118 'banner': 'Python driving it all',
119 },
120 'shell': {
121 'command': f'{SHELL[1]} --version',
122 'banner': 'The shell under which this process executes',
123 },
124 'svgexport': {
125 'command': 'npm view svgexport',
126 'banner': 'Export SVG to PNG (rasterized version for inclusion in PDF)',
127 },
128 'taksonomia': {
129 'command': 'taksonomia --version',
130 'banner': 'Assess and document the inventory of folders and files',
131 },
132}
134ToolKey = str
136EXTERNALS: ExternalsType = {
137 'bookmatter': {
138 'id': 'templates/bookmatter.tex.in',
139 'is_custom': False,
140 },
141 'driver': {
142 'id': 'templates/driver.tex.in',
143 'is_custom': False,
144 },
145 'publisher': {
146 'id': 'templates/publisher.tex.in',
147 'is_custom': False,
148 },
149 'metadata': {
150 'id': 'templates/metadata.tex.in',
151 'is_custom': False,
152 },
153 'setup': {
154 'id': 'templates/setup.tex.in',
155 'is_custom': False,
156 },
157}
159BOOKMATTER_TEMPLATE = os.getenv('LIITOS_BOOKMATTER_TEMPLATE', '')
160if BOOKMATTER_TEMPLATE: 160 ↛ 161line 160 didn't jump to line 161 because the condition on line 160 was never true
161 EXTERNALS['bookmatter'] = {'id': BOOKMATTER_TEMPLATE, 'is_custom': True}
163DRIVER_TEMPLATE = os.getenv('LIITOS_DRIVER_TEMPLATE', '')
164if DRIVER_TEMPLATE: 164 ↛ 165line 164 didn't jump to line 165 because the condition on line 164 was never true
165 EXTERNALS['driver'] = {'id': DRIVER_TEMPLATE, 'is_custom': True}
167METADATA_TEMPLATE = os.getenv('LIITOS_METADATA_TEMPLATE', '')
168if METADATA_TEMPLATE: 168 ↛ 169line 168 didn't jump to line 169 because the condition on line 168 was never true
169 EXTERNALS['metadata'] = {'id': METADATA_TEMPLATE, 'is_custom': True}
171PUBLISHER_TEMPLATE = os.getenv('LIITOS_PUBLISHER_TEMPLATE', '')
172if PUBLISHER_TEMPLATE: 172 ↛ 173line 172 didn't jump to line 173 because the condition on line 172 was never true
173 EXTERNALS['publisher'] = {'id': PUBLISHER_TEMPLATE, 'is_custom': True}
175SETUP_TEMPLATE = os.getenv('LIITOS_SETUP_TEMPLATE', '')
176if SETUP_TEMPLATE: 176 ↛ 177line 176 didn't jump to line 177 because the condition on line 176 was never true
177 EXTERNALS['setup'] = {'id': SETUP_TEMPLATE, 'is_custom': True}
179TS_FORMAT_LOG = '%Y-%m-%dT%H:%M:%S'
180TS_FORMAT_PAYLOADS = '%Y-%m-%d %H:%M:%S.%f UTC'
182__all__: list[str] = [
183 'APP_ALIAS',
184 'APP_ENV',
185 'APP_VERSION',
186 'APPROVALS_STRATEGY',
187 'DEFAULT_STRUCTURE_NAME',
188 'ENCODING',
189 'EXTERNALS',
190 'ExternalsType',
191 'CONTEXT',
192 'FILTER_CS_LIST',
193 'FROM_FORMAT_SPEC',
194 'KEY_APPROVALS',
195 'KEY_BIND',
196 'KEY_CHANGES',
197 'KEY_LAYOUT',
198 'KEY_META',
199 'KEYS_REQUIRED',
200 'KNOWN_APPROVALS_STRATEGIES',
201 'LATEX_PAYLOAD_NAME',
202 'LOG_SEPARATOR',
203 'OptionsType',
204 'PathLike',
205 'TOOL_VERSION_COMMAND_MAP',
206 'ToolKey',
207 'TS_FORMAT_PAYLOADS',
208 'log',
209 'parse_csl',
210]
213def parse_csl(csl: str) -> list[str]:
214 """DRY."""
215 return [fmt.strip() for fmt in csl.split(COMMA) if fmt.strip()]
218@no_type_check
219def formatTime_RFC3339(self, record, datefmt=None): # noqa
220 """HACK A DID ACK we could inject .astimezone() to localize ..."""
221 return dti.datetime.fromtimestamp(record.created, dti.timezone.utc).isoformat() # pragma: no cover
224@no_type_check
225def init_logger(name=None, level=None):
226 """Initialize module level logger"""
227 global log # pylint: disable=global-statement
229 log_format = {
230 'format': '%(asctime)s %(levelname)s [%(name)s]: %(message)s',
231 'datefmt': TS_FORMAT_LOG,
232 # 'filename': LOG_PATH,
233 'level': LOG_LEVEL if level is None else level,
234 }
235 logging.Formatter.formatTime = formatTime_RFC3339
236 logging.basicConfig(**log_format)
237 log = logging.getLogger(APP_ENV if name is None else name)
238 log.propagate = True
241init_logger(name=APP_ENV, level=logging.DEBUG if DEBUG else None)