Skip to content

md_multiline_table

md_multiline_table

Multiline Table extension for Python Markdown.

Classes:

Name Description
MultilineTableExtension

Multiline Table Python Markdown extension.

MultilineTableProcessor

Multiline Table Python Markdown extension markdown code block processor.

Functions:

Name Description
make_extension

Make a new instance of MultilineTableExtension.

Classes

MultilineTableExtension

Bases: TableExtension


              flowchart TD
              md_multiline_table.MultilineTableExtension[MultilineTableExtension]

              

              click md_multiline_table.MultilineTableExtension href "" "md_multiline_table.MultilineTableExtension"
            

Multiline Table Python Markdown extension.

Methods:

Name Description
extendMarkdown

Extend markdown instance with multiline table extension.

Source code in src/md_multiline_table/__init__.py
class MultilineTableExtension(TableExtension):
    """Multiline Table Python Markdown extension."""

    def extendMarkdown(self, md: Markdown) -> None:
        """
        Extend markdown instance with multiline table extension.

        Args:
            md (Markdown): markdown instance to extend
        """
        if "|" not in md.ESCAPED_CHARS:
            md.ESCAPED_CHARS.append("|")
        if ":" not in md.ESCAPED_CHARS:
            md.ESCAPED_CHARS.append(":")
        processor = MultilineTableProcessor(md.parser, self.getConfigs())
        md.parser.blockprocessors.register(processor, "md-multiline-table", 106)
Methods:
extendMarkdown
extendMarkdown(md: Markdown) -> None

Extend markdown instance with multiline table extension.

Parameters:

Name Type Description Default
md Markdown

markdown instance to extend

required
Source code in src/md_multiline_table/__init__.py
def extendMarkdown(self, md: Markdown) -> None:
    """
    Extend markdown instance with multiline table extension.

    Args:
        md (Markdown): markdown instance to extend
    """
    if "|" not in md.ESCAPED_CHARS:
        md.ESCAPED_CHARS.append("|")
    if ":" not in md.ESCAPED_CHARS:
        md.ESCAPED_CHARS.append(":")
    processor = MultilineTableProcessor(md.parser, self.getConfigs())
    md.parser.blockprocessors.register(processor, "md-multiline-table", 106)

MultilineTableProcessor

Bases: TableProcessor


              flowchart TD
              md_multiline_table.MultilineTableProcessor[MultilineTableProcessor]

              

              click md_multiline_table.MultilineTableProcessor href "" "md_multiline_table.MultilineTableProcessor"
            

Multiline Table Python Markdown extension markdown code block processor.

Methods:

Name Description
__init__

Initialize new instance of the multiline table python extension markdown block processor.

run

Transform multiline table to standard markdown table.

test

Ensure that the first few rows contains a valid table head (column header and separator row).

Attributes:

Name Type Description
RE_END_BORDER
Source code in src/md_multiline_table/__init__.py
class MultilineTableProcessor(TableProcessor):
    """Multiline Table Python Markdown extension markdown code block processor."""

    RE_END_BORDER = re.compile(r"(?<!\\)(?:\\\\)*(\|\+?|:)$")

    def __init__(self, parser: BlockParser, config: dict[str, Any]) -> None:
        """
        Initialize new instance of the multiline table python extension markdown block processor.

        Args:
            parser (BlockParser): block parser
            config (dict[str, Any]): all configuration options
        """
        super().__init__(parser, config)
        self._logger = getLogger("MARKDOWN")

    @no_type_check
    def test(self, parent: etree.Element, block: str) -> bool:
        """
        Ensure that the first few rows contains a valid table head (column header and separator row).

        This method does exactly the same like its super() version except that the separator row
        does not have to be in the second row to support multiline headers.
        See variable `format_row`.

        Args:
            parent (etree.Element): parent html element
            block (str): markdown code

        Returns:
            bool: whether markdown code is a table or not
        """
        is_table = False
        rows = [row.strip(" ") for row in block.split("\n")]
        if len(rows) > 1:
            header0 = rows[0]
            self.border = PIPE_NONE
            if header0.startswith("|"):
                self.border |= PIPE_LEFT
            if self.RE_END_BORDER.search(header0) is not None:
                self.border |= PIPE_RIGHT
            row = self._split_row(header0)
            row0_len = len(row)
            is_table = row0_len > 1

            # Each row in a single column table needs at least one pipe.
            if not is_table and row0_len == 1 and self.border:
                for index in range(1, len(rows)):
                    is_table = rows[index].startswith("|")
                    if not is_table:
                        is_table = self.RE_END_BORDER.search(rows[index]) is not None
                    if not is_table:
                        break

            if is_table:
                format_row = next((row for idx, row in enumerate(rows) if idx > 0 and row.endswith("|")))
                row = self._split_row(format_row)
                is_table = (len(row) == row0_len) and set("".join(row)) <= set("|:- ")
                if is_table:
                    self.separator = row

        return is_table

    def run(self, parent: etree.Element, blocks: list[str]) -> None:
        """
        Transform multiline table to standard markdown table.

        Args:
            parent (etree.Element): parent html element
            blocks (list[str]): markdown code blocks
        """
        lines = blocks.pop(0).split("\n")

        # sanity check which should never fail due to self.test(...)
        if len(lines) < 2:
            self._logger.warning("Broken table with less then 2 lines detected.")
            return
        column_count = len(re.split(r"(?<!\\)(?:\\\\)*\|", lines[0])) - 2
        if column_count < 1:
            self._logger.warning("Broken table with less then 1 column detected.")
            return

        # convert multiline table to default markdown table format
        blocks.insert(0, self._convert_table(lines, column_count))
        super().run(parent, blocks)

    def _convert_table(self, lines: list[str], column_count: int) -> str:
        line_idx = 1
        while line_idx < len(lines):
            line = lines[line_idx]
            if line.strip()[0] != ":" and not line.strip().endswith("|+"):
                line_idx += 1
                continue

            re_split_pattern = r"(?<!\\)(?:\\\\)*:" if not line.strip().endswith("|+") else r"(?<!\\)(?:\\\\)*\|"
            columns = re.split(re_split_pattern, line)[1:-1]
            if len(columns) != column_count:
                self._logger.warning(
                    "Table column count mismatch. Row has %d instead of the expected %d columns.",
                    len(columns),
                    column_count,
                )

            columns_previous = re.split(r"(?<!\\)(?:\\\\)*\|", lines[line_idx - 1])[1:-1]
            lines[line_idx - 1] = (
                "| "
                + " | ".join(
                    " ".join(part.strip() for part in column_parts).strip()
                    for column_parts in zip(columns_previous, columns, strict=False)
                )
                + " |"
            )
            lines.pop(line_idx)

        return "\n".join(lines)
Attributes
RE_END_BORDER class-attribute instance-attribute
RE_END_BORDER = re.compile('(?<!\\\\)(?:\\\\\\\\)*(\\|\\+?|:)$')
Methods:
__init__
__init__(parser: BlockParser, config: dict[str, Any]) -> None

Initialize new instance of the multiline table python extension markdown block processor.

Parameters:

Name Type Description Default
parser BlockParser

block parser

required
config dict[str, Any]

all configuration options

required
Source code in src/md_multiline_table/__init__.py
def __init__(self, parser: BlockParser, config: dict[str, Any]) -> None:
    """
    Initialize new instance of the multiline table python extension markdown block processor.

    Args:
        parser (BlockParser): block parser
        config (dict[str, Any]): all configuration options
    """
    super().__init__(parser, config)
    self._logger = getLogger("MARKDOWN")
run
run(parent: Element, blocks: list[str]) -> None

Transform multiline table to standard markdown table.

Parameters:

Name Type Description Default
parent Element

parent html element

required
blocks list[str]

markdown code blocks

required
Source code in src/md_multiline_table/__init__.py
def run(self, parent: etree.Element, blocks: list[str]) -> None:
    """
    Transform multiline table to standard markdown table.

    Args:
        parent (etree.Element): parent html element
        blocks (list[str]): markdown code blocks
    """
    lines = blocks.pop(0).split("\n")

    # sanity check which should never fail due to self.test(...)
    if len(lines) < 2:
        self._logger.warning("Broken table with less then 2 lines detected.")
        return
    column_count = len(re.split(r"(?<!\\)(?:\\\\)*\|", lines[0])) - 2
    if column_count < 1:
        self._logger.warning("Broken table with less then 1 column detected.")
        return

    # convert multiline table to default markdown table format
    blocks.insert(0, self._convert_table(lines, column_count))
    super().run(parent, blocks)
test
test(parent: Element, block: str) -> bool

Ensure that the first few rows contains a valid table head (column header and separator row).

This method does exactly the same like its super() version except that the separator row does not have to be in the second row to support multiline headers. See variable format_row.

Parameters:

Name Type Description Default
parent Element

parent html element

required
block str

markdown code

required

Returns:

Name Type Description
bool bool

whether markdown code is a table or not

Source code in src/md_multiline_table/__init__.py
@no_type_check
def test(self, parent: etree.Element, block: str) -> bool:
    """
    Ensure that the first few rows contains a valid table head (column header and separator row).

    This method does exactly the same like its super() version except that the separator row
    does not have to be in the second row to support multiline headers.
    See variable `format_row`.

    Args:
        parent (etree.Element): parent html element
        block (str): markdown code

    Returns:
        bool: whether markdown code is a table or not
    """
    is_table = False
    rows = [row.strip(" ") for row in block.split("\n")]
    if len(rows) > 1:
        header0 = rows[0]
        self.border = PIPE_NONE
        if header0.startswith("|"):
            self.border |= PIPE_LEFT
        if self.RE_END_BORDER.search(header0) is not None:
            self.border |= PIPE_RIGHT
        row = self._split_row(header0)
        row0_len = len(row)
        is_table = row0_len > 1

        # Each row in a single column table needs at least one pipe.
        if not is_table and row0_len == 1 and self.border:
            for index in range(1, len(rows)):
                is_table = rows[index].startswith("|")
                if not is_table:
                    is_table = self.RE_END_BORDER.search(rows[index]) is not None
                if not is_table:
                    break

        if is_table:
            format_row = next((row for idx, row in enumerate(rows) if idx > 0 and row.endswith("|")))
            row = self._split_row(format_row)
            is_table = (len(row) == row0_len) and set("".join(row)) <= set("|:- ")
            if is_table:
                self.separator = row

    return is_table

Functions:

make_extension

make_extension(*_args, **kwargs) -> MultilineTableExtension

Make a new instance of MultilineTableExtension.

Parameters:

Name Type Description Default
_args

Will be ignored.

()
kwargs

Further Extension arguments.

{}

Returns:

Name Type Description
MultilineTableExtension MultilineTableExtension

new instance of MultilineTableExtension

Source code in src/md_multiline_table/__init__.py
def make_extension(*_args, **kwargs) -> MultilineTableExtension:
    """
    Make a new instance of `MultilineTableExtension`.

    Arguments:
        _args: Will be ignored.
        kwargs: Further Extension arguments.

    Returns:
        MultilineTableExtension: new instance of MultilineTableExtension
    """
    return MultilineTableExtension(**kwargs)