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 itertools
     31 import textwrap
     32 
     33 
     34 def build_md_page(page_info):
     35   """Given a PageInfo object, return markdown for the page.
     36 
     37   Args:
     38     page_info: must be a `parser.FunctionPageInfo`, `parser.ClassPageInfo`, or
     39         `parser.ModulePageInfo`
     40 
     41   Returns:
     42     Markdown for the page
     43 
     44   Raises:
     45     ValueError: if `page_info` is an instance of an unrecognized class
     46   """
     47   if page_info.for_function():
     48     return _build_function_page(page_info)
     49 
     50   if page_info.for_class():
     51     return _build_class_page(page_info)
     52 
     53   if page_info.for_module():
     54     return _build_module_page(page_info)
     55 
     56   raise ValueError('Unknown Page Info Type: %s' % type(page_info))
     57 
     58 
     59 def _build_function_page(page_info):
     60   """Given a FunctionPageInfo object Return the page as an md string."""
     61   parts = [_Metadata(page_info.full_name).build_html()]
     62   parts.append('# %s\n\n' % page_info.full_name)
     63 
     64   if len(page_info.aliases) > 1:
     65     parts.append('### Aliases:\n\n')
     66     parts.extend('* `%s`\n' % name for name in page_info.aliases)
     67     parts.append('\n')
     68 
     69   if page_info.signature is not None:
     70     parts.append(_build_signature(page_info))
     71 
     72   if page_info.defined_in:
     73     parts.append('\n\n')
     74     parts.append(str(page_info.defined_in))
     75 
     76   parts.append(page_info.guides)
     77   parts.append(page_info.doc.docstring)
     78   parts.append(_build_function_details(page_info.doc.function_details))
     79   parts.append(_build_compatibility(page_info.doc.compatibility))
     80 
     81   return ''.join(parts)
     82 
     83 
     84 def _build_class_page(page_info):
     85   """Given a ClassPageInfo object Return the page as an md string."""
     86   meta_data = _Metadata(page_info.full_name)
     87   for item in itertools.chain(
     88       page_info.classes,
     89       page_info.properties,
     90       page_info.methods,
     91       page_info.other_members):
     92     meta_data.append(item)
     93 
     94   parts = [meta_data.build_html()]
     95 
     96   parts.append('# {page_info.full_name}\n\n'.format(page_info=page_info))
     97 
     98   parts.append('## Class `%s`\n\n' % page_info.full_name.split('.')[-1])
     99   if page_info.bases:
    100     parts.append('Inherits From: ')
    101 
    102     link_template = '[`{short_name}`]({url})'
    103     parts.append(', '.join(
    104         link_template.format(**base.__dict__) for base in page_info.bases))
    105 
    106   parts.append('\n\n')
    107 
    108   if len(page_info.aliases) > 1:
    109     parts.append('### Aliases:\n\n')
    110     parts.extend('* Class `%s`\n' % name for name in page_info.aliases)
    111     parts.append('\n')
    112 
    113   if page_info.defined_in is not None:
    114     parts.append('\n\n')
    115     parts.append(str(page_info.defined_in))
    116 
    117   parts.append(page_info.guides)
    118   parts.append(page_info.doc.docstring)
    119   parts.append(_build_function_details(page_info.doc.function_details))
    120   parts.append(_build_compatibility(page_info.doc.compatibility))
    121 
    122   parts.append('\n\n')
    123 
    124   if page_info.classes:
    125     parts.append('## Child Classes\n')
    126 
    127     link_template = ('[`class {class_info.short_name}`]'
    128                      '({class_info.url})\n\n')
    129     class_links = sorted(
    130         link_template.format(class_info=class_info)
    131         for class_info in page_info.classes)
    132 
    133     parts.extend(class_links)
    134 
    135   if page_info.properties:
    136     parts.append('## Properties\n\n')
    137     for prop_info in sorted(page_info.properties):
    138       h3 = '<h3 id="{short_name}"><code>{short_name}</code></h3>\n\n'
    139       parts.append(h3.format(short_name=prop_info.short_name))
    140 
    141       parts.append(prop_info.doc.docstring)
    142       parts.append(_build_function_details(prop_info.doc.function_details))
    143       parts.append(_build_compatibility(prop_info.doc.compatibility))
    144 
    145       parts.append('\n\n')
    146 
    147     parts.append('\n\n')
    148 
    149   if page_info.methods:
    150     parts.append('## Methods\n\n')
    151     # Sort the methods list, but make sure constructors come first.
    152     constructors = ['__init__', '__new__']
    153     inits = [method for method in page_info.methods
    154              if method.short_name in constructors]
    155     others = [method for method in page_info.methods
    156               if method.short_name not in constructors]
    157 
    158     for method_info in sorted(inits) + sorted(others):
    159       h3 = ('<h3 id="{short_name}">'
    160             '<code>{short_name}</code>'
    161             '</h3>\n\n')
    162       parts.append(h3.format(**method_info.__dict__))
    163 
    164       if method_info.signature is not None:
    165         parts.append(_build_signature(method_info, use_full_name=False))
    166 
    167       parts.append(method_info.doc.docstring)
    168       parts.append(_build_function_details(method_info.doc.function_details))
    169       parts.append(_build_compatibility(method_info.doc.compatibility))
    170       parts.append('\n\n')
    171     parts.append('\n\n')
    172 
    173   if page_info.other_members:
    174     parts.append('## Class Members\n\n')
    175 
    176     # TODO(markdaoust): Document the value of the members,
    177     #                   at least for basic types.
    178 
    179     h3 = '<h3 id="{short_name}"><code>{short_name}</code></h3>\n\n'
    180     others_member_headings = (h3.format(short_name=info.short_name)
    181                               for info in sorted(page_info.other_members))
    182     parts.extend(others_member_headings)
    183 
    184   return ''.join(parts)
    185 
    186 
    187 def _build_module_page(page_info):
    188   """Given a ClassPageInfo object Return the page as an md string."""
    189   meta_data = _Metadata(page_info.full_name)
    190 
    191   # Objects with their own pages are not added to the matadata list for the
    192   # module, as the only thing on the module page is a link to the object's page.
    193   for item in page_info.other_members:
    194     meta_data.append(item)
    195 
    196   parts = [meta_data.build_html()]
    197 
    198   parts.append(
    199       '# Module: {full_name}\n\n'.format(full_name=page_info.full_name))
    200 
    201   if len(page_info.aliases) > 1:
    202     parts.append('### Aliases:\n\n')
    203     parts.extend('* Module `%s`\n' % name for name in page_info.aliases)
    204     parts.append('\n')
    205 
    206   if page_info.defined_in is not None:
    207     parts.append('\n\n')
    208     parts.append(str(page_info.defined_in))
    209 
    210   parts.append(page_info.doc.docstring)
    211   parts.append(_build_compatibility(page_info.doc.compatibility))
    212 
    213   parts.append('\n\n')
    214 
    215   if page_info.modules:
    216     parts.append('## Modules\n\n')
    217     template = '[`{short_name}`]({url}) module'
    218 
    219     for item in page_info.modules:
    220       parts.append(template.format(**item.__dict__))
    221 
    222       if item.doc.brief:
    223         parts.append(': ' + item.doc.brief)
    224 
    225       parts.append('\n\n')
    226 
    227   if page_info.classes:
    228     parts.append('## Classes\n\n')
    229     template = '[`class {short_name}`]({url})'
    230 
    231     for item in page_info.classes:
    232       parts.append(template.format(**item.__dict__))
    233 
    234       if item.doc.brief:
    235         parts.append(': ' + item.doc.brief)
    236 
    237       parts.append('\n\n')
    238 
    239   if page_info.functions:
    240     parts.append('## Functions\n\n')
    241     template = '[`{short_name}(...)`]({url})'
    242 
    243     for item in page_info.functions:
    244       parts.append(template.format(**item.__dict__))
    245 
    246       if item.doc.brief:
    247         parts.append(': ' + item.doc.brief)
    248 
    249       parts.append('\n\n')
    250 
    251   if page_info.other_members:
    252     # TODO(markdaoust): Document the value of the members,
    253     #                   at least for basic types.
    254     parts.append('## Other Members\n\n')
    255 
    256     for item in page_info.other_members:
    257       parts.append('`{short_name}`\n\n'.format(**item.__dict__))
    258 
    259   return ''.join(parts)
    260 
    261 
    262 def _build_signature(obj_info, use_full_name=True):
    263   """Returns a md code block showing the function signature."""
    264   # Special case tf.range, since it has an optional first argument
    265   if obj_info.full_name == 'tf.range':
    266     return (
    267         '``` python\n'
    268         "tf.range(limit, delta=1, dtype=None, name='range')\n"
    269         "tf.range(start, limit, delta=1, dtype=None, name='range')\n"
    270         '```\n\n')
    271 
    272   parts = ['``` python']
    273   parts.extend(['@' + dec for dec in obj_info.decorators])
    274   signature_template = '{name}({sig})'
    275 
    276   if not obj_info.signature:
    277     sig = ''
    278   elif len(obj_info.signature) == 1:
    279     sig = obj_info.signature[0]
    280   else:
    281     sig = ',\n'.join('    %s' % sig_item for sig_item in obj_info.signature)
    282     sig = '\n'+sig+'\n'
    283 
    284   if use_full_name:
    285     obj_name = obj_info.full_name
    286   else:
    287     obj_name = obj_info.short_name
    288   parts.append(signature_template.format(name=obj_name, sig=sig))
    289   parts.append('```\n\n')
    290 
    291   return '\n'.join(parts)
    292 
    293 
    294 def _build_compatibility(compatibility):
    295   """Return the compatibility section as an md string."""
    296   parts = []
    297   sorted_keys = sorted(compatibility.keys())
    298   for key in sorted_keys:
    299 
    300     value = compatibility[key]
    301     # Dedent so that it does not trigger markdown code formatting.
    302     value = textwrap.dedent(value)
    303     parts.append('\n\n#### %s Compatibility\n%s\n' % (key.title(), value))
    304 
    305   return ''.join(parts)
    306 
    307 
    308 def _build_function_details(function_details):
    309   """Return the function details section as an md string."""
    310   parts = []
    311   for detail in function_details:
    312     sub = []
    313     sub.append('#### ' + detail.keyword + ':\n\n')
    314     sub.append(textwrap.dedent(detail.header))
    315     for key, value in detail.items:
    316       sub.append('* <b>`%s`</b>: %s' % (key, value))
    317     parts.append(''.join(sub))
    318 
    319   return '\n'.join(parts)
    320 
    321 
    322 class _Metadata(object):
    323   """A class for building a page's Metadata block.
    324 
    325   Attributes:
    326     name: The name of the page being described by the Metadata block.
    327   """
    328 
    329   def __init__(self, name):
    330     """Create a Metadata builder.
    331 
    332     Args:
    333       name: The name of the page being described by the Metadata block.
    334     """
    335     self.name = name
    336     self._content = []
    337 
    338   def append(self, item):
    339     """Add an item from the page to the Metadata block.
    340 
    341     Args:
    342       item: The parsed page section to add.
    343     """
    344     self._content.append(item.short_name)
    345 
    346   def build_html(self):
    347     """Return the Metadata block as an Html string."""
    348     schema = 'http://developers.google.com/ReferenceObject'
    349     parts = ['<div itemscope itemtype="%s">' % schema]
    350 
    351     parts.append('<meta itemprop="name" content="%s" />' % self.name)
    352     for item in self._content:
    353       parts.append('<meta itemprop="property" content="%s"/>' % item)
    354 
    355     parts.extend(['</div>', '', ''])
    356 
    357     return '\n'.join(parts)
    358