Repository: mitsuhiko/minijinja (language: rust)

Issue:
Python object's truthiness is not respected
## Description

When using python objects in conditions inside, the object's truthiness is expected to work like it does outside of templates

## Example

```python
from minijinja import Environment

class Group:
    def __init__(self, items):
        self.items = items

    def __bool__(self):
        return bool(self.items)

group = Group([])
assert not group  # This passes, which is correct

env = Environment()
assert env.render_str("{{ 'has items' if group else 'empty' }}", group=group) == 'empty'  # This fails
```

Is it possible to check for Python objects' truthiness using its `__bool__` method, like we do for comparators (eg. `__lt__`)?


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d355f335..fbe6c50c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,8 @@ All notable changes to MiniJinja are documented here.
   variables referenced by macros.  #714
 - Fixed a deadlock in the Python binding when multiple threads were
   rendering from the same environment at once.  #717
+- The Python bindings handle `__bool__` correctly now for custom
+  objects in if-conditions and filters.  #719
 
 ## 2.8.0
 
diff --git a/minijinja-py/src/typeconv.rs b/minijinja-py/src/typeconv.rs
index 629a0802..8b823606 100644
--- a/minijinja-py/src/typeconv.rs
+++ b/minijinja-py/src/typeconv.rs
@@ -135,6 +135,13 @@ impl Object for DynamicObject {
         })
     }
 
+    fn is_true(self: &Arc<Self>) -> bool {
+        Python::with_gil(|py| {
+            let inner = self.inner.bind(py);
+            inner.is_truthy().unwrap_or(true)
+        })
+    }
+
     fn enumerate(self: &Arc<Self>) -> Enumerator {
         Python::with_gil(|py| {
             let inner = self.inner.bind(py);


Test patch:
diff --git a/minijinja-py/tests/test_basic.py b/minijinja-py/tests/test_basic.py
index e9127032..e47f3aa5 100644
--- a/minijinja-py/tests/test_basic.py
+++ b/minijinja-py/tests/test_basic.py
@@ -451,3 +451,25 @@ def busy_wait(value, seconds: float):
 
     executor.shutdown(wait=True)
     assert done == ["something"] * 4
+
+
+def test_truthy():
+    class Custom:
+        def __init__(self, is_true):
+            self.is_true = is_true
+
+        def __bool__(self):
+            return bool(self.is_true)
+
+    env = Environment()
+    assert env.eval_expr("x|bool", x=Custom(True)) is True
+    assert env.eval_expr("x|bool", x=Custom(False)) is False
+    assert env.eval_expr("x|bool", x=Custom(None)) is False
+    assert env.eval_expr("x|bool", x=Custom("")) is False
+    assert env.eval_expr("x|bool", x=Custom("foo")) is True
+
+    class Fallback:
+        def __bool__(self):
+            raise RuntimeError("swallowed but true")
+
+    assert env.eval_expr("x|bool", x=Fallback()) is True