Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions cylc/flow/parsec/jinja2support.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"""

from contextlib import suppress
from functools import lru_cache, wraps
from glob import glob
import importlib
import os
Expand Down Expand Up @@ -51,6 +52,65 @@
CONTEXT_LINES = 3


def restore_deprecated_interfaces():
"""Extend support for some deprecated interfaces in Jinja2 3.1.x.

Jinja2 renamed a bunch of interfaces. Warnings were added in version 3.0.0,
support for the old names was removed in 3.1.0, however, our users did not
notice these warnings (you have to manually turn them on) so did not take
action.

https://jinja.palletsprojects.com/en/stable/changes/#version-3-1-0

In https://github.com/cylc/cylc-flow/pull/7365 (8.6.6) we've captured
Jinja2 warnings and turned them into Cylc warnings to ensure they cannot be
missed.

To give users a chance to take action, this patch restores the renamed
interfaces just for ONE minor Cylc version.

BACK COMPAT: restore_deprecated_interfaces
FROM: 8.6.x
TO: 8.7.0
REMOVE AT: 8.8.0
"""
renamed_interfaces = {
'contextfilter': 'pass_context',
'contextfunction': 'pass_context',
'evalcontextfilter': 'pass_eval_context',
'evalcontextfunction': 'pass_eval_context',
'environmentfilter': 'pass_environment',
'environmentfunction': 'pass_environment',
}
url = 'https://jinja.palletsprojects.com/en/stable/changes/#version-3-1-0'

@lru_cache # suppress duplicate warnings
def _log_warning(old, new):
LOG.warning(
f'The Jinja2 function {old} was renamed to {new}.'
f'\nCylc has extended support for {old} until 8.8.0.'
f' Please search your workflow for {old} and upgrade any uses.'
f'\nSee {url}'
)

def _warn(fcn, old, new):
@wraps(fcn)
def _inner(*args, **kwargs):
_log_warning(old, new)
return fcn(*args, **kwargs)
return _inner

import jinja2
import jinja2.filters
for old, new in renamed_interfaces.items():
setattr(jinja2, old, _warn(getattr(jinja2, new), old, new))
setattr(jinja2.filters, old, _warn(getattr(jinja2, new), old, new))


# BACK COMPAT
restore_deprecated_interfaces()


class PyModuleLoader(BaseLoader):
"""Load python module as Jinja2 template.

Expand Down
38 changes: 38 additions & 0 deletions tests/unit/parsec/test_jinja2support.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
jinja2process,
raise_helper,
)
from cylc.flow.util import sstrip


def test_raise_helper():
Expand Down Expand Up @@ -137,3 +138,40 @@ def test_pymoduleloader_invalid_module(tmp_path):
module_loader = PyModuleLoader()
with pytest.raises(jinja2.TemplateNotFound):
module_loader.load(environment=env, name='no way jose')


def test_restore_deprecated_interfaces(tmp_path, log_filter):
"""It should log warnings when back-supported Jinja2 interfaces are used.

See https://github.com/cylc/cylc-flow/pull/7325
"""
# flow.cylc
flow_cylc = tmp_path / 'flow.cylc'
flow_cylc.write_text(sstrip('''
#!Jinja2
# {{ 1 | foo }}
'''))

# Jinja2Filters/foo.py
(tmp_path / 'Jinja2Filters').mkdir()
((tmp_path / 'Jinja2Filters') / 'foo.py').write_text(sstrip('''
from jinja2 import contextfilter

@contextfilter # NOTE: contextfilter is deprecated!
def foo(x, y):
return y * 2
''' + '\n'))

# process Jinja2 template
assert jinja2process(
flow_cylc,
flow_cylc.read_text().splitlines(),
str(tmp_path),
) == ['# 2']

# warning should be raised
assert log_filter(
contains=(
'The Jinja2 function contextfilter was renamed to pass_context'
)
)
Loading