Skip to content

Commit

Permalink
0.7.3 (#43)
Browse files Browse the repository at this point in the history
* 🩹 Make `default` filter work with `None`; Make `attr` filter work with dicts

* πŸ”– 0.7.3

* πŸ“ Update changelog
  • Loading branch information
pwwang committed Dec 7, 2021
1 parent 6c3705f commit 9ac12aa
Show file tree
Hide file tree
Showing 12 changed files with 52 additions and 17 deletions.
6 changes: 6 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 0.7.3

- 🩹 Make `default` filter work with `None`
- 🩹 Make `attr` filter work with dicts
- 🩹 Use filter `liquid_map`, in wild mode, instead of `map`, which is overridden by python's builtin `map`

# 0.7.2

- πŸ› Fix `date` filter issues (#38, #40)
Expand Down
1 change: 0 additions & 1 deletion docs/standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ You may checkout the documentation for standard liquid:

It always returns a `float` rather than an `integer` when `ndigits=0`


## Logical operators

The logical operators `and`/`or` collapse from left to right (it's right to left in `liquid`)
Expand Down
2 changes: 2 additions & 0 deletions docs/wild.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Below are some features it supports.
- See: https://jinja.palletsprojects.com/en/3.0.x/templates/?highlight=builtin%20filters#builtin-filters
- `ifelse`:
- See: https://pwwang.github.io/liquidpy/api/liquid.filters.wild/
- `map()`
- It is overridden by python's `builtins.map()`. To use the one from `liquid`, try `liquid_map()`

## Tests

Expand Down
2 changes: 1 addition & 1 deletion liquid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

patch_jinja()

__version__ = "0.7.2"
__version__ = "0.7.3"
2 changes: 1 addition & 1 deletion liquid/exts/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def filter_stream(self, stream: "TokenStream") -> Generator:
yield Token(token.lineno, TOKEN_COMMA, None)
yield tokens_ahead[3]
yield Token(token.lineno, TOKEN_ADD, None)
yield Token(token.lineno, TOKEN_INTEGER, 1)
yield Token(token.lineno, TOKEN_INTEGER, 1) # type: ignore
yield Token(token.lineno, TOKEN_RPAREN, None)

else:
Expand Down
4 changes: 2 additions & 2 deletions liquid/filters/manager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Provides filter manager"""
from typing import TYPE_CHECKING, Callable, Dict, Union
from typing import TYPE_CHECKING, Callable, Dict, Sequence, Union

if TYPE_CHECKING:
from jinja2 import Environment
Expand All @@ -19,7 +19,7 @@ def __init__(self) -> None:
self.filters: Dict[str, Callable] = {}

def register(
self, name_or_filter: Union[str, Callable] = None
self, name_or_filter: Union[str, Sequence[str], Callable] = None
) -> Callable:
"""Register a filter
Expand Down
19 changes: 16 additions & 3 deletions liquid/filters/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,17 @@ def __bool__(self):
return False


def _get_prop(obj, prop):
def _get_prop(obj, prop, _raise=False):
"""Get the property of the object, allow via getitem"""
try:
return obj[prop]
except (TypeError, KeyError):
return getattr(obj, prop)
try:
return getattr(obj, prop)
except AttributeError:
if _raise: # pragma: no cover
raise
return None


# Jinja comes with thses filters
Expand Down Expand Up @@ -196,6 +201,8 @@ def default(base, deft, allow_false=False):
Otherwise, return base"""
if allow_false and base is False:
return False
if base is None:
return deft
return FILTERS["default"](base, deft, isinstance(base, str))


Expand Down Expand Up @@ -347,12 +354,18 @@ def where(base, prop, value):
return ret or EmptyDrop()


@standard_filter_manager.register("map")
@standard_filter_manager.register(["liquid_map", "map"])
def liquid_map(base, prop):
"""Map a property to a list of objects"""
return [_get_prop(bas, prop) for bas in base]


@standard_filter_manager.register
def attr(base, prop):
"""Similar as `__getattr__()` but also works like `__getitem__()"""
return _get_prop(base, prop)


# @standard_filter_manager.register
# def join(base, sep):
# """Join a list by the sep"""
Expand Down
4 changes: 2 additions & 2 deletions liquid/liquid.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def __init__(
ext_conf[key] = val

loader = env_args.pop("loader", None)
fsloader = FileSystemLoader(search_paths)
fsloader = FileSystemLoader(search_paths) # type: ignore
if loader:
loader = ChoiceLoader([loader, fsloader])
else:
Expand Down Expand Up @@ -190,7 +190,7 @@ def __init__(
# in case template is a PathLike
self.template = env.get_template(str(template))
else:
self.template = env.from_string(template)
self.template = env.from_string(str(template))

def render(self, *args, **kwargs) -> Any:
"""Render the template.
Expand Down
8 changes: 4 additions & 4 deletions liquid/tags/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ def comment(token: "Token", parser: "Parser") -> nodes.Node:
"""
if parser.stream.current.type is TOKEN_BLOCK_END:
# no args provided, ignore whatever
parser.parse_statements(["name:endcomment"], drop_needle=True)
parser.parse_statements(("name:endcomment", ), drop_needle=True)
return nodes.Output([], lineno=token.lineno)

args = parser.parse_expression()
body = parser.parse_statements(["name:endcomment"], drop_needle=True)
body = parser.parse_statements(("name:endcomment", ), drop_needle=True)
body = decode_raw(body[0].nodes[0].data)
body_parts = body.split("\n", 1)
if not body_parts[0]:
Expand Down Expand Up @@ -194,7 +194,7 @@ def tablerow(
Returns:
The parsed node
"""
target = parser.parse_assign_target(extra_end_rules=("name:in"))
target = parser.parse_assign_target(extra_end_rules=("name:in", ))
parser.stream.expect("name:in")
iter_ = parser.parse_tuple(
with_condexpr=False,
Expand Down Expand Up @@ -222,7 +222,7 @@ def tablerow(
"load",
)
else:
inner_iter = iter_
inner_iter: nodes.Getitem = iter_

inner_body = [
nodes.Output(
Expand Down
5 changes: 3 additions & 2 deletions liquid/tags/wild.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import textwrap
from contextlib import redirect_stdout
from io import StringIO
from typing import TYPE_CHECKING, List
from typing import TYPE_CHECKING, List, Union

from jinja2 import nodes
from jinja2.exceptions import TemplateSyntaxError
Expand Down Expand Up @@ -151,6 +151,7 @@ def addfilter(
token = parser.stream.expect("name")
filtername = token.value

pass_env: Union[bool, Token]
if parser.stream.current.type is TOKEN_BLOCK_END:
# no pass_environment
pass_env = False
Expand All @@ -176,7 +177,7 @@ def addfilter(
) from None

if pass_env:
filterfunc = pass_environment(filterfunc)
filterfunc = pass_environment(filterfunc) # type: ignore
env.filters[filtername] = filterfunc

return nodes.Output([], lineno=token.lineno)
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"

[tool.poetry]
name = "liquidpy"
version = "0.7.2"
version = "0.7.3"
description = "A port of liquid template engine for python"
authors = [ "pwwang <[email protected]>",]
license = "MIT"
Expand Down
14 changes: 14 additions & 0 deletions tests/standard/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
import pytest
from datetime import datetime
from collections import namedtuple

from liquid import Liquid

Expand Down Expand Up @@ -394,3 +395,16 @@ def test_basic_typecasting(set_default_standard):
assert Liquid('{{ float("1") | plus: 1 }}').render() == "2.0"
assert Liquid('{{ str(1) | append: "1" }}').render() == "11"
assert Liquid('{{ bool(1) }}').render() == "True"

def test_attr(set_default_standard):
assert Liquid('{{x | attr: "y"}}').render(x = {}) == "None"
assert Liquid('{{x | attr: "y" | default: 1}}').render(x = {}) == "1"
assert Liquid('{{x | attr: "y"}}').render(x = {"y": 1}) == "1"
assert Liquid('{{x | attr: "y"}}').render(x=namedtuple("X", "y")(2)) == "2"

def test_liquid_map(set_default_standard):
assert Liquid('{{x | liquid_map: "y" | first}}').render(x=[{}]) == "None"
assert Liquid('{{x | liquid_map: "y" | first}}').render(x=[{"y": 1}]) == "1"
assert Liquid('{{x | liquid_map: "y" | last}}').render(
x=[namedtuple("X", "y")(2)]
) == "2"

0 comments on commit 9ac12aa

Please sign in to comment.