Repository: apache/superset
File: superset/jinja_context.py (Python)

Code before:
                "Jinja2 template server error",
                extra={
                    "error_message": error_msg,
                    "template_snippet": sql[:200] if sql else None,
                    "template_length": len(sql) if sql else 0,
                    "error_type": "SERVER_TEMPLATE_ERROR",
                    "exception_type": exception_type,
                },
                exc_info=True,
            )

            raise SupersetTemplateException(message) from ex

        kwargs.update(self._context)
        context = validate_template_context(self.engine, kwargs)

        try:
            return template.render(context)
        except RecursionError as ex:
            raise SupersetTemplateException(
                "Infinite recursion detected in template"
            ) from ex
        except UndefinedError:
            return sql
        except Exception as ex:
            if "undefined" in str(ex).lower():
                return sql
            raise


class JinjaTemplateProcessor(BaseTemplateProcessor):
    def _parse_datetime(self, dttm: str) -> datetime | None:
        """
        Try to parse a datetime and default to None in the worst case.

        Since this may have been rendered by different engines, the datetime may
        vary slightly in format. We try to make it consistent, and if all else
        fails, just return None.
        """
        try:
            return dateutil.parser.parse(dttm)
        except dateutil.parser.ParserError:
            return None

    def set_context(self, **kwargs: Any) -> None:
        super().set_context(**kwargs)
        extra_cache = ExtraCache(
            extra_cache_keys=self._extra_cache_keys,
            applied_filters=self._applied_filters,
            removed_filters=self._removed_filters,
            database=self._database,

Review comment (suggestion):
Is catching the case of `undefined` here the same as catching the case of a void return below?
```
                if re.search(function_pattern, original_sql, re.IGNORECASE):
                    return
```
I think that, rather than catching the error using something like `str(err)`, it would be better to add a custom Exception, raise it, and catch it accordingly.

Code after:
            error_msg = str(ex)
            exception_type = type(ex).__name__

            message = f"Internal Jinja2 template error ({exception_type}): {error_msg}"

            logger.error(
                "Jinja2 template server error",
                extra={
                    "error_message": error_msg,
                    "template_snippet": sql[:200] if sql else None,
                    "template_length": len(sql) if sql else 0,
                    "error_type": "SERVER_TEMPLATE_ERROR",
                    "exception_type": exception_type,
                },
                exc_info=True,
            )

            raise SupersetTemplateException(message) from ex

        kwargs.update(self._context)
        context = validate_template_context(self.engine, kwargs)

        try:
            return template.render(context)
        except RecursionError as ex:
            raise SupersetTemplateException(
                "Infinite recursion detected in template"
            ) from ex
        except UndefinedError as ex:
            match = re.search(r'["\']([^"\']+)["\']\s+is undefined', str(ex))
            undefined_name = match.group(1) if match else None
            if undefined_name and re.search(
                r"\{\{\s*(?:[\w\.]*\.)?" + re.escape(undefined_name) + r"\s*\(", sql
            ):
                raise UndefinedTemplateFunctionException(str(ex)) from ex
            raise


class JinjaTemplateProcessor(BaseTemplateProcessor):
    def _parse_datetime(self, dttm: str) -> datetime | None:
        """
        Try to parse a datetime and default to None in the worst case.

        Since this may have been rendered by different engines, the datetime may
        vary slightly in format. We try to make it consistent, and if all else
        fails, just return None.
        """
        try:
            return dateutil.parser.parse(dttm)
        except dateutil.parser.ParserError:
            return None