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

1"""Splice (Finnish liitos) contributions.""" 

2 

3import datetime as dti 

4import logging 

5import os 

6import pathlib 

7import shellingham # type: ignore 

8from typing import Union, no_type_check 

9 

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) 

16 

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' 

27 

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 

40 

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) 

48 

49CONTEXT: dict[str, str] = {} 

50KNOWN_APPROVALS_STRATEGIES = ('south', 'east') 

51APPROVALS_STRATEGY = os.getenv('LIITOS_APPROVALS_STRATEGY', '').lower() 

52 

53OptionsType = dict[str, Union[bool, str, None]] 

54PathLike = Union[str, pathlib.Path] 

55PathLikeOrBool = Union[PathLike, bool] 

56ExternalsType = dict[str, dict[str, PathLikeOrBool]] 

57 

58try: 

59 SHELL = shellingham.detect_shell() 

60except shellingham.ShellDetectionFailure: 

61 SHELL = ('', 'echo') 

62 

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} 

133 

134ToolKey = str 

135 

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} 

158 

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} 

162 

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} 

166 

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} 

170 

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} 

174 

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} 

178 

179TS_FORMAT_LOG = '%Y-%m-%dT%H:%M:%S' 

180TS_FORMAT_PAYLOADS = '%Y-%m-%d %H:%M:%S.%f UTC' 

181 

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] 

211 

212 

213def parse_csl(csl: str) -> list[str]: 

214 """DRY.""" 

215 return [fmt.strip() for fmt in csl.split(COMMA) if fmt.strip()] 

216 

217 

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 

222 

223 

224@no_type_check 

225def init_logger(name=None, level=None): 

226 """Initialize module level logger""" 

227 global log # pylint: disable=global-statement 

228 

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 

239 

240 

241init_logger(name=APP_ENV, level=logging.DEBUG if DEBUG else None)