Skip to content

Commit

Permalink
Handle interrupts in tablerow tags
Browse files Browse the repository at this point in the history
  • Loading branch information
jg-rp committed Aug 16, 2024
1 parent b01e86e commit 3fd92f0
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

- Fixed `{% case %}` / `{% when %}` behavior. When using [`liquid.future.Environment`](https://jg-rp.github.io/liquid/api/future-environment), we now render any number of `{% else %}` blocks and allow `{% when %}` tags to appear after `{% else %}` tags. The default `Environment` continues to raise a `LiquidSyntaxError` in such cases.

**Changed**

- Changed `{% break %}` and `{% continue %}` tag handling when they appear inside a `{% tablerow %}` tag. Now, when using `liquid.future.Environment`, interrupts follow Shopify/Liquid behavior introduced in [#1818](https://github.com/Shopify/liquid/pull/1818). Python Liquid's default environment is unchanged.

## Version 1.12.1

**Fixes**
Expand Down
43 changes: 39 additions & 4 deletions liquid/builtin/tags/tablerow_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from liquid.ast import ChildNode
from liquid.ast import Node
from liquid.context import Context
from liquid.exceptions import BreakLoop
from liquid.exceptions import ContinueLoop
from liquid.expression import NIL
from liquid.expression import LoopExpression
from liquid.limits import to_int
Expand Down Expand Up @@ -156,6 +158,9 @@ def step(self) -> None:
class TablerowNode(Node):
"""Parse tree node for the built-in "tablerow" tag."""

interrupts = False
"""If _true_, handle `break` and `continue` interrupts inside a tablerow loop."""

__slots__ = ("tok", "expression", "block")

def __init__(
Expand Down Expand Up @@ -194,18 +199,33 @@ def render_to_output(self, context: Context, buffer: TextIO) -> Optional[bool]:
}

buffer.write('<tr class="row1">\n')
_break = False

with context.extend(namespace):
for item in tablerow:
namespace[name] = item
buffer.write(f'<td class="col{tablerow.col}">')
self.block.render(context=context, buffer=buffer)

try:
self.block.render(context=context, buffer=buffer)
except BreakLoop:
if self.interrupts:
_break = True
else:
raise
except ContinueLoop:
if not self.interrupts:
raise

buffer.write("</td>")

if tablerow.col_last and not tablerow.last:
buffer.write(f'</tr>\n<tr class="row{tablerow.row + 1}">')

buffer.write("</tr>\n")
if _break:
break

buffer.write("</tr>\n")
return True

async def render_to_output_async(
Expand All @@ -227,18 +247,33 @@ async def render_to_output_async(
}

buffer.write('<tr class="row1">\n')
_break = False

with context.extend(namespace):
for item in tablerow:
namespace[name] = item
buffer.write(f'<td class="col{tablerow.col}">')
await self.block.render_async(context=context, buffer=buffer)

try:
await self.block.render_async(context=context, buffer=buffer)
except BreakLoop:
if self.interrupts:
_break = True
else:
raise
except ContinueLoop:
if not self.interrupts:
raise

buffer.write("</td>")

if tablerow.col_last and not tablerow.last:
buffer.write(f'</tr>\n<tr class="row{tablerow.row + 1}">')

buffer.write("</tr>\n")
if _break:
break

buffer.write("</tr>\n")
return True

def children(self) -> List[ChildNode]:
Expand Down
2 changes: 2 additions & 0 deletions liquid/future/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from ..environment import Environment as DefaultEnvironment
from ..template import FutureBoundTemplate
from .filters import split
from .tags import InterruptingTablerowTag
from .tags import LaxCaseTag
from .tags import LaxIfTag
from .tags import LaxUnlessTag
Expand All @@ -31,3 +32,4 @@ def setup_tags_and_filters(self) -> None:
self.add_tag(LaxCaseTag)
self.add_tag(LaxIfTag)
self.add_tag(LaxUnlessTag)
self.add_tag(InterruptingTablerowTag)
2 changes: 2 additions & 0 deletions liquid/future/tags/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from ._case_tag import LaxCaseTag # noqa: D104
from ._if_tag import LaxIfTag
from ._tablerow_tag import InterruptingTablerowTag
from ._unless_tag import LaxUnlessTag

__all__ = (
"InterruptingTablerowTag",
"LaxCaseTag",
"LaxIfTag",
"LaxUnlessTag",
Expand Down
14 changes: 14 additions & 0 deletions liquid/future/tags/_tablerow_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from liquid.builtin.tags.tablerow_tag import TablerowNode
from liquid.builtin.tags.tablerow_tag import TablerowTag


class InterruptingTablerowNode(TablerowNode):
"""A _tablerow_ node with interrupt handling enabled."""

interrupts = True


class InterruptingTablerowTag(TablerowTag):
"""A _tablerow_ tag that handles `break` and `continue` tags."""

node_class = InterruptingTablerowNode
55 changes: 55 additions & 0 deletions liquid/golden/tablerow_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,61 @@
"</tr>\n"
),
),
Case(
description="break from a tablerow loop",
template=(
r"{% tablerow n in (1..3) cols:2 %}"
r"{{n}}{% break %}{{n}}"
r"{% endtablerow %}"
),
expect='<tr class="row1">\n<td class="col1">1</td></tr>\n',
future=True,
),
Case(
description="continue from a tablerow loop",
template=(
r"{% tablerow n in (1..3) cols:2 %}"
r"{{n}}{% continue %}{{n}}"
r"{% endtablerow %}"
),
expect=(
'<tr class="row1">\n'
'<td class="col1">1</td>'
'<td class="col2">2</td>'
"</tr>\n"
'<tr class="row2">'
'<td class="col1">3</td>'
"</tr>\n"
),
future=True,
),
Case(
description="break from a tablerow loop inside a for loop",
template=(
r"{% for i in (1..2) -%}\n"
r"{% for j in (1..2) -%}\n"
r"{% tablerow k in (1..3) %}{% break %}{% endtablerow -%}\n"
r"loop j={{ j }}\n"
r"{% endfor -%}\n"
r"loop i={{ i }}\n"
r"{% endfor -%}\n"
r"after loop\n"
),
expect="\n".join(
[
r'\n\n<tr class="row1">',
r'<td class="col1"></td></tr>',
r'\nloop j=1\n\n<tr class="row1">',
r'<td class="col1"></td></tr>',
r'\nloop j=2\n\nloop i=1\n\n\n<tr class="row1">',
r'<td class="col1"></td></tr>',
r'\nloop j=1\n\n<tr class="row1">',
r'<td class="col1"></td></tr>',
r"\nloop j=2\n\nloop i=2\n\nafter loop\n",
]
),
future=True,
),
# Case(
# description="cols is non number string",
# template=(
Expand Down

0 comments on commit 3fd92f0

Please sign in to comment.