"""Code taken from pytorch

https://github.com/pytorch/pytorch/blob/385165ec674b764eb42ffe396f98fadd08a513eb/aten/src/ATen/code_template.py
"""
from pathlib import Path
import re
from typing import Optional, Dict, Union, List


ReplacementType = Union[str, int, List[Union[str, int]]]


class CodeTemplate:
    """Match ___identifier___ and replace with value in env

    If this identifier is at the beginning of whitespace on a line and its value is a list then it
    is treated as block subsitution by indenting to that depth and putting each element of the list
    on its own line. If the identifier is on a line starting with non-whitespace and a list
    then it is comma separated. ___foo___, will insert a comma after the list, if the list is not
    empty.
    """

    # Python 2.7.5 has a bug where the leading (^[^\n\S]*)? does not work,
    # workaround via appending another [^\n\S]? inside

    substitution_str = r"(^[^\n\S]*[^\n\S]?)?___([^\d\W]\w*)___(\,?)"

    # older versions of Python have a bug where \w* does not work,
    # so we need to replace with the non-shortened version [a-zA-Z0-9_]*
    # https://bugs.python.org/issue18647

    substitution_str = substitution_str.replace(r"\w", r"[a-zA-Z0-9_]")

    subtitution = re.compile(substitution_str, re.MULTILINE)

    @classmethod
    def from_file(cls, filename: Path) -> "CodeTemplate":
        with filename.open("r") as f:
            return cls(f.read())

    def __init__(self, pattern: str):
        self.pattern = pattern

    @staticmethod
    def indent_lines(indent: str, v: List[Union[str, int]], after: str) -> str:
        return "\n".join([indent + l + after for e in v for l in str(e).splitlines()])  # .rstrip()

    def substitute(
        self, env_: Optional[Dict[str, ReplacementType]] = None, **kwargs: ReplacementType
    ) -> str:
        env = env_ or {}

        def replace(match: "re.Match") -> str:
            indent = match.group(1)
            key = match.group(2)
            trailing_comma = match.group(3)

            # lookup
            v = kwargs[key] if key in kwargs else env[key]

            if indent is not None:
                if not isinstance(v, list):
                    v = [v]
                return self.indent_lines(indent, v, trailing_comma.rstrip())
            elif isinstance(v, list):
                middle = ", ".join([str(x) for x in v])
                if len(v) == 0:
                    return middle
                return middle + trailing_comma
            else:
                return str(v)

        return self.subtitution.sub(replace, self.pattern)


if __name__ == "__main__":
    pattern = """\
    def plot(___text_args___, ___label_args___)
    def bar(
        ___text_args___
        ___label_args___
    )
    def scatter(
        ___text_args___,
        ___label_args___,
    )
    """
    c = CodeTemplate(pattern)
    print(
        c.substitute(
            text_args=["fontsize: int", "tickness: float"],
            label_args=["size: float", "color: str"],
        )
    )

"""Code taken from pytorch

https://github.com/pytorch/pytorch/blob/385165ec674b764eb42ffe396f98fadd08a513eb/aten/src/ATen/code_template.py
"""
from pathlib import Path
import re
from typing import Optional, Dict, Union, List


ReplacementType = Union[str, int, List[Union[str, int]]]


class CodeTemplate:
    """Match ___identifier___ and replace with value in env

    If this identifier is at the beginning of whitespace on a line and its value is a list then it
    is treated as block subsitution by indenting to that depth and putting each element of the list
    on its own line. If the identifier is on a line starting with non-whitespace and a list
    then it is comma separated. ___foo___, will insert a comma after the list, if the list is not
    empty.
    """

    # Python 2.7.5 has a bug where the leading (^[^\n\S]*)? does not work,
    # workaround via appending another [^\n\S]? inside

    substitution_str = r"(^[^\n\S]*[^\n\S]?)?___([^\d\W]\w*)___(\,?)"

    # older versions of Python have a bug where \w* does not work,
    # so we need to replace with the non-shortened version [a-zA-Z0-9_]*
    # https://bugs.python.org/issue18647

    substitution_str = substitution_str.replace(r"\w", r"[a-zA-Z0-9_]")

    subtitution = re.compile(substitution_str, re.MULTILINE)

    @classmethod
    def from_file(cls, filename: Path) -> "CodeTemplate":
        with filename.open("r") as f:
            return cls(f.read())

    def __init__(self, pattern: str):
        self.pattern = pattern

    @staticmethod
    def indent_lines(indent: str, v: List[Union[str, int]], after: str) -> str:
        return "\n".join([indent + l + after for e in v for l in str(e).splitlines()])  # .rstrip()

    def substitute(
        self, env_: Optional[Dict[str, ReplacementType]] = None, **kwargs: ReplacementType
    ) -> str:
        env = env_ or {}

        def replace(match: "re.Match") -> str:
            indent = match.group(1)
            key = match.group(2)
            trailing_comma = match.group(3)

            # lookup
            v = kwargs[key] if key in kwargs else env[key]

            if indent is not None:
                if not isinstance(v, list):
                    v = [v]
                return self.indent_lines(indent, v, trailing_comma.rstrip())
            elif isinstance(v, list):
                middle = ", ".join([str(x) for x in v])
                if len(v) == 0:
                    return middle
                return middle + trailing_comma
            else:
                return str(v)

        return self.subtitution.sub(replace, self.pattern)


if __name__ == "__main__":
    pattern = """\
    def plot(___text_args___, ___label_args___)
    def bar(
        ___text_args___
        ___label_args___
    )
    def scatter(
        ___text_args___,
        ___label_args___,
    )
    """
    c = CodeTemplate(pattern)
    print(
        c.substitute(
            text_args=["fontsize: int", "tickness: float"],
            label_args=["size: float", "color: str"],
        )
    )

Code taken from pytorch  https://github.com/pytorch/pytorch/blob/385165ec674b764eb42ffe396f98fadd08a513eb/aten/src/ATen/code_template.py Match ___identifier___ and replace with value in env      If this identifier is at the beginning of whitespace on a line and its value is a list then it     is treated as block subsitution by indenting to that depth and putting each element of the list     on its own line. If the identifier is on a line starting with non-whitespace and a list     then it is comma separated. ___foo___, will insert a comma after the list, if the list is not     empty. # Python 2.7.5 has a bug where the leading (^[^\n\S]*)? does not work, # workaround via appending another [^\n\S]? inside # older versions of Python have a bug where \w* does not work, # so we need to replace with the non-shortened version [a-zA-Z0-9_]* # https://bugs.python.org/issue18647 # .rstrip() # lookup \     def plot(___text_args___, ___label_args___)     def bar(         ___text_args___         ___label_args___     )     def scatter(         ___text_args___,         ___label_args___,     )