diff --git a/benchmarks/component_rendering.py b/benchmarks/component_rendering.py
index e7c5cf31..2807e562 100644
--- a/benchmarks/component_rendering.py
+++ b/benchmarks/component_rendering.py
@@ -3,10 +3,14 @@ from time import perf_counter
from django.template import Context, Template
from django.test import override_settings
-from django_components.middleware import CSS_DEPENDENCY_PLACEHOLDER, JS_DEPENDENCY_PLACEHOLDER
-from tests.django_test_setup import * # NOQA
from django_components import component
-from tests.testutils import Django30CompatibleSimpleTestCase as SimpleTestCase, create_and_process_template_response
+from django_components.middleware import (
+ CSS_DEPENDENCY_PLACEHOLDER,
+ JS_DEPENDENCY_PLACEHOLDER,
+)
+from tests.django_test_setup import * # NOQA
+from tests.testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
+from tests.testutils import create_and_process_template_response
class SlottedComponent(component.Component):
@@ -31,14 +35,22 @@ class BreadcrumbComponent(component.Component):
template_name = "mdn_component_template.html"
LINKS = [
- ('https://developer.mozilla.org/en-US/docs/Learn',
- 'Learn web development'),
- ('https://developer.mozilla.org/en-US/docs/Learn/HTML',
- 'Structuring the web with HTML'),
- ('https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML',
- 'Introduction to HTML'),
- ('https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure',
- 'Document and website structure')
+ (
+ "https://developer.mozilla.org/en-US/docs/Learn",
+ "Learn web development",
+ ),
+ (
+ "https://developer.mozilla.org/en-US/docs/Learn/HTML",
+ "Structuring the web with HTML",
+ ),
+ (
+ "https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML",
+ "Introduction to HTML",
+ ),
+ (
+ "https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure",
+ "Document and website structure",
+ ),
]
def get_context_data(self, items):
@@ -46,7 +58,7 @@ class BreadcrumbComponent(component.Component):
items = 4
elif items < 0:
items = 0
- return {'links': self.LINKS[:items - 1]}
+ return {"links": self.LINKS[: items - 1]}
class Media:
css = {"all": ["test.css"]}
@@ -57,13 +69,15 @@ EXPECTED_CSS = """"""
EXPECTED_JS = """"""
-@override_settings(COMPONENTS={'RENDER_DEPENDENCIES': True})
+@override_settings(COMPONENTS={"RENDER_DEPENDENCIES": True})
class RenderBenchmarks(SimpleTestCase):
def setUp(self):
component.registry.clear()
- component.registry.register('test_component', SlottedComponent)
- component.registry.register('inner_component', SimpleComponent)
- component.registry.register('breadcrumb_component', BreadcrumbComponent)
+ component.registry.register("test_component", SlottedComponent)
+ component.registry.register("inner_component", SimpleComponent)
+ component.registry.register(
+ "breadcrumb_component", BreadcrumbComponent
+ )
@staticmethod
def timed_loop(func, iterations=1000):
@@ -76,55 +90,81 @@ class RenderBenchmarks(SimpleTestCase):
return total_elapsed * 1000 / iterations
def test_render_time_for_small_component(self):
- template = Template("{% load component_tags %}{% component_block 'test_component' %}"
- "{% slot \"header\" %}{% component 'inner_component' variable='foo' %}{% endslot %}"
- "{% endcomponent_block %}", name='root')
+ template = Template(
+ "{% load component_tags %}{% component_block 'test_component' %}"
+ "{% slot \"header\" %}{% component 'inner_component' variable='foo' %}{% endslot %}"
+ "{% endcomponent_block %}",
+ name="root",
+ )
- print(f'{self.timed_loop(lambda: template.render(Context({})))} ms per iteration')
+ print(
+ f"{self.timed_loop(lambda: template.render(Context({})))} ms per iteration"
+ )
def test_middleware_time_with_dependency_for_small_page(self):
- template = Template("{% load component_tags %}{% component_dependencies %}"
- "{% component_block 'test_component' %}{% slot \"header\" %}"
- "{% component 'inner_component' variable='foo' %}{% endslot %}{% endcomponent_block %}",
- name='root')
+ template = Template(
+ "{% load component_tags %}{% component_dependencies %}"
+ "{% component_block 'test_component' %}{% slot \"header\" %}"
+ "{% component 'inner_component' variable='foo' %}{% endslot %}{% endcomponent_block %}",
+ name="root",
+ )
# Sanity tests
response_content = create_and_process_template_response(template)
self.assertNotIn(CSS_DEPENDENCY_PLACEHOLDER, response_content)
self.assertNotIn(JS_DEPENDENCY_PLACEHOLDER, response_content)
- self.assertIn('style.css', response_content)
- self.assertIn('script.js', response_content)
+ self.assertIn("style.css", response_content)
+ self.assertIn("script.js", response_content)
- without_middleware = self.timed_loop(lambda: create_and_process_template_response(template,
- use_middleware=False))
- with_middleware = self.timed_loop(lambda: create_and_process_template_response(template, use_middleware=True))
+ without_middleware = self.timed_loop(
+ lambda: create_and_process_template_response(
+ template, use_middleware=False
+ )
+ )
+ with_middleware = self.timed_loop(
+ lambda: create_and_process_template_response(
+ template, use_middleware=True
+ )
+ )
- print('Small page middleware test')
+ print("Small page middleware test")
self.report_results(with_middleware, without_middleware)
def test_render_time_with_dependency_for_large_page(self):
from django.template.loader import get_template
- template = get_template('mdn_complete_page.html')
+ template = get_template("mdn_complete_page.html")
response_content = create_and_process_template_response(template, {})
self.assertNotIn(CSS_DEPENDENCY_PLACEHOLDER, response_content)
self.assertNotIn(JS_DEPENDENCY_PLACEHOLDER, response_content)
- self.assertIn('test.css', response_content)
- self.assertIn('test.js', response_content)
+ self.assertIn("test.css", response_content)
+ self.assertIn("test.js", response_content)
without_middleware = self.timed_loop(
- lambda: create_and_process_template_response(template, {}, use_middleware=False))
+ lambda: create_and_process_template_response(
+ template, {}, use_middleware=False
+ )
+ )
with_middleware = self.timed_loop(
- lambda: create_and_process_template_response(template, {}, use_middleware=True))
+ lambda: create_and_process_template_response(
+ template, {}, use_middleware=True
+ )
+ )
- print('Large page middleware test')
+ print("Large page middleware test")
self.report_results(with_middleware, without_middleware)
@staticmethod
def report_results(with_middleware, without_middleware):
- print(f'Middleware active\t\t{with_middleware:.3f} ms per iteration')
- print(f'Middleware inactive\t{without_middleware:.3f} ms per iteration')
+ print(f"Middleware active\t\t{with_middleware:.3f} ms per iteration")
+ print(
+ f"Middleware inactive\t{without_middleware:.3f} ms per iteration"
+ )
time_difference = with_middleware - without_middleware
if without_middleware > with_middleware:
- print(f'Decrease of {-100 * time_difference / with_middleware:.2f}%')
+ print(
+ f"Decrease of {-100 * time_difference / with_middleware:.2f}%"
+ )
else:
- print(f'Increase of {100 * time_difference / without_middleware:.2f}%')
+ print(
+ f"Increase of {100 * time_difference / without_middleware:.2f}%"
+ )
diff --git a/django_components/component.py b/django_components/component.py
index db74c2d6..6c80f7cd 100644
--- a/django_components/component.py
+++ b/django_components/component.py
@@ -9,10 +9,16 @@ from django.template.loader import get_template
from django.utils.safestring import mark_safe
# Allow "component.AlreadyRegistered" instead of having to import these everywhere
-from django_components.component_registry import AlreadyRegistered, ComponentRegistry, NotRegistered # noqa
+from django_components.component_registry import ( # noqa
+ AlreadyRegistered,
+ ComponentRegistry,
+ NotRegistered,
+)
-TEMPLATE_CACHE_SIZE = getattr(settings, "COMPONENTS", {}).get('TEMPLATE_CACHE_SIZE', 128)
-ACTIVE_SLOT_CONTEXT_KEY = '_DJANGO_COMPONENTS_ACTIVE_SLOTS'
+TEMPLATE_CACHE_SIZE = getattr(settings, "COMPONENTS", {}).get(
+ "TEMPLATE_CACHE_SIZE", 128
+)
+ACTIVE_SLOT_CONTEXT_KEY = "_DJANGO_COMPONENTS_ACTIVE_SLOTS"
class SimplifiedInterfaceMediaDefiningClass(MediaDefiningClass):
@@ -54,7 +60,9 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
def get_template_name(self, context=None):
if not self.template_name:
- raise ImproperlyConfigured(f'Template name is not set for Component {self.__class__.__name__}')
+ raise ImproperlyConfigured(
+ f"Template name is not set for Component {self.__class__.__name__}"
+ )
return self.template_name
@@ -75,13 +83,19 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
@staticmethod
def slots_in_template(template):
- return {node.name: node.nodelist for node in template.template.nodelist if Component.is_slot_node(node)}
+ return {
+ node.name: node.nodelist
+ for node in template.template.nodelist
+ if Component.is_slot_node(node)
+ }
@staticmethod
def is_slot_node(node):
- return (isinstance(node, Node)
- and node.token.token_type == TokenType.BLOCK
- and node.token.split_contents()[0] == "slot")
+ return (
+ isinstance(node, Node)
+ and node.token.token_type == TokenType.BLOCK
+ and node.token.split_contents()[0] == "slot"
+ )
@lru_cache(maxsize=TEMPLATE_CACHE_SIZE)
def get_processed_template(self, template_name):
@@ -117,16 +131,16 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
return component_template
def render(self, context):
- if hasattr(self, 'context'):
+ if hasattr(self, "context"):
warnings.warn(
- f'{self.__class__.__name__}: `context` method is deprecated, use `get_context` instead',
- DeprecationWarning
+ f"{self.__class__.__name__}: `context` method is deprecated, use `get_context` instead",
+ DeprecationWarning,
)
- if hasattr(self, 'template'):
+ if hasattr(self, "template"):
warnings.warn(
- f'{self.__class__.__name__}: `template` method is deprecated, set `template_name` or override `get_template_name` instead',
- DeprecationWarning
+ f"{self.__class__.__name__}: `template` method is deprecated, set `template_name` or override `get_template_name` instead",
+ DeprecationWarning,
)
template_name = self.template(context)
else:
diff --git a/django_components/component_registry.py b/django_components/component_registry.py
index 68843a60..0f807ff0 100644
--- a/django_components/component_registry.py
+++ b/django_components/component_registry.py
@@ -12,7 +12,9 @@ class ComponentRegistry(object):
def register(self, name=None, component=None):
if name in self._registry:
- raise AlreadyRegistered('The component "%s" is already registered' % name)
+ raise AlreadyRegistered(
+ 'The component "%s" is already registered' % name
+ )
self._registry[name] = component
diff --git a/django_components/middleware.py b/django_components/middleware.py
index 5e0b2fb8..fef98146 100644
--- a/django_components/middleware.py
+++ b/django_components/middleware.py
@@ -8,11 +8,13 @@ RENDERED_COMPONENTS_CONTEXT_KEY = "_COMPONENT_DEPENDENCIES"
CSS_DEPENDENCY_PLACEHOLDER = ''
JS_DEPENDENCY_PLACEHOLDER = '
- """).strip())
+ """
+ ).strip(),
+ )
- self.assertHTMLEqual(comp.render(context), dedent("""
+ self.assertHTMLEqual(
+ comp.render(context),
+ dedent(
+ """
Variable: test
- """).lstrip())
+ """
+ ).lstrip(),
+ )
def test_component_with_list_of_styles(self):
class MultistyleComponent(component.Component):
@@ -50,12 +60,17 @@ class ComponentTest(SimpleTestCase):
comp = MultistyleComponent("multistyle_component")
- self.assertHTMLEqual(comp.render_dependencies(), dedent("""
+ self.assertHTMLEqual(
+ comp.render_dependencies(),
+ dedent(
+ """
- """).strip())
+ """
+ ).strip(),
+ )
def test_component_with_filtered_template(self):
class FilteredComponent(component.Component):
@@ -70,15 +85,25 @@ class ComponentTest(SimpleTestCase):
comp = FilteredComponent("filtered_component")
context = Context(comp.get_context_data(var1="test1", var2="test2"))
- self.assertHTMLEqual(comp.render(context), dedent("""
+ self.assertHTMLEqual(
+ comp.render(context),
+ dedent(
+ """
Var1: test1
Var2 (uppercased): TEST2
- """).lstrip())
+ """
+ ).lstrip(),
+ )
def test_component_with_dynamic_template(self):
class SvgComponent(component.Component):
def get_context_data(self, name, css_class="", title="", **attrs):
- return {"name": name, "css_class": css_class, "title": title, **attrs}
+ return {
+ "name": name,
+ "css_class": css_class,
+ "title": title,
+ **attrs,
+ }
def get_template_name(self, context):
return f"svg_{context['name']}.svg"
@@ -86,15 +111,19 @@ class ComponentTest(SimpleTestCase):
comp = SvgComponent("svg_component")
self.assertHTMLEqual(
comp.render(Context(comp.get_context_data(name="dynamic1"))),
- dedent("""\
+ dedent(
+ """\
- """)
+ """
+ ),
)
self.assertHTMLEqual(
comp.render(Context(comp.get_context_data(name="dynamic2"))),
- dedent("""\
+ dedent(
+ """\
- """)
+ """
+ ),
)
@@ -108,10 +137,12 @@ class ComponentMediaTests(SimpleTestCase):
comp = SimpleComponent("")
self.assertHTMLEqual(
comp.render_dependencies(),
- dedent("""\
+ dedent(
+ """\
- """)
+ """
+ ),
)
def test_component_media_with_lists(self):
@@ -123,11 +154,13 @@ class ComponentMediaTests(SimpleTestCase):
comp = SimpleComponent("")
self.assertHTMLEqual(
comp.render_dependencies(),
- dedent("""\
+ dedent(
+ """\
- """)
+ """
+ ),
)
def test_component_media_with_dict_and_list(self):
@@ -143,12 +176,14 @@ class ComponentMediaTests(SimpleTestCase):
comp = SimpleComponent("")
self.assertHTMLEqual(
comp.render_dependencies(),
- dedent("""\
+ dedent(
+ """\
- """)
+ """
+ ),
)
def test_component_media_with_dict_with_list_and_list(self):
@@ -160,10 +195,12 @@ class ComponentMediaTests(SimpleTestCase):
comp = SimpleComponent("")
self.assertHTMLEqual(
comp.render_dependencies(),
- dedent("""\
+ dedent(
+ """\
- """)
+ """
+ ),
)
@@ -172,7 +209,7 @@ class ComponentIsolationTests(SimpleTestCase):
class SlottedComponent(component.Component):
template_name = "slotted_template.html"
- component.registry.register('test', SlottedComponent)
+ component.registry.register("test", SlottedComponent)
def test_instances_of_component_do_not_share_slots(self):
template = Template(
@@ -211,17 +248,17 @@ class ComponentIsolationTests(SimpleTestCase):
value=1;calls=1
', rendered) + self.assertEqual( + rendered, 'value=1;calls=1
', rendered + ) def test_one_context_call_with_simple_component_and_arg(self): - template = Template("{% load component_tags %}{% component name='incrementer' value='2' %}") + template = Template( + "{% load component_tags %}{% component name='incrementer' value='2' %}" + ) rendered = template.render(Context()).strip() - self.assertEqual(rendered, 'value=3;calls=1
', rendered) + self.assertEqual( + rendered, 'value=3;calls=1
', rendered + ) def test_one_context_call_with_component_block(self): - template = Template("{% load component_tags %}" - "{% component_block 'incrementer' %}{% endcomponent_block %}") + template = Template( + "{% load component_tags %}" + "{% component_block 'incrementer' %}{% endcomponent_block %}" + ) rendered = template.render(Context()).strip() - self.assertEqual(rendered, 'value=1;calls=1
', rendered) + self.assertEqual( + rendered, 'value=1;calls=1
', rendered + ) def test_one_context_call_with_component_block_and_arg(self): - template = Template("{% load component_tags %}" - "{% component_block 'incrementer' value='3' %}{% endcomponent_block %}") + template = Template( + "{% load component_tags %}" + "{% component_block 'incrementer' value='3' %}{% endcomponent_block %}" + ) rendered = template.render(Context()).strip() - self.assertEqual(rendered, 'value=4;calls=1
', rendered) + self.assertEqual( + rendered, 'value=4;calls=1
', rendered + ) def test_one_context_call_with_slot(self): - template = Template("{% load component_tags %}" - "{% component_block 'incrementer' %}{% slot 'content' %}" - "slot
{% endslot %}{% endcomponent_block %}") + template = Template( + "{% load component_tags %}" + "{% component_block 'incrementer' %}{% slot 'content' %}" + "slot
{% endslot %}{% endcomponent_block %}" + ) rendered = template.render(Context()).strip() - self.assertEqual(rendered, 'value=1;calls=1
\nslot
', rendered) + self.assertEqual( + rendered, + 'value=1;calls=1
\nslot
', + rendered, + ) def test_one_context_call_with_slot_and_arg(self): - template = Template("{% load component_tags %}" - "{% component_block 'incrementer' value='3' %}{% slot 'content' %}" - "slot
{% endslot %}{% endcomponent_block %}") + template = Template( + "{% load component_tags %}" + "{% component_block 'incrementer' value='3' %}{% slot 'content' %}" + "slot
{% endslot %}{% endcomponent_block %}" + ) rendered = template.render(Context()).strip() - self.assertEqual(rendered, 'value=4;calls=1
\nslot
', rendered) + self.assertEqual( + rendered, + 'value=4;calls=1
\nslot
', + rendered, + ) class ComponentsCanAccessOuterContext(SimpleTestCase): def test_simple_component_can_use_outer_context(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component 'simple_component' %}") - rendered = template.render(Context({'variable': 'outer_value'})).strip() - self.assertIn('outer_value', rendered, rendered) + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component 'simple_component' %}" + ) + rendered = template.render( + Context({"variable": "outer_value"}) + ).strip() + self.assertIn("outer_value", rendered, rendered) class IsolatedContextTests(SimpleTestCase): def test_simple_component_can_pass_outer_context_in_args(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component 'simple_component' variable only %}") - rendered = template.render(Context({'variable': 'outer_value'})).strip() - self.assertIn('outer_value', rendered, rendered) + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component 'simple_component' variable only %}" + ) + rendered = template.render( + Context({"variable": "outer_value"}) + ).strip() + self.assertIn("outer_value", rendered, rendered) def test_simple_component_cannot_use_outer_context(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component 'simple_component' only %}") - rendered = template.render(Context({'variable': 'outer_value'})).strip() - self.assertNotIn('outer_value', rendered, rendered) + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component 'simple_component' only %}" + ) + rendered = template.render( + Context({"variable": "outer_value"}) + ).strip() + self.assertNotIn("outer_value", rendered, rendered) class OuterContextPropertyTests(SimpleTestCase): def test_outer_context_property_with_component(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component 'outer_context_component' only %}") - rendered = template.render(Context({'variable': 'outer_value'})).strip() - self.assertIn('outer_value', rendered, rendered) + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component 'outer_context_component' only %}" + ) + rendered = template.render( + Context({"variable": "outer_value"}) + ).strip() + self.assertIn("outer_value", rendered, rendered) def test_outer_context_property_with_component_block(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'outer_context_component' only %}{% endcomponent_block %}") - rendered = template.render(Context({'variable': 'outer_value'})).strip() - self.assertIn('outer_value', rendered, rendered) + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'outer_context_component' only %}{% endcomponent_block %}" + ) + rendered = template.render( + Context({"variable": "outer_value"}) + ).strip() + self.assertIn("outer_value", rendered, rendered) diff --git a/tests/test_dependency_rendering.py b/tests/test_dependency_rendering.py index 7bad3b81..667636a2 100644 --- a/tests/test_dependency_rendering.py +++ b/tests/test_dependency_rendering.py @@ -1,11 +1,12 @@ from django.template import Template from django.test import override_settings -from .django_test_setup import * # NOQA from django_components import component +from .django_test_setup import * # NOQA from .test_templatetags import SimpleComponent -from .testutils import create_and_process_template_response, Django30CompatibleSimpleTestCase as SimpleTestCase +from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase +from .testutils import create_and_process_template_response class SimpleComponentAlternate(component.Component): @@ -38,7 +39,7 @@ class MultistyleComponent(component.Component): js = ["script.js", "script2.js"] -@override_settings(COMPONENTS={'RENDER_DEPENDENCIES': True}) +@override_settings(COMPONENTS={"RENDER_DEPENDENCIES": True}) class ComponentMediaRenderingTests(SimpleTestCase): def setUp(self): # NOTE: component.registry is global, so need to clear before each test @@ -47,150 +48,280 @@ class ComponentMediaRenderingTests(SimpleTestCase): def test_no_dependencies_when_no_components_used(self): component.registry.register(name="test", component=SimpleComponent) - template = Template("{% load component_tags %}{% component_dependencies %}") + template = Template( + "{% load component_tags %}{% component_dependencies %}" + ) rendered = create_and_process_template_response(template) self.assertInHTML('', rendered, count=0) - self.assertInHTML('', rendered, count=0) + self.assertInHTML( + '', + rendered, + count=0, + ) + self.assertInHTML( + '', + rendered, + count=0, + ) def test_no_js_dependencies_when_no_components_used(self): component.registry.register(name="test", component=SimpleComponent) - template = Template("{% load component_tags %}{% component_js_dependencies %}") + template = Template( + "{% load component_tags %}{% component_js_dependencies %}" + ) rendered = create_and_process_template_response(template) self.assertInHTML('', rendered, count=0) + self.assertInHTML( + '', + rendered, + count=0, + ) def test_single_component_dependencies_render_when_used(self): component.registry.register(name="test", component=SimpleComponent) - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component 'test' variable='foo' %}") + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component 'test' variable='foo' %}" + ) rendered = create_and_process_template_response(template) - self.assertInHTML('', rendered, count=1) + self.assertInHTML( + '', + rendered, + count=1, + ) self.assertInHTML('