Skip to content

Commit

Permalink
Merge pull request #600 from iommirocks/root-component-in-context
Browse files Browse the repository at this point in the history
  • Loading branch information
jlubcke authored Dec 5, 2024
2 parents 7297520 + 666010f commit 9389b74
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 37 deletions.
4 changes: 2 additions & 2 deletions docs/templates/albums.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
{{ query }}

<ul>
{% for car in cars %}
<li>{{ car }}</li>
{% for album in albums %}
<li>{{ album }}</li>
{% endfor %}
</ul>
1 change: 1 addition & 0 deletions docs/test_doc_architecture.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ class User:
row Artist
cells Cells
bound_cell Cell
root Table
"""
# @test
)
Expand Down
35 changes: 34 additions & 1 deletion docs/test_doc_cookbook_general.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
from tests.helpers import req
from django.template import Template

from docs.models import Album
from iommi import (
Form,
html,
Page,
)
from tests.helpers import (
req,
show_output,
)

request = req('get')

Expand Down Expand Up @@ -75,3 +86,25 @@ def test_how_do_i_find_the_path_to_a_parameter():
"""
# @test
assert True # Until I come up with a nice way to test this
# @end


def test_access_other_component(album):
# language=rst
"""
How do I access a sibling component?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Let's say we have a `Page` with a form and a custom template, and we want
to access the form, we can do that via the `root` object:
"""

class MyPage(Page):
edit_album = Form.edit(auto__model=Album, instance=album)
view_album = Template('''
{{ root.parts.edit_album.fields.name.value }}
''')

# @test
show_output(MyPage())
# @end
14 changes: 0 additions & 14 deletions iommi/base__tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,20 +101,6 @@ def test_get_display_name():
assert get_display_name(mock) == 'Some other THING'


def test_crash_in_templates():
# We should crash in template rendering during tests if we try to render non-existent stuff
with pytest.raises(Exception) as e:
Template('{{ foo }}').render(context=RequestContext(req('get')))

assert (
str(e.value) == 'Tried to render non-existent variable foo'
or str(e.value) == "Undefined template variable 'foo' in '<unknown source>'"
)

# ...but inside if it's fine
assert Template('{% if foo %}foo{% endif %}').render(context=RequestContext(req('get'))) == ''


def function_based_view(request):
return HttpResponse('hello!') # pragma: no cover

Expand Down
9 changes: 5 additions & 4 deletions iommi/form__tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1120,10 +1120,11 @@ class MyForm(Form):

assert [x.pk for x in MyForm().bind(request=req('get')).fields.foo.choices] == [user.pk]
assert MyForm().bind(request=req('get', foo=smart_str(user2.pk))).fields.foo._errors == {
'User matching query does not exist.'
f"User matching query does not exist.\n\nQuery kwargs:\n\n pk: '{user2.pk}'",
}
assert MyForm().bind(request=req('get', foo=[smart_str(user2.pk), smart_str(user3.pk)])).fields.foo._errors == {
'User matching query does not exist.'
f"User matching query does not exist.\n\nQuery kwargs:\n\n pk: '{user2.pk}'",
f"User matching query does not exist.\n\nQuery kwargs:\n\n pk: '{user3.pk}'",
}

form = MyForm().bind(request=req('get', foo=[smart_str(user.pk)]))
Expand Down Expand Up @@ -1167,7 +1168,7 @@ class MyForm(Form):

assert [x.pk for x in MyForm().bind(request=req('get')).fields.foo.choices] == [user.pk]
assert MyForm().bind(request=req('get', foo=smart_str(user2.pk))).fields.foo._errors == {
'User matching query does not exist.'
f"User matching query does not exist.\n\nQuery kwargs:\n\n pk: '{user2.pk}'"
}

form = MyForm().bind(request=req('get', foo=[smart_str(user.pk)]))
Expand Down Expand Up @@ -3638,7 +3639,7 @@ class MyForm(Form):

assert [x.pk for x in MyForm().bind(request=req('get')).fields.foo.choices] == [user.pk]
assert MyForm().bind(request=req('get', foo=smart_str(user2.pk))).fields.foo._errors == {
'User matching query does not exist.'
f"User matching query does not exist.\n\nQuery kwargs:\n\n pk: '{user2.pk}'"
}

form = MyForm().bind(request=req('get', foo=[smart_str(user.pk)]))
Expand Down
4 changes: 2 additions & 2 deletions iommi/traversable.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def on_bind(self) -> None:
pass

def own_evaluate_parameters(self):
return {}
return dict(root=self.iommi_root())

def iommi_evaluate_parameters(self):
return self._evaluate_parameters
Expand All @@ -287,7 +287,7 @@ def invoke_callback(self, callback, **kwargs):
):
raise TypeError(
f'TypeError when invoking callback {get_callable_description(callback)}.\n'
f'(Keyword arguments: {", ".join(sorted([*kwargs, *self.iommi_evaluate_parameters()]))})'
f'Keyword arguments:\n {"\n ".join(sorted([*kwargs, *self.iommi_evaluate_parameters()]))}'
) from e
raise

Expand Down
17 changes: 12 additions & 5 deletions iommi/traversable__tests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import itertools
from textwrap import dedent
from typing import Dict
from unittest import mock

Expand Down Expand Up @@ -471,10 +472,16 @@ def test_invoke_callback_error_message_lambda():
with pytest.raises(TypeError) as e:
t.invoke_callback(lambda a: None, b=2)

assert str(e.value) == (
'TypeError when invoking callback lambda found at: `t.invoke_callback(lambda a: None, b=2)`.\n'
'(Keyword arguments: b, params, request, traversable, user)'
)
assert str(e.value) == dedent('''
TypeError when invoking callback lambda found at: `t.invoke_callback(lambda a: None, b=2)`.
Keyword arguments:
b
params
request
root
traversable
user
''').strip()


def test_invoke_callback_error_message_function():
Expand All @@ -489,7 +496,7 @@ def broken_callback(a):
assert actual.startswith(
'TypeError when invoking callback `<function test_invoke_callback_error_message_function.<locals>.broken_callback at 0x'
)
assert actual.endswith('`.\n(Keyword arguments: params, request, traversable, user)')
assert actual.endswith('`.\nKeyword arguments:\n params\n request\n root\n traversable\n user')


def test_invoke_callback_transparent_type_error():
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ filterwarnings =
ignore:'cgi' is deprecated and slated for removal in Python 3.13.*
ignore:Passing unrecognized arguments to super
ignore:.*Reloading models is not advised as it can lead to inconsistencies, most notably with related models.*
ignore:.*FASTDEV_STRICT_IF.*

# Silence warning about pytest 6.0
junit_family=xunit1
Expand Down
1 change: 1 addition & 0 deletions test_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ pytest==8.1.1
pytest_snapshot==0.9.0
ruff==0.3.7
time-machine==2.14.1
django-fastdev
-rrequirements.txt
10 changes: 1 addition & 9 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,13 @@
TEMPLATE_DEBUG = True


class HighlightBrokenVariable:
def __contains__(self, item):
return True

def __mod__(self, other):
assert False, f'Tried to render non-existent variable {other}'


TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': TEMPLATE_DIRS,
'APP_DIRS': True,
'OPTIONS': {
'debug': TEMPLATE_DEBUG,
'string_if_invalid': HighlightBrokenVariable(),
'context_processors': [
'tests.context_processors.context_processor_is_called',
],
Expand Down Expand Up @@ -75,6 +66,7 @@ def __mod__(self, other):
'iommi',
'tests',
'docs',
'django_fastdev',
]


Expand Down

0 comments on commit 9389b74

Please sign in to comment.