def __post_init__(self): super().__post_init__() members = self.members if self.object.kind in ["class", "dataclass"] and not self.docstring: for member in members: if member.object.name == "__init__" and member.docstring: markdown = member.docstring.sections[0].markdown if not markdown.startswith("Initialize self"): self.docstring = member.docstring self.members = [m for m in members if m.object.name != "__init__"] doc = self.docstring if doc and "property" in self.object.kind: if not doc.type and len(doc.sections) == 1 and doc.sections[0].name == "": section = doc.sections[0] markdown = section.markdown type, markdown = preprocess.split_type(markdown) if type: doc.type = Type(type) section.markdown = markdown if doc and doc.type: self.object.type = doc.type doc.type = Type()
def parse_attribute(doc: Docstring, obj: Any): """Parses attributes' docstring to inspect type and description from source.""" signature = get_signature(obj) attrs = signature.attributes attrs_desc = signature.attributes_desc if inspect.ismethod(obj): print(obj) print(attrs) if not attrs: return section = doc["Attributes"] if section is None: if any(x for x in attrs_desc.values()): section = Section("Attributes") doc["Attributes"] = section else: return items = [] for name in attrs: item = section[name] if item: if not item.type.markdown: item.type = Type(attrs[name]) if not item.markdown: item.markdown = attrs_desc[name] del section[name] elif attrs_desc[name]: item = Item(name, attrs_desc[name], type=Type(attrs[name])) else: continue items.append(item) items.extend(section.items) section.items = items
def postprocess(doc: Docstring, obj: Any): parse_bases(doc, obj) if inspect.ismodule(obj) or inspect.isclass(obj): parse_attribute(doc, obj) if isinstance(obj, property): parse_property(doc, obj) if not callable(obj): return signature = get_signature(obj) if signature.signature is None: return def get_type(type: str) -> Type: if type.startswith("("): # tuple type = type[1:-1] return Type(type) if doc["Parameters"] is not None: for item in doc["Parameters"].items: if not item.type and item.name in signature.parameters: item.type = get_type(signature.parameters[item.name]) if "{default}" in item.desc.markdown and item.name in signature: default = signature.defaults[item.name] item.desc.markdown = item.desc.markdown.replace( "{default}", default) if doc["Attributes"] is not None and signature.attributes: for item in doc["Attributes"].items: if not item.type and item.name in signature.attributes: item.type = get_type(signature.attributes[item.name]) for name in ["Returns", "Yields"]: section = doc[name] if section is not None and not section.type: section.type = Type(getattr(signature, name.lower())) if doc["Returns"] is None and doc["Yields"] is None: from mkapi.core.node import get_kind kind = get_kind(obj) if kind == "generator": doc.type = Type(signature.yields) else: doc.type = Type(signature.returns) for section in doc.sections: if section.name in ["Example", "Examples"]: break if section.markdown: section.markdown = replace_link(obj, section.markdown) else: for item in section.items: if item.markdown: item.markdown = replace_link(obj, item.markdown)
def parse_property(doc: Docstring, obj: Any): """Parses property's docstring to inspect type.""" doc.type = Type(get_signature(obj.fget).returns) if not doc.type: section = doc.sections[0] markdown = section.markdown type, markdown = preprocess.split_type(markdown) if type: doc.type = Type(type) section.markdown = markdown
def get_section(name: str, doc: str, style: str) -> Section: """Returns a [Section]() instance.""" type = "" markdown = "" items = [] if name in ["Parameters", "Attributes", "Raises"]: items = [ Item(n, m, Type(t)) for n, m, t in parse_parameters(doc, style) ] elif name in ["Returns", "Yields"]: markdown, type = parse_returns(doc, style) else: markdown = doc return Section(name, markdown, items, Type(type))
def postprocess(doc: Docstring, obj: Any): parse_bases(doc, obj) parse_source(doc, obj) if not callable(obj): return signature = get_signature(obj) if signature.signature is None: return if "Parameters" in doc: for item in doc["Parameters"].items: description = item.description if "{default}" in description.name and item.name in signature: default = signature.defaults[item.name] description.markdown = description.name.replace( "{default}", default) for name in ["Returns", "Yields"]: if name in doc: section = doc[name] if not section.type: section.type = Type(getattr(signature, name.lower())) if "Returns" not in doc and "Yields" not in doc: from mkapi.core.node import get_kind kind = get_kind(obj) if kind == "generator": doc.type = Type(signature.yields) else: doc.type = Type(signature.returns) sections: List[Section] = [] for section in doc.sections: if section.name not in ["Example", "Examples"]: for base in section: base.markdown = replace_link(obj, base.markdown) if section.name in ["Note", "Notes", "Warning", "Warnings"]: markdown = preprocess.admonition(section.name, section.markdown) if sections and sections[-1].name == "": sections[-1].markdown += "\n\n" + markdown continue else: section.name = "" section.markdown = markdown sections.append(section) doc.sections = sections
def parse_parameter(lines: List[str], style: str) -> Item: """Returns a Item instance that represents a parameter. Args: lines: Splitted parameter docstring lines. style: Docstring style. `google` or `numpy`. """ if style == "google": name, _, line = lines[0].partition(":") name = name.strip() parsed = [line.strip()] pattern = r"(.*?)\s*?\((.*?)\)" else: name = lines[0].strip() parsed = [] pattern = r"([^ ]*?)\s*:\s*(.*)" if len(lines) > 1: indent = get_indent(lines[1]) for line in lines[1:]: parsed.append(line[indent:]) m = re.match(pattern, name) if m: name, type = m.group(1), m.group(2) else: type = "" return Item(name, Type(type), Inline("\n".join(parsed)))
def set_attributes(self): """ Examples: >>> from mkapi.core.base import Base >>> s = Signature(Base) >>> s.parameters['name'].to_tuple() ('name', 'str, optional', 'Name of self.') >>> s.attributes['html'].to_tuple() ('html', 'str', 'HTML output after conversion.') """ items = [] for name, (type, description) in get_attributes(self.obj).items(): if isinstance(type, str) and type: type = resolve_forward_ref(self.obj, type) else: type = to_string(type, obj=self.obj) if type else "" if not type: type, description = preprocess.split_type(description) item = Item(name, Type(type), Inline(description)) if is_dataclass(self.obj): if name in self.parameters: self.parameters[name].set_description(item.description) if self.obj.__dataclass_fields__[name].type != InitVar: items.append(item) else: items.append(item) self.attributes = Section("Attributes", items=items)
def inherit_signature(node: Node, name: str = ""): """Inherits Parameters or Attributes section from signature. Args: node: Node instance. name: Section name: 'Parameters' or 'Attributes', or ''. If name is '', both sections are inherited. Examples: >>> from mkapi.core.object import get_object >>> base = Node(get_object('mkapi.core.base.Base')) >>> [item.name for item in base.docstring['Attributes'].items] ['html'] >>> inherit_signature(base) >>> [item.name for item in base.docstring['Attributes'].items] ['name', 'markdown', 'html'] """ if not name: for name in ["Parameters", "Attributes"]: inherit_signature(node, name) return _, params = get_params(node, name) if not params: return node_section = node.docstring[name] items = [] for item_name, type in params.items(): if node_section is None or item_name not in node_section: item = Item(item_name, markdown="", type=Type(type)) else: item = node_section[item_name] # type:ignore items.append(item) node.docstring[name] = Section(name, items=items)
def parse_bases(doc: Docstring, obj: Any): """Parses base classes to create a Base(s) line.""" if not inspect.isclass(obj) or not hasattr(obj, "mro"): return objs = get_mro(obj)[1:] if not objs: return types = [get_link(obj, include_module=True) for obj in objs] items = [Item(type=Type(type)) for type in types if type] doc.set_section(Section("Bases", items=items))
def get_type(node: Node) -> Type: type = node.object.type if type: name = type.name else: for name in ["Returns", "Yields"]: if name in node.docstring: section = node.docstring[name] if section.type: name = section.type.name break else: name = "" return Type(name)
def get_type(node: Node) -> Type: type = node.object.type if type: name = type.name else: for name in ["Returns", "Yields"]: section = node.docstring[name] if section and section.type: name = section.type.name break else: name = "" if name.startswith("("): name = name[1:-1] return Type(name)
def __post_init__(self): if self.obj is None: return try: self.signature = inspect.signature(self.obj) except (TypeError, ValueError): self.set_attributes() return items = [] for name, parameter in self.signature.parameters.items(): if name == "self": continue elif parameter.kind is inspect.Parameter.VAR_POSITIONAL: name = "*" + name elif parameter.kind is inspect.Parameter.VAR_KEYWORD: name = "**" + name if isinstance(parameter.annotation, str): type = resolve_forward_ref(self.obj, parameter.annotation) else: type = to_string(parameter.annotation, obj=self.obj) default = parameter.default if default == inspect.Parameter.empty: self.defaults[name] = default else: self.defaults[name] = f"{default!r}" if not type: type = "optional" elif not type.endswith(", optional"): type += ", optional" items.append(Item(name, Type(type))) self.parameters = Section("Parameters", items=items) self.set_attributes() return_annotation = self.signature.return_annotation if isinstance(return_annotation, str): self.returns = resolve_forward_ref(self.obj, return_annotation) else: self.returns = to_string(return_annotation, "returns", obj=self.obj) self.yields = to_string(return_annotation, "yields", obj=self.obj)
def get_type(type: str) -> Type: if type.startswith("("): # tuple type = type[1:-1] return Type(type)