"""
This module contains various helper functions and classes.
"""
import sys
from functools import partial
from types import ModuleType

__all__ = ["make_proc", "Module", "make_module"]


def _call_proc(code, variables, *a, **kw):
    v = variables[len(a) :]
    if v:
        a = list(a)
        for k in v:
            a.append(kw.pop(k, None))
    eval(code, kw)  # pylint: disable=eval-used
    code = kw["_proc"]
    return code(*a)


def make_proc(code, variables, path, *, use_async=False):  # pylint: disable=redefined-builtin
    """Compile this code block to a procedure.

    Args:
        code: the code block to execute. Text, will be indented.
        vars: variable names to pass into the code
        path: the location where the code is stored
        use_async: False if sync code, True if async, None if in thread
    Returns:
        the procedure to call. All keyval arguments will be in the local
        dict.
    """
    hdr = f"""\
def _proc({ ",".join(variables) }):
    """

    if use_async:
        hdr = "async " + hdr
    code = hdr + code.replace("\n", "\n    ")
    code = compile(code, str(path), "exec")

    return partial(_call_proc, code, variables)


class Module(ModuleType):
    def __repr__(self):
        return "<Module %s>" % (self.__class__.__name__,)


def make_module(code, path):
    """Compile this code block to something module-ish.

    Args:
        code: the code block to execute
        path: the location where the code is / shall be stored
    Returns:
        the procedure to call. All keyval arguments will be in the local
        dict.
    """
    name = ".".join(str(x) for x in path)
    code = compile(code, name, "exec")
    m = sys.modules.get(name, None)
    if m is None:
        m = ModuleType(name)
    eval(code, m.__dict__)  # pylint: disable=eval-used
    sys.modules[name] = m
    return m

"""
This module contains various helper functions and classes.
"""
import sys
from functools import partial
from types import ModuleType

__all__ = ["make_proc", "Module", "make_module"]


def _call_proc(code, variables, *a, **kw):
    v = variables[len(a) :]
    if v:
        a = list(a)
        for k in v:
            a.append(kw.pop(k, None))
    eval(code, kw)  # pylint: disable=eval-used
    code = kw["_proc"]
    return code(*a)


def make_proc(code, variables, path, *, use_async=False):  # pylint: disable=redefined-builtin
    """Compile this code block to a procedure.

    Args:
        code: the code block to execute. Text, will be indented.
        vars: variable names to pass into the code
        path: the location where the code is stored
        use_async: False if sync code, True if async, None if in thread
    Returns:
        the procedure to call. All keyval arguments will be in the local
        dict.
    """
    hdr = f"""\
def _proc({ ",".join(variables) }):
    """

    if use_async:
        hdr = "async " + hdr
    code = hdr + code.replace("\n", "\n    ")
    code = compile(code, str(path), "exec")

    return partial(_call_proc, code, variables)


class Module(ModuleType):
    def __repr__(self):
        return "<Module %s>" % (self.__class__.__name__,)


def make_module(code, path):
    """Compile this code block to something module-ish.

    Args:
        code: the code block to execute
        path: the location where the code is / shall be stored
    Returns:
        the procedure to call. All keyval arguments will be in the local
        dict.
    """
    name = ".".join(str(x) for x in path)
    code = compile(code, name, "exec")
    m = sys.modules.get(name, None)
    if m is None:
        m = ModuleType(name)
    eval(code, m.__dict__)  # pylint: disable=eval-used
    sys.modules[name] = m
    return m

This module contains various helper functions and classes. # pylint: disable=eval-used # pylint: disable=redefined-builtin Compile this code block to a procedure.      Args:         code: the code block to execute. Text, will be indented.         vars: variable names to pass into the code         path: the location where the code is stored         use_async: False if sync code, True if async, None if in thread     Returns:         the procedure to call. All keyval arguments will be in the local         dict. \ def _proc({ ",".join(variables) }): Compile this code block to something module-ish.      Args:         code: the code block to execute         path: the location where the code is / shall be stored     Returns:         the procedure to call. All keyval arguments will be in the local         dict. # pylint: disable=eval-used