Skip to content

Commit

Permalink
Merge pull request #1 from lincolnloop/upgrade-jinja-django-5
Browse files Browse the repository at this point in the history
Upgrade Jinja support to work with Django 5
  • Loading branch information
mtrythall authored Feb 26, 2024
2 parents 351ca10 + 2592c71 commit 13e6632
Show file tree
Hide file tree
Showing 17 changed files with 478 additions and 366 deletions.
44 changes: 21 additions & 23 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,38 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- run: pipx install "poetry>=1.1.12,<2"
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version-file: pyproject.toml
python-version: "3.12"
cache: 'poetry'
- run: pip install tox
- run: tox -e lint,py311-dj42
- run: tox -e lint,py312-dj50
test_compatibility:
needs: test
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
# Test with all supported Django versions, for all compatible Python versions.
# See https://docs.djangoproject.com/en/4.0/faq/install/#what-python-version-can-i-use-with-django for the official matrix.
# See https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django for the official matrix.
# Additionally test on Django’s main branch with the most recent Python version.
- python: "3.8"
toxenv: py38-dj32,py38-dj41,py38-dj42
toxenv: py38-dj32,py38-dj42
- python: "3.9"
toxenv: py39-dj32,py39-dj41,py39-dj42
toxenv: py39-dj32,py39-dj42
- python: "3.10"
# Skip testing Django 4.0, already tested in previous workflow job.
toxenv: py310-dj32,py310-dj41,py310-dj42,py310-djmain
toxenv: py310-dj32,py310-dj42,py310-dj50,py310-djmain
- python: "3.11"
toxenv: py311-dj41,py311-dj42,py311-djmain
# Tentative support for next Python pre-release.
# When django supports the version, uncomment this
# - python: "3.12"
# toxenv: py312-dj41
toxenv: py311-dj42,py311-dj50,py311-djmain
- python: "3.12"
toxenv: py312-dj42,py312-djmain
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- run: pipx install "poetry>=1.1.12,<2"
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
allow-prereleases: true
Expand All @@ -53,12 +51,12 @@ jobs:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- id: node-cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
# Cache node_modules rather than the npm cache, as we rarely update npm packages.
path: node_modules
Expand All @@ -67,7 +65,7 @@ jobs:
run: npm ci --no-audit
- run: npm run build
- run: pipx install "poetry>=1.1.12,<2"
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version-file: pyproject.toml
cache: 'poetry'
Expand All @@ -86,8 +84,8 @@ jobs:
- run: cat pyproject.toml | awk '{sub(/^version = .+/,"version = \"0.0.0.dev\"")}1' > pyproject.toml.tmp && mv pyproject.toml.tmp pyproject.toml
- run: poetry build
- run: mv dist site
- uses: actions/configure-pages@v3
- uses: actions/upload-pages-artifact@v2
- uses: actions/configure-pages@v4
- uses: actions/upload-pages-artifact@v3
with:
path: site
deploy_site:
Expand All @@ -101,4 +99,4 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/deploy-pages@v2
- uses: actions/deploy-pages@v4
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [1.2.0](https://github.com/torchbox/django-pattern-library/releases/tag/v1.2.0) - 2024-01-16

### Added

- Add support for Django 5.0 ([#241](https://github.com/torchbox/django-pattern-library/pull/241))

### Changed

- 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)).

## [1.1.0](https://github.com/torchbox/django-pattern-library/releases/tag/v1.1.0) - 2023-10-25

### Added
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/automated-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
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:

- 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.
- 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)).
- 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)).

## Setting up automated UI tests

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ YAML isn’t everyone’s favorite markup language, but it has the advantage of

Here is what you need to know:

- Use `.yaml` or `.yml` as the file extension for pattern configuration files. If both are present, the `.yaml` file takes precendence.
- Use `.yaml` or `.yml` as the file extension for pattern configuration files. If both are present, the `.yaml` file takes precedence.
- Use Mappings in place of Python Dictionaries.
- Use Sequences in place of Python lists (or iterables like QuerySets).
- The pattern library uses [PyYAML](https://pyyaml.org/wiki/PyYAMLDocumentation) in particular
Expand Down
5 changes: 3 additions & 2 deletions pattern_library/loader_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,10 @@ def do_include(parser, token):
isolated_context=isolated_context,
)


def visit_extends(self, node, frame):
"""This method overrides the jinja extends tag
Is called as part of the compiler CodeGenerator
Is called as part of the compiler CodeGenerator
and adds a line to use the template_new_context as
part of the runtime render to pull in the dpl context
Handles visiting extends
Expand Down Expand Up @@ -196,4 +197,4 @@ def template_new_context(

return new_context(
self.environment, self.name, self.blocks, vars, shared, self.globals, locals
)
)
6 changes: 1 addition & 5 deletions pattern_library/management/commands/render_patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@
from django.test.client import RequestFactory

from pattern_library import get_base_template_names, get_pattern_base_template_name
from pattern_library.utils import (
get_pattern_context,
render_pattern,
get_renderer,
)
from pattern_library.utils import get_pattern_context, get_renderer, render_pattern


class Command(BaseCommand):
Expand Down
36 changes: 30 additions & 6 deletions pattern_library/monkey_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import inspect
import logging
from typing import Optional
import typing
import warnings

import django
from django.template.library import SimpleNode
Expand All @@ -11,7 +13,9 @@


def override_tag(
register: django.template.Library, name: str, default_html: Optional[str] = None
register: django.template.Library,
name: str,
default_html: typing.Optional[typing.Any] = UNSPECIFIED,
):
"""
An utility that helps you override original tags for use in your pattern library.
Expand All @@ -29,7 +33,7 @@ def node_render(context):
tag_overridden = False
result = ""

# Get overriden tag config.
# Get overridden tag config.
tag_overrides = context.get("__pattern_library_tag_overrides", {})

# Extract values for lookup from the token
Expand Down Expand Up @@ -79,6 +83,23 @@ def node_render(context):
# See https://github.com/torchbox/django-pattern-library/issues/166.
return str(result)
elif default_html is not UNSPECIFIED:
# Ensure default_html is a string.
if not isinstance(default_html, str):
# Save the caller for the override tag in case it's needed for error reporting.
trace = inspect.stack()[1]
if django.VERSION < (4, 0):
warnings.warn(
"default_html argument to override_tag should be a string to ensure compatibility "
'with Django >= 4.0 (line %s in "%s")'
% (trace.lineno, trace.filename),
Warning,
)
else:
raise TypeError(
'default_html argument to override_tag must be a string (line %s in "%s")'
% (trace.lineno, trace.filename)
)

# Render provided default;
# if no stub data supplied.
return default_html
Expand All @@ -97,23 +118,26 @@ def node_render(context):

return tag_func


# have to export the original jinja visit Extends
# in the case jinja tags are being overriden
jinja_visit_Extends = None


def override_jinja_tags():
"""
Overrides jinja extends and include tags for use in your pattern library.
Call it in your settings to override tags
Call it in your settings to override tags
"""
global jinja_visit_Extends
try:
from jinja2.compiler import CodeGenerator as JinjaCodeGenerator
from jinja2.environment import Template as JinjaTemplate
except ModuleNotFoundError:
ModuleNotFoundError("install jinja2 to override jinja tags")

from .loader_tags import template_new_context, visit_extends

jinja_visit_Extends = JinjaCodeGenerator.visit_Extends
JinjaTemplate.new_context = template_new_context
JinjaCodeGenerator.visit_Extends = visit_extends
JinjaCodeGenerator.visit_Extends = visit_extends
17 changes: 10 additions & 7 deletions pattern_library/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from django.template.loader import get_template, render_to_string
from django.template.loader_tags import ExtendsNode
from django.template.loaders.app_directories import get_app_template_dirs
from django.utils.html import escape
from django.utils.safestring import mark_safe

import markdown
Expand All @@ -22,9 +23,6 @@
from pattern_library.exceptions import TemplateIsNotPattern



from django.utils.html import escape

def path_to_section():
section_config = get_sections()
sections = {}
Expand Down Expand Up @@ -243,14 +241,17 @@ def get_pattern_source(cls, template):
@classmethod
def get_template_ancestors(cls, template_name, context=None):
template = get_template(template_name)
return cls._get_engine(template).get_template_ancestors(template_name, context=context)
return cls._get_engine(template).get_template_ancestors(
template_name, context=context
)

@classmethod
def _get_engine(cls, template):
if "jinja" in str(type(template)).lower():
return JinjaTemplateRenderer
return DTLTemplateRenderer


class DTLTemplateRenderer:
@staticmethod
def get_pattern_source(template):
Expand Down Expand Up @@ -287,7 +288,7 @@ class JinjaTemplateRenderer:
@staticmethod
def get_pattern_source(template):
with open(template.template.filename) as f:
source = escape(f.read())
source = escape(f.read())
return source

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

pattern_template = get_template(template_name)
#todo - make sure envrionment has context passed in
# todo - make sure envrionment has context passed in
environment = pattern_template.template.environment
nodelist = environment.parse(pattern_template.template.name)
parent_template_name = nodelist.find(Extends)
if parent_template_name:
ancestors.append(parent_template_name)
cls.get_template_ancestors(parent_template_name, context=context, ancestors=ancestors)
cls.get_template_ancestors(
parent_template_name, context=context, ancestors=ancestors
)

return ancestors
2 changes: 1 addition & 1 deletion pattern_library/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
get_pattern_config_str,
get_pattern_context,
get_pattern_markdown,
get_renderer,
get_sections,
is_pattern,
render_pattern,
get_renderer,
)


Expand Down
Loading

0 comments on commit 13e6632

Please sign in to comment.