Skip to content

Commit 1f8696c

Browse files
authored
feat(query): Add bqetl query render support for Jinja imports/includes from outside the ETL directory (DENG-9950) (#8485)
* feat(query): Add `bqetl query render` support for Jinja imports/includes from outside the ETL directory (DENG-9950). * feat(util): Add a `get_bqetl_project_root()` function to make that logic reusable and cache the return value.
1 parent 72b9577 commit 1f8696c

File tree

1 file changed

+24
-4
lines changed

1 file changed

+24
-4
lines changed

bigquery_etl/util/common.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import string
99
import tempfile
1010
import warnings
11+
from functools import cache
1112
from pathlib import Path
1213
from typing import List, Optional, Set, Tuple
1314
from uuid import uuid4
@@ -17,7 +18,7 @@
1718
from google.cloud import bigquery
1819
from jinja2 import Environment, FileSystemLoader
1920

20-
from bigquery_etl.config import ConfigLoader
21+
from bigquery_etl.config import BQETL_PROJECT_CONFIG, ConfigLoader
2122
from bigquery_etl.format_sql.formatter import reformat
2223
from bigquery_etl.metrics import MetricHub
2324

@@ -70,6 +71,18 @@ def random_str(length: int = 12) -> str:
7071
return "".join(random.choice(string.ascii_lowercase) for i in range(length))
7172

7273

74+
@cache
75+
def get_bqetl_project_root() -> Path | None:
76+
"""Return the root path of the bqetl project the user is currently in."""
77+
cwd = Path.cwd()
78+
search_paths = [cwd]
79+
search_paths.extend(cwd.parents)
80+
for possible_project_root in search_paths:
81+
if (possible_project_root / BQETL_PROJECT_CONFIG).exists():
82+
return possible_project_root
83+
return None
84+
85+
7386
def render(
7487
sql_filename,
7588
template_folder=".",
@@ -78,7 +91,8 @@ def render(
7891
**kwargs,
7992
) -> str:
8093
"""Render a given template query using Jinja."""
81-
path = Path(template_folder) / sql_filename
94+
template_folder_path = Path(template_folder)
95+
path = template_folder_path / sql_filename
8296
skip = {
8397
file
8498
for skip in ConfigLoader.get("render", "skip", fallback=[])
@@ -122,14 +136,20 @@ def render(
122136
checks_template.write_text(
123137
macro_imports
124138
+ "\n"
125-
+ (Path(template_folder) / sql_filename).read_text()
139+
+ (template_folder_path / sql_filename).read_text()
126140
)
127141

128142
file_loader = FileSystemLoader(f"{str(checks_template.parent)}")
129143
env = Environment(loader=file_loader)
130144
main_sql = env.get_template(checks_template.name)
131145
else:
132-
file_loader = FileSystemLoader(f"{template_folder}")
146+
# Add the bigquery-etl project root to the search path to support Jinja imports/includes.
147+
file_loader_search_paths = [template_folder_path, ROOT]
148+
# Also dynamically detect the project root so imports/includes in the private-bigquery-etl repo work.
149+
if bqetl_project_root := get_bqetl_project_root():
150+
if bqetl_project_root not in file_loader_search_paths:
151+
file_loader_search_paths.append(bqetl_project_root)
152+
file_loader = FileSystemLoader(file_loader_search_paths)
133153
env = Environment(loader=file_loader)
134154
main_sql = env.get_template(sql_filename)
135155

0 commit comments

Comments
 (0)