Home | History | Annotate | Download | only in docs
      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 
     17 The adjacent `parser` module creates `PageInfo` objects, containing all data
     18 necessary to document an element of the TensorFlow API.
     19 
     20 This 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 
     26 from __future__ import absolute_import
     27 from __future__ import division
     28 from __future__ import print_function
     29 
     30 import textwrap
     31 
     32 
     33 def 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 
     58 def _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 
     82 def _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 
    172 def _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 
    199 def _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 
    265 def _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 
    297 def _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 
    311 def _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