tools.py
5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
from os import path
import subprocess
import logging
import re
import yaml
from markdown import markdown
# import mistune
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import html
# setup logger for this module
logger = logging.getLogger(__name__)
# ---------------------------------------------------------------------------
# Setup markdown renderer with support for math in LaTeX notation.
# ---------------------------------------------------------------------------
# class MathBlockGrammar(mistune.BlockGrammar):
# block_math = re.compile(r"^\$\$(.*?)\$\$", re.DOTALL)
# latex_environment = re.compile(r"^\\begin\{([a-z]*\*?)\}(.*?)\\end\{\1\}", re.DOTALL)
# class MathBlockLexer(mistune.BlockLexer):
# default_rules = ['block_math', 'latex_environment'] + mistune.BlockLexer.default_rules
# def __init__(self, rules=None, **kwargs):
# if rules is None:
# rules = MathBlockGrammar()
# super(MathBlockLexer, self).__init__(rules, **kwargs)
# def parse_block_math(self, m):
# """Parse a $$math$$ block"""
# self.tokens.append({
# 'type': 'block_math',
# 'text': m.group(1)
# })
# def parse_latex_environment(self, m):
# self.tokens.append({
# 'type': 'latex_environment',
# 'name': m.group(1),
# 'text': m.group(2)
# })
# class MathInlineGrammar(mistune.InlineGrammar):
# math = re.compile(r"^\$(.+?)\$", re.DOTALL)
# block_math = re.compile(r"^\$\$(.+?)\$\$", re.DOTALL)
# text = re.compile(r'^[\s\S]+?(?=[\\<!\[_*`~$]|https?://| {2,}\n|$)')
# class MathInlineLexer(mistune.InlineLexer):
# default_rules = ['block_math', 'math'] + mistune.InlineLexer.default_rules
# def __init__(self, renderer, rules=None, **kwargs):
# if rules is None:
# rules = MathInlineGrammar()
# super(MathInlineLexer, self).__init__(renderer, rules, **kwargs)
# def output_math(self, m):
# return self.renderer.inline_math(m.group(1))
# def output_block_math(self, m):
# return self.renderer.block_math(m.group(1))
# class MarkdownWithMath(mistune.Markdown):
# def __init__(self, renderer, **kwargs):
# if 'inline' not in kwargs:
# kwargs['inline'] = MathInlineLexer
# if 'block' not in kwargs:
# kwargs['block'] = MathBlockLexer
# super().__init__(renderer, **kwargs)
# def output_block_math(self):
# return self.renderer.block_math(self.token['text'])
# def output_latex_environment(self):
# return self.renderer.latex_environment(self.token['name'], self.token['text'])
# class HighlightRenderer(mistune.Renderer):
# def block_code(self, code, lang=None):
# if lang is None:
# return f'\n<pre><code>{mistune.escape(code)}</code></pre>\n'
# else:
# lexer = get_lexer_by_name(lang, stripall=True)
# formatter = html.HtmlFormatter()
# return highlight(code, lexer, formatter)
# def image(self, src, title, text):
# src = 'FIXME' # FIXME
# return super().image(src, title, text)
# def block_math(self, text):
# return r'\[ %s \]' % text
# def inline_math(self, text):
# return r'\( %s \)' % text
# renderer = HighlightRenderer(hard_wrap=True)
# markdown = mistune.Markdown(renderer=renderer)
# ---------------------------------------------------------------------------
# load data from yaml file
# ---------------------------------------------------------------------------
def load_yaml(filename, default=None):
try:
f = open(path.expanduser(filename), 'r', encoding='utf-8')
except IOError:
logger.error(f'Can\'t open file "{filename}"')
return default
else:
with f:
try:
return yaml.load(f)
except yaml.YAMLError as e:
mark = e.problem_mark
logger.error('In YAML file "{0}" near line {1}, column {2}.'.format(filename, mark.line, mark.column+1))
return default
# ---------------------------------------------------------------------------
# Runs a script and returns its stdout parsed as yaml, or None on error.
# ---------------------------------------------------------------------------
def run_script(script, stdin='', timeout=5):
script = path.expanduser(script)
try:
p = subprocess.run([script],
input=stdin,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
timeout=timeout,
)
except FileNotFoundError:
logger.error(f'Script "{script}" not found.')
except PermissionError:
logger.error(f'Script "{script}" not executable. Wrong permissions?')
except subprocess.TimeoutExpired:
logger.error(f'Timeout {timeout}s exceeded while running "{script}".')
else:
if p.returncode != 0:
logger.error(f'Script "{script}" returned error code {p.returncode}.')
else:
try:
output = yaml.load(p.stdout)
except:
logger.error('Error parsing yaml output of "{script}"')
else:
return output
# ---------------------------------------------------------------------------
def md_to_html(text, q=None):
return markdown(text)