def test_getsource_indexerror_resilience(self): """Validates that parser gracefully handles IndexErrors. getsource() can raise an IndexError in some cases. It's unclear why this happens, but it consistently repros on the `get` method of collections.MutableMapping subclasses. """ m = types.ModuleType('m') m.__file__ = __file__ m.ConcreteMutableMapping = ConcreteMutableMapping generator = generate_lib.DocGenerator( root_title='test', py_modules=[('m', m)], code_url_prefix='https://tensorflow.org') parser_config = generator.run_extraction() api_node = doc_generator_visitor.ApiTreeNode( path=('m', 'ConcreteMutableMapping'), py_object=ConcreteMutableMapping) page_info = docs_for_object.docs_for_object( api_node=api_node, parser_config=parser_config) self.assertIn(ConcreteMutableMapping.get, [m.py_object for m in page_info.methods])
def test_strips_default_arg_memory_address(self): """Validates that parser strips memory addresses out out default argspecs. argspec.defaults can contain object memory addresses, which can change between invocations. It's desirable to strip these out to reduce churn. See: `help(collections.MutableMapping.pop)` """ m = types.ModuleType('m') m.__file__ = __file__ m.fun = lambda x=object(): x generator = generate_lib.DocGenerator( root_title='test', py_modules=[('m', m)], code_url_prefix='https://tensorflow.org') parser_config = generator.run_extraction() api_node = doc_generator_visitor.ApiTreeNode(path=('m', 'fun'), py_object=m.fun) page_info = docs_for_object.docs_for_object( api_node=api_node, parser_config=parser_config) output = str(page_info.signature) self.assertNotIn('object at 0x', output) self.assertIn('<object object>', output)
def test_docs_for_module(self): m = types.ModuleType('m') m.__file__ = __file__ m.test_function = test_function m.test_function_with_args_kwargs = test_function_with_args_kwargs m.TestClass = TestClass generator = generate_lib.DocGenerator( root_title='test', py_modules=[('m', m)], code_url_prefix='https://tensorflow.org') parser_config = generator.run_extraction() api_node = doc_generator_visitor.ApiTreeNode(path=('m', ), py_object=test_module) page_info = docs_for_object.docs_for_object( api_node=api_node, parser_config=parser_config) # Make sure the brief docstring is present self.assertEqual( inspect.getdoc(test_module).split('\n')[0], page_info.doc.brief) # Make sure that the members are there funcs = {f_info.py_object for f_info in page_info.functions} self.assertEqual({test_function, test_function_with_args_kwargs}, funcs) classes = {cls_info.py_object for cls_info in page_info.classes} self.assertEqual({TestClass}, classes)
def test_docs_for_function_with_kwargs(self): m = types.ModuleType('m') m.__file__ = __file__ m.test_function_with_args_kwargs = test_function_with_args_kwargs generator = generate_lib.DocGenerator( root_title='test', py_modules=[('m', m)], code_url_prefix='https://tensorflow.org') parser_config = generator.run_extraction() api_node = doc_generator_visitor.ApiTreeNode( path=('test_function_with_args_kwargs', ), py_object=test_function_with_args_kwargs) page_info = docs_for_object.docs_for_object( api_node=api_node, 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('(\n unused_arg, *unused_args, **unused_kwargs\n)', str(page_info.signature))
def test_docs_for_class_should_skip(self): class Parent(object): @doc_controls.do_not_doc_inheritable def a_method(self, arg='default'): pass class Child(Parent): def a_method(self, arg='default'): pass m = types.ModuleType('m') m.__file__ = __file__ m.Child = Child generator = generate_lib.DocGenerator( root_title='test', py_modules=[('m', m)], code_url_prefix='https://tensorflow.org') parser_config = generator.run_extraction() api_node = doc_generator_visitor.ApiTreeNode(path=( 'm', 'Child', ), py_object=Child) page_info = docs_for_object.docs_for_object( api_node=api_node, parser_config=parser_config) # Make sure the `a_method` is not present self.assertEmpty(page_info.methods)
def test_type_alias_signature(self, alias, expected_sig): api_node = doc_generator_visitor.ApiTreeNode(path=tuple( 'tfdocs.api_generator.generate_lib.DocGenerator'.split('.')), py_object=alias) info_obj = type_alias_page.TypeAliasPageInfo( api_node=api_node, parser_config=self.parser_config) with self.parser_config.reference_resolver.temp_prefix('../../..'): info_obj.collect_docs() self.assertEqual(info_obj.signature, expected_sig)
def test_docs_for_class(self): m = types.ModuleType('m') m.__file__ = __file__ m.TestClass = TestClass generator = generate_lib.DocGenerator( root_title='test', py_modules=[('m', m)], code_url_prefix='https://tensorflow.org') parser_config = generator.run_extraction() api_node = doc_generator_visitor.ApiTreeNode(path=( 'm', 'TestClass', ), py_object=TestClass) page_info = docs_for_object.docs_for_object( api_node=api_node, 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 method_infos = { method_info.short_name: method_info for method_info in page_info.methods } self.assertIs(method_infos['a_method'].py_object, TestClass.a_method) # Make sure that the signature is extracted properly and omits self. self.assertEqual('(\n arg='default'\n)', str(method_infos['a_method'].signature)) self.assertEqual(method_infos['static_method'].decorators, ['staticmethod']) self.assertEqual(method_infos['class_method'].decorators, ['classmethod']) # Make sure the property is present attrs = page_info.attr_block self.assertIsInstance(attrs, parser.TitleBlock) self.assertIn('a_property', [name for name, desc in attrs.items]) # Make sure there is a link to the child class and it points the right way. self.assertIs(TestClass.ChildClass, page_info.classes[0].py_object)
def _build_page_info(self): m = types.ModuleType('m') m.__file__ = __file__ m.TestClass = TestClass generator = generate_lib.DocGenerator( root_title='test', py_modules=[('m', m)], code_url_prefix='https://tensorflow.org') parser_config = generator.run_extraction() api_node = doc_generator_visitor.ApiTreeNode(path=('m', 'TestClass'), py_object=TestClass) return docs_for_object.docs_for_object(api_node=api_node, parser_config=parser_config)
def _get_test_page_builder(self, search_hints): def test_function(): pass api_node = doc_generator_visitor.ApiTreeNode(path=('abc', ), py_object=test_function, children={}) page_info = function_page.FunctionPageInfo(api_node=api_node, search_hints=search_hints) docstring_info = parser.DocstringInfo( brief='hello `tensorflow`', docstring_parts=['line1', 'line2'], compatibility={}) page_info.set_doc(docstring_info) page_builder = function_page.FunctionPageBuilder(page_info) return page_builder
def test_dataclass_attributes_table(self): m = types.ModuleType('m') m.__file__ = __file__ m.ExampleDataclass = ExampleDataclass generator = generate_lib.DocGenerator( root_title='test', py_modules=[('m', m)], code_url_prefix='https://tensorflow.org') parser_config = generator.run_extraction() api_node = doc_generator_visitor.ApiTreeNode( path=('m', 'ExampleDataclass'), py_object=ExampleDataclass) page_info = docs_for_object.docs_for_object( api_node=api_node, parser_config=parser_config) self.assertCountEqual( ['a', 'b', 'c', 'x', 'y', 'z'], [name for name, value in page_info.attr_block.items])
def test_empty_defined_in(self, cls, method, py_object): """Validates that the parser omits the defined_in location properly. This test covers two cases where the parser should omit the defined_in location: 1. built-ins. 2. methods automatically generated by python attr library. For built-ins, if without special handling, the defined-in URL ends up like: http://prefix/<embedded stdlib>/_collections_abc.py For methods automatically generated by python attr library, if without special handling, the defined-in URL ends up like: http://prefix/<attrs generated eq ...> Args: cls: The class name to generate docs for. method: The class method name to generate docs for. py_object: The python object for the specified cls.method. """ m = types.ModuleType('m') m.__file__ = __file__ m.ConcreteMutableMapping = ConcreteMutableMapping generator = generate_lib.DocGenerator( root_title='test', py_modules=[('m', m)], code_url_prefix='https://tensorflow.org') parser_config = generator.run_extraction() api_node = doc_generator_visitor.ApiTreeNode(path=(cls, method), py_object=py_object) function_info = docs_for_object.docs_for_object( api_node=api_node, parser_config=parser_config) self.assertIsNone(function_info.defined_in)
def test_docs_for_message_class(self): class CMessage(object): def hidden(self): pass class Message(object): def hidden2(self): pass class MessageMeta(object): def hidden3(self): pass class ChildMessage(CMessage, Message, MessageMeta): def my_method(self): pass m = types.ModuleType('m') m.__file__ = __file__ m.ChildMessage = ChildMessage generator = generate_lib.DocGenerator( root_title='test', py_modules=[('m', m)], code_url_prefix='https://tensorflow.org') parser_config = generator.run_extraction() api_node = doc_generator_visitor.ApiTreeNode(path=('m', 'ChildMessage'), py_object=ChildMessage) page_info = docs_for_object.docs_for_object( api_node=api_node, parser_config=parser_config) self.assertLen(page_info.methods, 1) self.assertEqual('my_method', page_info.methods[0].short_name)
def test_namedtuple_field_order_respects_hidden(self): namedtupleclass = collections.namedtuple( 'namedtupleclass', ['z', 'y', 'x', 'hidden', 'w', 'v', 'u']) m = types.ModuleType('m') m.__file__ = __file__ m.namedtupleclass = namedtupleclass def hide(path, parent, children): return [(name, value) for name, value in children if name != 'hidden'] generator = generate_lib.DocGenerator( root_title='test', py_modules=[('m', m)], code_url_prefix='https://tensorflow.org', callbacks=[hide]) parser_config = generator.run_extraction() api_node = doc_generator_visitor.ApiTreeNode(path=('m', 'namedtupleclass'), py_object=namedtupleclass) page_info = docs_for_object.docs_for_object( api_node=api_node, parser_config=parser_config) self.assertIsNone(page_info._namedtuplefields['hidden']) # Each namedtiple field has a docstring of the form: # 'Alias for field number ##'. These props are returned sorted. def field_number(desc): return int(desc.split(' ')[-1]) self.assertSequenceEqual( [0, 1, 2, 4, 5, 6], [field_number(desc) for name, desc in page_info.attr_block.items])