From bb2c6623be214e27f218c22875bc45d3bd0c8078 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 19 May 2021 22:24:58 +0900 Subject: [PATCH 1/3] Close #9216: Support jinja2-3.0 --- CHANGES | 2 ++ setup.py | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 67ef71dd7..f09b310d3 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,8 @@ Release 4.0.2 (in development) Dependencies ------------ +* #9216: Support jinja2-3.0 + Incompatible changes -------------------- diff --git a/setup.py b/setup.py index 7ce37f9ea..b669afc00 100644 --- a/setup.py +++ b/setup.py @@ -21,8 +21,7 @@ install_requires = [ 'sphinxcontrib-htmlhelp', 'sphinxcontrib-serializinghtml', 'sphinxcontrib-qthelp', - 'Jinja2>=2.3,<3.0', - 'MarkupSafe<2.0', + 'Jinja2>=2.3', 'Pygments>=2.0', 'docutils>=0.14,<0.18', 'snowballstemmer>=1.1', From e5c905c9617d3e10d6b37d6548761c9f1ceb48b9 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 3 May 2021 13:48:17 +0900 Subject: [PATCH 2/3] Support jinja2-3.0 Since jinja2-3.0, some utility functions like contextfunction and environmentfilter are renamed to new name. This follows the updates to support jinja2-3.0 or above. --- sphinx/jinja2glue.py | 13 +++++++++---- sphinx/util/rst.py | 10 ++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py index c239f5a4a..cd6e8066b 100644 --- a/sphinx/jinja2glue.py +++ b/sphinx/jinja2glue.py @@ -12,7 +12,7 @@ from os import path from pprint import pformat from typing import TYPE_CHECKING, Any, Callable, Dict, Iterator, List, Tuple, Union -from jinja2 import BaseLoader, FileSystemLoader, TemplateNotFound, contextfunction +from jinja2 import BaseLoader, FileSystemLoader, TemplateNotFound from jinja2.environment import Environment from jinja2.sandbox import SandboxedEnvironment from jinja2.utils import open_if_exists @@ -22,6 +22,11 @@ from sphinx.theming import Theme from sphinx.util import logging from sphinx.util.osutil import mtimes_of_files +try: + from jinja2.utils import pass_context # type: ignore # jinja2-3.0 or above +except ImportError: + from jinja2 import contextfunction as pass_context + if TYPE_CHECKING: from sphinx.builders import Builder @@ -101,7 +106,7 @@ class idgen: next = __next__ # Python 2/Jinja compatibility -@contextfunction +@pass_context def warning(context: Dict, message: str, *args: Any, **kwargs: Any) -> str: if 'pagename' in context: filename = context.get('pagename') + context.get('file_suffix', '') @@ -180,9 +185,9 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader): self.environment.filters['toint'] = _toint self.environment.filters['todim'] = _todim self.environment.filters['slice_index'] = _slice_index - self.environment.globals['debug'] = contextfunction(pformat) + self.environment.globals['debug'] = pass_context(pformat) self.environment.globals['warning'] = warning - self.environment.globals['accesskey'] = contextfunction(accesskey) + self.environment.globals['accesskey'] = pass_context(accesskey) self.environment.globals['idgen'] = idgen if use_i18n: self.environment.install_gettext_translations(builder.app.translator) diff --git a/sphinx/util/rst.py b/sphinx/util/rst.py index 82b3f6bda..8103a8b10 100644 --- a/sphinx/util/rst.py +++ b/sphinx/util/rst.py @@ -18,11 +18,17 @@ from docutils.parsers.rst import roles from docutils.parsers.rst.languages import en as english from docutils.statemachine import StringList from docutils.utils import Reporter -from jinja2 import Environment, environmentfilter +from jinja2 import Environment from sphinx.locale import __ from sphinx.util import docutils, logging +try: + from jinja2.utils import pass_environment # type: ignore # jinja2-3.0 or above +except ImportError: + from jinja2 import environmentfilter as pass_environment + + logger = logging.getLogger(__name__) docinfo_re = re.compile(':\\w+:.*?') @@ -51,7 +57,7 @@ def textwidth(text: str, widechars: str = 'WF') -> int: return sum(charwidth(c, widechars) for c in text) -@environmentfilter +@pass_environment def heading(env: Environment, text: str, level: int = 1) -> str: """Create a heading for *level*.""" assert level <= 3 From 4b8fb688c10645739e52c54c9c46a1d6756feb62 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 13 May 2021 01:12:25 +0900 Subject: [PATCH 3/3] Fix type annotations (for jinja2) Jinja2 starts to bundle its typehints since v3.0. As a result, mypy warns "ignore" hint is needless. This removes them all to keep it valid. --- sphinx/jinja2glue.py | 2 +- sphinx/util/__init__.py | 2 +- sphinx/util/rst.py | 4 ++-- sphinx/util/tags.py | 14 +++++++------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py index cd6e8066b..f1b57537f 100644 --- a/sphinx/jinja2glue.py +++ b/sphinx/jinja2glue.py @@ -23,7 +23,7 @@ from sphinx.util import logging from sphinx.util.osutil import mtimes_of_files try: - from jinja2.utils import pass_context # type: ignore # jinja2-3.0 or above + from jinja2.utils import pass_context except ImportError: from jinja2 import contextfunction as pass_context diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index d420f4f77..7e24909ee 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -235,7 +235,7 @@ def save_traceback(app: "Sphinx") -> str: platform.python_version(), platform.python_implementation(), docutils.__version__, docutils.__version_details__, - jinja2.__version__, # type: ignore + jinja2.__version__, last_msgs)).encode()) if app is not None: for ext in app.extensions.values(): diff --git a/sphinx/util/rst.py b/sphinx/util/rst.py index 8103a8b10..26e63cc2a 100644 --- a/sphinx/util/rst.py +++ b/sphinx/util/rst.py @@ -24,7 +24,7 @@ from sphinx.locale import __ from sphinx.util import docutils, logging try: - from jinja2.utils import pass_environment # type: ignore # jinja2-3.0 or above + from jinja2.utils import pass_environment except ImportError: from jinja2 import environmentfilter as pass_environment @@ -61,7 +61,7 @@ def textwidth(text: str, widechars: str = 'WF') -> int: def heading(env: Environment, text: str, level: int = 1) -> str: """Create a heading for *level*.""" assert level <= 3 - width = textwidth(text, WIDECHARS[env.language]) # type: ignore + width = textwidth(text, WIDECHARS[env.language]) sectioning_char = SECTIONING_CHARS[level - 1] return '%s\n%s' % (text, sectioning_char * width) diff --git a/sphinx/util/tags.py b/sphinx/util/tags.py index cf3d53400..1499123e5 100644 --- a/sphinx/util/tags.py +++ b/sphinx/util/tags.py @@ -69,18 +69,18 @@ class Tags: def eval_node(node: Node) -> bool: if isinstance(node, nodes.CondExpr): - if eval_node(node.test): # type: ignore - return eval_node(node.expr1) # type: ignore + if eval_node(node.test): + return eval_node(node.expr1) else: - return eval_node(node.expr2) # type: ignore + return eval_node(node.expr2) elif isinstance(node, nodes.And): - return eval_node(node.left) and eval_node(node.right) # type: ignore + return eval_node(node.left) and eval_node(node.right) elif isinstance(node, nodes.Or): - return eval_node(node.left) or eval_node(node.right) # type: ignore + return eval_node(node.left) or eval_node(node.right) elif isinstance(node, nodes.Not): - return not eval_node(node.node) # type: ignore + return not eval_node(node.node) elif isinstance(node, nodes.Name): - return self.tags.get(node.name, False) # type: ignore + return self.tags.get(node.name, False) else: raise ValueError('invalid node, check parsing')