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_docs_for_function(self): index = {'test_function': test_function} reference_resolver = parser.ReferenceResolver(duplicate_of={}, doc_index={}, index=index, py_module_names=['tf']) tree = {'': ['test_function']} parser_config = parser.ParserConfig( reference_resolver=reference_resolver, duplicates={}, tree=tree, reverse_index={}, guide_index={}, base_dir='/') page_info = parser.docs_for_object(full_name='test_function', py_object=test_function, parser_config=parser_config) # Make sure the brief docstring is present self.assertEqual( inspect.getdoc(test_function).split('\n')[0], page_info.doc.brief) # Make sure the extracted signature is good. self.assertEqual(['unused_arg', "unused_kwarg='default'"], page_info.signature) # Make sure this file is contained as the definition location. self.assertEqual(os.path.relpath(__file__, '/'), page_info.defined_in.path)
def test_docs_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, py_module_names=['tf']) tree = {'': ['test_function_with_args_kwargs']} parser_config = parser.ParserConfig( reference_resolver=reference_resolver, duplicates={}, tree=tree, reverse_index={}, guide_index={}, base_dir='/') page_info = parser.docs_for_object( full_name='test_function_with_args_kwargs', py_object=test_function_with_args_kwargs, parser_config=parser_config) # Make sure the brief docstring is present self.assertEqual( inspect.getdoc(test_function_with_args_kwargs).split('\n')[0], page_info.doc.brief) # Make sure the extracted signature is good. self.assertEqual(['unused_arg', '*unused_args', '**unused_kwargs'], page_info.signature)
def test_parse_md_docstring(self): def test_function_with_fancy_docstring(arg): """Function with a fancy docstring. And a bunch of references: @{tf.reference}, another @{tf.reference}, a member @{tf.reference.foo}, and a @{tf.third}. Args: arg: An argument. Raises: an exception Returns: arg: the input, and arg: the input, again. @compatibility(numpy) NumPy has nothing as awesome as this function. @end_compatibility @compatibility(theano) Theano has nothing as awesome as this function. Check it out. @end_compatibility """ return arg, arg class HasOneMember(object): def foo(self): pass duplicate_of = {'tf.third': 'tf.fourth'} index = { 'tf.fancy': test_function_with_fancy_docstring, 'tf.reference': HasOneMember, 'tf.reference.foo': HasOneMember.foo, 'tf.third': HasOneMember, 'tf.fourth': HasOneMember } reference_resolver = parser.ReferenceResolver( duplicate_of=duplicate_of, doc_index={}, index=index, py_module_names=['tf']) doc_info = parser._parse_md_docstring(test_function_with_fancy_docstring, '../..', reference_resolver) self.assertNotIn('@', doc_info.docstring) self.assertNotIn('compatibility', doc_info.docstring) self.assertNotIn('Raises:', doc_info.docstring) self.assertEqual(len(doc_info.function_details), 3) self.assertEqual(set(doc_info.compatibility.keys()), {'numpy', 'theano'}) self.assertEqual(doc_info.compatibility['numpy'], 'NumPy has nothing as awesome as this function.\n')
def testSaveReferenceResolver(self): you_cant_serialize_this = object() duplicate_of = {'AClass': ['AClass2']} doc_index = {'doc': you_cant_serialize_this} is_fragment = { 'tf': False, 'tf.VERSION': True, 'tf.AClass': False, 'tf.AClass.method': True, 'tf.AClass2': False, 'tf.function': False } py_module_names = ['tf', 'tfdbg'] resolver = parser.ReferenceResolver(duplicate_of, doc_index, is_fragment, py_module_names) outdir = googletest.GetTempDir() filepath = os.path.join(outdir, 'resolver.json') resolver.to_json_file(filepath) resolver2 = parser.ReferenceResolver.from_json_file( filepath, doc_index) # There are no __slots__, so all fields are visible in __dict__. self.assertEqual(resolver.__dict__, resolver2.__dict__)
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_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_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_replace_references(self): class HasOneMember(object): def foo(self): pass string = ('A @{tf.reference}, another @{tf.reference}, ' 'a member @{tf.reference.foo}, and a @{tf.third}.') duplicate_of = {'tf.third': 'tf.fourth'} index = { 'tf.reference': HasOneMember, 'tf.reference.foo': HasOneMember.foo, 'tf.third': HasOneMember, 'tf.fourth': HasOneMember } reference_resolver = parser.ReferenceResolver( duplicate_of=duplicate_of, doc_index={}, index=index, py_module_names=['tf']) result = reference_resolver.replace_references(string, '../..') self.assertEqual( 'A [`tf.reference`](../../tf/reference.md), another ' '[`tf.reference`](../../tf/reference.md), ' 'a member [`tf.reference.foo`](../../tf/reference.md#foo), ' 'and a [`tf.third`](../../tf/fourth.md).', result)
def test_docs_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 } reference_resolver = parser.ReferenceResolver(duplicate_of={}, doc_index={}, index=index, py_module_names=['tf']) tree = { 'TestClass': ['a_method', 'a_property', 'ChildClass', 'CLASS_MEMBER'] } parser_config = parser.ParserConfig( reference_resolver=reference_resolver, duplicates={}, tree=tree, reverse_index={}, guide_index={}, base_dir='/') page_info = parser.docs_for_object(full_name='TestClass', py_object=TestClass, parser_config=parser_config) # Make sure the brief docstring is present self.assertEqual( inspect.getdoc(TestClass).split('\n')[0], page_info.doc.brief) # Make sure the method is present self.assertEqual(TestClass.a_method, page_info.methods[0].obj) # Make sure that the signature is extracted properly and omits self. self.assertEqual(["arg='default'"], page_info.methods[0].signature) # Make sure the property is present self.assertIs(TestClass.a_property, page_info.properties[0].obj) # Make sure there is a link to the child class and it points the right way. self.assertIs(TestClass.ChildClass, page_info.classes[0].obj) # Make sure this file is contained as the definition location. self.assertEqual(os.path.relpath(__file__, '/'), page_info.defined_in.path)
def test_docs_for_module(self): # Get the current module. 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, } reference_resolver = parser.ReferenceResolver(duplicate_of={}, doc_index={}, index=index, py_module_names=['tf']) tree = { 'TestModule': ['TestClass', 'test_function', 'test_function_with_args_kwargs'] } parser_config = parser.ParserConfig( reference_resolver=reference_resolver, duplicates={}, tree=tree, reverse_index={}, guide_index={}, base_dir='/') page_info = parser.docs_for_object(full_name='TestModule', py_object=module, parser_config=parser_config) # Make sure the brief docstring is present self.assertEqual( inspect.getdoc(module).split('\n')[0], page_info.doc.brief) # Make sure that the members are there funcs = {f_info.obj for f_info in page_info.functions} self.assertEqual({test_function, test_function_with_args_kwargs}, funcs) classes = {cls_info.obj for cls_info in page_info.classes} self.assertEqual({TestClass}, classes) # Make sure this file is contained as the definition location. self.assertEqual(os.path.relpath(__file__, '/'), page_info.defined_in.path)
def main(src_dir, output_dir, base_dir, modules): """Generate docs from `src_dir` to `output_dir`.""" doc_index = _build_doc_index(src_dir) visitor = extract(modules) reference_resolver = parser.ReferenceResolver( duplicate_of=visitor.duplicate_of, doc_index=doc_index, index=visitor.index) _write(os.path.join(output_dir, 'api_docs/python'), base_dir, reference_resolver, _build_guide_index(os.path.join(src_dir, 'api_guides/python')), visitor) _other_docs(src_dir, output_dir, reference_resolver) if parser.all_errors: print('Errors during processing:' + '\n '.join(parser.all_errors)) return 1 return 0
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 } reference_resolver = parser.ReferenceResolver(duplicate_of={}, doc_index={}, index=index) tree = { 'TestClass': ['a_method', 'a_property', 'ChildClass', 'CLASS_MEMBER'] } docs = parser.generate_markdown(full_name='TestClass', py_object=TestClass, reference_resolver=reference_resolver, duplicates={}, 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_doc_replace_references(self): string = '@{$doc1} @{$doc1#abc} @{$doc1$link} @{$doc1#def$zelda} @{$do/c2}' class DocInfo(object): pass doc1 = DocInfo() doc1.title = 'Title1' doc1.url = 'URL1' doc2 = DocInfo() doc2.title = 'Two words' doc2.url = 'somewhere/else' doc_index = {'doc1': doc1, 'do/c2': doc2} reference_resolver = parser.ReferenceResolver( duplicate_of={}, doc_index=doc_index, index={}, py_module_names=['tf']) result = reference_resolver.replace_references(string, 'python') self.assertEqual( '[Title1](../URL1) [Title1](../URL1#abc) [link](../URL1) ' '[zelda](../URL1#def) [Two words](../somewhere/else)', result)
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, } reference_resolver = parser.ReferenceResolver(duplicate_of={}, doc_index={}, index=index) tree = { 'TestModule': ['TestClass', 'test_function', 'test_function_with_args_kwargs'] } docs = parser.generate_markdown(full_name='TestModule', py_object=module, reference_resolver=reference_resolver, duplicates={}, 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_write(self): module = sys.modules[__name__] index = { 'tf': sys, # Can be any module, this test doesn't care about content. 'tf.TestModule': module, 'tf.test_function': test_function, 'tf.TestModule.test_function': test_function, 'tf.TestModule.TestClass': TestClass, 'tf.TestModule.TestClass.ChildClass': TestClass.ChildClass, 'tf.TestModule.TestClass.ChildClass.GrandChildClass': TestClass.ChildClass.GrandChildClass, } tree = { 'tf': ['TestModule', 'test_function'], 'tf.TestModule': ['test_function', 'TestClass'], 'tf.TestModule.TestClass': ['ChildClass'], 'tf.TestModule.TestClass.ChildClass': ['GrandChildClass'], 'tf.TestModule.TestClass.ChildClass.GrandChildClass': [] } duplicate_of = {'tf.TestModule.test_function': 'tf.test_function'} duplicates = { 'tf.test_function': ['tf.test_function', 'tf.TestModule.test_function'] } output_dir = tempfile.mkdtemp() base_dir = os.path.dirname(__file__) reference_resolver = parser.ReferenceResolver( duplicate_of=duplicate_of, doc_index={}, index=index, py_module_names=['tf']) parser_config = parser.ParserConfig( reference_resolver=reference_resolver, duplicates=duplicates, tree=tree, reverse_index={}, guide_index={}, base_dir=base_dir) generate_lib.write_docs(output_dir, parser_config, duplicate_of, index, yaml_toc=True) # Make sure that the right files are written to disk. self.assertTrue(os.path.exists(os.path.join(output_dir, 'index.md'))) self.assertTrue(os.path.exists(os.path.join(output_dir, 'tf.md'))) self.assertTrue(os.path.exists(os.path.join(output_dir, '_toc.yaml'))) self.assertTrue( os.path.exists(os.path.join(output_dir, 'tf/TestModule.md'))) self.assertTrue( os.path.exists(os.path.join(output_dir, 'tf/test_function.md'))) self.assertTrue( os.path.exists( os.path.join(output_dir, 'tf/TestModule/TestClass.md'))) self.assertTrue( os.path.exists( os.path.join(output_dir, 'tf/TestModule/TestClass/ChildClass.md'))) self.assertTrue( os.path.exists( os.path.join( output_dir, 'tf/TestModule/TestClass/ChildClass/GrandChildClass.md'))) # Make sure that duplicates are not written self.assertFalse( os.path.exists( os.path.join(output_dir, 'tf/TestModule/test_function.md')))
def make_reference_resolver(self, visitor, doc_index): return parser.ReferenceResolver(duplicate_of=visitor.duplicate_of, doc_index=doc_index, index=visitor.index, py_module_names=self.py_module_names())