Repository: pallets/jinja
Pull request #1858 (2023-06-01 17:40:09)

fix check for empty required block
An update to flake8-bugbear started showing issues with multiple statements in `with pytest.raises` blocks. Fixing the test revealed that the later statements were failing. This fixes the check to only descend into `Output` nodes, failing immediately for other nodes. Also, mypy would have caught this, but there was an `ignore` comment on it.


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index f609ab34..36db0843 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,6 +5,9 @@ Version 3.1.3
 
 Unreleased
 
+-   Fix compiler error when checking if required blocks in parent templates are
+    empty. :pr:`1858`
+
 
 Version 3.1.2
 -------------
diff --git a/src/jinja2/parser.py b/src/jinja2/parser.py
index cefce2df..12703a97 100644
--- a/src/jinja2/parser.py
+++ b/src/jinja2/parser.py
@@ -311,12 +311,14 @@ class Parser:
         # enforce that required blocks only contain whitespace or comments
         # by asserting that the body, if not empty, is just TemplateData nodes
         # with whitespace data
-        if node.required and not all(
-            isinstance(child, nodes.TemplateData) and child.data.isspace()
-            for body in node.body
-            for child in body.nodes  # type: ignore
-        ):
-            self.fail("Required blocks can only contain comments or whitespace")
+        if node.required:
+            for body_node in node.body:
+                if not isinstance(body_node, nodes.Output) or any(
+                    not isinstance(output_node, nodes.TemplateData)
+                    or not output_node.data.isspace()
+                    for output_node in body_node.nodes
+                ):
+                    self.fail("Required blocks can only contain comments or whitespace")
 
         self.stream.skip_if("name:" + node.name)
         return node


Test patch:
diff --git a/tests/test_inheritance.py b/tests/test_inheritance.py
index 0c20d4da..8398f4df 100644
--- a/tests/test_inheritance.py
+++ b/tests/test_inheritance.py
@@ -287,26 +287,34 @@ class TestInheritance:
         env = Environment(
             loader=DictLoader(
                 {
-                    "default": "{% block x required %}data {# #}{% endblock %}",
-                    "default1": "{% block x required %}{% block y %}"
-                    "{% endblock %}  {% endblock %}",
-                    "default2": "{% block x required %}{% if true %}"
-                    "{% endif %}  {% endblock %}",
-                    "level1": "{% if default %}{% extends default %}"
-                    "{% else %}{% extends 'default' %}{% endif %}"
-                    "{%- block x %}CHILD{% endblock %}",
+                    "empty": "{% block x required %}{% endblock %}",
+                    "blank": "{% block x required %} {# c #}{% endblock %}",
+                    "text": "{% block x required %}data {# c #}{% endblock %}",
+                    "block": "{% block x required %}{% block y %}"
+                    "{% endblock %}{% endblock %}",
+                    "if": "{% block x required %}{% if true %}"
+                    "{% endif %}{% endblock %}",
+                    "top": "{% extends t %}{% block x %}CHILD{% endblock %}",
                 }
             )
         )
-        t = env.get_template("level1")
+        t = env.get_template("top")
+        assert t.render(t="empty") == "CHILD"
+        assert t.render(t="blank") == "CHILD"
 
-        with pytest.raises(
+        required_block_check = pytest.raises(
             TemplateSyntaxError,
             match="Required blocks can only contain comments or whitespace",
-        ):
-            assert t.render(default="default")
-            assert t.render(default="default2")
-            assert t.render(default="default3")
+        )
+
+        with required_block_check:
+            t.render(t="text")
+
+        with required_block_check:
+            t.render(t="block")
+
+        with required_block_check:
+            t.render(t="if")
 
     def test_required_with_scope(self, env):
         env = Environment(