本文由「大千AI助手」原创发布,专注用真话讲AI,回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我,一起撕掉过度包装,学习真实的AI技术!
问题描述
HiveOperator中以类似../xxx.hql或/yyy/xxx.hql的方式设置参数hql时, 任务执行时报错找不到对应的模板- 比如
TemplateNotFound异常
- 比如
本文由「大千AI助手」原创发布,专注用真话讲AI,回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我,一起撕掉过度包装,学习真实的AI技术!
往期文章推荐:
- 20.DeepSeek V3.2 vs V3.2-Speciale:到底差在哪?该怎么选?
- 19.BashOperator 中 bash_command 以 .sh 结尾会被误判为模板文件的问题分析
- 18.别让你的数据写串了!Python 多进程文件锁 FileLock 避坑指南
- 17.DeepSeek V3.2 到底有多强?一文看懂各大测试基准与真实水平
- 16.DeepSeek V3.2 技术解读:一次不靠"堆参数"的模型升级
- 15.Kaldi:开源语音识别工具链的核心架构与技术演进
- 14.CodeXGLUE:代码智能的基准测试与评估框架
- 13.程序合约:形式化验证中的规范与实现框架
- 12.SantaCoder:专注于代码生成的轻量级高效大语言模型
- 11.基于OpenAPI生成的 SDK 的工业级和消费级概念区别
- 10.超越表面正确性:HUMANEVAL+如何重塑代码生成大模型的评估基准
- 9.一文看懂openapi-python-client生成的SDK和openai-python库的风格差异
- 8.告别 Java 风格代码:使用 openapi-python-client 生成原生 Pythonic 的企业级 SDK
- 7.DeepSeek-Coder:开源代码大模型的架构演进与技术突破
- 6.MBPP:评估大语言模型代码生成能力的基准数据集
- 5.RepoCoder:基于迭代检索与生成的仓库级代码补全框架
- 4.Py150数据集:Python代码建模与分析的基准资源
- 3.GPT-Neo:开源大型自回归语言模型的实现与影响
- 2.编辑相似度(Edit Similarity):原理、演进与多模态扩展
- 1.CodeSearchNet:一个大规模代码-文档检索数据集的构建、应用与挑战
问题分析
- 1.
HiveOperator的参数hql用来配置运行的 hive sql 或者 包含hive sql的jinja2模板文件 - 2.并且参数
hql被设置为可以被jinja2引擎渲染的 - 3.并且参数
hql被设置为如果以.hql或.sql结尾,那么hql被整体当作 模板文件名 来通过jinja2渲染,其余情况都被当作 hive sql字符串 直接渲染
python
# 源码 HiveOperator 中的部分参数设置
template_fields = ('hql', 'schema', 'hive_cli_conn_id', 'mapred_queue',
'hiveconfs', 'mapred_job_name', 'mapred_queue_priority')
template_ext = ('.hql', '.sql',)
# 源码 airflow/models/baseoperator.py
class BaseOperator(LoggingMixin):
...
def render_template(self, content, context, jinja_env=None, seen_oids=None):
if not jinja_env:
jinja_env = self.get_template_env()
if isinstance(content, six.string_types):
if any(content.endswith(ext) for ext in self.template_ext):
# Content contains a filepath
return jinja_env.get_template(content).render(**context)
else:
return jinja_env.from_string(content).render(**context)
...
- 4.而Jinja2 在模板加载阶段会对路径进行安全校验,明确禁止目录回溯和越权访问
jinja2在通过FileSystemLoader加载本地模板文件时会对路径进行特殊检查和处理- 如果模板文件路径中包含
..则直接抛出异常TemplateNotFound表示找不到模板文件- 通过条件
piece == os.path.pardir来判断的 (os.path.pardir代表父目录,取值为..)
- 通过条件
- 并且模板都是在
searchpath路径下查找的,如果用了绝对路径,那肯定也是找不到的(除非searchpath = '/')
- 如果模板文件路径中包含
python
# 源码路径: jinja2/loaders.py
def split_template_path(template: str) -> t.List[str]:
"""Split a path into segments and perform a sanity check. If it detects
'..' in the path it will raise a `TemplateNotFound` error.
"""
pieces = []
for piece in template.split("/"):
if (
os.path.sep in piece
or (os.path.altsep and os.path.altsep in piece)
or piece == os.path.pardir
):
raise TemplateNotFound(template)
elif piece and piece != ".":
pieces.append(piece)
return pieces
class FileSystemLoader(BaseLoader):
...
def get_source(
self, environment: "Environment", template: str
) -> t.Tuple[str, str, t.Callable[[], bool]]:
pieces = split_template_path(template)
for searchpath in self.searchpath:
# Use posixpath even on Windows to avoid "drive:" or UNC
# segments breaking out of the search directory.
filename = posixpath.join(searchpath, *pieces)
if os.path.isfile(filename):
break
else:
plural = "path" if len(self.searchpath) == 1 else "paths"
paths_str = ", ".join(repr(p) for p in self.searchpath)
raise TemplateNotFound(
template,
f"{template!r} not found in search {plural}: {paths_str}",
)
...
- 5.而使用的
searchpath包括 任务所在的dag的定义的文件的路径 和 创建dag时传的参数 template_searchpathself.folder就是定义该任务的dag的文件所在的父目录- 一般在任务执行日志中能看到
'-sd', 'DAGS_FOLDER/xxx.py'
- 一般在任务执行日志中能看到
python
# 源码 airflow/models/dag.py
class DAG(BaseDag, LoggingMixin):
...
@property
def folder(self):
"""Folder location of where the DAG object is instantiated."""
return os.path.dirname(self.full_filepath)
...
def get_template_env(self): # type: () -> jinja2.Environment
"""Build a Jinja2 environment."""
# Collect directories to search for template files
searchpath = [self.folder]
if self.template_searchpath:
searchpath += self.template_searchpath
# Default values (for backward compatibility)
jinja_env_options = {
'loader': jinja2.FileSystemLoader(searchpath),
'undefined': self.template_undefined or jinja2.Undefined,
'extensions': ["jinja2.ext.do"],
'cache_size': 0
}
if self.jinja_environment_kwargs:
jinja_env_options.update(self.jinja_environment_kwargs)
env = jinja2.Environment(**jinja_env_options) # type: ignore
# Add any user defined items. Safe to edit globals as long as no templates are rendered yet.
# http://jinja.pocoo.org/docs/2.10/api/#jinja2.Environment.globals
if self.user_defined_macros:
env.globals.update(self.user_defined_macros)
if self.user_defined_filters:
env.filters.update(self.user_defined_filters)
return env
...
解决方案
- 方案1.使用 相对路径 和 与对应的
DAG定义文件同级或子目录存放模板文件 - 方案2.通过参数
template_searchpath来设置当前DAG特有的模板搜索目录- 创建
DAG时的初始化参数
- 创建
- 方案3.通过
HiveOperator执行的hql模板文件 禁止用绝对路径 和 带..的路径
本文由「大千AI助手」原创发布,专注用真话讲AI,回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我,一起撕掉过度包装,学习真实的AI技术!