# Path: pykern/pkinspect.py
# _VALID_IDENTIFIER_RE = re.compile(r'^[a-z_]\w*$', re.IGNORECASE)
# class Call(PKDict):
#     def __init__(self, frame_or_log):
#     def __str__(self):
# def caller(ignore_modules=None, exclude_first=True):
# def caller_module(exclude_first=True):
# def is_caller_main():
# def is_valid_identifier(string):
# def module_basename(obj):
# def module_name_join(names):
# def module_name_split(obj):
# def module_functions(func_prefix, module=None):
# def root_package(obj):
# def submodule_name(obj):
# def this_module():
# 
# Path: pykern/pkio.py
# TEXT_ENCODING = 'utf-8'
# def atomic_write(path, contents, **kwargs):
# def exception_is_not_found(exc):
# def expand_user_path(path):
# def has_file_extension(filename, to_check):
# def mkdir_parent(path):
# def mkdir_parent_only(path):
# def open_text(filename, **kwargs):
# def py_path(path=None):
# def random_base62(length=16):
# def read_binary(filename):
# def read_text(filename):
# def save_chdir(dirname, mkdir=False, is_pkunit_prefix=False):
# def sorted_glob(path, key=None):
#     def _path_sort_attr(path):
# def unchecked_remove(*paths):
# def walk_tree(dirname, file_re=None):
#     def _walk(dirname):
# def write_binary(path, contents):
# def write_text(path, contents):
# 
# Path: pykern/pkresource.py
# def file_path(relative_filename, caller_context=None, packages=None):
# def filename(relative_filename, caller_context=None, packages=None):
# def glob_paths(relative_path, caller_context=None, packages=None):
# def _files(path, caller_context, packages):
# def _raise_no_file_found(packages, path):
# 
# Path: pykern/pkdebug.py
# def pkdc(fmt, *args, **kwargs):
#     """Conditional print a message to `output` selectively based on `control`.
# 
#     Controlled output that you can leave in your code permanently.
# 
#     Args:
#         fmt (str): how to :func:`str.format`
#         args: what to format
#         kwargs: what to format
#     """
#     # Since calls are left in for product, this check has
#     # some value.
#     if _have_control:
#         _printer._write(fmt, args, kwargs, with_control=True)
# 
# def pkdp(fmt_or_arg, *args, **kwargs):
#     """Print a message to `output` unconditionally, possibly returning its arg
# 
#     Use for print statements in your code that you don't intend
#     to keep in the system.
# 
#     Use `pkdlog` for permanent log messages.
# 
#     Args:
#         fmt_or_arg (object): how to :func:`str.format`, or object to print
#         args: what to format
#         kwargs: what to format
# 
#     Returns:
#         object: Will return fmt_or_arg, if args and kwargs are empty
#     """
#     if args or kwargs:
#         _printer._write(fmt_or_arg, args, kwargs, with_control=False)
#     else:
#         _printer._write('{}', [fmt_or_arg], {}, with_control=False)
#         return fmt_or_arg


from pykern import pkinspect
from pykern import pkio
from pykern import pkresource
from pykern.pkdebug import pkdc, pkdp
import jinja2
RESOURCE_SUFFIX = '.jinja'


def render_file(filename, j2_ctx, output=None, strict_undefined=False, jinja_env=None):
    """Render filename as template with j2_ctx.

    Args:
        basename (str): name without jinja extension
        j2_ctx (dict): how to replace values in Jinja2 template
        output (str): file name of output; if None, return str
        strict_undefined (bool): set `jinja2.StrictUndefined` if True
        jinja_env (dict): add values to jinja2 environment

    Returns:
        str: rendered template
    """
    t = pkio.read_text(filename)
    kw = dict(
        trim_blocks=True,
        lstrip_blocks=True,
        keep_trailing_newline=True,
        extensions=['jinja2.ext.do'],
    )
    if strict_undefined:
        kw['undefined'] = jinja2.StrictUndefined
    if jinja_env:
        kw.update(jinja_env)
    je = jinja2.Environment(**kw)
    res = je.from_string(t).render(j2_ctx)
    if output:

# Path: pykern/pkinspect.py
# _VALID_IDENTIFIER_RE = re.compile(r'^[a-z_]\w*$', re.IGNORECASE)
# class Call(PKDict):
#     def __init__(self, frame_or_log):
#     def __str__(self):
# def caller(ignore_modules=None, exclude_first=True):
# def caller_module(exclude_first=True):
# def is_caller_main():
# def is_valid_identifier(string):
# def module_basename(obj):
# def module_name_join(names):
# def module_name_split(obj):
# def module_functions(func_prefix, module=None):
# def root_package(obj):
# def submodule_name(obj):
# def this_module():
# 
# Path: pykern/pkio.py
# TEXT_ENCODING = 'utf-8'
# def atomic_write(path, contents, **kwargs):
# def exception_is_not_found(exc):
# def expand_user_path(path):
# def has_file_extension(filename, to_check):
# def mkdir_parent(path):
# def mkdir_parent_only(path):
# def open_text(filename, **kwargs):
# def py_path(path=None):
# def random_base62(length=16):
# def read_binary(filename):
# def read_text(filename):
# def save_chdir(dirname, mkdir=False, is_pkunit_prefix=False):
# def sorted_glob(path, key=None):
#     def _path_sort_attr(path):
# def unchecked_remove(*paths):
# def walk_tree(dirname, file_re=None):
#     def _walk(dirname):
# def write_binary(path, contents):
# def write_text(path, contents):
# 
# Path: pykern/pkresource.py
# def file_path(relative_filename, caller_context=None, packages=None):
# def filename(relative_filename, caller_context=None, packages=None):
# def glob_paths(relative_path, caller_context=None, packages=None):
# def _files(path, caller_context, packages):
# def _raise_no_file_found(packages, path):
# 
# Path: pykern/pkdebug.py
# def pkdc(fmt, *args, **kwargs):
#     """Conditional print a message to `output` selectively based on `control`.
# 
#     Controlled output that you can leave in your code permanently.
# 
#     Args:
#         fmt (str): how to :func:`str.format`
#         args: what to format
#         kwargs: what to format
#     """
#     # Since calls are left in for product, this check has
#     # some value.
#     if _have_control:
#         _printer._write(fmt, args, kwargs, with_control=True)
# 
# def pkdp(fmt_or_arg, *args, **kwargs):
#     """Print a message to `output` unconditionally, possibly returning its arg
# 
#     Use for print statements in your code that you don't intend
#     to keep in the system.
# 
#     Use `pkdlog` for permanent log messages.
# 
#     Args:
#         fmt_or_arg (object): how to :func:`str.format`, or object to print
#         args: what to format
#         kwargs: what to format
# 
#     Returns:
#         object: Will return fmt_or_arg, if args and kwargs are empty
#     """
#     if args or kwargs:
#         _printer._write(fmt_or_arg, args, kwargs, with_control=False)
#     else:
#         _printer._write('{}', [fmt_or_arg], {}, with_control=False)
#         return fmt_or_arg


# Path: pykern/pkjinja.py
from pykern import pkinspect
from pykern import pkio
from pykern import pkresource
from pykern.pkdebug import pkdc, pkdp
import jinja2

RESOURCE_SUFFIX = '.jinja'


def render_file(filename, j2_ctx, output=None, strict_undefined=False, jinja_env=None):
    """Render filename as template with j2_ctx.

    Args:
        basename (str): name without jinja extension
        j2_ctx (dict): how to replace values in Jinja2 template
        output (str): file name of output; if None, return str
        strict_undefined (bool): set `jinja2.StrictUndefined` if True
        jinja_env (dict): add values to jinja2 environment

    Returns:
        str: rendered template
    """
    t = pkio.read_text(filename)
    kw = dict(
        trim_blocks=True,
        lstrip_blocks=True,
        keep_trailing_newline=True,
        extensions=['jinja2.ext.do'],
    )
    if strict_undefined:
        kw['undefined'] = jinja2.StrictUndefined
    if jinja_env:
        kw.update(jinja_env)
    je = jinja2.Environment(**kw)
    res = je.from_string(t).render(j2_ctx)
    if output:

        pkio.write_text(output, res)