Ejemplo n.º 1
0
    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)`
    """
        index = {
            'ConcreteMutableMapping': ConcreteMutableMapping,
            'ConcreteMutableMapping.pop': ConcreteMutableMapping.pop
        }
        visitor = DummyVisitor(index=index, duplicate_of={})
        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor, py_module_names=['tf'])

        tree = {'ConcreteMutableMapping': ['pop']}
        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates={},
            duplicate_of={},
            tree=tree,
            index=index,
            reverse_index={},
            base_dir='/',
            code_url_prefix='/')

        page_info = docs_for_object.docs_for_object(
            full_name='ConcreteMutableMapping',
            py_object=ConcreteMutableMapping,
            parser_config=parser_config)

        pop_default_arg = page_info.methods[0].signature.arguments[1]
        self.assertNotIn('object at 0x', pop_default_arg)
        self.assertIn('<object>', pop_default_arg)
Ejemplo n.º 2
0
    def test_dataclass_attributes_table(self):

        index = {
            'ExampleDataclass': ExampleDataclass,
        }

        visitor = DummyVisitor(index=index, duplicate_of={})

        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor, py_module_names=['tf'])

        tree = {'ExampleDataclass': []}

        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates={},
            duplicate_of={},
            tree=tree,
            index=index,
            reverse_index={},
            base_dir='/',
            code_url_prefix='/')

        page_info = docs_for_object.docs_for_object(
            full_name='ExampleDataclass',
            py_object=ExampleDataclass,
            parser_config=parser_config)

        self.assertCountEqual(
            ['a', 'b', 'c', 'x', 'y', 'z'],
            [name for name, value in page_info.attr_block.items])
Ejemplo n.º 3
0
    def test_get_other_member_doc_object_doc_attr(self):
        class A():
            """Class docs."""
            pass

        a = A()
        a.__doc__ = 'Object doc'

        parser_config = config.ParserConfig(reference_resolver=None,
                                            duplicates={},
                                            duplicate_of={},
                                            tree={},
                                            index={},
                                            reverse_index={},
                                            base_dir='/',
                                            code_url_prefix='/')

        result = parser._get_other_member_doc(a, parser_config, {})

        expected = textwrap.dedent("""\
      Instance of `__main__.A`

      Object doc""")

        self.assertEqual(expected, result)
Ejemplo n.º 4
0
    def test_docs_for_function_with_kwargs(self):
        index = {
            'test_function_with_args_kwargs': test_function_with_args_kwargs
        }

        visitor = DummyVisitor(index=index, duplicate_of={})

        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor, py_module_names=['tf'])

        tree = {'': ['test_function_with_args_kwargs']}
        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates={},
            duplicate_of={},
            tree=tree,
            index=index,
            reverse_index={},
            base_dir='/',
            code_url_prefix='/')

        page_info = docs_for_object.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.arguments)
Ejemplo n.º 5
0
 def make_parser_config(self, visitor, reference_resolver):
     return config.ParserConfig(reference_resolver=reference_resolver,
                                duplicates=visitor.duplicates,
                                duplicate_of=visitor.duplicate_of,
                                tree=visitor.tree,
                                index=visitor.index,
                                reverse_index=visitor.reverse_index,
                                base_dir=self._base_dir,
                                code_url_prefix=self._code_url_prefix)
Ejemplo n.º 6
0
    def get_test_objects(self):
        # These are all mutable objects, so rebuild them for each test.
        # Don't cache the objects.
        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.test_function': 'tf.TestModule.test_function'}

        duplicates = {
            'tf.TestModule.test_function':
            ['tf.test_function', 'tf.TestModule.test_function']
        }

        base_dir = os.path.dirname(__file__)

        visitor = DummyVisitor(index, duplicate_of)

        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor,
            py_module_names=['tf'],
            link_prefix='api_docs/python')

        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates=duplicates,
            duplicate_of=duplicate_of,
            tree=tree,
            index=index,
            reverse_index={},
            base_dir=base_dir,
            code_url_prefix='/')

        return reference_resolver, parser_config
Ejemplo n.º 7
0
 def make_parser_config(self,
                        visitor: doc_generator_visitor.DocGeneratorVisitor):
     reference_resolver = self.make_reference_resolver(visitor)
     return config.ParserConfig(reference_resolver=reference_resolver,
                                duplicates=visitor.duplicates,
                                duplicate_of=visitor.duplicate_of,
                                tree=visitor.tree,
                                index=visitor.index,
                                reverse_index=visitor.reverse_index,
                                path_tree=visitor.path_tree,
                                api_tree=visitor.api_tree,
                                base_dir=self._base_dir,
                                code_url_prefix=self._code_url_prefix)
Ejemplo n.º 8
0
    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

        index = {
            'ChildMessage': ChildMessage,
            'ChildMessage.hidden': ChildMessage.hidden,
            'ChildMessage.hidden2': ChildMessage.hidden2,
            'ChildMessage.hidden3': ChildMessage.hidden3,
            'ChildMessage.my_method': ChildMessage.my_method,
        }

        visitor = DummyVisitor(index=index, duplicate_of={})

        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor, py_module_names=['tf'])

        tree = {'ChildMessage': ['hidden', 'hidden2', 'hidden3', 'my_method']}

        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates={},
            duplicate_of={},
            tree=tree,
            index=index,
            reverse_index={},
            base_dir='/',
            code_url_prefix='/')

        page_info = docs_for_object.docs_for_object(
            full_name='ChildMessage',
            py_object=ChildMessage,
            parser_config=parser_config)

        self.assertLen(page_info.methods, 1)
        self.assertEqual('my_method', page_info.methods[0].short_name)
Ejemplo n.º 9
0
    def test_docs_for_module(self):

        index = {
            'TestModule': test_module,
            'TestModule.test_function': test_function,
            'TestModule.test_function_with_args_kwargs':
            test_function_with_args_kwargs,
            'TestModule.TestClass': TestClass,
        }

        visitor = DummyVisitor(index=index, duplicate_of={})

        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor, py_module_names=['tf'])

        tree = {
            'TestModule':
            ['TestClass', 'test_function', 'test_function_with_args_kwargs']
        }
        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates={},
            duplicate_of={},
            tree=tree,
            index=index,
            reverse_index={},
            base_dir='/',
            code_url_prefix='/')

        page_info = docs_for_object.docs_for_object(
            full_name='TestModule',
            py_object=test_module,
            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)
Ejemplo n.º 10
0
    def test_namedtuple_field_order(self):
        namedtupleclass = collections.namedtuple(
            'namedtupleclass', ['z', 'y', 'x', 'hidden', 'w', 'v', 'u'])

        index = {
            'namedtupleclass': namedtupleclass,
            'namedtupleclass.u': namedtupleclass.u,
            'namedtupleclass.v': namedtupleclass.v,
            'namedtupleclass.w': namedtupleclass.w,
            'namedtupleclass.x': namedtupleclass.x,
            'namedtupleclass.y': namedtupleclass.y,
            'namedtupleclass.z': namedtupleclass.z,
        }

        visitor = DummyVisitor(index=index, duplicate_of={})

        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor, py_module_names=['tf'])

        tree = {'namedtupleclass': {'u', 'v', 'w', 'x', 'y', 'z'}}
        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates={},
            duplicate_of={},
            tree=tree,
            index=index,
            reverse_index={},
            base_dir='/',
            code_url_prefix='/')

        page_info = docs_for_object.docs_for_object(
            full_name='namedtupleclass',
            py_object=namedtupleclass,
            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])
Ejemplo n.º 11
0
    def test_getsource_indexerror_resilience(self):
        """Validates that parser gracefully handles IndexErrors.

    inspect.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.
    """

        # This isn't the full set of APIs from MutableMapping, but sufficient for
        # testing.
        index = {
            'ConcreteMutableMapping': ConcreteMutableMapping,
            'ConcreteMutableMapping.__init__': ConcreteMutableMapping.__init__,
            'ConcreteMutableMapping.__getitem__':
            ConcreteMutableMapping.__getitem__,
            'ConcreteMutableMapping.__setitem__':
            ConcreteMutableMapping.__setitem__,
            'ConcreteMutableMapping.values': ConcreteMutableMapping.values,
            'ConcreteMutableMapping.get': ConcreteMutableMapping.get
        }
        visitor = DummyVisitor(index=index, duplicate_of={})
        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor, py_module_names=['tf'])

        tree = {
            'ConcreteMutableMapping':
            ['__init__', '__getitem__', '__setitem__', 'values', 'get']
        }
        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates={},
            duplicate_of={},
            tree=tree,
            index=index,
            reverse_index={},
            base_dir='/',
            code_url_prefix='/')

        page_info = docs_for_object.docs_for_object(
            full_name='ConcreteMutableMapping',
            py_object=ConcreteMutableMapping,
            parser_config=parser_config)

        self.assertIn(ConcreteMutableMapping.get,
                      [m.py_object for m in page_info.methods])
Ejemplo n.º 12
0
    def test_get_other_member_doc_known_class(self):
        class A():
            """Class docs."""
            pass

        a = A()

        parser_config = config.ParserConfig(reference_resolver=None,
                                            duplicates={},
                                            duplicate_of={},
                                            tree={},
                                            index={},
                                            reverse_index={id(A): 'tf.test.A'},
                                            base_dir='/',
                                            code_url_prefix='/')

        result = parser._get_other_member_doc(a, parser_config, {})

        self.assertEqual('Instance of `tf.test.A`', result)
Ejemplo n.º 13
0
    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.
    """

        visitor = DummyVisitor(index={}, duplicate_of={})
        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor, py_module_names=['tf'])

        tree = {cls: [method]}
        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates={},
            duplicate_of={},
            tree=tree,
            index={},
            reverse_index={},
            base_dir='/',
            code_url_prefix='/')

        function_info = docs_for_object.docs_for_object(
            full_name='%s.%s' % (cls, method),
            py_object=py_object,
            parser_config=parser_config)

        self.assertIsNone(function_info.defined_in)
Ejemplo n.º 14
0
    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

        index = {
            'Child': Child,
            'Child.a_method': Child.a_method,
        }

        visitor = DummyVisitor(index=index, duplicate_of={})

        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor, py_module_names=['tf'])

        tree = {
            'Child': ['a_method'],
        }

        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates={},
            duplicate_of={},
            tree=tree,
            index=index,
            reverse_index={},
            base_dir='/',
            code_url_prefix='/')

        page_info = docs_for_object.docs_for_object(
            full_name='Child', py_object=Child, parser_config=parser_config)

        # Make sure the `a_method` is not present
        self.assertEmpty(page_info.methods)
Ejemplo n.º 15
0
 def setUp(self):
     super().setUp()
     self.known_object = object()
     reference_resolver = reference_resolver_lib.ReferenceResolver(
         duplicate_of={},
         is_fragment={
             'tfdocs.api_generator.signature.extract_decorators': False
         },
         py_module_names=[])
     self.parser_config = config.ParserConfig(
         reference_resolver=reference_resolver,
         duplicates={},
         duplicate_of={},
         tree={},
         index={},
         reverse_index={
             id(self.known_object):
             'location.of.object.in.api',
             id(signature.extract_decorators):
             'tfdocs.api_generator.signature.extract_decorators',
         },
         base_dir='/',
         code_url_prefix='/')
Ejemplo n.º 16
0
    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.static_method': TestClass.static_method,
            'TestClass.class_method': TestClass.class_method,
            'TestClass.CLASS_MEMBER': TestClass.CLASS_MEMBER,
        }

        visitor = DummyVisitor(index=index, duplicate_of={})

        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor, py_module_names=['tf'])

        tree = {
            'TestClass': [
                'a_method', 'class_method', 'static_method', 'a_property',
                'ChildClass', 'CLASS_MEMBER'
            ]
        }
        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates={},
            duplicate_of={},
            tree=tree,
            index=index,
            reverse_index={},
            base_dir='/',
            code_url_prefix='/')

        page_info = docs_for_object.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
        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(['arg=&#x27;default&#x27;'],
                         method_infos['a_method'].signature.arguments)

        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)
Ejemplo n.º 17
0
    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(two words!)
      Theano has nothing as awesome as this function.

      @tf.function

      Check it out.
      @end_compatibility

      """
            return arg, arg

        class HasOneMember(object):
            def foo(self):
                pass

        duplicate_of = {'tf.third': 'tf.fourth'}
        index = {
            'tf': test_module,
            'tf.fancy': test_function_with_fancy_docstring,
            'tf.reference': HasOneMember,
            'tf.reference.foo': HasOneMember.foo,
            'tf.third': HasOneMember,
            'tf.fourth': HasOneMember
        }

        visitor = DummyVisitor(index=index, duplicate_of=duplicate_of)

        reference_resolver = reference_resolver_lib.ReferenceResolver.from_visitor(
            visitor=visitor, py_module_names=['tf'], link_prefix='../..')
        parser_config = config.ParserConfig(
            reference_resolver=reference_resolver,
            duplicates={},
            duplicate_of={},
            tree={},
            index=index,
            reverse_index={},
            base_dir='/',
            code_url_prefix='/')

        doc_info = parser.parse_md_docstring(
            test_function_with_fancy_docstring,
            full_name=None,
            parser_config=parser_config)

        freeform_docstring = '\n'.join(part
                                       for part in doc_info.docstring_parts
                                       if isinstance(part, str))
        self.assertNotIn('@', freeform_docstring)
        self.assertNotIn('compatibility', freeform_docstring)
        self.assertNotIn('Raises:', freeform_docstring)

        title_blocks = [
            part for part in doc_info.docstring_parts
            if not isinstance(part, str)
        ]

        self.assertLen(title_blocks, 3)

        self.assertCountEqual(doc_info.compatibility.keys(),
                              {'numpy', 'two words!'})

        self.assertEqual(doc_info.compatibility['numpy'],
                         'NumPy has nothing as awesome as this function.\n')