1# Copyright 2015 The TensorFlow Authors. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# ============================================================================== 15"""A module for converting parsed doc content into markdown pages. 16 17The adjacent `parser` module creates `PageInfo` objects, containing all data 18necessary to document an element of the TensorFlow API. 19 20This module contains one public function, which handels the conversion of these 21`PageInfo` objects into a markdown string: 22 23 md_page = build_md_page(page_info) 24""" 25 26from __future__ import absolute_import 27from __future__ import division 28from __future__ import print_function 29 30import textwrap 31 32 33def build_md_page(page_info): 34 """Given a PageInfo object, return markdown for the page. 35 36 Args: 37 page_info: must be a `parser.FunctionPageInfo`, `parser.ClassPageInfo`, or 38 `parser.ModulePageInfo` 39 40 Returns: 41 Markdown for the page 42 43 Raises: 44 ValueError: if `page_info` is an instance of an unrecognized class 45 """ 46 if page_info.for_function(): 47 return _build_function_page(page_info) 48 49 if page_info.for_class(): 50 return _build_class_page(page_info) 51 52 if page_info.for_module(): 53 return _build_module_page(page_info) 54 55 raise ValueError('Unknown Page Info Type: %s' % type(page_info)) 56 57 58def _build_function_page(page_info): 59 """Given a FunctionPageInfo object Return the page as an md string.""" 60 parts = ['# %s\n\n' % page_info.full_name] 61 62 if len(page_info.aliases) > 1: 63 parts.append('### Aliases:\n\n') 64 parts.extend('* `%s`\n' % name for name in page_info.aliases) 65 parts.append('\n') 66 67 if page_info.signature is not None: 68 parts.append(_build_signature(page_info)) 69 70 if page_info.defined_in: 71 parts.append('\n\n') 72 parts.append(str(page_info.defined_in)) 73 74 parts.append(page_info.guides) 75 parts.append(page_info.doc.docstring) 76 parts.append(_build_function_details(page_info.doc.function_details)) 77 parts.append(_build_compatibility(page_info.doc.compatibility)) 78 79 return ''.join(parts) 80 81 82def _build_class_page(page_info): 83 """Given a ClassPageInfo object Return the page as an md string.""" 84 parts = ['# {page_info.full_name}\n\n'.format(page_info=page_info)] 85 86 parts.append('## Class `%s`\n\n' % page_info.full_name.split('.')[-1]) 87 if page_info.bases: 88 parts.append('Inherits From: ') 89 90 link_template = '[`{short_name}`]({url})' 91 parts.append(', '.join( 92 link_template.format(**base._asdict()) for base in page_info.bases)) 93 94 parts.append('\n\n') 95 96 # Sort the methods list, but make sure constructors come first. 97 constructor_names = ['__init__', '__new__'] 98 constructors = sorted( 99 method for method in page_info.methods 100 if method.short_name in constructor_names) 101 other_methods = sorted( 102 method for method in page_info.methods 103 if method.short_name not in constructor_names) 104 105 if len(page_info.aliases) > 1: 106 parts.append('### Aliases:\n\n') 107 parts.extend('* Class `%s`\n' % name for name in page_info.aliases) 108 parts.append('\n') 109 110 if page_info.defined_in is not None: 111 parts.append('\n\n') 112 parts.append(str(page_info.defined_in)) 113 114 parts.append(page_info.guides) 115 parts.append(page_info.doc.docstring) 116 parts.append(_build_function_details(page_info.doc.function_details)) 117 parts.append(_build_compatibility(page_info.doc.compatibility)) 118 119 parts.append('\n\n') 120 121 if constructors: 122 for method_info in constructors: 123 parts.append(_build_method_section(method_info, heading_level=2)) 124 parts.append('\n\n') 125 126 if page_info.classes: 127 parts.append('## Child Classes\n') 128 129 link_template = ('[`class {class_info.short_name}`]' 130 '({class_info.url})\n\n') 131 class_links = sorted( 132 link_template.format(class_info=class_info) 133 for class_info in page_info.classes) 134 135 parts.extend(class_links) 136 137 if page_info.properties: 138 parts.append('## Properties\n\n') 139 for prop_info in page_info.properties: 140 h3 = '<h3 id="{short_name}"><code>{short_name}</code></h3>\n\n' 141 parts.append(h3.format(short_name=prop_info.short_name)) 142 143 parts.append(prop_info.doc.docstring) 144 parts.append(_build_function_details(prop_info.doc.function_details)) 145 parts.append(_build_compatibility(prop_info.doc.compatibility)) 146 147 parts.append('\n\n') 148 149 parts.append('\n\n') 150 151 if other_methods: 152 parts.append('## Methods\n\n') 153 154 for method_info in other_methods: 155 parts.append(_build_method_section(method_info)) 156 parts.append('\n\n') 157 158 if page_info.other_members: 159 parts.append('## Class Members\n\n') 160 161 # TODO(markdaoust): Document the value of the members, 162 # at least for basic types. 163 164 h3 = '<h3 id="{short_name}"><code>{short_name}</code></h3>\n\n' 165 others_member_headings = (h3.format(short_name=info.short_name) 166 for info in sorted(page_info.other_members)) 167 parts.extend(others_member_headings) 168 169 return ''.join(parts) 170 171 172def _build_method_section(method_info, heading_level=3): 173 """Generates a markdown section for a method. 174 175 Args: 176 method_info: A `MethodInfo` object. 177 heading_level: An Int, which HTML heading level to use. 178 179 Returns: 180 A markdown string. 181 """ 182 parts = [] 183 heading = ('<h{heading_level} id="{short_name}">' 184 '<code>{short_name}</code>' 185 '</h{heading_level}>\n\n') 186 parts.append(heading.format(heading_level=heading_level, 187 **method_info._asdict())) 188 189 if method_info.signature is not None: 190 parts.append(_build_signature(method_info, use_full_name=False)) 191 192 parts.append(method_info.doc.docstring) 193 parts.append(_build_function_details(method_info.doc.function_details)) 194 parts.append(_build_compatibility(method_info.doc.compatibility)) 195 parts.append('\n\n') 196 return ''.join(parts) 197 198 199def _build_module_page(page_info): 200 """Given a ClassPageInfo object Return the page as an md string.""" 201 parts = ['# Module: {full_name}\n\n'.format(full_name=page_info.full_name)] 202 203 if len(page_info.aliases) > 1: 204 parts.append('### Aliases:\n\n') 205 parts.extend('* Module `%s`\n' % name for name in page_info.aliases) 206 parts.append('\n') 207 208 if page_info.defined_in is not None: 209 parts.append('\n\n') 210 parts.append(str(page_info.defined_in)) 211 212 parts.append(page_info.doc.docstring) 213 parts.append(_build_compatibility(page_info.doc.compatibility)) 214 215 parts.append('\n\n') 216 217 if page_info.modules: 218 parts.append('## Modules\n\n') 219 template = '[`{short_name}`]({url}) module' 220 221 for item in page_info.modules: 222 parts.append(template.format(**item._asdict())) 223 224 if item.doc.brief: 225 parts.append(': ' + item.doc.brief) 226 227 parts.append('\n\n') 228 229 if page_info.classes: 230 parts.append('## Classes\n\n') 231 template = '[`class {short_name}`]({url})' 232 233 for item in page_info.classes: 234 parts.append(template.format(**item._asdict())) 235 236 if item.doc.brief: 237 parts.append(': ' + item.doc.brief) 238 239 parts.append('\n\n') 240 241 if page_info.functions: 242 parts.append('## Functions\n\n') 243 template = '[`{short_name}(...)`]({url})' 244 245 for item in page_info.functions: 246 parts.append(template.format(**item._asdict())) 247 248 if item.doc.brief: 249 parts.append(': ' + item.doc.brief) 250 251 parts.append('\n\n') 252 253 if page_info.other_members: 254 # TODO(markdaoust): Document the value of the members, 255 # at least for basic types. 256 parts.append('## Other Members\n\n') 257 258 h3 = '<h3 id="{short_name}"><code>{short_name}</code></h3>\n\n' 259 for item in page_info.other_members: 260 parts.append(h3.format(**item._asdict())) 261 262 return ''.join(parts) 263 264 265def _build_signature(obj_info, use_full_name=True): 266 """Returns a md code block showing the function signature.""" 267 # Special case tf.range, since it has an optional first argument 268 if obj_info.full_name == 'tf.range': 269 return ( 270 '``` python\n' 271 "tf.range(limit, delta=1, dtype=None, name='range')\n" 272 "tf.range(start, limit, delta=1, dtype=None, name='range')\n" 273 '```\n\n') 274 275 parts = ['``` python'] 276 parts.extend(['@' + dec for dec in obj_info.decorators]) 277 signature_template = '{name}({sig})' 278 279 if not obj_info.signature: 280 sig = '' 281 elif len(obj_info.signature) == 1: 282 sig = obj_info.signature[0] 283 else: 284 sig = ',\n'.join(' %s' % sig_item for sig_item in obj_info.signature) 285 sig = '\n'+sig+'\n' 286 287 if use_full_name: 288 obj_name = obj_info.full_name 289 else: 290 obj_name = obj_info.short_name 291 parts.append(signature_template.format(name=obj_name, sig=sig)) 292 parts.append('```\n\n') 293 294 return '\n'.join(parts) 295 296 297def _build_compatibility(compatibility): 298 """Return the compatibility section as an md string.""" 299 parts = [] 300 sorted_keys = sorted(compatibility.keys()) 301 for key in sorted_keys: 302 303 value = compatibility[key] 304 # Dedent so that it does not trigger markdown code formatting. 305 value = textwrap.dedent(value) 306 parts.append('\n\n#### %s Compatibility\n%s\n' % (key.title(), value)) 307 308 return ''.join(parts) 309 310 311def _build_function_details(function_details): 312 """Return the function details section as an md string.""" 313 parts = [] 314 for detail in function_details: 315 sub = [] 316 sub.append('#### ' + detail.keyword + ':\n\n') 317 sub.append(textwrap.dedent(detail.header)) 318 for key, value in detail.items: 319 sub.append('* <b>`%s`</b>: %s' % (key, value)) 320 parts.append(''.join(sub)) 321 322 return '\n'.join(parts) 323