|  | 
| 9 | 9 | from pathlib import Path | 
| 10 | 10 | from typing import TYPE_CHECKING, Any, Callable, Optional | 
| 11 | 11 | 
 | 
| 12 |  | -import jinja2 | 
| 13 | 12 | import typer | 
| 14 | 13 | import ujson | 
| 15 | 14 | from rich.console import Console | 
|  | 
| 18 | 17 | from rich.panel import Panel | 
| 19 | 18 | from rich.pretty import Pretty | 
| 20 | 19 | from rich.table import Table | 
| 21 |  | -from rich.traceback import Traceback | 
| 22 | 20 | 
 | 
| 23 | 21 | from .. import __version__ as sdk_version | 
| 24 | 22 | from ..async_typer import AsyncTyper | 
|  | 
| 44 | 42 | ) | 
| 45 | 43 | from ..ctl.validate import app as validate_app | 
| 46 | 44 | from ..exceptions import GraphQLError, ModuleImportError | 
| 47 |  | -from ..jinja2 import identify_faulty_jinja_code | 
| 48 | 45 | from ..schema import MainSchemaTypesAll, SchemaRoot | 
|  | 46 | +from ..template import Jinja2Template | 
|  | 47 | +from ..template.exceptions import ( | 
|  | 48 | +    JinjaTemplateError, | 
|  | 49 | +    JinjaTemplateNotFoundError, | 
|  | 50 | +    JinjaTemplateSyntaxError, | 
|  | 51 | +    JinjaTemplateUndefinedError, | 
|  | 52 | +) | 
| 49 | 53 | from ..utils import get_branch, write_to_file | 
| 50 | 54 | from ..yaml import SchemaFile | 
| 51 | 55 | from .exporter import dump | 
| @@ -174,37 +178,50 @@ async def run( | 
| 174 | 178 |     await func(client=client, log=log, branch=branch, **variables_dict) | 
| 175 | 179 | 
 | 
| 176 | 180 | 
 | 
| 177 |  | -def render_jinja2_template(template_path: Path, variables: dict[str, str], data: dict[str, Any]) -> str: | 
|  | 181 | +async def render_jinja2_template(template_path: Path, variables: dict[str, Any], data: dict[str, Any]) -> str: | 
| 178 | 182 |     if not template_path.is_file(): | 
| 179 | 183 |         console.print(f"[red]Unable to locate the template at {template_path}") | 
| 180 | 184 |         raise typer.Exit(1) | 
| 181 | 185 | 
 | 
| 182 |  | -    templateLoader = jinja2.FileSystemLoader(searchpath=".") | 
| 183 |  | -    templateEnv = jinja2.Environment(loader=templateLoader, trim_blocks=True, lstrip_blocks=True) | 
| 184 |  | -    template = templateEnv.get_template(str(template_path)) | 
| 185 |  | - | 
|  | 186 | +    variables["data"] = data | 
|  | 187 | +    jinja_template = Jinja2Template(template_directory=Path()) | 
| 186 | 188 |     try: | 
| 187 |  | -        rendered_tpl = template.render(**variables, data=data)  # type: ignore[arg-type] | 
| 188 |  | -    except jinja2.TemplateSyntaxError as exc: | 
| 189 |  | -        console.print("[red]Syntax Error detected on the template") | 
| 190 |  | -        console.print(f"[yellow]  {exc}") | 
|  | 189 | +        rendered_tpl = await jinja_template.render_from_file(template=template_path, variables=variables) | 
|  | 190 | +    except JinjaTemplateNotFoundError as exc: | 
|  | 191 | +        console.print("[red]An error occurred while rendering the jinja template") | 
|  | 192 | +        console.print("") | 
|  | 193 | +        if exc.base_template: | 
|  | 194 | +            console.print(f"Base template: [yellow]{exc.base_template}") | 
|  | 195 | +        console.print(f"Missing template: [yellow]{exc.filename}") | 
| 191 | 196 |         raise typer.Exit(1) from exc | 
| 192 | 197 | 
 | 
| 193 |  | -    except jinja2.UndefinedError as exc: | 
|  | 198 | +    except JinjaTemplateUndefinedError as exc: | 
|  | 199 | +        console.print("[red]An error occurred while rendering the jinja template") | 
|  | 200 | +        for error in exc.errors: | 
|  | 201 | +            console.print(f"[yellow]{error.frame.filename} on line {error.frame.lineno}\n") | 
|  | 202 | +            console.print(error.syntax) | 
|  | 203 | +        console.print("") | 
|  | 204 | +        console.print(exc.message) | 
|  | 205 | +        raise typer.Exit(1) from exc | 
|  | 206 | +    except JinjaTemplateSyntaxError as exc: | 
|  | 207 | +        console.print("[red]A syntax error was encountered within the template") | 
|  | 208 | +        console.print("") | 
|  | 209 | +        if exc.filename: | 
|  | 210 | +            console.print(f"Filename: [yellow]{exc.filename}") | 
|  | 211 | +        console.print(f"Line number: [yellow]{exc.lineno}") | 
|  | 212 | +        console.print() | 
|  | 213 | +        console.print(exc.message) | 
|  | 214 | +        raise typer.Exit(1) from exc | 
|  | 215 | +    except JinjaTemplateError as exc: | 
| 194 | 216 |         console.print("[red]An error occurred while rendering the jinja template") | 
| 195 |  | -        traceback = Traceback(show_locals=False) | 
| 196 |  | -        errors = identify_faulty_jinja_code(traceback=traceback) | 
| 197 |  | -        for frame, syntax in errors: | 
| 198 |  | -            console.print(f"[yellow]{frame.filename} on line {frame.lineno}\n") | 
| 199 |  | -            console.print(syntax) | 
| 200 | 217 |         console.print("") | 
| 201 |  | -        console.print(traceback.trace.stacks[0].exc_value) | 
|  | 218 | +        console.print(f"[yellow]{exc.message}") | 
| 202 | 219 |         raise typer.Exit(1) from exc | 
| 203 | 220 | 
 | 
| 204 | 221 |     return rendered_tpl | 
| 205 | 222 | 
 | 
| 206 | 223 | 
 | 
| 207 |  | -def _run_transform( | 
|  | 224 | +async def _run_transform( | 
| 208 | 225 |     query_name: str, | 
| 209 | 226 |     variables: dict[str, Any], | 
| 210 | 227 |     transform_func: Callable, | 
| @@ -249,15 +266,15 @@ def _run_transform( | 
| 249 | 266 |         raise typer.Abort() | 
| 250 | 267 | 
 | 
| 251 | 268 |     if asyncio.iscoroutinefunction(transform_func): | 
| 252 |  | -        output = asyncio.run(transform_func(response)) | 
|  | 269 | +        output = await transform_func(response) | 
| 253 | 270 |     else: | 
| 254 | 271 |         output = transform_func(response) | 
| 255 | 272 |     return output | 
| 256 | 273 | 
 | 
| 257 | 274 | 
 | 
| 258 | 275 | @app.command(name="render") | 
| 259 | 276 | @catch_exception(console=console) | 
| 260 |  | -def render( | 
|  | 277 | +async def render( | 
| 261 | 278 |     transform_name: str = typer.Argument(default="", help="Name of the Python transformation", show_default=False), | 
| 262 | 279 |     variables: Optional[list[str]] = typer.Argument( | 
| 263 | 280 |         None, help="Variables to pass along with the query. Format key=value key=value." | 
| @@ -289,7 +306,7 @@ def render( | 
| 289 | 306 |     transform_func = functools.partial(render_jinja2_template, transform_config.template_path, variables_dict) | 
| 290 | 307 | 
 | 
| 291 | 308 |     # Query GQL and run the transform | 
| 292 |  | -    result = _run_transform( | 
|  | 309 | +    result = await _run_transform( | 
| 293 | 310 |         query_name=transform_config.query, | 
| 294 | 311 |         variables=variables_dict, | 
| 295 | 312 |         transform_func=transform_func, | 
|  | 
0 commit comments