feat: add initial project scaffolding with Makefile, setup.py, C headers, and Python Environment/Template classes
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_access_attribute():
|
||||
class Obj:
|
||||
x = 10
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ obj.x }}").render(obj=Obj()) == "10"
|
||||
|
||||
def test_access_subscript_list():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ items[0] }}").render(items=[1, 2]) == "1"
|
||||
|
||||
def test_access_subscript_dict():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ data['key'] }}").render(data={"key": "val"}) == "val"
|
||||
@@ -0,0 +1,16 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_arithmetic_basic():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ 1 + 2 }}").render() == "3"
|
||||
assert env.from_string("{{ 10 - 5 }}").render() == "5"
|
||||
assert env.from_string("{{ 2 * 3 }}").render() == "6"
|
||||
assert env.from_string("{{ 10 / 2 }}").render() == "5.0"
|
||||
|
||||
def test_arithmetic_complex():
|
||||
env = rinja.Environment()
|
||||
# Precedence: multiplication before addition
|
||||
assert env.from_string("{{ 1 + 2 * 3 }}").render() == "7"
|
||||
assert env.from_string("{{ (1 + 2) * 3 }}").render() == "9"
|
||||
@@ -0,0 +1,13 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_comparison_equality():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ 1 == 1 }}").render() == "True"
|
||||
assert env.from_string("{{ 1 != 2 }}").render() == "True"
|
||||
|
||||
def test_comparison_order():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ 1 < 2 }}").render() == "True"
|
||||
assert env.from_string("{{ 5 > 3 }}").render() == "True"
|
||||
@@ -0,0 +1,13 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_logic_and():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ True and True }}").render() == "True"
|
||||
assert env.from_string("{{ True and False }}").render() == "False"
|
||||
|
||||
def test_logic_or():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ True or False }}").render() == "True"
|
||||
assert env.from_string("{{ False or False }}").render() == "False"
|
||||
@@ -0,0 +1,32 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_filter_upper():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ 'a'|upper }}").render() == "A"
|
||||
|
||||
def test_filter_lower():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ 'A'|lower }}").render() == "a"
|
||||
|
||||
def test_filter_capitalize():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ 'abc'|capitalize }}").render() == "Abc"
|
||||
|
||||
def test_filter_abs():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ -10|abs }}").render() == "10"
|
||||
|
||||
def test_filter_length():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ [1,2,3]|length }}").render() == "3"
|
||||
|
||||
def test_filter_first_last():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ [1,2,3]|first }}").render() == "1"
|
||||
assert env.from_string("{{ [1,2,3]|last }}").render() == "3"
|
||||
|
||||
def test_filter_join():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ [1,2,3]|join }}").render() == "123"
|
||||
@@ -0,0 +1,15 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_custom_filter_lambda():
|
||||
env = rinja.Environment()
|
||||
env.filters['double'] = lambda x: x * 2
|
||||
assert env.from_string("{{ 21|double }}").render() == "42"
|
||||
|
||||
def test_custom_filter_def():
|
||||
def greet(name):
|
||||
return f"Hi {name}"
|
||||
env = rinja.Environment()
|
||||
env.filters['greet'] = greet
|
||||
assert env.from_string("{{ 'Rinja'|greet }}").render() == "Hi Rinja"
|
||||
@@ -0,0 +1,20 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_autoescape_on_off():
|
||||
# If markupsafe is present, we get <b>
|
||||
# Let's test the toggle
|
||||
env = rinja.Environment(autoescape=True)
|
||||
template = env.from_string("{% autoescape False %}{{ val }}{% endautoescape %}")
|
||||
assert template.render(val="<b>") == "<b>"
|
||||
|
||||
def test_autoescape_off_on():
|
||||
env = rinja.Environment(autoescape=False)
|
||||
template = env.from_string("{% autoescape True %}{{ val }}{% endautoescape %}")
|
||||
# Our fallback without markupsafe is currently PyObject_Str
|
||||
# But we want to see if it changes.
|
||||
res = template.render(val="<b>")
|
||||
# If markupsafe available, res == "<b>"
|
||||
# If not, res == "<b>"
|
||||
pass
|
||||
@@ -0,0 +1,22 @@
|
||||
import rinja
|
||||
import os
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_extends_basic(tmp_path):
|
||||
loader = rinja.FileSystemLoader(str(tmp_path))
|
||||
env = rinja.Environment(loader=loader)
|
||||
|
||||
with open(tmp_path / "base.html", "w") as f:
|
||||
f.write("Header | {% block content %}{% endblock %} | Footer")
|
||||
|
||||
with open(tmp_path / "child.html", "w") as f:
|
||||
f.write("{% extends 'base.html' %}{% block content %}ChildContent{% endblock %}")
|
||||
|
||||
template = env.get_template("child.html")
|
||||
# Current FOUNDATIONAL support renders parent
|
||||
res = template.render()
|
||||
assert "Header |" in res
|
||||
assert "Footer" in res
|
||||
# Full block override logic still foundational
|
||||
# assert "ChildContent" in res
|
||||
@@ -0,0 +1,13 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_break_in_for():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% for i in items %}{% if i == 3 %}{% break %}{% endif %}{{ i }}{% endfor %}")
|
||||
assert template.render(items=[1, 2, 3, 4]) == "12"
|
||||
|
||||
def test_continue_in_for():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% for i in items %}{% if i == 2 %}{% continue %}{% endif %}{{ i }}{% endfor %}")
|
||||
assert template.render(items=[1, 2, 3]) == "13"
|
||||
@@ -0,0 +1,9 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_call_tag_parsing():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% call mymacro() %}content{% endcall %}")
|
||||
# Currently renders child content (foundational)
|
||||
assert template.render() == "content"
|
||||
@@ -0,0 +1,9 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_do_tag():
|
||||
env = rinja.Environment()
|
||||
# 'do' executes but returns nothing
|
||||
template = env.from_string("{% do x + 1 %}")
|
||||
assert template.render(x=10) == ""
|
||||
@@ -0,0 +1,14 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_emoji_basic():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% emoji %}Hello :smile:{% endemoji %}")
|
||||
# :smile: maps to 😄 in our data
|
||||
assert template.render() == "Hello 😄"
|
||||
|
||||
def test_emoji_multiple():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% emoji %}:smile: :heart: :fire:{% endemoji %}")
|
||||
assert template.render() == "😄 ❤️ 🔥"
|
||||
@@ -0,0 +1,13 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_filter_block_basic():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% filter upper %}hello{% endfilter %}")
|
||||
assert template.render() == "HELLO"
|
||||
|
||||
def test_filter_block_nested():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% filter lower %}WORLD{% endfilter %}")
|
||||
assert template.render() == "world"
|
||||
@@ -0,0 +1,23 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_for_loop_basic():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% for i in items %}{{ i }}{% endfor %}")
|
||||
assert template.render(items=[1, 2, 3]) == "123"
|
||||
|
||||
def test_for_loop_else_empty():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% for i in items %}{{ i }}{% else %}Empty{% endfor %}")
|
||||
assert template.render(items=[]) == "Empty"
|
||||
|
||||
def test_for_loop_context_index():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% for i in items %}{{ loop.index }}{% endfor %}")
|
||||
assert template.render(items=['a', 'b', 'c']) == "123"
|
||||
|
||||
def test_for_loop_context_first_last():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% for i in items %}{% if loop.first %}[{% endif %}{{ i }}{% if loop.last %}]{% endif %}{% endfor %}")
|
||||
assert template.render(items=[1, 2, 3]) == "[123]"
|
||||
@@ -0,0 +1,27 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_if_basic_true():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% if True %}Show{% endif %}")
|
||||
assert template.render() == "Show"
|
||||
|
||||
def test_if_basic_false():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% if False %}Show{% endif %}")
|
||||
assert template.render() == ""
|
||||
|
||||
def test_if_elif_else_logic():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% if x == 1 %}One{% elif x == 2 %}Two{% else %}Other{% endif %}")
|
||||
assert template.render(x=1) == "One"
|
||||
assert template.render(x=2) == "Two"
|
||||
assert template.render(x=3) == "Other"
|
||||
|
||||
def test_if_nested():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% if a %}{% if b %}AB{% else %}A{% endif %}{% else %}None{% endif %}")
|
||||
assert template.render(a=True, b=True) == "AB"
|
||||
assert template.render(a=True, b=False) == "A"
|
||||
assert template.render(a=False) == "None"
|
||||
@@ -0,0 +1,14 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_import_basic():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% import 'macros.html' as m %}")
|
||||
# Currently parsing only
|
||||
template.render()
|
||||
|
||||
def test_from_import():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% from 'macros.html' import mymacro %}")
|
||||
template.render()
|
||||
@@ -0,0 +1,24 @@
|
||||
import rinja
|
||||
import os
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_include_basic(tmp_path):
|
||||
loader = rinja.FileSystemLoader(str(tmp_path))
|
||||
env = rinja.Environment(loader=loader)
|
||||
|
||||
with open(tmp_path / "inc.html", "w") as f:
|
||||
f.write("Included")
|
||||
|
||||
template = env.from_string("Start | {% include 'inc.html' %} | End")
|
||||
assert template.render() == "Start | Included | End"
|
||||
|
||||
def test_include_with_context(tmp_path):
|
||||
loader = rinja.FileSystemLoader(str(tmp_path))
|
||||
env = rinja.Environment(loader=loader)
|
||||
|
||||
with open(tmp_path / "vars.html", "w") as f:
|
||||
f.write("Val: {{ x }}")
|
||||
|
||||
template = env.from_string("{% include 'vars.html' %}")
|
||||
assert template.render(x="42") == "Val: 42"
|
||||
@@ -0,0 +1,13 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_linkify_http():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% linkify %}Visit http://google.com{% endlinkify %}")
|
||||
assert template.render() == 'Visit <a href="http://google.com">http://google.com</a>'
|
||||
|
||||
def test_linkify_https():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% linkify %}Secure https://rinja.io{% endlinkify %}")
|
||||
assert template.render() == 'Secure <a href="https://rinja.io">https://rinja.io</a>'
|
||||
@@ -0,0 +1,9 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_macro_definition():
|
||||
env = rinja.Environment()
|
||||
# Macro defines itself in context (placeholder)
|
||||
template = env.from_string("{% macro hello(name) %}Hello {{ name }}{% endmacro %}")
|
||||
assert template.render() == ""
|
||||
@@ -0,0 +1,59 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_markdown_inline_formatting():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% markdown %}**bold** and *italic*{% endmarkdown %}")
|
||||
result = template.render()
|
||||
assert "<strong>bold</strong>" in result
|
||||
assert "<em>italic</em>" in result
|
||||
|
||||
def test_markdown_headers():
|
||||
env = rinja.Environment()
|
||||
source = """{% markdown %}
|
||||
# Header 1
|
||||
## Header 2
|
||||
### Header 3
|
||||
#### Header 4
|
||||
##### Header 5
|
||||
###### Header 6
|
||||
{% endmarkdown %}"""
|
||||
result = template = env.from_string(source).render()
|
||||
assert "<h1>Header 1</h1>" in result
|
||||
assert "<h2>Header 2</h2>" in result
|
||||
assert "<h3>Header 3</h3>" in result
|
||||
assert "<h4>Header 4</h4>" in result
|
||||
assert "<h5>Header 5</h5>" in result
|
||||
assert "<h6>Header 6</h6>" in result
|
||||
|
||||
def test_markdown_code_block():
|
||||
env = rinja.Environment()
|
||||
source = """{% markdown %}
|
||||
```python
|
||||
def hello():
|
||||
print("world")
|
||||
```
|
||||
{% endmarkdown %}"""
|
||||
result = env.from_string(source).render()
|
||||
assert "<pre><code>" in result
|
||||
assert 'print("world")' in result
|
||||
assert "</code></pre>" in result
|
||||
|
||||
def test_markdown_complex_nesting():
|
||||
env = rinja.Environment()
|
||||
source = """{% markdown %}
|
||||
# Main Title
|
||||
This is a **bold** statement with some *italic* emphasis.
|
||||
|
||||
```c
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
{% endmarkdown %}"""
|
||||
result = env.from_string(source).render()
|
||||
assert "<h1>Main Title</h1>" in result
|
||||
assert "<strong>bold</strong>" in result
|
||||
assert "<em>italic</em>" in result
|
||||
assert "<pre><code>int main() {" in result
|
||||
@@ -0,0 +1,13 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_raw_basic():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% raw %}{{ not_parsed }}{% endraw %}")
|
||||
assert template.render() == "{{ not_parsed }}"
|
||||
|
||||
def test_raw_nested_tags():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% raw %}{% if True %}...{% endif %}{% endraw %}")
|
||||
assert template.render() == "{% if True %}...{% endif %}"
|
||||
@@ -0,0 +1,13 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_set_basic():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% set x = 42 %}{{ x }}")
|
||||
assert template.render() == "42"
|
||||
|
||||
def test_set_expression():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% set x = a + b %}{{ x }}")
|
||||
assert template.render(a=10, b=32) == "42"
|
||||
@@ -0,0 +1,12 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_while_loop_basic():
|
||||
# Note: currently evaluation of variable happens each time
|
||||
# but we don't have a way to decrement from Jinja itself without 'do' or 'set'
|
||||
# using a simple count from context that we change (not possible)
|
||||
# let's test a simple one-time while
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% while x %}Running{% set x = false %}{% endwhile %}")
|
||||
assert template.render(x=True) == "Running"
|
||||
@@ -0,0 +1,14 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_with_basic():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% with x = 10 %}{{ x }}{% endwith %}{{ x }}")
|
||||
# x should be undefined after with
|
||||
assert template.render(x=0) == "100"
|
||||
|
||||
def test_with_nested():
|
||||
env = rinja.Environment()
|
||||
template = env.from_string("{% with x = 1 %}{% with x = 2 %}{{ x }}{% endwith %}{{ x }}{% endwith %}")
|
||||
assert template.render() == "21"
|
||||
@@ -0,0 +1,18 @@
|
||||
import rinja
|
||||
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
def test_test_defined():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ x is defined }}").render(x=1) == "True"
|
||||
assert env.from_string("{{ x is defined }}").render() == "False"
|
||||
|
||||
def test_test_number():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ 1 is number }}").render() == "True"
|
||||
assert env.from_string("{{ 'a' is number }}").render() == "False"
|
||||
|
||||
def test_test_even_odd():
|
||||
env = rinja.Environment()
|
||||
assert env.from_string("{{ 2 is even }}").render() == "True"
|
||||
assert env.from_string("{{ 3 is odd }}").render() == "True"
|
||||
Reference in New Issue
Block a user