Coverage for src/lcdoc/mkdocs/find_pages/autodocs.py: 25.78%
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import string lp
2import time lp
3from ast import literal_eval lp
4from contextlib import contextmanager lp
5from functools import partial lp
6from hashlib import md5 lp
9from lcdoc.mkdocs.tools import MDPlugin, app, config_options, find_md_files lp
10from lcdoc.tools import ( lp
11 OD,
12 dirname,
13 exists,
14 flatten,
15 os,
16 project,
17 read_file,
18 unflatten,
19 write_file,
20)
23def find_pages(find, config, stats): lp
24 """
25 find: [{'after': 'index.md', 'dir': 'blueprint'}] or just 'blueprint'
26 """
27 ev = [i.strip() for i in os.environ.get('find_pages', '').split(',')] lp
28 ev = [i for i in ev if i] lp
29 find.extend(ev) lp
30 f = {} lp
31 for k in find: lp
32 if isinstance(k, str): 32 ↛ 34line 32 didn't jump to line 34, because the condition on line 32 was never falselp
33 f[k] = {'dir': k, 'after': None} lp
34 elif isinstance(k, dict):
35 f[k['dir']] = k
36 else:
37 app.die('format problem', element=k)
38 find = f.values() lp
39 stats['find_terms'] = len(find) lp
40 if not find: 40 ↛ 41line 40 didn't jump to line 41, because the condition on line 40 was never truelp
41 return
42 for m in find: lp
43 m['found'] = f = find_md_files(match=m['dir'], config=config) lp
44 if not f: 44 ↛ 45line 44 didn't jump to line 45, because the condition on line 44 was never truelp
45 app.info('No pages found', match=m['dir'])
46 stats['matching'] = sum([len(f['found']) for f in find]) lp
47 return find lp
50class autodocs: lp
51 def do_spec(pconfig, name, spec, config, stats): lp
52 """entry point from mkdocs.yml config"""
53 d_src = project.root() + '/' + spec.get('src-dir', 'build/autodocs')
54 if not exists(d_src):
55 return app.warning('No autodocs source', name=name, **spec)
56 d_target = project.root() + '/docs/autodocs/' + name
57 autodocs.link_over(d_src, d_target)
58 d = 'autodocs/%s:follow' % name
59 # find pages returns list of dicts where 'found' key is the list of pages found:
60 f = [k['found'] for k in find_pages([d], config, stats)]
61 found = [j for i in f for j in i]
62 if not found:
63 return
64 s = spec.get('nav', 'autodocs')
65 autodocs.insert_nav_plain(config['nav'], found, s)
66 S.dir = d_target
67 S.spec = spec
68 S.plugin_conf = pconfig
69 S.stats = stats
70 S.build()
72 def link_over(d_src, d_target): lp
73 if exists(d_target):
74 if os.readlink(d_target) == d_src:
75 return
76 os.unlink(d_target)
77 d = os.path.dirname(d_target)
78 if not exists(d):
79 os.makedirs(d, exist_ok=True)
80 os.symlink(d_src, d_target)
81 app.info('Created symlink', d_src=d_src, d_target=d_target)
83 def insert_nav_plain(nav, found, ins): lp
84 if isinstance(nav, str):
85 return
86 if isinstance(nav, dict):
87 return [autodocs.insert_nav_plain(v, found, ins) for v in nav.values()]
88 if ins in nav:
89 i = nav.index(ins)
90 nav.pop(i)
91 return [nav.insert(i, k) for k in reversed(found)]
92 [autodocs.insert_nav_plain(i, found, ins) for i in nav]
95hsh = lambda src: md5(src.encode('utf-8')).hexdigest() 95 ↛ exitline 95 didn't run the lambda on line 95lp
98def walk_dir(directory, crit=None): lp
99 crit = (lambda *a: True) if crit is None else crit
100 files = []
101 j = os.path.join
102 for (dirpath, dirnames, filenames) in os.walk(directory):
103 files += [j(dirpath, file) for file in filenames if crit(dirpath, file)]
104 return files
107def scan_d_autodocs(typ): lp
108 files = walk_dir(S.dir, crit=lambda d, fn: fn.endswith(typ))
109 app.info('have %s files' % typ, json=files)
110 return files
113now = time.time lp
115from lcdoc.mkdocs.lp.plugs.kroki import run as kroki lp
118class S: lp
119 dir = spec = plugin_conf = stats = None lp
121 def build(): lp
122 S.graph_easy.build()
123 S.plant.build()
125 @contextmanager lp
126 def has_changed(fn, ext):
127 src = read_file(fn)
128 fnb = fn.rsplit(ext, 1)[0]
129 sfn, fnhsh, skip = '%s.svg' % fnb, '%s%s.md5' % (fnb, ext), False
130 cur_hash = hsh(src)
131 if exists(sfn):
132 if read_file(fnhsh, dflt='') == cur_hash:
133 app.debug('graph up to date', fn=fn)
134 sfn, src = (None, None)
135 yield (sfn, src)
136 write_file(fnhsh, cur_hash)
138 class graph_easy: lp
139 cmd = lambda: 'graph-easy' 139 ↛ exitline 139 didn't run the lambda on line 139lp
141 def build(): lp
142 S.stats['graph_easy_count'] = 0
143 S.stats['graph_easy_built_count'] = 0
144 S.stats['graph_easy_build_time'] = 0
145 S.graph_easy.files = fs = scan_d_autodocs('.graph_easy.src')
146 if not fs:
147 return app.info('no .graph_easy.src files')
148 if not S.graph_easy.cmd():
149 app.die('have no graph_easy resource')
151 [S.graph_easy.create(f) for f in fs]
153 def create(fn): lp
154 """check if the src changed via a hash written at last create"""
155 S.stats['graph_easy_count'] += 1
156 with S.has_changed(fn, '.graph_easy.src') as nfos:
157 if not nfos[0]:
158 return
159 fn_svg, src = nfos
160 app.info('rebuilding', fmt='svg', fn=fn_svg)
161 cmd = '"%s" --as %s --output "%s" "%s"'
162 cmd = cmd % (S.graph_easy.cmd(), 'svg', fn_svg, fn,)
163 t0 = now()
164 err = os.system(cmd)
165 if err:
166 app.die('svg creation failed', fn=fn, cmd=cmd)
167 S.stats['graph_easy_built_count'] += 1
168 S.stats['graph_easy_build_time'] += now() - t0
170 class plant: lp
171 def build(): lp
172 S.stats['plantuml_count'] = 0
173 S.stats['plantuml_built_count'] = 0
174 S.stats['plantuml_build_time'] = 0
175 S.plant.files = fs = scan_d_autodocs('.plantuml')
176 if not fs:
177 return app.info('no .plantuml files')
178 # import base64, zlib, requests
179 # import httpx as requests
181 # st = None
182 # if FLG.plantuml_style != 'default':
183 # # also in use there for the 'normal' mkdocs plantuml_markdown inline plants:
184 # fn_st = d_assets() + '/mkdocs/lcd/assets/plantuml/%s' % FLG.plantuml_style
185 # if not exists(fn_st):
186 # app.die('Style not found', fn=fn_st)
187 # st = read_file(fn_st, dflt='')
189 for f in fs:
190 with S.has_changed(f, '.plantuml') as nfos:
191 S.stats['plantuml_built_count'] += 1
192 if not nfos[0]:
193 continue
194 fn_svg, src = nfos
195 t0 = now()
197 S.stats['plantuml_count'] += 1
198 res = kroki(
199 src,
200 {'mode': 'kroki:plantuml', 'puml': 'dark_blue', 'get_svg': True,},
201 )
202 # g = base64.urlsafe_b64encode(zlib.compress(s.encode('utf-8')))
203 write_file(fn_svg, res)
204 app.info('created plant svg', fn=fn_svg)
205 S.stats['plantuml_build_time'] += now() - t0
207 # now, insert the svgs INTO the md files, inline. Then mouse overs work:
208 # the md files got placeholders for the svgs, while pytesting:
209 all_mds_with_plants = set([fnp.rsplit('/', 2)[0] for fnp in fs])
210 dir_autodocs = S.dir + '/'
211 for md in all_mds_with_plants:
212 s = read_file(md + '.md')
213 for f in [i for i in fs if i.startswith(md)]:
214 fsvg = f.replace('.plantuml', '.svg')
215 svg = read_file(fsvg).split('>', 1)[1].strip().split('<!--MD5', 1)[0]
216 if not svg.endswith('</svg>'):
217 svg += '</g></svg>'
218 # testname (e.g. test02_foo) for links building in js
219 n_test = fsvg.rsplit('/', 2)[-2]
220 id = '<svg pth_rel="%s/" id="%s" class="call_flow_chart" '
221 id = id % (n_test, n_test)
222 svg = svg.replace('<svg ', id)
223 fsvg = fsvg.rsplit(dir_autodocs, 1)[1]
224 app.info('Embedding svg into md', md=md, svg=f)
225 tsvg = '[svg_placeholder:%s]' % fsvg
226 s = s.replace(tsvg, svg)
227 write_file(md + '.md', s)