Source code for codegen.env

"""Jinja2 environment creation and template rendering."""

from typing import TYPE_CHECKING

import jinja2

if TYPE_CHECKING:
    from collections.abc import Mapping
    from pathlib import Path

    from jinja2.runtime import Context

    from codegen.imports import ImportCollector
    from codegen.naming import Name


[docs] def create_jinja_env( *template_dirs: Path, extra_globals: Mapping[str, object] | None = None, ) -> jinja2.Environment: env = jinja2.Environment( loader=jinja2.FileSystemLoader( [str(template_dir) for template_dir in template_dirs], ), extensions=["jinja2.ext.do"], trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True, undefined=jinja2.StrictUndefined, autoescape=False, # noqa: S701 — generating source, not HTML ) env.globals["import_from"] = _import_from # type: ignore[assignment] env.globals["import_name"] = _import_name # type: ignore[assignment] if extra_globals: env.globals.update(extra_globals) # type: ignore[arg-type] return env
def _collector(ctx: Context) -> ImportCollector: collector = ctx.get("_imports") if collector is None: msg = ( "import_from/import_module called with no active import " "collector; add `with context` to the `{% import %}` / " "`{% from ... import %}` of the macro that declares imports" ) raise RuntimeError(msg) return collector @jinja2.pass_context def _import_from(ctx: Context, module: str, *names: str) -> str: _collector(ctx).add_from(module, *names) return "" @jinja2.pass_context def _import_name(ctx: Context, *names: Name) -> str: _collector(ctx).add_name(*names) return ""
[docs] def render_template( env: jinja2.Environment, template_name: str, **context: object, ) -> str: try: return env.get_template(template_name).render(**context) except Exception as exception: raise Exception( str(exception), { key: value for key, value in context.items() if key not in {"ctx", "_imports", "import_block"} }, ) from exception