def test_generate_index(self): module = sys.modules[__name__] index = { 'TestModule': module, 'test_function': test_function, 'TestModule.test_function': test_function, 'TestModule.TestClass': TestClass, 'TestModule.TestClass.a_method': TestClass.a_method, 'TestModule.TestClass.a_property': TestClass.a_property, 'TestModule.TestClass.ChildClass': TestClass.ChildClass, } duplicate_of = { 'TestModule.test_function': 'test_function' } docs = parser.generate_global_index('TestLibrary', index=index, duplicate_of=duplicate_of) # Make sure duplicates and non-top-level symbols are in the index, but # methods and properties are not. self.assertTrue('a_method' not in docs) self.assertTrue('a_property' not in docs) self.assertTrue('TestModule.TestClass' in docs) self.assertTrue('TestModule.TestClass.ChildClass' in docs) self.assertTrue('TestModule.test_function' in docs) # Leading backtick to make sure it's included top-level. # This depends on formatting, but should be stable. self.assertTrue('`test_function' in docs)
def test_generate_index(self): module = sys.modules[__name__] index = { 'TestModule': module, 'test_function': test_function, 'TestModule.test_function': test_function, 'TestModule.TestClass': TestClass, 'TestModule.TestClass.a_method': TestClass.a_method, 'TestModule.TestClass.a_property': TestClass.a_property, 'TestModule.TestClass.ChildClass': TestClass.ChildClass, } duplicate_of = {'TestModule.test_function': 'test_function'} reference_resolver = parser.ReferenceResolver( duplicate_of=duplicate_of, doc_index={}, index=index, py_module_names=['tf']) docs = parser.generate_global_index( 'TestLibrary', index=index, reference_resolver=reference_resolver) # Make sure duplicates and non-top-level symbols are in the index, but # methods and properties are not. self.assertNotIn('a_method', docs) self.assertNotIn('a_property', docs) self.assertIn('TestModule.TestClass', docs) self.assertIn('TestModule.TestClass.ChildClass', docs) self.assertIn('TestModule.test_function', docs) # Leading backtick to make sure it's included top-level. # This depends on formatting, but should be stable. self.assertIn('`test_function', docs)
def test_generate_index(self): module = sys.modules[__name__] index = { 'TestModule': module, 'test_function': test_function, 'TestModule.test_function': test_function, 'TestModule.TestClass': TestClass, 'TestModule.TestClass.a_method': TestClass.a_method, 'TestModule.TestClass.a_property': TestClass.a_property, 'TestModule.TestClass.ChildClass': TestClass.ChildClass, } duplicate_of = { 'TestModule.test_function': 'test_function' } visitor = DummyVisitor(index=index, duplicate_of=duplicate_of) reference_resolver = parser.ReferenceResolver.from_visitor( visitor=visitor, doc_index={}, py_module_names=['tf']) docs = parser.generate_global_index('TestLibrary', index=index, reference_resolver=reference_resolver) # Make sure duplicates and non-top-level symbols are in the index, but # methods and properties are not. self.assertNotIn('a_method', docs) self.assertNotIn('a_property', docs) self.assertIn('TestModule.TestClass', docs) self.assertIn('TestModule.TestClass.ChildClass', docs) self.assertIn('TestModule.test_function', docs) # Leading backtick to make sure it's included top-level. # This depends on formatting, but should be stable. self.assertIn('<code>test_function', docs)
def write_docs(output_dir, parser_config, yaml_toc, root_title='TensorFlow', search_hints=True, site_api_path=None): """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_api_path: Used to write the api-duplicates _redirects.yaml file. if None (the default) the file is not generated. Raises: ValueError: if `output_dir` is not an absolute path """ # Make output_dir. if not os.path.isabs(output_dir): raise ValueError("'output_dir' must be an absolute path.\n" " output_dir='%s'" % output_dir) if not os.path.exists(output_dir): os.makedirs(output_dir) # 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 = {} # - symbol name(string):pathname (string) symbol_to_file = {} # Collect redirects for an api _redirects.yaml file. redirects = ['redirects:\n'] # Parse and write Markdown pages, resolving cross-links (@{symbol}). for full_name, py_object in six.iteritems(parser_config.index): parser_config.reference_resolver.current_doc_full_name = 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 (tf_inspect.ismodule(py_object) or tf_inspect.isclass(py_object) or _is_free_function( py_object, full_name, parser_config.index)): continue sitepath = os.path.join('api_docs/python', parser.documentation_path(full_name)[:-3]) # For TOC, we need to store a mapping from full_name to the file # we're generating symbol_to_file[full_name] = sitepath # For a module, remember the module for the table-of-contents if tf_inspect.ismodule(py_object): if full_name in parser_config.tree: module_children.setdefault(full_name, []) # 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 tf_inspect.ismodule(parser_config.index[subname]): module_children.setdefault(subname, []).append(full_name) break # Generate docs for `py_object`, resolving references. page_info = parser.docs_for_object(full_name, py_object, parser_config) path = os.path.join(output_dir, parser.documentation_path(full_name)) directory = os.path.dirname(path) try: if not os.path.exists(directory): os.makedirs(directory) # This function returns raw bytes in PY2 or unicode in PY3. if search_hints: content = [page_info.get_metadata_html()] else: content = [''] content.append(pretty_docs.build_md_page(page_info)) text = '\n'.join(content) if six.PY3: text = text.encode('utf-8') with open(path, 'wb') as f: f.write(text) except OSError: raise OSError('Cannot write documentation for %s to %s' % (full_name, directory)) if site_api_path: duplicates = parser_config.duplicates.get(full_name, []) if not duplicates: continue duplicates = [item for item in duplicates if item != full_name] template = ('- from: /{}\n' ' to: /{}\n') for dup in duplicates: from_path = os.path.join(site_api_path, dup.replace('.', '/')) to_path = os.path.join(site_api_path, full_name.replace('.', '/')) redirects.append(template.format(from_path, to_path)) if site_api_path: api_redirects_path = os.path.join(output_dir, '_redirects.yaml') with open(api_redirects_path, 'w') as redirect_file: redirect_file.write(''.join(redirects)) if yaml_toc: # Generate table of contents # Put modules in alphabetical order, case-insensitive modules = sorted(module_children.keys(), key=lambda a: a.upper()) leftnav_path = os.path.join(output_dir, '_toc.yaml') with open(leftnav_path, 'w') as f: # Generate header f.write( '# Automatically generated file; please do not edit\ntoc:\n') for module in modules: indent_num = module.count('.') # Don't list `tf.submodule` inside `tf` indent_num = max(indent_num, 1) indent = ' ' * indent_num if indent_num > 1: # tf.contrib.baysflow.entropy will be under # tf.contrib->baysflow->entropy title = module.split('.')[-1] else: title = module header = [ '- title: ' + title, ' section:', ' - title: Overview', ' path: /TARGET_DOC_ROOT/VERSION/' + symbol_to_file[module] ] header = ''.join([indent + line + '\n' for line in header]) f.write(header) symbols_in_module = module_children.get(module, []) # Sort case-insensitive, if equal sort case sensitive (upper first) symbols_in_module.sort(key=lambda a: (a.upper(), a)) for full_name in symbols_in_module: item = [ ' - title: ' + full_name[len(module) + 1:], ' path: /TARGET_DOC_ROOT/VERSION/' + symbol_to_file[full_name] ] item = ''.join([indent + line + '\n' for line in item]) f.write(item) # Write a global index containing all full names with links. with open(os.path.join(output_dir, 'index.md'), 'w') as f: f.write( parser.generate_global_index(root_title, parser_config.index, parser_config.reference_resolver))
def write_docs(output_dir, base_dir, duplicate_of, duplicates, index, tree, reverse_index, doc_index, guide_index): """Write previously extracted docs to disk. Write a docs page for each symbol in `index` 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. `duplicate_of` and `duplicates` are used to determine which docs pages to write. Args: output_dir: Directory to write documentation markdown files to. Will be created if it doesn't exist. base_dir: Base directory of the code being documented. This prefix is stripped from all file paths that are part of the documentation. duplicate_of: A `dict` mapping fully qualified names to "master" names. This is used to resolve "@{symbol}" references to the "master" name. duplicates: A `dict` mapping fully qualified names to a set of all aliases of this name. This is used to automatically generate a list of all aliases for each name. index: A `dict` mapping fully qualified names to the corresponding Python objects. Used to produce docs for child objects, and to check the validity of "@{symbol}" references. tree: A `dict` mapping a fully qualified name to the names of all its members. Used to populate the members section of a class or module page. reverse_index: A `dict` mapping object ids to fully qualified names. doc_index: A `dict` mapping a doc key to a DocInfo. guide_index: A `dict` mapping symbol name strings to GuideRef. """ # Make output_dir. try: if not os.path.exists(output_dir): os.makedirs(output_dir) except OSError as e: print('Creating output dir "%s" failed: %s' % (output_dir, e)) raise # Parse and write Markdown pages, resolving cross-links (@{symbol}). for full_name, py_object in six.iteritems(index): if full_name in 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 inspect.isfunction(py_object)): continue print('Writing docs for %s (%r).' % (full_name, py_object)) # Generate docs for `py_object`, resolving references. markdown = parser.generate_markdown(full_name, py_object, duplicate_of=duplicate_of, duplicates=duplicates, index=index, tree=tree, reverse_index=reverse_index, doc_index=doc_index, guide_index=guide_index, base_dir=base_dir) # TODO(deannarubin): use _tree to generate sidebar information. path = os.path.join(output_dir, parser.documentation_path(full_name)) directory = os.path.dirname(path) try: if not os.path.exists(directory): os.makedirs(directory) with open(path, 'w') as f: f.write(markdown) except OSError as e: print('Cannot write documentation for %s to %s: %s' % (full_name, directory, e)) raise # TODO(deannarubin): write sidebar file? # Write a global index containing all full names with links. with open(os.path.join(output_dir, 'index.md'), 'w') as f: f.write(parser.generate_global_index('TensorFlow', index, duplicate_of))
def write_docs(output_dir, parser_config, yaml_toc, root_title='TensorFlow'): """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. Raises: ValueError: if `output_dir` is not an absolute path """ # Make output_dir. if not os.path.isabs(output_dir): raise ValueError("'output_dir' must be an absolute path.\n" " output_dir='%s'" % output_dir) try: if not os.path.exists(output_dir): os.makedirs(output_dir) except OSError as e: print('Creating output dir "%s" failed: %s' % (output_dir, e)) raise # 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 = {} # - symbol name(string):pathname (string) symbol_to_file = {} # Parse and write Markdown pages, resolving cross-links (@{symbol}). for full_name, py_object in six.iteritems(parser_config.index): parser_config.reference_resolver.current_doc_full_name = 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 (tf_inspect.ismodule(py_object) or tf_inspect.isclass(py_object) or _is_free_function(py_object, full_name, parser_config.index)): continue sitepath = os.path.join('api_docs/python', parser.documentation_path(full_name)[:-3]) # For TOC, we need to store a mapping from full_name to the file # we're generating symbol_to_file[full_name] = sitepath # For a module, remember the module for the table-of-contents if tf_inspect.ismodule(py_object): if full_name in parser_config.tree: module_children.setdefault(full_name, []) # 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 tf_inspect.ismodule(parser_config.index[subname]): module_children.setdefault(subname, []).append(full_name) break print('Writing docs for %s (%r).' % (full_name, py_object)) # Generate docs for `py_object`, resolving references. page_info = parser.docs_for_object(full_name, py_object, parser_config) path = os.path.join(output_dir, parser.documentation_path(full_name)) directory = os.path.dirname(path) try: if not os.path.exists(directory): os.makedirs(directory) with open(path, 'w') as f: f.write(pretty_docs.build_md_page(page_info)) except OSError as e: print('Cannot write documentation for %s to %s: %s' % (full_name, directory, e)) raise if yaml_toc: # Generate table of contents # Put modules in alphabetical order, case-insensitive modules = sorted(module_children.keys(), key=lambda a: a.upper()) leftnav_path = os.path.join(output_dir, '_toc.yaml') with open(leftnav_path, 'w') as f: # Generate header f.write('# Automatically generated file; please do not edit\ntoc:\n') for module in modules: indent_num = module.count('.') # Don't list `tf.submodule` inside `tf` indent_num = max(indent_num, 1) indent = ' '*indent_num if indent_num > 1: # tf.contrib.baysflow.entropy will be under # tf.contrib->baysflow->entropy title = module.split('.')[-1] else: title = module header = [ '- title: ' + title, ' section:', ' - title: Overview', ' path: /TARGET_DOC_ROOT/VERSION/' + symbol_to_file[module]] header = ''.join([indent+line+'\n' for line in header]) f.write(header) symbols_in_module = module_children.get(module, []) # Sort case-insensitive, if equal sort case sensitive (upper first) symbols_in_module.sort(key=lambda a: (a.upper(), a)) for full_name in symbols_in_module: item = [ ' - title: ' + full_name[len(module) + 1:], ' path: /TARGET_DOC_ROOT/VERSION/' + symbol_to_file[full_name]] item = ''.join([indent+line+'\n' for line in item]) f.write(item) # Write a global index containing all full names with links. with open(os.path.join(output_dir, 'index.md'), 'w') as f: f.write( parser.generate_global_index(root_title, parser_config.index, parser_config.reference_resolver))
def write_docs(output_dir, parser_config, duplicate_of, index, yaml_toc): """Write previously extracted docs to disk. Write a docs page for each symbol in `index` 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. duplicate_of: A `dict` mapping fully qualified names to "master" names. Used to determine which docs pages to write. index: A `dict` mapping fully qualified names to the corresponding Python objects. Used to produce docs for child objects. yaml_toc: Set to `True` to generate a "_toc.yaml" file. """ # Make output_dir. try: if not os.path.exists(output_dir): os.makedirs(output_dir) except OSError as e: print('Creating output dir "%s" failed: %s' % (output_dir, e)) raise # 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 = {} # - symbol name(string):pathname (string) symbol_to_file = {} # Parse and write Markdown pages, resolving cross-links (@{symbol}). for full_name, py_object in six.iteritems(index): if full_name in 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 _is_free_function(py_object, full_name, index)): continue sitepath = os.path.join('api_docs/python', parser.documentation_path(full_name)[:-3]) # For TOC, we need to store a mapping from full_name to the file # we're generating symbol_to_file[full_name] = sitepath # For a module, remember the module for the table-of-contents if inspect.ismodule(py_object): if full_name in parser_config.tree: module_children.setdefault(full_name, []) # 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(index[subname]): module_children.setdefault(subname, []).append(full_name) break print('Writing docs for %s (%r).' % (full_name, py_object)) # Generate docs for `py_object`, resolving references. page_info = parser.docs_for_object(full_name, py_object, parser_config) path = os.path.join(output_dir, parser.documentation_path(full_name)) directory = os.path.dirname(path) try: if not os.path.exists(directory): os.makedirs(directory) with open(path, 'w') as f: f.write(pretty_docs.build_md_page(page_info)) except OSError as e: print('Cannot write documentation for %s to %s: %s' % (full_name, directory, e)) raise if yaml_toc: # Generate table of contents # Put modules in alphabetical order, case-insensitive modules = sorted(module_children.keys(), key=lambda a: a.upper()) leftnav_path = os.path.join(output_dir, '_toc.yaml') with open(leftnav_path, 'w') as f: # Generate header f.write('# Automatically generated file; please do not edit\ntoc:\n') for module in modules: f.write(' - title: ' + module + '\n' ' section:\n' + ' - title: Overview\n' + ' path: /TARGET_DOC_ROOT/' + symbol_to_file[module] + '\n') symbols_in_module = module_children.get(module, []) symbols_in_module.sort(key=lambda a: a.upper()) for full_name in symbols_in_module: f.write(' - title: ' + full_name[len(module)+1:] + '\n' ' path: /TARGET_DOC_ROOT/' + symbol_to_file[full_name] + '\n') # Write a global index containing all full names with links. with open(os.path.join(output_dir, 'index.md'), 'w') as f: f.write(parser.generate_global_index( 'TensorFlow', index, parser_config.reference_resolver))
def write_docs(output_dir, parser_config, yaml_toc): """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. Raises: ValueError: if `output_dir` is not an absolute path """ # Make output_dir. if not os.path.isabs(output_dir): raise ValueError( "'output_dir' must be an absolute path.\n" " output_dir='%s'" % output_dir) try: if not os.path.exists(output_dir): os.makedirs(output_dir) except OSError as e: print('Creating output dir "%s" failed: %s' % (output_dir, e)) raise # 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 = {} # - symbol name(string):pathname (string) symbol_to_file = {} # Parse and write Markdown pages, resolving cross-links (@{symbol}). for full_name, py_object in six.iteritems(parser_config.index): if full_name in parser_config.duplicate_of: continue # Methods and some routines are documented only as part of their class. if not (tf_inspect.ismodule(py_object) or tf_inspect.isclass(py_object) or _is_free_function(py_object, full_name, parser_config.index)): continue sitepath = os.path.join('api_docs/python', parser.documentation_path(full_name)[:-3]) # For TOC, we need to store a mapping from full_name to the file # we're generating symbol_to_file[full_name] = sitepath # For a module, remember the module for the table-of-contents if tf_inspect.ismodule(py_object): if full_name in parser_config.tree: module_children.setdefault(full_name, []) # 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 tf_inspect.ismodule(parser_config.index[subname]): module_children.setdefault(subname, []).append(full_name) break print('Writing docs for %s (%r).' % (full_name, py_object)) # Generate docs for `py_object`, resolving references. page_info = parser.docs_for_object(full_name, py_object, parser_config) path = os.path.join(output_dir, parser.documentation_path(full_name)) directory = os.path.dirname(path) try: if not os.path.exists(directory): os.makedirs(directory) with open(path, 'w') as f: f.write(pretty_docs.build_md_page(page_info)) except OSError as e: print('Cannot write documentation for %s to %s: %s' % (full_name, directory, e)) raise if yaml_toc: # Generate table of contents # Put modules in alphabetical order, case-insensitive modules = sorted(module_children.keys(), key=lambda a: a.upper()) leftnav_path = os.path.join(output_dir, '_toc.yaml') with open(leftnav_path, 'w') as f: # Generate header f.write('# Automatically generated file; please do not edit\ntoc:\n') for module in modules: f.write(' - title: ' + module + '\n' ' section:\n' + ' - title: Overview\n' + ' path: /TARGET_DOC_ROOT/VERSION/' + symbol_to_file[module] + '\n') symbols_in_module = module_children.get(module, []) # Sort case-insensitive, if equal sort case sensitive (upper first) symbols_in_module.sort(key=lambda a: (a.upper(), a)) for full_name in symbols_in_module: f.write(' - title: ' + full_name[len(module) + 1:] + '\n' ' path: /TARGET_DOC_ROOT/VERSION/' + symbol_to_file[full_name] + '\n') # Write a global index containing all full names with links. with open(os.path.join(output_dir, 'index.md'), 'w') as f: f.write( parser.generate_global_index('TensorFlow', parser_config.index, parser_config.reference_resolver))
def write_docs(output_dir, base_dir, duplicate_of, duplicates, index, tree, reverse_index, guide_index): """Write previously extracted docs to disk. Write a docs page for each symbol in `index` 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. `duplicate_of` and `duplicates` are used to determine which docs pages to write. Args: output_dir: Directory to write documentation markdown files to. Will be created if it doesn't exist. base_dir: Base directory of the code being documented. This prefix is stripped from all file paths that are part of the documentation. duplicate_of: A `dict` mapping fully qualified names to "master" names. This is used to resolve "@{symbol}" references to the "master" name. duplicates: A `dict` mapping fully qualified names to a set of all aliases of this name. This is used to automatically generate a list of all aliases for each name. index: A `dict` mapping fully qualified names to the corresponding Python objects. Used to produce docs for child objects, and to check the validity of "@{symbol}" references. tree: A `dict` mapping a fully qualified name to the names of all its members. Used to populate the members section of a class or module page. reverse_index: A `dict` mapping object ids to fully qualified names. guide_index: A `dict` mapping symbol name strings to GuideRef. """ # Make output_dir. try: if not os.path.exists(output_dir): os.makedirs(output_dir) except OSError as e: print('Creating output dir "%s" failed: %s' % (output_dir, e)) raise # Parse and write Markdown pages, resolving cross-links (@{symbol}). for full_name, py_object in six.iteritems(index): if full_name in duplicate_of: print('Not writing docs for %s, duplicate of %s.' % ( full_name, duplicate_of[full_name])) 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 inspect.isfunction(py_object)): print('Not writing docs for %s, not a class, module, or function.' % ( full_name)) continue print('Writing docs for %s (%r).' % (full_name, py_object)) # Generate docs for `py_object`, resolving references. markdown = parser.generate_markdown(full_name, py_object, duplicate_of=duplicate_of, duplicates=duplicates, index=index, tree=tree, reverse_index=reverse_index, guide_index=guide_index, base_dir=base_dir) # TODO(deannarubin): use _tree to generate sidebar information. path = os.path.join(output_dir, parser.documentation_path(full_name)) directory = os.path.dirname(path) try: if not os.path.exists(directory): os.makedirs(directory) with open(path, 'w') as f: f.write(markdown) except OSError as e: print('Cannot write documentation for %s to %s: %s' % (full_name, directory, e)) raise # TODO(deannarubin): write sidebar file? # Write a global index containing all full names with links. with open(os.path.join(output_dir, 'index.md'), 'w') as f: f.write(parser.generate_global_index('TensorFlow', index, duplicate_of))
def write_docs(output_dir, parser_config, yaml_toc, root_title='TensorFlow', search_hints=True, site_api_path=''): """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_api_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 """ # Make output_dir. if not os.path.isabs(output_dir): raise ValueError("'output_dir' must be an absolute path.\n" " output_dir='%s'" % output_dir) if not os.path.exists(output_dir): os.makedirs(output_dir) # 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 = {} # - symbol name(string):pathname (string) symbol_to_file = {} # Collect redirects for an api _redirects.yaml file. redirects = [] # Parse and write Markdown pages, resolving cross-links (@{symbol}). for full_name, py_object in six.iteritems(parser_config.index): parser_config.reference_resolver.current_doc_full_name = 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 (tf_inspect.ismodule(py_object) or tf_inspect.isclass(py_object) or parser.is_free_function(py_object, full_name, parser_config.index)): continue sitepath = os.path.join('api_docs/python', parser.documentation_path(full_name)[:-3]) # For TOC, we need to store a mapping from full_name to the file # we're generating symbol_to_file[full_name] = sitepath # For a module, remember the module for the table-of-contents if tf_inspect.ismodule(py_object): if full_name in parser_config.tree: module_children.setdefault(full_name, []) # 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 tf_inspect.ismodule(parser_config.index[subname]): module_children.setdefault(subname, []).append(full_name) break # Generate docs for `py_object`, resolving references. page_info = parser.docs_for_object(full_name, py_object, parser_config) path = os.path.join(output_dir, parser.documentation_path(full_name)) directory = os.path.dirname(path) try: if not os.path.exists(directory): os.makedirs(directory) # This function returns raw bytes in PY2 or unicode in PY3. if search_hints: content = [page_info.get_metadata_html()] else: content = [''] content.append(pretty_docs.build_md_page(page_info)) text = '\n'.join(content) if six.PY3: text = text.encode('utf-8') with open(path, 'wb') as f: f.write(text) except OSError: raise OSError( 'Cannot write documentation for %s to %s' % (full_name, directory)) 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 = os.path.join(site_api_path, dup.replace('.', '/')) to_path = os.path.join(site_api_path, full_name.replace('.', '/')) redirects.append(( os.path.join('/', from_path), os.path.join('/', to_path))) if redirects: redirects = sorted(redirects) template = ('- from: {}\n' ' to: {}\n') redirects = [template.format(f, t) for f, t in redirects] api_redirects_path = os.path.join(output_dir, '_redirects.yaml') with open(api_redirects_path, 'w') as redirect_file: redirect_file.write('redirects:\n') redirect_file.write(''.join(redirects)) if yaml_toc: # Generate table of contents # Put modules in alphabetical order, case-insensitive modules = sorted(module_children.keys(), key=lambda a: a.upper()) leftnav_path = os.path.join(output_dir, '_toc.yaml') with open(leftnav_path, 'w') as f: # Generate header f.write('# Automatically generated file; please do not edit\ntoc:\n') for module in modules: indent_num = module.count('.') # Don't list `tf.submodule` inside `tf` indent_num = max(indent_num, 1) indent = ' '*indent_num if indent_num > 1: # tf.contrib.baysflow.entropy will be under # tf.contrib->baysflow->entropy title = module.split('.')[-1] else: title = module header = [ '- title: ' + title, ' section:', ' - title: Overview', ' path: ' + os.path.join('/', site_api_path, symbol_to_file[module])] header = ''.join([indent+line+'\n' for line in header]) f.write(header) symbols_in_module = module_children.get(module, []) # Sort case-insensitive, if equal sort case sensitive (upper first) symbols_in_module.sort(key=lambda a: (a.upper(), a)) for full_name in symbols_in_module: item = [ ' - title: ' + full_name[len(module) + 1:], ' path: ' + os.path.join('/', site_api_path, symbol_to_file[full_name])] item = ''.join([indent+line+'\n' for line in item]) f.write(item) # Write a global index containing all full names with links. with open(os.path.join(output_dir, 'index.md'), 'w') as f: f.write( parser.generate_global_index(root_title, parser_config.index, parser_config.reference_resolver))
def write_docs(output_dir, base_dir, duplicate_of, duplicates, index, tree, reverse_index, doc_index, guide_index): """Write previously extracted docs to disk. Write a docs page for each symbol in `index` 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. `duplicate_of` and `duplicates` are used to determine which docs pages to write. Args: output_dir: Directory to write documentation markdown files to. Will be created if it doesn't exist. base_dir: Base directory of the code being documented. This prefix is stripped from all file paths that are part of the documentation. duplicate_of: A `dict` mapping fully qualified names to "master" names. This is used to resolve "@{symbol}" references to the "master" name. duplicates: A `dict` mapping fully qualified names to a set of all aliases of this name. This is used to automatically generate a list of all aliases for each name. index: A `dict` mapping fully qualified names to the corresponding Python objects. Used to produce docs for child objects, and to check the validity of "@{symbol}" references. tree: A `dict` mapping a fully qualified name to the names of all its members. Used to populate the members section of a class or module page. reverse_index: A `dict` mapping object ids to fully qualified names. doc_index: A `dict` mapping a doc key to a DocInfo. guide_index: A `dict` mapping symbol name strings to GuideRef. """ # Make output_dir. try: if not os.path.exists(output_dir): os.makedirs(output_dir) except OSError as e: print('Creating output dir "%s" failed: %s' % (output_dir, e)) raise # 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 = {} # - symbol name(string):pathname (string) symbol_to_file = {} # Parse and write Markdown pages, resolving cross-links (@{symbol}). for full_name, py_object in six.iteritems(index): if full_name in 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 inspect.isfunction(py_object)): continue sitepath = os.path.join('api_docs/python', parser.documentation_path(full_name)[:-3]) # For TOC, we need to store a mapping from full_name to the file # we're generating symbol_to_file[full_name] = sitepath # For a module, remember the module for the table-of-contents if inspect.ismodule(py_object): if full_name in tree: module_children.setdefault(full_name, []) # 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(index[subname]): module_children.setdefault(subname, []).append(full_name) break print('Writing docs for %s (%r).' % (full_name, py_object)) # Generate docs for `py_object`, resolving references. markdown = parser.generate_markdown(full_name, py_object, duplicate_of=duplicate_of, duplicates=duplicates, index=index, tree=tree, reverse_index=reverse_index, doc_index=doc_index, guide_index=guide_index, base_dir=base_dir) path = os.path.join(output_dir, parser.documentation_path(full_name)) directory = os.path.dirname(path) try: if not os.path.exists(directory): os.makedirs(directory) with open(path, 'w') as f: f.write(markdown) except OSError as e: print('Cannot write documentation for %s to %s: %s' % (full_name, directory, e)) raise # Generate table of contents # Put modules in alphabetical order, case-insensitive modules = sorted(module_children.keys(), key=lambda a: a.upper()) leftnav_path = os.path.join(output_dir, '_toc.yaml') with open(leftnav_path, 'w') as f: # Generate header f.write('# Automatically generated file; please do not edit\ntoc:\n') for module in modules: f.write(' - title: ' + module + '\n' ' section:\n' + ' - title: Overview\n' + ' path: /TARGET_DOC_ROOT/' + symbol_to_file[module] + '\n') symbols_in_module = module_children.get(module, []) symbols_in_module.sort(key=lambda a: a.upper()) for full_name in symbols_in_module: f.write(' - title: ' + full_name[len(module) + 1:] + '\n' ' path: /TARGET_DOC_ROOT/' + symbol_to_file[full_name] + '\n') # Write a global index containing all full names with links. with open(os.path.join(output_dir, 'index.md'), 'w') as f: f.write(parser.generate_global_index('TensorFlow', index, duplicate_of))