• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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