def get_section(node: Node, name: str, mode: str) -> Section: """Returns a tuple of (docstring section, signature section). Args: node: Node instance. name: Section name: `Parameters` or `Attributes`. mode: Mode name: `Docstring` or `Signature`. Examples: >>> node = get_node('mkapi.core.base.Type') >>> section = get_section(node, 'Parameters', 'Docstring') >>> 'name' in section True >>> section['name'].to_tuple() ('name', 'str, optional', '') """ if mode == "Docstring": if name in node.docstring: return node.docstring[name] else: return Section(name) else: if hasattr(node.object.signature, name.lower()): return node.object.signature[name] else: return Section(name)
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 test_docstring_copy(): d = Docstring() a = Section("Parameters") d.set_section(a) assert "Parameters" in d assert d["Parameters"] is a a = Section("Arguments") d.set_section(a, copy=True) assert "Arguments" in d assert d["Arguments"] is not a
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 test_section_delete_item(): a = Item("a", "int", "aaa") b = Item("b", "str", "bbb") c = Item("c", "float", "ccc") s = Section("Parameters", items=[a, b, c]) del s["b"] assert "b" not in s with pytest.raises(KeyError): del s["x"]
def func(): """Internal link example. * [Section]() --- Imported object. * [](get_docstring) --- Imported object. * [Section.set_html]() --- Member of imported object. * [Section definition](Section) --- Alternative text. """ return Section(), get_docstring(None)
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_section(name: str, doc: str, style: str) -> Section: """Returns a [Section]() instance.""" type = "" markdown = "" items = [] if name in ["Parameters", "Attributes", "Raises"]: items = parse_parameters(doc, style) elif name in ["Returns", "Yields"]: type, markdown = parse_returns(doc, style) else: markdown = doc return Section(name, markdown, items, Type(type))
def get_docstring(obj: Any) -> Docstring: """Returns a [Docstring]() instance.""" doc = inspect.getdoc(obj) if doc: sections = [] for section in split_section(doc): sections.append(get_section(*section)) docstring = Docstring(sections) elif inspect.isclass(obj) and hasattr(obj, "mro"): bases = obj.mro()[1:-1] if not bases: return Docstring() docstring = Docstring([Section()]) else: return Docstring() postprocess(docstring, obj) return docstring
def transform_property(node: Node): section = node.docstring["Attributes"] members = [] for member in node.members: if "property" in member.object.kind: if section is None: section = Section("Attributes") node.docstring['Attributes'] = section name = member.object.name kind = member.object.kind type = member.object.type markdown = member.docstring.sections[0].markdown item = Item(name, markdown, type=type, kind=kind) section.items.append(item) else: members.append(member) node.members = members
def transform_members(node: Node, mode: str, filters: Optional[List[str]] = None): def is_member(kind): if mode in ["method", "function"]: return mode in kind or kind == "generator" else: return mode in kind and "method" not in kind members = [ member for member in node.members if is_member(member.object.kind) ] if not members: return name = mode[0].upper() + mode[1:] + ("es" if mode == "class" else "s") section = Section(name) for member in members: object = member.object kind = object.kind type = get_type(member) if member.docstring and "" in member.docstring: description = member.docstring[""].markdown if "\n\n" in description: description = description.split("\n\n")[0] else: description = "" item = Item(object.name, type, Inline(description), kind) item.markdown, url = "", "" if filters and ("link" in filters or "all" in filters): url = "#" + object.id elif filters and "apilink" in filters: url = "../" + node.object.id + "#" + object.id signature: Dict[str, Any] = {} if object.kind not in ["class", "dataclass"]: args = [item.name for item in object.signature.parameters.items] signature["arguments"] = args else: signature["arguments"] = None item.html = renderer.render_object_member(object.name, url, signature) if filters and "sourcelink" in filters: source_link_from_section_item(item, object) section.items.append(item) node.docstring.set_section(section)
def inherit_base(node: Node, base: Node, name: str = ""): """Inherits Parameters or Attributes section from base class. Args: node: Node instance. base: Node instance of a super class. 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')) >>> node = Node(get_object('mkapi.core.base.Type')) >>> [item.name for item in base.docstring['Parameters'].items] ['name', 'markdown'] >>> node.docstring['Parameters'] is None True >>> inherit_base(node, base) >>> [item.name for item in node.docstring['Parameters'].items] ['name', 'markdown'] """ if not name: for name in ["Parameters", "Attributes"]: inherit_base(node, base, name) return base_section = base.docstring[name] if base_section is None: return _, node_params = get_params(node, name) _, base_params = get_params(base, name) node_section = node.docstring[name] items = [] for item in base_section.items: if node_section is None or item.name not in node_section: if (item.name in node_params and node_params[item.name] == base_params[item.name]): items.append(item) if node_section is not None: for item in node_section.items: if item not in items: items.append(item) node.docstring[name] = Section(name, items=items) # type:ignore
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 transform_members(node: Node, mode: str, filters: Optional[List[str]] = None): def is_member(kind): if mode in ["method", "function"]: return mode in kind or kind == "generator" else: return mode in kind and 'method' not in kind members = [ member for member in node.members if is_member(member.object.kind) ] if not members: return name = mode[0].upper() + mode[1:] + ("es" if mode == "class" else "s") section = Section(name) for member in members: object = member.object kind = object.kind type = get_type(member) section_ = member.docstring[""] if section_: markdown = section_.markdown if "\n\n" in markdown: markdown = markdown.split("\n\n")[0] item = Item(name, markdown, type=type, kind=kind) item.markdown, url, signature = "", "", "" if filters and "link" in filters: url = "#" + object.id if object.kind not in ["class", "dataclass"]: signature = "(" + ",".join( object.signature.parameters.keys()) + ")" item.html = renderer.render_object_member(object.name, url, signature) section.items.append(item) node.docstring[name] = section
def test_section_merge(): a = Section("a") b = Section("b") with pytest.raises(ValueError): a.merge(b)