1- import csv
2- from itertools import zip_longest
1+
32import os
4- import re
5- import string
63
74from docutils import nodes , utils
8- from docutils .parsers .rst import directives
9- from docutils .parsers .rst .directives .tables import Table
105
116
127def libpr_role (name , rawtext , text , lineno , inliner , options = None , content = None ):
@@ -29,56 +24,6 @@ def ghuser_role(name, rawtext, text, lineno, inliner, options=None, content=None
2924 return [make_link_node (rawtext , "@{}" .format (text ), ref , options )], []
3025
3126
32- value_re = re .compile (r"^(.*)\s*<(.*)>$" )
33- DOXYGEN_LOOKUP = {}
34- for s in string .ascii_lowercase + string .digits :
35- DOXYGEN_LOOKUP [s ] = s
36- for s in string .ascii_uppercase :
37- DOXYGEN_LOOKUP [s ] = "_{}" .format (s .lower ())
38- DOXYGEN_LOOKUP [":" ] = "_1"
39- DOXYGEN_LOOKUP ["_" ] = "__"
40- DOXYGEN_LOOKUP ["." ] = "_8"
41-
42-
43- def split_text_value (value ):
44- match = value_re .match (value )
45- if match is None :
46- return None , value
47- return match .group (1 ), match .group (2 )
48-
49-
50- def encode_doxygen (value ):
51- value = value .split ("/" )[- 1 ]
52- try :
53- return "" .join (DOXYGEN_LOOKUP [s ] for s in value )
54- except KeyError :
55- raise ValueError ("Unknown character in doxygen string! '{}'" .format (value ))
56-
57-
58- def apiref_role (name , rawtext , text , lineno , inliner , options = None , content = None ):
59- text , value = split_text_value (text )
60- if text is None :
61- text = "API Reference"
62- ref = "/api/{}.html" .format (encode_doxygen (value ))
63- return [make_link_node (rawtext , text , ref , options )], []
64-
65-
66- def apiclass_role (name , rawtext , text , lineno , inliner , options = None , content = None ):
67- text , value = split_text_value (text )
68- if text is None :
69- text = value
70- ref = "/api/classesphome_1_1{}.html" .format (encode_doxygen (value ))
71- return [make_link_node (rawtext , text , ref , options )], []
72-
73-
74- def apistruct_role (name , rawtext , text , lineno , inliner , options = None , content = None ):
75- text , value = split_text_value (text )
76- if text is None :
77- text = value
78- ref = "/api/structesphome_1_1{}.html" .format (encode_doxygen (value ))
79- return [make_link_node (rawtext , text , ref , options )], []
80-
81-
8227def ghedit_role (name , rawtext , text , lineno , inliner , options = None , content = None ):
8328 path = os .path .relpath (
8429 inliner .document .current_source , inliner .document .settings .env .app .srcdir
@@ -93,195 +38,12 @@ def make_link_node(rawtext, text, ref, options=None):
9338 return node
9439
9540
96- # https://stackoverflow.com/a/3415150/8924614
97- def grouper (n , iterable , fillvalue = None ):
98- """Pythonic way to iterate over sequence, 4 items at a time.
99-
100- grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx
101- """
102- args = [iter (iterable )] * n
103- return zip_longest (fillvalue = fillvalue , * args )
104-
105-
106- # Based on https://www.slideshare.net/doughellmann/better-documentation-through-automation-creating-docutils-sphinx-extensions
107- class ImageTableDirective (Table ):
108-
109- option_spec = {
110- "columns" : directives .positive_int ,
111- }
112-
113- def run (self ):
114- cols = self .options .get ("columns" , 3 )
115-
116- items = []
117-
118- data = list (csv .reader (self .content ))
119- for row in data :
120- if not row :
121- continue
122- name , page , image = row [0 :3 ]
123- link = page .strip ()
124- if link .startswith ("http" ):
125- pass
126- else :
127- if not link .startswith ("/" ):
128- link = "/{}" .format (link )
129- if ".html" not in link :
130- link += ".html"
131- category = None
132- dark_invert = False
133- if len (row ) == 4 :
134- if row [3 ].strip () == "dark-invert" :
135- dark_invert = True
136- else :
137- category = row [3 ].strip ()
138- if len (row ) == 5 and row [4 ].strip () == "dark-invert" :
139- category = row [3 ].strip ()
140- dark_invert = True
141- items .append (
142- {
143- "name" : name .strip (),
144- "link" : link ,
145- "image" : "/images/{}" .format (image .strip ()),
146- "category" : category ,
147- "dark_invert" : dark_invert ,
148- }
149- )
150-
151- title , messages = self .make_title ()
152- table = nodes .table ()
153- table ["classes" ].append ("table-center" )
154- table ["classes" ].append ("colwidths-given" )
155-
156- # Set up column specifications based on widths
157- tgroup = nodes .tgroup (cols = cols )
158- table += tgroup
159- tgroup .extend (nodes .colspec (colwidth = 1 ) for _ in range (cols ))
160-
161- tbody = nodes .tbody ()
162- tgroup += tbody
163- rows = []
164- for value in grouper (cols , items ):
165- trow = nodes .row ()
166- for cell in value :
167- entry = nodes .entry ()
168- if cell is None :
169- entry += nodes .paragraph ()
170- trow += entry
171- continue
172- name = cell ["name" ]
173- link = cell ["link" ]
174- image = cell ["image" ]
175- reference_node = nodes .reference (refuri = link )
176- img = nodes .image (uri = directives .uri (image ), alt = name )
177- img ["classes" ].append ("component-image" )
178- if cell ["dark_invert" ]:
179- img ["classes" ].append ("dark-invert" )
180- reference_node += img
181- para = nodes .paragraph ()
182- para += reference_node
183- entry += para
184- trow += entry
185- rows .append (trow )
186-
187- trow = nodes .row ()
188- for cell in value :
189- entry = nodes .entry ()
190- if cell is None :
191- entry += nodes .paragraph ()
192- trow += entry
193- continue
194- name = cell ["name" ]
195- link = cell ["link" ]
196- ref = nodes .reference (name , name , refuri = link )
197- para = nodes .paragraph ()
198- para += ref
199- entry += para
200- cat_text = cell ["category" ]
201- if cat_text :
202- cat = nodes .paragraph (text = cat_text )
203- entry += cat
204- trow += entry
205- rows .append (trow )
206- tbody .extend (rows )
207-
208- self .add_name (table )
209- if title :
210- table .insert (0 , title )
211-
212- return [table ] + messages
213-
214-
215- class PinTableDirective (Table ):
216- option_spec = {}
217-
218- def run (self ):
219- items = []
220-
221- data = list (csv .reader (self .content ))
222- for row in data :
223- if not row :
224- continue
225- if len (row ) == 3 :
226- items .append ((row [0 ], row [1 ], True ))
227- else :
228- items .append ((row [0 ], row [1 ], False ))
229-
230- col_widths = self .get_column_widths (2 )
231- title , messages = self .make_title ()
232- table = nodes .table ()
233-
234- # Set up column specifications based on widths
235- tgroup = nodes .tgroup (cols = 2 )
236- table += tgroup
237- tgroup .extend (nodes .colspec (colwidth = col_width ) for col_width in col_widths )
238-
239- thead = nodes .thead ()
240- tgroup += thead
241- trow = nodes .row ()
242- thead += trow
243- trow .extend (
244- nodes .entry (h , nodes .paragraph (text = h )) for h in ("Pin" , "Function" )
245- )
246-
247- tbody = nodes .tbody ()
248- tgroup += tbody
249- for name , func , important in items :
250- trow = nodes .row ()
251- entry = nodes .entry ()
252- para = nodes .paragraph ()
253- para += nodes .literal (text = name )
254- entry += para
255- trow += entry
256-
257- entry = nodes .entry ()
258- if important :
259- para = nodes .paragraph ()
260- para += nodes .strong (text = func )
261- else :
262- para = nodes .paragraph (text = func )
263- entry += para
264- trow += entry
265- tbody += trow
266-
267- self .add_name (table )
268- if title :
269- table .insert (0 , title )
270-
271- return [table ] + messages
272-
273-
27441def setup (app ):
27542 app .add_role ("libpr" , libpr_role )
27643 app .add_role ("corepr" , libpr_role )
27744 app .add_role ("yamlpr" , yamlpr_role )
27845 app .add_role ("esphomepr" , yamlpr_role )
27946 app .add_role ("docspr" , docspr_role )
28047 app .add_role ("ghuser" , ghuser_role )
281- app .add_role ("apiref" , apiref_role )
282- app .add_role ("apiclass" , apiclass_role )
283- app .add_role ("apistruct" , apistruct_role )
28448 app .add_role ("ghedit" , ghedit_role )
285- app .add_directive ("imgtable" , ImageTableDirective )
286- app .add_directive ("pintable" , PinTableDirective )
28749 return {"version" : "1.0.0" , "parallel_read_safe" : True , "parallel_write_safe" : True }
0 commit comments