3 # The MIT License (MIT)
5 # Copyright (c) 2015 Philippe Proulx <pproulx@efficios.com>
7 # Permission is hereby granted, free of charge, to any person obtaining a copy
8 # of this software and associated documentation files (the "Software"), to deal
9 # in the Software without restriction, including without limitation the rights
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 # copies of the Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 from termcolor
import colored
32 _TOC_PATH
= 'toc/docs.yml'
33 _CONTENTS_ROOT_PATH
= 'contents'
40 class _IntLink(_Link
):
41 def __init__(self
, section
):
42 self
._section
= section
48 def __eq__(self
, other
):
49 if type(self
) != type(other
):
52 return self
._section
== other
._section
55 return hash(self
._section
)
59 'section': self
._section
,
63 class _ExtLink(_Link
):
64 def __init__(self
, url
):
71 def __eq__(self
, other
):
72 if type(self
) != type(other
):
75 return self
._url
== other
._url
78 return hash(self
._url
)
87 def __init__(self
, path
):
89 self
._in
_links
= set()
90 self
._out
_links
= set()
102 return self
._out
_links
104 def add_in_link(self
, link
):
105 self
._in
_links
.add(link
)
107 def add_out_link(self
, link
):
108 self
._out
_links
.add(link
)
117 for in_link
in self
.in_links
:
118 in_links_json
.append(in_link
.to_json())
120 for out_link
in self
.out_links
:
121 out_links_json
.append(out_link
.to_json())
123 section_json
['in-links'] = in_links_json
124 section_json
['out-links'] = out_links_json
131 self
._section
_infos
= {}
133 def register_section_info(self
, sid
, section_info
):
134 self
._section
_infos
[sid
] = section_info
136 def _resolve_in_links(self
):
137 for sid
in self
._section
_infos
:
138 section_info
= self
._section
_infos
[sid
]
139 for out_link
in section_info
.out_links
:
140 if type(out_link
) != _IntLink
:
143 target_sid
= out_link
.section
144 target_section_info
= self
._section
_infos
[target_sid
]
145 target_section_info
.add_in_link(_IntLink(sid
))
148 self
._resolve
_in
_links
()
151 for sid
, section_info
in self
._section
_infos
.items():
152 sections_json
[sid
] = section_info
.to_json()
154 return json
.dumps(sections_json
)
157 def _perror(filename
, msg
):
158 s
= '{} {} {}'.format(filename
, colored('Error:', 'red'),
159 colored(msg
, 'red', attrs
=['bold']))
160 print(s
, file=sys
.stderr
)
163 def _pwarn(filename
, msg
):
164 s
= '{} {} {}'.format(filename
, colored('Warning:', 'yellow'),
165 colored(msg
, 'yellow', attrs
=['bold']))
166 print(s
, file=sys
.stderr
)
169 def _get_files(root
):
172 for dirpath
, dirnames
, filenames
in os
.walk(root
):
174 files
.append(os
.path
.join(dirpath
, f
))
179 def _get_toc_ids(path
):
180 p
= re
.compile(r
'id\s*:\s*(.+)$', flags
=re
.M
)
182 with
open(path
) as f
:
183 orig_ids
= p
.findall(f
.read())
187 if len(ids
) != len(orig_ids
):
188 _perror(path
, 'Duplicate IDs')
194 _id_re
= re
.compile(r
'^\s*id:\s*([a-zA-Z0-9_-]+)\s*$', flags
=re
.M
)
197 def _get_sid_from_file(path
, c
):
201 _perror(path
, 'No ID found')
207 _ilink_re
= re
.compile(r
'\[[^\]]+\]\(([^)]+)\)', flags
=re
.M
)
208 _elink_re
= re
.compile(r
'<a(?:\s+[^>]+|\s*)>')
209 _name_re
= re
.compile(r
'name="([^"]+)"')
210 _href_re
= re
.compile(r
'href="([^"]+)"')
211 _classes_re
= re
.compile(r
'class="([^"]+)"')
214 def _register_section_info(registry
, toc_ids
, path
, c
):
215 sid
= _get_sid_from_file(path
, c
)
221 ilinks
= _ilink_re
.findall(c
)
222 elinks
= _elink_re
.findall(c
)
223 section_info
= _SectionInfo(path
)
226 href
= _href_re
.search(link
)
227 name
= _name_re
.search(link
)
228 classes
= _classes_re
.search(link
)
230 if name
and not href
:
235 _pwarn(path
, 'External link has no "ext" class: "{}"'.format(link
))
238 classes
= classes
.group(1).split(' ')
240 if 'int' in classes
and 'ext' in classes
:
241 _pwarn(path
, 'External link has both "ext" and "int" classes: "{}"'.format(link
))
242 elif 'int' not in classes
and 'ext' not in classes
:
243 _pwarn(path
, 'External link has no "ext" or "int" class: "{}"'.format(link
))
248 if href
.startswith('#') and 'int' not in classes
:
249 _pwarn(path
, 'External link starts with #: "{}"'.format(href
))
255 section_info
.add_out_link(_ExtLink(href
))
257 _perror(path
, 'External link with no "href" or "name" attribute: "{}"'.format(link
))
261 if not link
.startswith('#doc-'):
262 s
= 'Internal link does not start with "#doc-": "{}"'.format(link
)
267 target_sid
= link
[5:]
269 if target_sid
not in toc_ids
:
270 _perror(path
, 'Dead internal link: "{}"'.format(link
))
273 section_info
.add_out_link(_IntLink(target_sid
))
275 registry
.register_section_info(sid
, section_info
)
280 def _docs2json(toc_ids
, contents_files
):
282 registry
= _Registry()
286 for path
in contents_files
:
287 with
open(path
) as f
:
290 ret
&= _register_section_info(registry
, toc_ids
, path
, c
)
292 print(registry
.to_json())
297 def _check_non_md(files
):
301 if not f
.endswith('.md'):
302 _perror(f
, 'Wrong, non-Markdown file: "{}"'.format(f
))
309 toc_ids
= _get_toc_ids(_TOC_PATH
)
314 contents_files
= _get_files(_CONTENTS_ROOT_PATH
)
316 if not _check_non_md(contents_files
):
319 if not _docs2json(toc_ids
, contents_files
):
325 if __name__
== '__main__':
326 sys
.exit(0 if docs2json() else 1)
This page took 0.045191 seconds and 5 git commands to generate.