def test_do_not_doc_inheritable_property(self): class Parent(object): @property @doc_controls.do_not_doc_inheritable def my_method(self): pass class Child(Parent): @property def my_method(self): pass class GrandChild(Child): pass self.assertTrue(doc_controls.should_skip(Parent.my_method)) self.assertFalse(doc_controls.should_skip(Child.my_method)) self.assertFalse(doc_controls.should_skip(GrandChild.my_method)) self.assertTrue(doc_controls.should_skip_class_attr(Parent, 'my_method')) self.assertTrue(doc_controls.should_skip_class_attr(Child, 'my_method')) self.assertTrue( doc_controls.should_skip_class_attr(GrandChild, 'my_method'))
def test_doc_in_current_and_subclasses(self): class Parent: @doc_controls.do_not_doc_in_subclasses def my_method(self): pass class Child1(Parent): @doc_controls.doc_in_current_and_subclasses def my_method(self): pass class Child11(Child1): pass class Child2(Parent): pass self.assertFalse( doc_controls.should_skip_class_attr(Parent, 'my_method')) self.assertFalse( doc_controls.should_skip_class_attr(Child1, 'my_method')) self.assertFalse( doc_controls.should_skip_class_attr(Child11, 'my_method')) self.assertTrue( doc_controls.should_skip_class_attr(Child2, 'my_method'))
def test_do_not_doc_on_method(self): """The simple decorator is not aware of inheritance.""" class Parent(object): @doc_controls.do_not_generate_docs def my_method(self): pass class Child(Parent): def my_method(self): pass class GrandChild(Child): pass self.assertTrue(doc_controls.should_skip(Parent.my_method)) self.assertFalse(doc_controls.should_skip(Child.my_method)) self.assertFalse(doc_controls.should_skip(GrandChild.my_method)) self.assertTrue(doc_controls.should_skip_class_attr(Parent, 'my_method')) self.assertFalse(doc_controls.should_skip_class_attr(Child, 'my_method')) self.assertFalse( doc_controls.should_skip_class_attr(GrandChild, 'my_method'))
def test_for_subclass_implementers(self): class GrandParent(object): def my_method(self): pass class Parent(GrandParent): @doc_controls.for_subclass_implementers def my_method(self): pass class Child(Parent): pass class GrandChild(Child): def my_method(self): pass class Grand2Child(Child): pass self.assertFalse( doc_controls.should_skip_class_attr(GrandParent, 'my_method')) self.assertFalse(doc_controls.should_skip_class_attr(Parent, 'my_method')) self.assertTrue(doc_controls.should_skip_class_attr(Child, 'my_method')) self.assertTrue( doc_controls.should_skip_class_attr(GrandChild, 'my_method')) self.assertTrue( doc_controls.should_skip_class_attr(Grand2Child, 'my_method'))
def test_do_not_doc_inheritable_property(self): class Parent(object): @property @doc_controls.do_not_doc_inheritable def my_method(self): pass class Child(Parent): @property def my_method(self): pass class GrandChild(Child): pass self.assertTrue(doc_controls.should_skip(Parent.my_method)) self.assertFalse(doc_controls.should_skip(Child.my_method)) self.assertFalse(doc_controls.should_skip(GrandChild.my_method)) self.assertTrue( doc_controls.should_skip_class_attr(Parent, 'my_method')) self.assertTrue(doc_controls.should_skip_class_attr( Child, 'my_method')) self.assertTrue( doc_controls.should_skip_class_attr(GrandChild, 'my_method'))
def test_lowest_decorator_wins(self): class GrandParent(object): @doc_controls.for_subclass_implementers def my_method(self): pass class Parent(GrandParent): def my_method(self): pass class Child(Parent): @doc_controls.doc_in_current_and_subclasses def my_method(self): pass class GrandChild(Child): @doc_controls.for_subclass_implementers def my_method(self): pass class Grand2Child(GrandChild): pass self.assertFalse( doc_controls.should_skip_class_attr(GrandParent, 'my_method')) self.assertTrue( doc_controls.should_skip_class_attr(Parent, 'my_method')) self.assertFalse( doc_controls.should_skip_class_attr(Child, 'my_method')) self.assertFalse( doc_controls.should_skip_class_attr(GrandChild, 'my_method')) self.assertTrue( doc_controls.should_skip_class_attr(Grand2Child, 'my_method'))
def test_for_subclass_implementers_short_circuit(self): class GrandParent(object): @doc_controls.for_subclass_implementers def my_method(self): pass class Parent(GrandParent): def my_method(self): pass class Child(Parent): @doc_controls.do_not_doc_inheritable def my_method(self): pass class GrandChild(Child): @doc_controls.for_subclass_implementers def my_method(self): pass class Grand2Child(Child): pass self.assertFalse( doc_controls.should_skip_class_attr(GrandParent, 'my_method')) self.assertTrue( doc_controls.should_skip_class_attr(Parent, 'my_method')) self.assertTrue(doc_controls.should_skip_class_attr( Child, 'my_method')) self.assertFalse( doc_controls.should_skip_class_attr(GrandChild, 'my_method')) self.assertTrue( doc_controls.should_skip_class_attr(Grand2Child, 'my_method'))
def _is_private(self, path, parent, name, obj): """Returns whether a name is private or not.""" # Skip objects blocked by doc_controls. if doc_controls.should_skip(obj): return True if isinstance(obj, type): if doc_controls.should_skip_class_attr(parent, name): return True if doc_controls.should_doc_private(obj): return False if inspect.ismodule(obj): mod_base_dirs = get_module_base_dirs(obj) # This check only handles normal packages/modules. Namespace-package # contents will get filtered when the submodules are checked. if len(mod_base_dirs) == 1: mod_base_dir = mod_base_dirs[0] # Check that module is in one of the `self._base_dir`s if not any(base in mod_base_dir.parents for base in self._base_dir): return True # Skip objects blocked by the private_map if name in self._private_map.get('.'.join(path), []): return True # Skip "_" hidden attributes if name.startswith('_') and name not in ALLOWED_DUNDER_METHODS: return True return False
def test_nested_class_do_not_document(self): class Outer1: @doc_controls.do_not_generate_docs class Inner: pass class Outer2: Inner = Outer1.Inner class Outer3: class Inner(Outer1.Inner): pass self.assertTrue(doc_controls.should_skip_class_attr(Outer1, 'Inner')) self.assertTrue(doc_controls.should_skip_class_attr(Outer2, 'Inner')) self.assertFalse(doc_controls.should_skip_class_attr(Outer3, 'Inner'))
def test_nested_class_inheritable_decorators(self): class Outer1: @doc_controls.do_not_doc_inheritable class Inner: pass class Outer2(Outer1): pass class Outer3(Outer2): @doc_controls.doc_in_current_and_subclasses class Inner(Outer2.Inner): pass self.assertTrue(doc_controls.should_skip_class_attr(Outer1, 'Inner')) self.assertTrue(doc_controls.should_skip_class_attr(Outer2, 'Inner')) self.assertFalse(doc_controls.should_skip_class_attr(Outer3, 'Inner'))
def filter_doc_controls_skip(path: Sequence[str], parent: Any, children: Children) -> Children: del path for name, child in children: if doc_controls.should_skip(child): continue if isinstance(parent, type): if doc_controls.should_skip_class_attr(parent, name): continue yield (name, child)
def test_skip_class_short_circuit(self): class GrandParent(object): def my_method(self): pass @doc_controls.do_not_generate_docs class Parent(GrandParent): pass class Child(Parent): pass self.assertFalse(doc_controls.should_skip(Child)) self.assertTrue(doc_controls.should_skip(Parent)) self.assertFalse(doc_controls.should_skip(GrandParent)) self.assertFalse( doc_controls.should_skip_class_attr(GrandParent, 'my_method')) self.assertFalse(doc_controls.should_skip_class_attr(Parent, 'my_method')) self.assertTrue(doc_controls.should_skip_class_attr(Child, 'my_method'))
def test_skip_class_short_circuit(self): class GrandParent(object): def my_method(self): pass @doc_controls.do_not_generate_docs class Parent(GrandParent): pass class Child(Parent): pass self.assertFalse(doc_controls.should_skip(Child)) self.assertTrue(doc_controls.should_skip(Parent)) self.assertFalse(doc_controls.should_skip(GrandParent)) self.assertFalse( doc_controls.should_skip_class_attr(GrandParent, 'my_method')) self.assertFalse( doc_controls.should_skip_class_attr(Parent, 'my_method')) self.assertFalse( doc_controls.should_skip_class_attr(Child, 'my_method'))
def collect_docs(self): """Collects information necessary specifically for a class's doc page. Mainly, this is details about the class's members. """ py_class = self.py_object self._set_bases() class_path_node = self.parser_config.path_tree[self.api_node.path] for _, path_node in sorted(class_path_node.children.items()): # Don't document anything that is defined in object or by protobuf. defining_class = parser.get_defining_class(py_class, path_node.short_name) if defining_class in [ object, type, tuple, BaseException, Exception ]: continue # The following condition excludes most protobuf-defined symbols. if (defining_class and defining_class.__name__ in ['CMessage', 'Message', 'MessageMeta']): continue if doc_controls.should_skip_class_attr(py_class, path_node.short_name): continue child_doc = parser.parse_md_docstring(path_node.py_object, self.full_name, self.parser_config, self._extra_docs) child_url = self.parser_config.reference_resolver.reference_to_url( path_node.full_name) member_info = base_page.MemberInfo(path_node.short_name, path_node.full_name, path_node.py_object, child_doc, child_url) self._add_member(member_info, defining_class) self.set_attr_block(self._augment_attributes(self.doc.docstring_parts))
def collect_docs_for_class(self, py_class, parser_config): """Collects information necessary specifically for a class's doc page. Mainly, this is details about the class's members. Args: py_class: The class object being documented parser_config: An instance of ParserConfig. """ self.set_namedtuplefields(py_class) doc_path = documentation_path(self.full_name) relative_path = os.path.relpath( path='.', start=os.path.dirname(doc_path) or '.') self._set_bases(relative_path, parser_config) for short_name in parser_config.tree[self.full_name]: # Remove builtin members that we never want to document. if short_name in [ '__class__', '__base__', '__weakref__', '__doc__', '__module__', '__dict__', '__abstractmethods__', '__slots__', '__getnewargs__', '__str__', '__repr__', '__hash__', '__reduce__' ]: continue child_name = '.'.join([self.full_name, short_name]) child = parser_config.py_name_to_object(child_name) # Don't document anything that is defined in object or by protobuf. defining_class = _get_defining_class(py_class, short_name) if defining_class in [object, type, tuple, BaseException, Exception]: continue # The following condition excludes most protobuf-defined symbols. if (defining_class and defining_class.__name__ in ['CMessage', 'Message', 'MessageMeta']): continue # TODO(markdaoust): Add a note in child docs showing the defining class. if doc_controls.should_skip_class_attr(py_class, short_name): continue child_doc = _parse_md_docstring(child, relative_path, parser_config.reference_resolver) if isinstance(child, property): self._add_property(short_name, child_name, child, child_doc) elif tf_inspect.isclass(child): if defining_class is None: continue url = parser_config.reference_resolver.reference_to_url( child_name, relative_path) self._add_class(short_name, child_name, child, child_doc, url) elif (tf_inspect.ismethod(child) or tf_inspect.isfunction(child) or tf_inspect.isroutine(child)): if defining_class is None: continue # Omit methods defined by namedtuple. original_method = defining_class.__dict__[short_name] if (hasattr(original_method, '__module__') and (original_method.__module__ or '').startswith('namedtuple')): continue # Some methods are often overridden without documentation. Because it's # obvious what they do, don't include them in the docs if there's no # docstring. if not child_doc.brief.strip() and short_name in [ '__del__', '__copy__' ]: continue try: child_signature = _generate_signature(child, parser_config.reverse_index) except TypeError: # If this is a (dynamically created) slot wrapper, tf_inspect will # raise typeerror when trying to get to the code. Ignore such # functions. continue child_decorators = [] try: if isinstance(py_class.__dict__[short_name], classmethod): child_decorators.append('classmethod') except KeyError: pass try: if isinstance(py_class.__dict__[short_name], staticmethod): child_decorators.append('staticmethod') except KeyError: pass self._add_method(short_name, child_name, child, child_doc, child_signature, child_decorators) else: # Exclude members defined by protobuf that are useless if issubclass(py_class, ProtoMessage): if (short_name.endswith('_FIELD_NUMBER') or short_name in ['__slots__', 'DESCRIPTOR']): continue # TODO(wicke): We may want to also remember the object itself. self._add_other_member(short_name, child_name, child, child_doc)