Example #1
0
def _get_headers(page_info: parser.PageInfo, search_hints: bool) -> List[str]:
    """Returns the list of header lines for this page."""
    hidden = doc_controls.should_hide_from_search(page_info.py_object)
    brief_no_backticks = page_info.doc.brief.replace('`', '').strip()
    headers = []
    if brief_no_backticks:
        headers.append(f'description: {brief_no_backticks}')

    # It's easier to read if there's a blank line after the `name:value` headers.
    if search_hints and not hidden:
        if headers:
            headers.append('')
        headers.append(page_info.get_metadata_html())
    else:
        headers.append('robots: noindex')
        headers.append('')

    return headers
Example #2
0
def write_docs(
    *,
    output_dir: Union[str, pathlib.Path],
    parser_config: parser.ParserConfig,
    yaml_toc: bool,
    root_module_name: str,
    root_title: str = 'TensorFlow',
    search_hints: bool = True,
    site_path: str = 'api_docs/python',
    gen_redirects: bool = True,
    table_view: bool = True,
    gen_report: bool = False,
):
    """Write previously extracted docs to disk.

  Write a docs page for each symbol included in the indices of parser_config to
  a tree of docs at `output_dir`.

  Symbols with multiple aliases will have only one page written about
  them, which is referenced for all aliases.

  Args:
    output_dir: Directory to write documentation markdown files to. Will be
      created if it doesn't exist.
    parser_config: A `parser.ParserConfig` object, containing all the necessary
      indices.
    yaml_toc: Set to `True` to generate a "_toc.yaml" file.
    root_module_name: (str) the name of the root module (`tf` for tensorflow).
    root_title: The title name for the root level index.md.
    search_hints: (bool) include meta-data search hints at the top of each
      output file.
    site_path: The output path relative to the site root. Used in the
      `_toc.yaml` and `_redirects.yaml` files.
    gen_redirects: Bool which decides whether to generate _redirects.yaml file
      or not.
    table_view: If True, `Args`, `Returns`, `Raises` or `Attributes` will be
      converted to a tabular format while generating markdown. If False, they
      will be converted to a markdown List view.
    gen_report: If True, a report for the library is generated by linting the
      docstrings of its public API symbols.

  Raises:
    ValueError: if `output_dir` is not an absolute path
  """
    output_dir = pathlib.Path(output_dir)
    site_path = pathlib.Path('/', site_path)

    # Make output_dir.
    if not output_dir.is_absolute():
        raise ValueError("'output_dir' must be an absolute path.\n"
                         f"    output_dir='{output_dir}'")
    output_dir.mkdir(parents=True, exist_ok=True)

    # These dictionaries are used for table-of-contents generation below
    # They will contain, after the for-loop below::
    #  - module name(string):classes and functions the module contains(list)
    module_children = {}

    # Collect redirects for an api _redirects.yaml file.
    redirects = []

    if gen_report:
        api_report_obj = utils.ApiReport()

    # Parse and write Markdown pages, resolving cross-links (`tf.symbol`).
    for full_name in sorted(parser_config.index.keys(),
                            key=lambda k: k.lower()):
        py_object = parser_config.index[full_name]

        if full_name in parser_config.duplicate_of:
            continue

        # Methods constants are only documented only as part of their parent's page.
        if parser_config.reference_resolver.is_fragment(full_name):
            continue

        # Remove the extension from the path.
        docpath, _ = os.path.splitext(parser.documentation_path(full_name))

        # For a module, remember the module for the table-of-contents
        if inspect.ismodule(py_object):
            if full_name in parser_config.tree:
                mod_obj = Module(module=full_name,
                                 py_object=py_object,
                                 path=str(site_path / docpath))
                module_children[full_name] = mod_obj
        # For something else that's documented,
        # figure out what module it lives in
        else:
            subname = str(full_name)
            while True:
                subname = subname[:subname.rindex('.')]
                if inspect.ismodule(parser_config.index[subname]):
                    module_name = parser_config.duplicate_of.get(
                        subname, subname)
                    child_mod = ModuleChild(name=full_name,
                                            py_object=py_object,
                                            parent=module_name,
                                            path=str(site_path / docpath))
                    module_children[module_name].add_children(child_mod)
                    break

        # Generate docs for `py_object`, resolving references.
        try:
            page_info = parser.docs_for_object(full_name, py_object,
                                               parser_config)
        except:
            raise ValueError(
                f'Failed to generate docs for symbol: `{full_name}`')

        if gen_report and not full_name.startswith(
            ('tf.compat.v', 'tf.keras.backend')):
            api_report_obj.fill_metrics(page_info)
            continue

        path = output_dir / parser.documentation_path(full_name)
        try:
            path.parent.mkdir(exist_ok=True, parents=True)
            # This function returns unicode in PY3.
            hidden = doc_controls.should_hide_from_search(page_info.py_object)
            brief_no_backticks = page_info.doc.brief.replace('`', '').strip()
            content = []
            if brief_no_backticks:
                content.append(f'description: {brief_no_backticks}\n')

            if search_hints and not hidden:
                content.append(page_info.get_metadata_html())
            else:
                content.append('robots: noindex\n')

            content.append(pretty_docs.build_md_page(page_info, table_view))
            text = '\n'.join(content)
            path.write_text(text, encoding='utf-8')
        except OSError:
            raise OSError('Cannot write documentation for '
                          f'{full_name} to {path.parent}')

        duplicates = parser_config.duplicates.get(full_name, [])
        if not duplicates:
            continue

        duplicates = [item for item in duplicates if item != full_name]

        if gen_redirects:
            for dup in duplicates:
                from_path = site_path / dup.replace('.', '/')
                to_path = site_path / full_name.replace('.', '/')
                redirects.append({'from': str(from_path), 'to': str(to_path)})

    if gen_report:
        serialized_proto = api_report_obj.api_report.SerializeToString()
        raw_proto = output_dir / 'api_report.pb'
        raw_proto.write_bytes(serialized_proto)
        return

    if yaml_toc:
        toc_gen = GenerateToc(module_children)
        toc_dict = toc_gen.generate()

        # Replace the overview path *only* for 'TensorFlow' to
        # `/api_docs/python/tf_overview`. This will be redirected to
        # `/api_docs/python/tf`.
        toc_values = toc_dict['toc'][0]
        if toc_values['title'] == 'tf':
            section = toc_values['section'][0]
            section['path'] = str(site_path / 'tf_overview')

        leftnav_toc = output_dir / root_module_name / '_toc.yaml'
        with open(leftnav_toc, 'w') as toc_file:
            yaml.dump(toc_dict, toc_file, default_flow_style=False)

    if redirects and gen_redirects:
        if yaml_toc and toc_values['title'] == 'tf':
            redirects.append({
                'from': str(site_path / 'tf_overview'),
                'to': str(site_path / 'tf'),
            })
        redirects_dict = {
            'redirects': sorted(redirects,
                                key=lambda redirect: redirect['from'])
        }

        api_redirects_path = output_dir / root_module_name / '_redirects.yaml'
        with open(api_redirects_path, 'w') as redirect_file:
            yaml.dump(redirects_dict, redirect_file, default_flow_style=False)

    # Write a global index containing all full names with links.
    with open(output_dir / root_module_name / 'all_symbols.md', 'w') as f:
        global_index = parser.generate_global_index(
            root_title, parser_config.index, parser_config.reference_resolver)
        if not search_hints:
            global_index = 'robots: noindex\n' + global_index
        f.write(global_index)
def write_docs(output_dir,
               parser_config,
               yaml_toc,
               root_title='TensorFlow',
               search_hints=True,
               site_path='api_docs/python'):
    """Write previously extracted docs to disk.

  Write a docs page for each symbol included in the indices of parser_config to
  a tree of docs at `output_dir`.

  Symbols with multiple aliases will have only one page written about
  them, which is referenced for all aliases.

  Args:
    output_dir: Directory to write documentation markdown files to. Will be
      created if it doesn't exist.
    parser_config: A `parser.ParserConfig` object, containing all the necessary
      indices.
    yaml_toc: Set to `True` to generate a "_toc.yaml" file.
    root_title: The title name for the root level index.md.
    search_hints: (bool) include meta-data search hints at the top of each
      output file.
    site_path: The output path relative to the site root. Used in the
      `_toc.yaml` and `_redirects.yaml` files.

  Raises:
    ValueError: if `output_dir` is not an absolute path
  """
    output_dir = pathlib.Path(output_dir)
    site_path = pathlib.Path('/', site_path)

    # Make output_dir.
    if not output_dir.is_absolute():
        raise ValueError("'output_dir' must be an absolute path.\n"
                         f"    output_dir='{output_dir}'")
    output_dir.mkdir(parents=True, exist_ok=True)

    # These dictionaries are used for table-of-contents generation below
    # They will contain, after the for-loop below::
    #  - module name(string):classes and functions the module contains(list)
    module_children = {}

    # Collect redirects for an api _redirects.yaml file.
    redirects = []

    # Parse and write Markdown pages, resolving cross-links (`tf.symbol`).
    for full_name in sorted(parser_config.index.keys(),
                            key=lambda k: k.lower()):
        py_object = parser_config.index[full_name]

        if full_name in parser_config.duplicate_of:
            continue

        # Methods and some routines are documented only as part of their class.
        if not (inspect.ismodule(py_object)
                or inspect.isclass(py_object) or parser.is_free_function(
                    py_object, full_name, parser_config.index)):
            continue

        # Remove the extension from the path.
        docpath, _ = os.path.splitext(parser.documentation_path(full_name))

        # For a module, remember the module for the table-of-contents
        if inspect.ismodule(py_object):
            if full_name in parser_config.tree:
                mod_obj = Module(module=full_name,
                                 py_object=py_object,
                                 path=str(site_path / docpath))
                module_children[full_name] = mod_obj
        # For something else that's documented,
        # figure out what module it lives in
        else:
            subname = str(full_name)
            while True:
                subname = subname[:subname.rindex('.')]
                if inspect.ismodule(parser_config.index[subname]):
                    module_name = parser_config.duplicate_of.get(
                        subname, subname)
                    child_mod = ModuleChild(name=full_name,
                                            py_object=py_object,
                                            parent=module_name,
                                            path=str(site_path / docpath))
                    module_children[module_name].add_children(child_mod)
                    break

        # Generate docs for `py_object`, resolving references.
        try:
            page_info = parser.docs_for_object(full_name, py_object,
                                               parser_config)
        except:
            raise ValueError(
                f'Failed to generate docs for symbol: `{full_name}`')

        path = output_dir / parser.documentation_path(full_name)
        try:
            path.parent.mkdir(exist_ok=True, parents=True)
            # This function returns unicode in PY3.
            hidden = doc_controls.should_hide_from_search(page_info.py_object)
            if search_hints and not hidden:
                content = [page_info.get_metadata_html()]
            else:
                content = ['robots: noindex\n']

            content.append(pretty_docs.build_md_page(page_info))
            text = '\n'.join(content)
            path.write_text(text, encoding='utf-8')
        except OSError:
            raise OSError('Cannot write documentation for '
                          f'{full_name} to {path.parent}')

        duplicates = parser_config.duplicates.get(full_name, [])
        if not duplicates:
            continue

        duplicates = [item for item in duplicates if item != full_name]

        for dup in duplicates:
            from_path = site_path / dup.replace('.', '/')
            to_path = site_path / full_name.replace('.', '/')
            redirects.append({'from': str(from_path), 'to': str(to_path)})

    if yaml_toc:
        toc_gen = GenerateToc(module_children)
        toc_dict = toc_gen.generate()

        # Replace the overview path *only* for 'TensorFlow' to
        # `/api_docs/python/tf_overview`. This will be redirected to
        # `/api_docs/python/tf`.
        toc_values = toc_dict['toc'][0]
        if toc_values['title'] == 'tf':
            section = toc_values['section'][0]
            section['path'] = str(site_path / 'tf_overview')

        leftnav_toc = output_dir / '_toc.yaml'
        with open(leftnav_toc, 'w') as toc_file:
            yaml.dump(toc_dict, toc_file, default_flow_style=False)

    if redirects:
        if yaml_toc and toc_values['title'] == 'tf':
            redirects.append({
                'from': str(site_path / 'tf_overview'),
                'to': str(site_path / 'tf'),
            })
        redirects_dict = {
            'redirects': sorted(redirects,
                                key=lambda redirect: redirect['from'])
        }

        api_redirects_path = output_dir / '_redirects.yaml'
        with open(api_redirects_path, 'w') as redirect_file:
            yaml.dump(redirects_dict, redirect_file, default_flow_style=False)

    # Write a global index containing all full names with links.
    with open(output_dir / 'index.md', 'w') as f:
        f.write(
            parser.generate_global_index(root_title, parser_config.index,
                                         parser_config.reference_resolver))