def attrs(self) -> Iterator["ResolvedNode[Node]"]: if isinstance(self.node, Module): module_name = dot_join(self.module.name if self.module else None, self.name) for attr, node in self.node.attrs.items(): yield ResolvedNode(BoundNode(module_name, self.node), None, attr, node) elif isinstance(self.node, Class): class_name = dot_join(self.class_.name if self.class_ else None, self.name) for attr, node in self.node.attrs.items(): yield ResolvedNode(self.module, BoundNode(class_name, self.node), attr, node)
def aux(resolved: ResolvedNode[Node], name: str) -> None: node = resolved.node if hasattr(node, "docstring"): var_name = name.replace(".", "_") + "_DOC" if args.header: output_file.write("extern ") output_file.write(f"const char {var_name}[]") if not args.header: output_file.write(" =") signature, lines = formatter.format(cast( ResolvedNode[DocumentedNode], resolved), rst=False) if signature: lines[0:0] = [ name.rpartition(".")[2] + signature, "", ] if lines: for i, line in enumerate(lines): output_file.write(f'\n\t"{escape_string(line)}') if i != len(lines) - 1: output_file.write("\\n") output_file.write('"') else: output_file.write(' ""') output_file.write(";\n") if args.header: output_file.write(f"#define {var_name} (char *){var_name}\n") for attr in resolved.attrs(): if isinstance(node, Class) and attr.name == "__init__": continue aux(attr, dot_join(name, attr.name))
def attr(self, attr: str) -> "ResolvedNode[Node]": if isinstance(self.node, Module): module_name = dot_join(self.module.name if self.module else None, self.name) return ResolvedNode(BoundNode(module_name, self.node), None, attr, self.node.attrs[attr]) elif isinstance(self.node, Class): class_name = dot_join(self.class_.name if self.class_ else None, self.name) return ResolvedNode( self.module, BoundNode(class_name, self.node), attr, self.node.attrs[attr], ) else: raise KeyError(attr)
def _run_module( self, top_name: str, attr_name: str, resolved: ResolvedNode[Module], docnode: docutils.nodes.Node, ) -> None: node = resolved.node if node.docstring is None: # Not documented. Ignore it. return sourcename = node.path or "" if sourcename: self.env.note_dependency(sourcename) contents = docutils.statemachine.StringList( node.docstring.splitlines(), sourcename ) sphinx.util.nodes.nested_parse_with_titles(self.state, contents, docnode) # If the module docstring defines any sections, then the contents # should go inside of the last one. section = docnode for child in reversed(docnode.children): if isinstance(child, docutils.nodes.section): section = child break try: old_py_module = self.env.ref_context["py:module"] have_old_py_module = True except KeyError: have_old_py_module = False self.env.ref_context["py:module"] = dot_join(top_name, attr_name) for attr in resolved.attrs(): self._run( top_name, dot_join(attr_name, attr.name), attr.name, attr, section ) if have_old_py_module: self.env.ref_context["py:module"] = old_py_module else: del self.env.ref_context["py:module"]
def _run( self, top_name: str, attr_name: str, name: str, resolved: ResolvedNode[Node], docnode: docutils.nodes.Node, ) -> None: exclude_pattern = self.options.get("exclude") if exclude_pattern is not None and re.fullmatch( exclude_pattern, attr_name): return if isinstance(resolved.node, (Import, ImportFrom)): # Only include imports that are explicitly aliased (i.e., import # ... as ... or from ... import ... as ...). # TODO: we should also include imports listed in __all__. if not resolved.node.aliased: return imported = self.env.drgndoc_namespace.resolve_name_in_scope( resolved.modules, resolved.classes, resolved.name) if not isinstance(imported, ResolvedNode): return resolved = imported resolved = cast(ResolvedNode[DocumentedNode], resolved) if isinstance(resolved.node, Module): return self._run_module(top_name, attr_name, cast(ResolvedNode[Module], resolved), docnode) lines = self.env.drgndoc_formatter.format( resolved, name, self.env.ref_context.get("py:module", ""), ".".join(self.env.ref_context.get("py:classes", ())), ) if not lines: # Not documented. Ignore it. return sourcename = "" if resolved.modules and resolved.modules[-1].node.path: sourcename = resolved.modules[-1].node.path if sourcename: self.env.note_dependency(sourcename) contents = docutils.statemachine.StringList(lines, sourcename) contents.append("", sourcename) self.state.nested_parse(contents, 0, docnode) if isinstance(resolved.node, Class): for desc in reversed(docnode.children): if isinstance(desc, sphinx.addnodes.desc): break else: logger.warning("desc node not found") return for desc_content in reversed(desc.children): if isinstance(desc_content, sphinx.addnodes.desc_content): break else: logger.warning("desc_content node not found") return py_classes = self.env.ref_context.setdefault("py:classes", []) py_classes.append(resolved.name) self.env.ref_context["py:class"] = resolved.name for member in resolved.attrs(): if member.name != "__init__": self._run( top_name, dot_join(attr_name, member.name), member.name, member, desc_content, ) py_classes.pop() self.env.ref_context[ "py:class"] = py_classes[-1] if py_classes else None
def _run( self, top_name: str, attr_name: str, resolved: ResolvedNode[Node], docnode: docutils.nodes.Node, ) -> None: if not self._include_attr(resolved, attr_name): return resolved = cast(ResolvedNode[DocumentedNode], resolved) node = resolved.node if isinstance(node, Module): directive = "py:module" return self._run_module( top_name, attr_name, cast(ResolvedNode[Module], resolved), docnode ) sourcename = "" if resolved.module and resolved.module.node.path: sourcename = resolved.module.node.path if sourcename: self.env.note_dependency(sourcename) if isinstance(node, Class): directive = "py:class" elif isinstance(node, Function): directive = "py:method" if resolved.class_ else "py:function" elif isinstance(node, Variable): directive = "py:attribute" if resolved.class_ else "py:data" else: assert False, type(node).__name__ argument = (attr_name or top_name).rpartition(".")[2] extra_argument, lines = self.env.drgndoc_formatter.format( resolved, self.env.ref_context.get("py:module", ""), ".".join(self.env.ref_context.get("py:classes", ())), ) contents = docutils.statemachine.StringList() contents.append( f".. {directive}:: {argument}{extra_argument}", sourcename, ) if isinstance(node, Function): if node.async_: contents.append(" :async:", sourcename) if resolved.class_: if node.have_decorator("classmethod"): contents.append(" :classmethod:", sourcename) if node.have_decorator("staticmethod"): contents.append(" :staticmethod:", sourcename) contents.append("", sourcename) if lines: for line in lines: contents.append(" " + line, sourcename) contents.append("", sourcename) self.state.nested_parse(contents, 0, docnode) if isinstance(node, Class): for desc in reversed(docnode.children): if isinstance(desc, sphinx.addnodes.desc): break else: logger.warning("desc node not found") return for desc_content in reversed(desc.children): if isinstance(desc_content, sphinx.addnodes.desc_content): break else: logger.warning("desc_content node not found") return py_classes = self.env.ref_context.setdefault("py:classes", []) py_classes.append(resolved.name) self.env.ref_context["py:class"] = resolved.name for member in resolved.attrs(): self._run( top_name, dot_join(attr_name, member.name), member, desc_content ) py_classes.pop() self.env.ref_context["py:class"] = py_classes[-1] if py_classes else None
def qualified_name(self) -> str: return dot_join( self.module.name if self.module else None, self.class_.name if self.class_ else None, self.name, )