def test_generate_markdown_for_class(self): index = { 'TestClass': TestClass, 'TestClass.a_method': TestClass.a_method, 'TestClass.a_property': TestClass.a_property, 'TestClass.ChildClass': TestClass.ChildClass, 'TestClass.CLASS_MEMBER': TestClass.CLASS_MEMBER } tree = { 'TestClass': ['a_method', 'a_property', 'ChildClass', 'CLASS_MEMBER'] } docs = parser.generate_markdown( full_name='TestClass', py_object=TestClass, duplicate_of={}, duplicates={}, index=index, tree=tree, reverse_index={}, doc_index={}, guide_index={}, base_dir='/') # Make sure all required docstrings are present. self.assertTrue(inspect.getdoc(TestClass) in docs) self.assertTrue(inspect.getdoc(TestClass.a_method) in docs) self.assertTrue(inspect.getdoc(TestClass.a_property) in docs) # Make sure that the signature is extracted properly and omits self. self.assertTrue('a_method(arg=\'default\')' in docs) # Make sure there is a link to the child class and it points the right way. self.assertTrue('[`class ChildClass`](./TestClass/ChildClass.md)' in docs) # Make sure CLASS_MEMBER is mentioned. self.assertTrue('CLASS_MEMBER' in docs) # Make sure this file is contained as the definition location. self.assertTrue(os.path.relpath(__file__, '/') in docs)
def test_generate_markdown_for_class(self): index = { 'TestClass': TestClass, 'TestClass.a_method': TestClass.a_method, 'TestClass.a_property': TestClass.a_property, 'TestClass.ChildClass': TestClass.ChildClass, 'TestClass.CLASS_MEMBER': TestClass.CLASS_MEMBER } tree = { 'TestClass': ['a_method', 'a_property', 'ChildClass', 'CLASS_MEMBER'] } docs = parser.generate_markdown(full_name='TestClass', py_object=TestClass, duplicate_of={}, duplicates={}, index=index, tree=tree, reverse_index={}, guide_index={}, base_dir='/') # Make sure all required docstrings are present. self.assertTrue(inspect.getdoc(TestClass) in docs) self.assertTrue(inspect.getdoc(TestClass.a_method) in docs) self.assertTrue(inspect.getdoc(TestClass.a_property) in docs) # Make sure that the signature is extracted properly and omits self. self.assertTrue('a_method(arg=\'default\')' in docs) # Make sure there is a link to the child class and it points the right way. self.assertTrue('[`class ChildClass`](./TestClass/ChildClass.md)' in docs) # Make sure CLASS_MEMBER is mentioned. self.assertTrue('CLASS_MEMBER' in docs) # Make sure this file is contained as the definition location. self.assertTrue(os.path.relpath(__file__, '/') in docs)
def test_generate_markdown_for_module(self): module = sys.modules[__name__] index = { 'TestModule': module, 'TestModule.test_function': test_function, 'TestModule.test_function_with_args_kwargs': test_function_with_args_kwargs, 'TestModule.TestClass': TestClass, } tree = { 'TestModule': ['TestClass', 'test_function', 'test_function_with_args_kwargs'] } docs = parser.generate_markdown(full_name='TestModule', py_object=module, duplicate_of={}, duplicates={}, index=index, tree=tree, reverse_index={}, doc_index={}, guide_index={}, base_dir='/') # Make sure all required docstrings are present. self.assertTrue(inspect.getdoc(module) in docs) # Make sure that links to the members are there (not asserting on exact link # text for functions). self.assertTrue('./TestModule/test_function.md' in docs) self.assertTrue('./TestModule/test_function_with_args_kwargs.md' in docs) # Make sure there is a link to the child class and it points the right way. self.assertTrue('[`class TestClass`](./TestModule/TestClass.md)' in docs) # Make sure this file is contained as the definition location. self.assertTrue(os.path.relpath(__file__, '/') in docs)
def test_docstring_special_section(self): index = {'test_function': test_function_with_fancy_docstring} reference_resolver = parser.ReferenceResolver(duplicate_of={}, doc_index={}, index=index) tree = {'': 'test_function'} docs = parser.generate_markdown( full_name='test_function', py_object=test_function_with_fancy_docstring, reference_resolver=reference_resolver, duplicates={}, tree=tree, reverse_index={}, guide_index={}, base_dir='/') expected = '\n'.join([ 'Function with a fancy docstring.', '', '#### Args:', '', '* <b>`arg`</b>: An argument.', '', '', '#### Returns:', '', '* <b>`arg`</b>: the input, and', '* <b>`arg`</b>: the input, again.', '', '', '', '', '', '#### numpy compatibility', 'NumPy has nothing as awesome as this function.', '', '', '', '#### theano compatibility', 'Theano has nothing as awesome as this function.', '', 'Check it out.', '', '', '' ]) self.assertTrue(expected in docs)
def test_generate_markdown_for_function_with_kwargs(self): index = { 'test_function_with_args_kwargs': test_function_with_args_kwargs } reference_resolver = parser.ReferenceResolver(duplicate_of={}, doc_index={}, index=index) tree = {'': ['test_function_with_args_kwargs']} docs = parser.generate_markdown( full_name='test_function_with_args_kwargs', py_object=test_function_with_args_kwargs, reference_resolver=reference_resolver, duplicates={}, tree=tree, reverse_index={}, guide_index={}, base_dir='/') # Make sure docstring shows up. self.assertTrue(inspect.getdoc(test_function_with_args_kwargs) in docs) # Make sure the extracted signature is good. self.assertTrue('test_function_with_args_kwargs(unused_arg,' ' *unused_args, **unused_kwargs)' in docs)
def test_references_replaced_in_generated_markdown(self): index = { 'test_function_for_markdown_reference': test_function_for_markdown_reference } reference_resolver = parser.ReferenceResolver(duplicate_of={}, doc_index={}, index=index) tree = {'': ['test_function_for_markdown_reference']} docs = parser.generate_markdown( full_name='test_function_for_markdown_reference', py_object=test_function_for_markdown_reference, reference_resolver=reference_resolver, duplicates={}, tree=tree, reverse_index={}, guide_index={}, base_dir='/') # Make sure docstring shows up and is properly processed. expected_docs = reference_resolver.replace_references( inspect.getdoc(test_function_for_markdown_reference), relative_path_to_root='.') self.assertTrue(expected_docs in docs)
def test_generate_markdown_for_function(self): index = {'test_function': test_function} reference_resolver = parser.ReferenceResolver(duplicate_of={}, doc_index={}, index=index) tree = {'': ['test_function']} docs = parser.generate_markdown(full_name='test_function', py_object=test_function, reference_resolver=reference_resolver, duplicates={}, tree=tree, reverse_index={}, guide_index={}, base_dir='/') # Make sure docstring shows up. self.assertTrue(inspect.getdoc(test_function) in docs) # Make sure the extracted signature is good. self.assertTrue( 'test_function(unused_arg, unused_kwarg=\'default\')' in docs) # Make sure this file is contained as the definition location. self.assertTrue(os.path.relpath(__file__, '/') in docs)
def test_docstring_special_section(self): index = { 'test_function': test_function_with_fancy_docstring } tree = { '': 'test_function' } docs = parser.generate_markdown( full_name='test_function', py_object=test_function_with_fancy_docstring, duplicate_of={}, duplicates={}, index=index, tree=tree, reverse_index={}, base_dir='/') expected = '\n'.join([ 'Function with a fancy docstring.', '', '#### Args:', '', '* <b>`arg`</b>: An argument.', '', '', '#### Returns:', '', '* <b>`arg`</b>: the input, and', '* <b>`arg`</b>: the input, again.', '']) self.assertTrue(expected in docs)
def test_generate_markdown_for_module(self): module = sys.modules[__name__] index = { 'TestModule': module, 'TestModule.test_function': test_function, 'TestModule.test_function_with_args_kwargs': test_function_with_args_kwargs, 'TestModule.TestClass': TestClass, } tree = { 'TestModule': ['TestClass', 'test_function', 'test_function_with_args_kwargs'] } docs = parser.generate_markdown(full_name='TestModule', py_object=module, duplicate_of={}, duplicates={}, index=index, tree=tree, reverse_index={}, guide_index={}, base_dir='/') # Make sure all required docstrings are present. self.assertTrue(inspect.getdoc(module) in docs) # Make sure that links to the members are there (not asserting on exact link # text for functions). self.assertTrue('./TestModule/test_function.md' in docs) self.assertTrue('./TestModule/test_function_with_args_kwargs.md' in docs) # Make sure there is a link to the child class and it points the right way. self.assertTrue('[`class TestClass`](./TestModule/TestClass.md)' in docs) # Make sure this file is contained as the definition location. self.assertTrue(os.path.relpath(__file__, '/') in docs)
def test_generate_markdown_for_function(self): index = { 'test_function': test_function } reference_resolver = parser.ReferenceResolver( duplicate_of={}, doc_index={}, index=index) tree = { '': ['test_function'] } docs = parser.generate_markdown(full_name='test_function', py_object=test_function, reference_resolver=reference_resolver, duplicates={}, tree=tree, reverse_index={}, guide_index={}, base_dir='/') # Make sure docstring shows up. self.assertTrue(inspect.getdoc(test_function) in docs) # Make sure the extracted signature is good. self.assertTrue( 'test_function(unused_arg, unused_kwarg=\'default\')' in docs) # Make sure this file is contained as the definition location. self.assertTrue(os.path.relpath(__file__, '/') in docs)
def test_docstring_special_section(self): index = { 'test_function': test_function_with_fancy_docstring } reference_resolver = parser.ReferenceResolver( duplicate_of={}, doc_index={}, index=index) tree = { '': 'test_function' } docs = parser.generate_markdown( full_name='test_function', py_object=test_function_with_fancy_docstring, reference_resolver=reference_resolver, duplicates={}, tree=tree, reverse_index={}, guide_index={}, base_dir='/') expected = '\n'.join([ 'Function with a fancy docstring.', '', '#### Args:', '', '* <b>`arg`</b>: An argument.', '', '', '#### Returns:', '', '* <b>`arg`</b>: the input, and', '* <b>`arg`</b>: the input, again.', '', '', '', '', '', '#### numpy compatibility', 'NumPy has nothing as awesome as this function.', '', '', '', '#### theano compatibility', 'Theano has nothing as awesome as this function.', '', 'Check it out.', '', '', '']) self.assertTrue(expected in docs)
def test_generate_markdown_for_function_with_kwargs(self): index = { 'test_function_with_args_kwargs': test_function_with_args_kwargs } tree = { '': ['test_function_with_args_kwargs'] } docs = parser.generate_markdown(full_name='test_function_with_args_kwargs', py_object=test_function_with_args_kwargs, duplicate_of={}, duplicates={}, index=index, tree=tree, base_dir='/') # Make sure docstring shows up. self.assertTrue(inspect.getdoc(test_function_with_args_kwargs) in docs) # Make sure the extracted signature is good. self.assertTrue( 'test_function_with_args_kwargs(unused_arg,' ' *unused_args, **unused_kwargs)' in docs)
def test_references_replaced_in_generated_markdown(self): index = { 'test_function_for_markdown_reference': test_function_for_markdown_reference } tree = { '': ['test_function_for_markdown_reference'] } docs = parser.generate_markdown( full_name='test_function_for_markdown_reference', py_object=test_function_for_markdown_reference, duplicate_of={}, duplicates={}, index=index, tree=tree, reverse_index={}, doc_index={}, guide_index={}, base_dir='/') # Make sure docstring shows up and is properly processed. expected_docs = parser.replace_references( inspect.getdoc(test_function_for_markdown_reference), relative_path_to_root='.', duplicate_of={}, doc_index={}, index={}) self.assertTrue(expected_docs in docs)
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, base_dir, duplicate_of, duplicates, index, tree, reverse_index, reference_resolver, 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. reference_resolver: A parser.ReferenceResolver object. 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, reference_resolver=reference_resolver, duplicates=duplicates, tree=tree, reverse_index=reverse_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, 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, 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))