Skip to content

Commit 13e6632

Browse files
authored
Merge pull request #1 from lincolnloop/upgrade-jinja-django-5
Upgrade Jinja support to work with Django 5
2 parents 351ca10 + 2592c71 commit 13e6632

File tree

17 files changed

+478
-366
lines changed

17 files changed

+478
-366
lines changed

.github/workflows/ci.yml

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,38 @@ jobs:
88
test:
99
runs-on: ubuntu-latest
1010
steps:
11-
- uses: actions/checkout@v3
11+
- uses: actions/checkout@v4
1212
- run: pipx install "poetry>=1.1.12,<2"
13-
- uses: actions/setup-python@v4
13+
- uses: actions/setup-python@v5
1414
with:
15-
python-version-file: pyproject.toml
15+
python-version: "3.12"
1616
cache: 'poetry'
1717
- run: pip install tox
18-
- run: tox -e lint,py311-dj42
18+
- run: tox -e lint,py312-dj50
1919
test_compatibility:
2020
needs: test
2121
runs-on: ubuntu-latest
2222
strategy:
23+
fail-fast: false
2324
matrix:
2425
include:
2526
# Test with all supported Django versions, for all compatible Python versions.
26-
# See https://docs.djangoproject.com/en/4.0/faq/install/#what-python-version-can-i-use-with-django for the official matrix.
27+
# See https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django for the official matrix.
2728
# Additionally test on Django’s main branch with the most recent Python version.
2829
- python: "3.8"
29-
toxenv: py38-dj32,py38-dj41,py38-dj42
30+
toxenv: py38-dj32,py38-dj42
3031
- python: "3.9"
31-
toxenv: py39-dj32,py39-dj41,py39-dj42
32+
toxenv: py39-dj32,py39-dj42
3233
- python: "3.10"
33-
# Skip testing Django 4.0, already tested in previous workflow job.
34-
toxenv: py310-dj32,py310-dj41,py310-dj42,py310-djmain
34+
toxenv: py310-dj32,py310-dj42,py310-dj50,py310-djmain
3535
- python: "3.11"
36-
toxenv: py311-dj41,py311-dj42,py311-djmain
37-
# Tentative support for next Python pre-release.
38-
# When django supports the version, uncomment this
39-
# - python: "3.12"
40-
# toxenv: py312-dj41
36+
toxenv: py311-dj42,py311-dj50,py311-djmain
37+
- python: "3.12"
38+
toxenv: py312-dj42,py312-djmain
4139
steps:
42-
- uses: actions/checkout@v3
40+
- uses: actions/checkout@v4
4341
- run: pipx install "poetry>=1.1.12,<2"
44-
- uses: actions/setup-python@v4
42+
- uses: actions/setup-python@v5
4543
with:
4644
python-version: ${{ matrix.python }}
4745
allow-prereleases: true
@@ -53,12 +51,12 @@ jobs:
5351
needs: test
5452
runs-on: ubuntu-latest
5553
steps:
56-
- uses: actions/checkout@v3
57-
- uses: actions/setup-node@v3
54+
- uses: actions/checkout@v4
55+
- uses: actions/setup-node@v4
5856
with:
5957
node-version-file: '.nvmrc'
6058
- id: node-cache
61-
uses: actions/cache@v2
59+
uses: actions/cache@v3
6260
with:
6361
# Cache node_modules rather than the npm cache, as we rarely update npm packages.
6462
path: node_modules
@@ -67,7 +65,7 @@ jobs:
6765
run: npm ci --no-audit
6866
- run: npm run build
6967
- run: pipx install "poetry>=1.1.12,<2"
70-
- uses: actions/setup-python@v4
68+
- uses: actions/setup-python@v5
7169
with:
7270
python-version-file: pyproject.toml
7371
cache: 'poetry'
@@ -86,8 +84,8 @@ jobs:
8684
- run: cat pyproject.toml | awk '{sub(/^version = .+/,"version = \"0.0.0.dev\"")}1' > pyproject.toml.tmp && mv pyproject.toml.tmp pyproject.toml
8785
- run: poetry build
8886
- run: mv dist site
89-
- uses: actions/configure-pages@v3
90-
- uses: actions/upload-pages-artifact@v2
87+
- uses: actions/configure-pages@v4
88+
- uses: actions/upload-pages-artifact@v3
9189
with:
9290
path: site
9391
deploy_site:
@@ -101,4 +99,4 @@ jobs:
10199
url: ${{ steps.deployment.outputs.page_url }}
102100
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
103101
steps:
104-
- uses: actions/deploy-pages@v2
102+
- uses: actions/deploy-pages@v4

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## [1.2.0](https://github.com/torchbox/django-pattern-library/releases/tag/v1.2.0) - 2024-01-16
4+
5+
### Added
6+
7+
- Add support for Django 5.0 ([#241](https://github.com/torchbox/django-pattern-library/pull/241))
8+
9+
### Changed
10+
11+
- From Django >= 4.0, calls to `Node.render()` must always return a string, but this app previously allowed non-string values to be passed in the `default_html` parameter to `override_tag`. Passing a non-string now raises a `TypeError` when using Django >= 4.0, and raises a warning for older versions ([issue #211](https://github.com/torchbox/django-pattern-library/issues/211)).
12+
313
## [1.1.0](https://github.com/torchbox/django-pattern-library/releases/tag/v1.1.0) - 2023-10-25
414

515
### Added

docs/guides/automated-tests.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Although pattern libraries often start as tools for manual tests during development, they can also be useful for automated UI testing. There are a few benefits to doing UI tests with a pattern library:
44

55
- Test the components in isolation. When tests fail, you will know exactly which component has issues, rather than having to inspect whole pages to understand what might have changed.
6-
- Test the components with mock data. One of the issues with UI tests is to have test data for your UIs to render – you can reuse the pattern library data for this purpose (althoug there are [limitations](../guides/multiple-variants.md)).
6+
- Test the components with mock data. One of the issues with UI tests is to have test data for your UIs to render – you can reuse the pattern library data for this purpose (although there are [limitations](../guides/multiple-variants.md)).
77

88
## Setting up automated UI tests
99

docs/reference/api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ YAML isn’t everyone’s favorite markup language, but it has the advantage of
66

77
Here is what you need to know:
88

9-
- Use `.yaml` or `.yml` as the file extension for pattern configuration files. If both are present, the `.yaml` file takes precendence.
9+
- Use `.yaml` or `.yml` as the file extension for pattern configuration files. If both are present, the `.yaml` file takes precedence.
1010
- Use Mappings in place of Python Dictionaries.
1111
- Use Sequences in place of Python lists (or iterables like QuerySets).
1212
- The pattern library uses [PyYAML](https://pyyaml.org/wiki/PyYAMLDocumentation) in particular

pattern_library/loader_tags.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,10 @@ def do_include(parser, token):
151151
isolated_context=isolated_context,
152152
)
153153

154+
154155
def visit_extends(self, node, frame):
155156
"""This method overrides the jinja extends tag
156-
Is called as part of the compiler CodeGenerator
157+
Is called as part of the compiler CodeGenerator
157158
and adds a line to use the template_new_context as
158159
part of the runtime render to pull in the dpl context
159160
Handles visiting extends
@@ -196,4 +197,4 @@ def template_new_context(
196197

197198
return new_context(
198199
self.environment, self.name, self.blocks, vars, shared, self.globals, locals
199-
)
200+
)

pattern_library/management/commands/render_patterns.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@
55
from django.test.client import RequestFactory
66

77
from pattern_library import get_base_template_names, get_pattern_base_template_name
8-
from pattern_library.utils import (
9-
get_pattern_context,
10-
render_pattern,
11-
get_renderer,
12-
)
8+
from pattern_library.utils import get_pattern_context, get_renderer, render_pattern
139

1410

1511
class Command(BaseCommand):

pattern_library/monkey_utils.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import inspect
12
import logging
2-
from typing import Optional
3+
import typing
4+
import warnings
35

46
import django
57
from django.template.library import SimpleNode
@@ -11,7 +13,9 @@
1113

1214

1315
def override_tag(
14-
register: django.template.Library, name: str, default_html: Optional[str] = None
16+
register: django.template.Library,
17+
name: str,
18+
default_html: typing.Optional[typing.Any] = UNSPECIFIED,
1519
):
1620
"""
1721
An utility that helps you override original tags for use in your pattern library.
@@ -29,7 +33,7 @@ def node_render(context):
2933
tag_overridden = False
3034
result = ""
3135

32-
# Get overriden tag config.
36+
# Get overridden tag config.
3337
tag_overrides = context.get("__pattern_library_tag_overrides", {})
3438

3539
# Extract values for lookup from the token
@@ -79,6 +83,23 @@ def node_render(context):
7983
# See https://github.com/torchbox/django-pattern-library/issues/166.
8084
return str(result)
8185
elif default_html is not UNSPECIFIED:
86+
# Ensure default_html is a string.
87+
if not isinstance(default_html, str):
88+
# Save the caller for the override tag in case it's needed for error reporting.
89+
trace = inspect.stack()[1]
90+
if django.VERSION < (4, 0):
91+
warnings.warn(
92+
"default_html argument to override_tag should be a string to ensure compatibility "
93+
'with Django >= 4.0 (line %s in "%s")'
94+
% (trace.lineno, trace.filename),
95+
Warning,
96+
)
97+
else:
98+
raise TypeError(
99+
'default_html argument to override_tag must be a string (line %s in "%s")'
100+
% (trace.lineno, trace.filename)
101+
)
102+
82103
# Render provided default;
83104
# if no stub data supplied.
84105
return default_html
@@ -97,23 +118,26 @@ def node_render(context):
97118

98119
return tag_func
99120

121+
100122
# have to export the original jinja visit Extends
101123
# in the case jinja tags are being overriden
102124
jinja_visit_Extends = None
103125

126+
104127
def override_jinja_tags():
105128
"""
106129
Overrides jinja extends and include tags for use in your pattern library.
107-
Call it in your settings to override tags
130+
Call it in your settings to override tags
108131
"""
109132
global jinja_visit_Extends
110133
try:
111134
from jinja2.compiler import CodeGenerator as JinjaCodeGenerator
112135
from jinja2.environment import Template as JinjaTemplate
113136
except ModuleNotFoundError:
114137
ModuleNotFoundError("install jinja2 to override jinja tags")
115-
138+
116139
from .loader_tags import template_new_context, visit_extends
140+
117141
jinja_visit_Extends = JinjaCodeGenerator.visit_Extends
118142
JinjaTemplate.new_context = template_new_context
119-
JinjaCodeGenerator.visit_Extends = visit_extends
143+
JinjaCodeGenerator.visit_Extends = visit_extends

pattern_library/utils.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from django.template.loader import get_template, render_to_string
99
from django.template.loader_tags import ExtendsNode
1010
from django.template.loaders.app_directories import get_app_template_dirs
11+
from django.utils.html import escape
1112
from django.utils.safestring import mark_safe
1213

1314
import markdown
@@ -22,9 +23,6 @@
2223
from pattern_library.exceptions import TemplateIsNotPattern
2324

2425

25-
26-
from django.utils.html import escape
27-
2826
def path_to_section():
2927
section_config = get_sections()
3028
sections = {}
@@ -243,14 +241,17 @@ def get_pattern_source(cls, template):
243241
@classmethod
244242
def get_template_ancestors(cls, template_name, context=None):
245243
template = get_template(template_name)
246-
return cls._get_engine(template).get_template_ancestors(template_name, context=context)
244+
return cls._get_engine(template).get_template_ancestors(
245+
template_name, context=context
246+
)
247247

248248
@classmethod
249249
def _get_engine(cls, template):
250250
if "jinja" in str(type(template)).lower():
251251
return JinjaTemplateRenderer
252252
return DTLTemplateRenderer
253253

254+
254255
class DTLTemplateRenderer:
255256
@staticmethod
256257
def get_pattern_source(template):
@@ -287,7 +288,7 @@ class JinjaTemplateRenderer:
287288
@staticmethod
288289
def get_pattern_source(template):
289290
with open(template.template.filename) as f:
290-
source = escape(f.read())
291+
source = escape(f.read())
291292
return source
292293

293294
@classmethod
@@ -306,12 +307,14 @@ def get_template_ancestors(cls, template_name, context=None, ancestors=None):
306307
context = Context()
307308

308309
pattern_template = get_template(template_name)
309-
#todo - make sure envrionment has context passed in
310+
# todo - make sure envrionment has context passed in
310311
environment = pattern_template.template.environment
311312
nodelist = environment.parse(pattern_template.template.name)
312313
parent_template_name = nodelist.find(Extends)
313314
if parent_template_name:
314315
ancestors.append(parent_template_name)
315-
cls.get_template_ancestors(parent_template_name, context=context, ancestors=ancestors)
316+
cls.get_template_ancestors(
317+
parent_template_name, context=context, ancestors=ancestors
318+
)
316319

317320
return ancestors

pattern_library/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
get_pattern_config_str,
1717
get_pattern_context,
1818
get_pattern_markdown,
19+
get_renderer,
1920
get_sections,
2021
is_pattern,
2122
render_pattern,
22-
get_renderer,
2323
)
2424

2525

0 commit comments

Comments
 (0)