Source code for cosapp.tools.views.markdown

"""Mardown viewers for Module and Port."""
from typing import Union
from cosapp.ports.port import BasePort
from cosapp.systems import System


[docs] def upper_first(s: str) -> str: """Return a copy of the input string, with the first character in upper case.""" return s[0].upper() + s[1:]
[docs] def with_final_dot(s: str) -> str: """Return a copy of the input string, ensuring it ends with a dot.""" return s if s.endswith(".") else s + "."
[docs] class PortMarkdownFormatter: """Markdown table formatter for ports""" def __init__(self, port: BasePort) -> None: self.port = port
[docs] def content(self, contextual=True) -> list[str]: """Returns the port representation in Markdown format, as a list of strings. Parameters ---------- contextual: bool If `True` (default), uses port full name; if `False`, displays only port name. Returns ------- list[str] list of Markdown strings representing port variables as a table, with header. """ port = self.port name = port.full_name() if contextual else port.name info = type(port).__name__ if (desc := port.description.strip()): info += with_final_dot(f". {upper_first(desc)}") doc = [] doc.append(f"`{name}`: {info}") doc.extend(self.var_repr()) return doc
[docs] def var_repr(self) -> list[str]: """Returns the representation of port variables in Markdown format, as a list of strings (same as `content`, without name header). Returns ------- list[str] list of Markdown strings representing port variables as a table. """ content = [ f" {variable._repr_markdown_()}" for variable in self.port.variables() ] return self.wrap(content)
[docs] def markdown(self, contextual=True) -> str: """Returns the port representation in Markdown format. Parameters ---------- contextual: bool If `True` (default), uses port full name; if `False`, displays only port name. Returns ------- str Markdown formatted representation """ return "\n".join(self.content(contextual))
[docs] @classmethod def wrap(cls, content: Union[str, list[str]]) -> list[str]: if isinstance(content, str): content = [content] header = ["| | |", "---|---"] doc = ["", cls.div_header(), ""] doc.extend(header) doc.extend(content) doc.append("</div>") return doc
[docs] @classmethod def div_header(cls) -> str: """Div header used to override Jupyter Lab table CSS""" return "".join([ r"<div class='cosapp-port-table' style='margin-left: 25px; margin-top: -12px'>", r"<style type='text/css'>", r".cosapp-port-table >table >thead{display: none}", # suppress empty table header r".cosapp-port-table tbody tr{background: rgba(255, 255, 255, 0)!important}", # override even/odd coloring r".cosapp-port-table tbody tr:hover{background: #e1f5fe!important}", # set hover color r".cosapp-port-table >table {margin-left: unset; margin-right: unset}", r"</style>", ])
[docs] def port_to_md(port: BasePort) -> str: """Returns the representation of `port` in Markdown format. Parameters ---------- port: BasePort Port to describe Returns ------- str Markdown formatted representation """ formatter = PortMarkdownFormatter(port) return formatter.markdown()
[docs] def system_to_md(system: System) -> str: """Returns the representation of `system` in Markdown format. Parameters ---------- system: System System to describe Returns ------- str Markdown formatted representation """ doc = [] if (tags := system.tags): doc.extend(["", f"**Tags**: {list(tags)!s}", ""]) def get_child_doc(s: System) -> str: info = type(s).__name__ if (desc := s.description.strip()): info += f". {with_final_dot(upper_first(desc))}" return f"- `{s.name}`: {info}" if (children := system.children): doc.extend(["", "### Child components", ""]) doc.extend(map(get_child_doc, children.values())) def dump_port_data(header, port_dict: dict[str, BasePort]): port_docs = [] for port in port_dict.values(): if len(port) > 0: formatter = PortMarkdownFormatter(port) port_doc = formatter.content(contextual=False) port_doc[0] = f"\n- {port_doc[0]}" port_docs.extend(port_doc) if port_docs: doc.extend(["", f"### {header.title()}", ""]) doc.extend(port_docs) dump_port_data("inputs", system.inputs) dump_port_data("outputs", system.outputs) if (residues := system.residues): doc.extend(["", "### Residues", ""]) doc.append(", ".join(f"`{key}`" for key in residues)) return "\n".join(doc)