1# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"). You 4# may not use this file except in compliance with the License. A copy of 5# the License is located at 6# 7# http://aws.amazon.com/apache2.0/ 8# 9# or in the "license" file accompanying this file. This file is 10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11# ANY KIND, either express or implied. See the License for the specific 12# language governing permissions and limitations under the License. 13 14import logging 15 16logger = logging.getLogger('bcdocs') 17 18 19class BaseStyle(object): 20 21 def __init__(self, doc, indent_width=2): 22 self.doc = doc 23 self.indent_width = indent_width 24 self._indent = 0 25 self.keep_data = True 26 27 @property 28 def indentation(self): 29 return self._indent 30 31 @indentation.setter 32 def indentation(self, value): 33 self._indent = value 34 35 def new_paragraph(self): 36 return '\n%s' % self.spaces() 37 38 def indent(self): 39 self._indent += 1 40 41 def dedent(self): 42 if self._indent > 0: 43 self._indent -= 1 44 45 def spaces(self): 46 return ' ' * (self._indent * self.indent_width) 47 48 def bold(self, s): 49 return s 50 51 def ref(self, link, title=None): 52 return link 53 54 def h2(self, s): 55 return s 56 57 def h3(self, s): 58 return s 59 60 def underline(self, s): 61 return s 62 63 def italics(self, s): 64 return s 65 66 67class ReSTStyle(BaseStyle): 68 69 def __init__(self, doc, indent_width=2): 70 BaseStyle.__init__(self, doc, indent_width) 71 self.do_p = True 72 self.a_href = None 73 74 def new_paragraph(self): 75 if self.do_p: 76 self.doc.write('\n\n%s' % self.spaces()) 77 78 def new_line(self): 79 if self.do_p: 80 self.doc.write('\n%s' % self.spaces()) 81 82 def _start_inline(self, markup): 83 self.doc.write(markup) 84 85 def _end_inline(self, markup): 86 # Sometimes the HTML markup has whitespace between the end 87 # of the text inside the inline markup and the closing element 88 # (e.g. <b>foobar </b>). This trailing space will cause 89 # problems in the ReST inline markup so we remove it here 90 # by popping the last item written off the stack, striping 91 # the whitespace and then pushing it back on the stack. 92 last_write = self.doc.pop_write() 93 self.doc.push_write(last_write.rstrip(' ')) 94 self.doc.write(markup + ' ') 95 96 def start_bold(self, attrs=None): 97 self._start_inline('**') 98 99 def end_bold(self): 100 self._end_inline('**') 101 102 def start_b(self, attrs=None): 103 self.doc.do_translation = True 104 self.start_bold(attrs) 105 106 def end_b(self): 107 self.doc.do_translation = False 108 self.end_bold() 109 110 def bold(self, s): 111 if s: 112 self.start_bold() 113 self.doc.write(s) 114 self.end_bold() 115 116 def ref(self, title, link=None): 117 if link is None: 118 link = title 119 self.doc.write(':doc:`%s <%s>`' % (title, link)) 120 121 def _heading(self, s, border_char): 122 border = border_char * len(s) 123 self.new_paragraph() 124 self.doc.write('%s\n%s\n%s' % (border, s, border)) 125 self.new_paragraph() 126 127 def h1(self, s): 128 self._heading(s, '*') 129 130 def h2(self, s): 131 self._heading(s, '=') 132 133 def h3(self, s): 134 self._heading(s, '-') 135 136 def start_italics(self, attrs=None): 137 self._start_inline('*') 138 139 def end_italics(self): 140 self._end_inline('*') 141 142 def italics(self, s): 143 if s: 144 self.start_italics() 145 self.doc.write(s) 146 self.end_italics() 147 148 def start_p(self, attrs=None): 149 if self.do_p: 150 self.doc.write('\n\n%s' % self.spaces()) 151 152 def end_p(self): 153 if self.do_p: 154 self.doc.write('\n\n') 155 156 def start_code(self, attrs=None): 157 self.doc.do_translation = True 158 self._start_inline('``') 159 160 def end_code(self): 161 self.doc.do_translation = False 162 self._end_inline('``') 163 164 def code(self, s): 165 if s: 166 self.start_code() 167 self.doc.write(s) 168 self.end_code() 169 170 def start_note(self, attrs=None): 171 self.new_paragraph() 172 self.doc.write('.. note::') 173 self.indent() 174 self.new_paragraph() 175 176 def end_note(self): 177 self.dedent() 178 self.new_paragraph() 179 180 def start_important(self, attrs=None): 181 self.new_paragraph() 182 self.doc.write('.. warning::') 183 self.indent() 184 self.new_paragraph() 185 186 def end_important(self): 187 self.dedent() 188 self.new_paragraph() 189 190 def start_a(self, attrs=None): 191 if attrs: 192 for attr_key, attr_value in attrs: 193 if attr_key == 'href': 194 self.a_href = attr_value 195 self.doc.write('`') 196 else: 197 # There are some model documentation that 198 # looks like this: <a>DescribeInstances</a>. 199 # In this case we just write out an empty 200 # string. 201 self.doc.write(' ') 202 self.doc.do_translation = True 203 204 def link_target_definition(self, refname, link): 205 self.doc.writeln('.. _%s: %s' % (refname, link)) 206 207 def sphinx_reference_label(self, label, text=None): 208 if text is None: 209 text = label 210 if self.doc.target == 'html': 211 self.doc.write(':ref:`%s <%s>`' % (text, label)) 212 else: 213 self.doc.write(text) 214 215 def end_a(self): 216 self.doc.do_translation = False 217 if self.a_href: 218 last_write = self.doc.pop_write() 219 last_write = last_write.rstrip(' ') 220 if last_write and last_write != '`': 221 if ':' in last_write: 222 last_write = last_write.replace(':', r'\:') 223 self.doc.push_write(last_write) 224 self.doc.hrefs[last_write] = self.a_href 225 self.doc.write('`_') 226 elif last_write == '`': 227 # Look at start_a(). It will do a self.doc.write('`') 228 # which is the start of the link title. If that is the 229 # case then there was no link text. We should just 230 # use an inline link. The syntax of this is 231 # `<http://url>`_ 232 self.doc.push_write('`<%s>`_' % self.a_href) 233 else: 234 self.doc.push_write(self.a_href) 235 self.doc.hrefs[self.a_href] = self.a_href 236 self.doc.write('`_') 237 self.a_href = None 238 self.doc.write(' ') 239 240 def start_i(self, attrs=None): 241 self.doc.do_translation = True 242 self.start_italics() 243 244 def end_i(self): 245 self.doc.do_translation = False 246 self.end_italics() 247 248 def start_li(self, attrs=None): 249 self.new_line() 250 self.do_p = False 251 self.doc.write('* ') 252 253 def end_li(self): 254 self.do_p = True 255 self.new_line() 256 257 def li(self, s): 258 if s: 259 self.start_li() 260 self.doc.writeln(s) 261 self.end_li() 262 263 def start_ul(self, attrs=None): 264 self.new_paragraph() 265 266 def end_ul(self): 267 self.new_paragraph() 268 269 def start_ol(self, attrs=None): 270 # TODO: Need to control the bullets used for LI items 271 self.new_paragraph() 272 273 def end_ol(self): 274 self.new_paragraph() 275 276 def start_examples(self, attrs=None): 277 self.doc.keep_data = False 278 279 def end_examples(self): 280 self.doc.keep_data = True 281 282 def start_fullname(self, attrs=None): 283 self.doc.keep_data = False 284 285 def end_fullname(self): 286 self.doc.keep_data = True 287 288 def start_codeblock(self, attrs=None): 289 self.doc.write('::') 290 self.indent() 291 self.new_paragraph() 292 293 def end_codeblock(self): 294 self.dedent() 295 self.new_paragraph() 296 297 def codeblock(self, code): 298 """ 299 Literal code blocks are introduced by ending a paragraph with 300 the special marker ::. The literal block must be indented 301 (and, like all paragraphs, separated from the surrounding 302 ones by blank lines). 303 """ 304 self.start_codeblock() 305 self.doc.writeln(code) 306 self.end_codeblock() 307 308 def toctree(self): 309 if self.doc.target == 'html': 310 self.doc.write('\n.. toctree::\n') 311 self.doc.write(' :maxdepth: 1\n') 312 self.doc.write(' :titlesonly:\n\n') 313 else: 314 self.start_ul() 315 316 def tocitem(self, item, file_name=None): 317 if self.doc.target == 'man': 318 self.li(item) 319 else: 320 if file_name: 321 self.doc.writeln(' %s' % file_name) 322 else: 323 self.doc.writeln(' %s' % item) 324 325 def hidden_toctree(self): 326 if self.doc.target == 'html': 327 self.doc.write('\n.. toctree::\n') 328 self.doc.write(' :maxdepth: 1\n') 329 self.doc.write(' :hidden:\n\n') 330 331 def hidden_tocitem(self, item): 332 if self.doc.target == 'html': 333 self.tocitem(item) 334 335 def table_of_contents(self, title=None, depth=None): 336 self.doc.write('.. contents:: ') 337 if title is not None: 338 self.doc.writeln(title) 339 if depth is not None: 340 self.doc.writeln(' :depth: %s' % depth) 341 342 def start_sphinx_py_class(self, class_name): 343 self.new_paragraph() 344 self.doc.write('.. py:class:: %s' % class_name) 345 self.indent() 346 self.new_paragraph() 347 348 def end_sphinx_py_class(self): 349 self.dedent() 350 self.new_paragraph() 351 352 def start_sphinx_py_method(self, method_name, parameters=None): 353 self.new_paragraph() 354 content = '.. py:method:: %s' % method_name 355 if parameters is not None: 356 content += '(%s)' % parameters 357 self.doc.write(content) 358 self.indent() 359 self.new_paragraph() 360 361 def end_sphinx_py_method(self): 362 self.dedent() 363 self.new_paragraph() 364 365 def write_py_doc_string(self, docstring): 366 docstring_lines = docstring.splitlines() 367 for docstring_line in docstring_lines: 368 self.doc.writeln(docstring_line) 369