Skip to content

Commit

Permalink
Merge pull request #163 from jg-rp/line_numbers
Browse files Browse the repository at this point in the history
Fix exception line numbers when parsing some expressions
  • Loading branch information
jg-rp authored Dec 26, 2024
2 parents 415f6d9 + 0f729c0 commit 87dc8ea
Show file tree
Hide file tree
Showing 16 changed files with 161 additions and 46 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
os: [ubuntu-22.04, windows-latest, macos-latest]
python-version: ["3.7.17", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
exclude:
- os: macos-latest
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
**Fixes**

- 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.
- Fixed line numbers in some error messages. When parsing some Liquid expressions, we were always getting a line number of `1` in the event of a syntax error. See [issue #162](https://github.com/jg-rp/liquid/issues/162).

**Changed**

Expand Down
4 changes: 3 additions & 1 deletion liquid/builtin/statement.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,6 @@ class Statement(Tag):
def parse(self, stream: TokenStream) -> StatementNode:
tok = stream.current
expect(stream, TOKEN_STATEMENT)
return self.node_class(tok, self.env.parse_filtered_expression_value(tok.value))
return self.node_class(
tok, self.env.parse_filtered_expression_value(tok.value, tok.linenum)
)
10 changes: 7 additions & 3 deletions liquid/builtin/tags/assign_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ class AssignTag(Tag):
block = False
node_class = AssignNode

def _parse_expression(self, value: str) -> Expression:
return self.env.parse_filtered_expression_value(value)
def _parse_expression(self, value: str, linenum: int) -> Expression:
return self.env.parse_filtered_expression_value(value, linenum)

def parse(self, stream: TokenStream) -> AssignNode:
expect(stream, TOKEN_TAG, value=TAG_ASSIGN)
Expand All @@ -83,5 +83,9 @@ def parse(self, stream: TokenStream) -> AssignNode:
)

return self.node_class(
tok, AssignmentExpression(name, self._parse_expression(right))
tok,
AssignmentExpression(
name,
self._parse_expression(right, stream.current.linenum),
),
)
4 changes: 3 additions & 1 deletion liquid/builtin/tags/decrement_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ def parse(self, stream: TokenStream) -> DecrementNode:
tok=tok,
identifier=str(
parse_unchained_identifier(
ExprTokenStream(tokenize(stream.current.value))
ExprTokenStream(
tokenize(stream.current.value, stream.current.linenum)
)
)
),
)
6 changes: 3 additions & 3 deletions liquid/builtin/tags/echo_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class EchoTag(Tag):
block = False
node_class = EchoNode

def _parse_expression(self, value: str) -> Expression:
return self.env.parse_filtered_expression_value(value)
def _parse_expression(self, value: str, linenum: int) -> Expression:
return self.env.parse_filtered_expression_value(value, linenum)

def parse(self, stream: TokenStream) -> Node: # noqa: D102
expect(stream, TOKEN_TAG, value=TAG_ECHO)
Expand All @@ -42,5 +42,5 @@ def parse(self, stream: TokenStream) -> Node: # noqa: D102
expr: Expression = NIL
else:
expect(stream, TOKEN_EXPRESSION)
expr = self._parse_expression(stream.current.value)
expr = self._parse_expression(stream.current.value, tok.linenum)
return self.node_class(tok, expression=expr)
5 changes: 4 additions & 1 deletion liquid/builtin/tags/for_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,10 @@ def parse(self, stream: TokenStream) -> Node:
stream.next_token()

expect(stream, TOKEN_EXPRESSION)
expr = self.env.parse_loop_expression_value(stream.current.value)
expr = self.env.parse_loop_expression_value(
stream.current.value,
stream.current.linenum,
)
stream.next_token()

block = parser.parse_block(stream, ENDFORBLOCK)
Expand Down
5 changes: 4 additions & 1 deletion liquid/builtin/tags/if_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,10 @@ def __init__(self, env: Environment):
def parse_expression(self, stream: TokenStream) -> Expression:
"""Pare a boolean expression from a stream of tokens."""
expect(stream, TOKEN_EXPRESSION)
return self.env.parse_boolean_expression_value(stream.current.value)
return self.env.parse_boolean_expression_value(
stream.current.value,
stream.current.linenum,
)

def parse(self, stream: TokenStream) -> Node:
expect(stream, TOKEN_TAG, value=TAG_IF)
Expand Down
4 changes: 3 additions & 1 deletion liquid/builtin/tags/increment_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ def parse(self, stream: TokenStream) -> IncrementNode:
tok=tok,
identifier=str(
parse_unchained_identifier(
ExprTokenStream(tokenize(stream.current.value))
ExprTokenStream(
tokenize(stream.current.value, stream.current.linenum)
)
)
),
)
5 changes: 4 additions & 1 deletion liquid/builtin/tags/render_tag.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Parse tree node and tag definition for the built in "render" tag."""

import sys
from typing import Dict
from typing import List
Expand Down Expand Up @@ -249,7 +250,9 @@ class RenderTag(Tag):
def parse(self, stream: TokenStream) -> Node:
tok = next(stream)
expect(stream, TOKEN_EXPRESSION)
expr_stream = ExprTokenStream(tokenize(stream.current.value))
expr_stream = ExprTokenStream(
tokenize(stream.current.value, stream.current.linenum)
)

# Need a string. 'render' does not accept identifiers that resolve to a string.
# This is the name of the template to be included.
Expand Down
4 changes: 3 additions & 1 deletion liquid/builtin/tags/tablerow_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,9 @@ def parse(self, stream: TokenStream) -> TablerowNode:
stream.next_token()

expect(stream, TOKEN_EXPRESSION)
loop_expression = self.env.parse_loop_expression_value(stream.current.value)
loop_expression = self.env.parse_loop_expression_value(
stream.current.value, stream.current.linenum
)
stream.next_token()

block = parser.parse_block(stream, END_TAGBLOCK)
Expand Down
4 changes: 3 additions & 1 deletion liquid/builtin/tags/unless_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ def __init__(self, env: Environment):
def parse_expression(self, stream: TokenStream) -> Expression:
"""Parse a boolean expression from a stream of tokens."""
expect(stream, TOKEN_EXPRESSION)
return self.env.parse_boolean_expression_value(stream.current.value)
return self.env.parse_boolean_expression_value(
stream.current.value, stream.current.linenum
)

def parse(self, stream: TokenStream) -> Union[UnlessNode, IllegalNode]:
expect(stream, TOKEN_TAG, value=TAG_UNLESS)
Expand Down
15 changes: 8 additions & 7 deletions liquid/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,8 @@ async def analyze_tags_async(
)

def make_globals(
self, globals: Optional[Mapping[str, object]] = None # noqa: A002
self,
globals: Optional[Mapping[str, object]] = None, # noqa: A002
) -> Dict[str, object]:
"""Combine environment globals with template globals."""
if globals:
Expand Down Expand Up @@ -577,12 +578,12 @@ def set_expression_cache_size(self, maxsize: int = 0) -> None:
def _get_expression_parsers(
self, cache_size: int = 0
) -> Tuple[
Callable[[str], "BooleanExpression"],
Callable[[str], "BooleanExpression"],
Callable[[str], "FilteredExpression"],
Callable[[str], "FilteredExpression"],
Callable[[str], "FilteredExpression"],
Callable[[str], "LoopExpression"],
Callable[[str, int], "BooleanExpression"],
Callable[[str, int], "BooleanExpression"],
Callable[[str, int], "FilteredExpression"],
Callable[[str, int], "FilteredExpression"],
Callable[[str, int], "FilteredExpression"],
Callable[[str, int], "LoopExpression"],
]:
if cache_size >= 1:
return (
Expand Down
23 changes: 13 additions & 10 deletions liquid/extra/tags/if_expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def parse(self, stream: TokenStream) -> StatementNode:
tok = stream.current
expect(stream, TOKEN_STATEMENT)
return StatementNode(
tok, self.env.parse_conditional_expression_value(tok.value)
tok, self.env.parse_conditional_expression_value(tok.value, tok.linenum)
)


Expand All @@ -35,17 +35,17 @@ class InlineIfAssignTag(AssignTag):
inline `if` expressions.
"""

def _parse_expression(self, value: str) -> Expression:
return self.env.parse_conditional_expression_value(value)
def _parse_expression(self, value: str, linenum: int) -> Expression:
return self.env.parse_conditional_expression_value(value, linenum)


class InlineIfEchoTag(EchoTag):
"""A drop-in replacement for the standard `echo` tag that supports
inline `if` expressions.
"""

def _parse_expression(self, value: str) -> Expression:
return self.env.parse_conditional_expression_value(value)
def _parse_expression(self, value: str, linenum: int) -> Expression:
return self.env.parse_conditional_expression_value(value, linenum)


class InlineIfStatementWithParens(Statement):
Expand All @@ -58,7 +58,10 @@ def parse(self, stream: TokenStream) -> StatementNode:
tok = stream.current
expect(stream, TOKEN_STATEMENT)
return StatementNode(
tok, self.env.parse_conditional_expression_value_with_parens(tok.value)
tok,
self.env.parse_conditional_expression_value_with_parens(
tok.value, tok.linenum
),
)


Expand All @@ -68,8 +71,8 @@ class InlineIfAssignTagWithParens(AssignTag):
terms with parentheses.
"""

def _parse_expression(self, value: str) -> Expression:
return self.env.parse_conditional_expression_value_with_parens(value)
def _parse_expression(self, value: str, linenum: int) -> Expression:
return self.env.parse_conditional_expression_value_with_parens(value, linenum)


class InlineIfEchoTagWithParens(EchoTag):
Expand All @@ -78,5 +81,5 @@ class InlineIfEchoTagWithParens(EchoTag):
terms with parentheses.
"""

def _parse_expression(self, value: str) -> Expression:
return self.env.parse_conditional_expression_value_with_parens(value)
def _parse_expression(self, value: str, linenum: int) -> Expression:
return self.env.parse_conditional_expression_value_with_parens(value, linenum)
4 changes: 3 additions & 1 deletion liquid/extra/tags/if_not.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ class IfNotTag(IfTag):
def parse_expression(self, stream: TokenStream) -> Expression:
"""Pare a boolean expression from a stream of tokens."""
expect(stream, TOKEN_EXPRESSION)
return parse_boolean_expression_with_parens(stream.current.value)
return parse_boolean_expression_with_parens(
stream.current.value, stream.current.linenum
)
Loading

0 comments on commit 87dc8ea

Please sign in to comment.