From e317c630a60fc5925bd10368c1c412b5fda62577 Mon Sep 17 00:00:00 2001 From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:14:45 +0100 Subject: [PATCH 1/2] Improve logging of warnings from Jinja2 lexing --- changes.d/7365.feat.md | 2 +- cylc/flow/parsec/fileparse.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/changes.d/7365.feat.md b/changes.d/7365.feat.md index ae8ff76376e..10f6ef284ce 100644 --- a/changes.d/7365.feat.md +++ b/changes.d/7365.feat.md @@ -1 +1 @@ -Python warnings raised in users' custom jinja2 filters, globals and tests are now logged. +Python warnings raised during Jinja2 preprocessing are now logged. diff --git a/cylc/flow/parsec/fileparse.py b/cylc/flow/parsec/fileparse.py index 849723136e7..be0f5873f0c 100644 --- a/cylc/flow/parsec/fileparse.py +++ b/cylc/flow/parsec/fileparse.py @@ -513,6 +513,13 @@ def read_and_proc( flines = jinja2process( fpath, flines, fdir, template_vars ) + for w in warns: + if w.filename.endswith(os.path.join('jinja2', 'lexer.py')): + # Warning originating from lexing of a jinja2 template. + # Unfortunately, we can't know exactly where it originated, + # so just set the config filename and vague line "number": + w.filename = fpath + w.lineno = '' # type: ignore if warns: LOG.warning( "The following warnings were raised during Jinja2 " From 1219755ffa0c1ac421dc8431bb9b3703d913ce2e Mon Sep 17 00:00:00 2001 From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> Date: Tue, 4 Aug 2026 17:20:26 +0100 Subject: [PATCH 2/2] Catch additional jinja2 filepath that warnings can come from during parsing of Cylc config --- cylc/flow/parsec/fileparse.py | 6 ++++-- tests/unit/parsec/test_fileparse.py | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/cylc/flow/parsec/fileparse.py b/cylc/flow/parsec/fileparse.py index be0f5873f0c..35c4be616ec 100644 --- a/cylc/flow/parsec/fileparse.py +++ b/cylc/flow/parsec/fileparse.py @@ -125,6 +125,8 @@ TEMPLATING_DETECTED: None } +_J2_WARN_LOCS = re.compile(rf'jinja2{re.escape(os.sep)}(lexer|runtime)\.py$') + def get_cylc_env_vars() -> dict[str, str]: """Return a restricted dict of CYLC_ environment variables for templating. @@ -514,8 +516,8 @@ def read_and_proc( fpath, flines, fdir, template_vars ) for w in warns: - if w.filename.endswith(os.path.join('jinja2', 'lexer.py')): - # Warning originating from lexing of a jinja2 template. + if _J2_WARN_LOCS.search(w.filename): + # Warning originating from processing of a jinja2 template. # Unfortunately, we can't know exactly where it originated, # so just set the config filename and vague line "number": w.filename = fpath diff --git a/tests/unit/parsec/test_fileparse.py b/tests/unit/parsec/test_fileparse.py index d0190246a7c..79caa7439a9 100644 --- a/tests/unit/parsec/test_fileparse.py +++ b/tests/unit/parsec/test_fileparse.py @@ -20,6 +20,7 @@ import re import sqlite3 from tempfile import NamedTemporaryFile +from textwrap import dedent from types import SimpleNamespace import warnings @@ -483,6 +484,28 @@ def mock_jinja2process(fpath, *a, **k): ) +def test_read_and_proc_jinja2_warnings_invalid_escape( + tmp_path: Path, caplog: pytest.LogCaptureFixture +): + """When a warning comes from a j2 template expression, it should be logged + with the cylc config filepath.""" + (fpath := tmp_path / 'flow.cylc').write_text( + dedent(r""" + #!jinja2 + {% from 'warnings' import warn %} + {% do warn('Mock warning') %} + """).lstrip() + ) + read_and_proc( + fpath=str(fpath), + viewcfg={'jinja2': True, 'contin': False, 'inline': False}, + ) + + assert ( + f"{fpath}:: UserWarning: Mock warning" in caplog.text + ) + + def test_parse_keys_only_singleline(): with NamedTemporaryFile() as of, NamedTemporaryFile() as tf: fpath = tf.name