-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Component autodiscovery #13
Comments
I'm not an expert in Python or Django, but I wanted to try to contribute. I most likely made various mistakes, yet you can get the idea of what I attempted to achieve. #slippers.py
import yaml
def autodiscover_components():
html_templates = get_template(".html")
for tag_name, template_path in html_templates:
register.tag(f"{tag_name}", create_component_tag(template_path))
register.tag(f"#{tag_name}", create_component_tag(template_path))
template = select_template(["components.yaml", "components.yml"])
dictionary = {"components": {tag_name: f"'{template_path}/{tag_name}.html'"}}
with open(template, "w") as yaml_file:
yaml.dump(dictionary, stream=yaml_file, default_flow_style=False) #apps.py
import asyncio
from pathlib import Path, PosixPath
from django.apps import AppConfig
from django.core.checks import Warning, register
from django.template.exceptions import TemplateDoesNotExist
from django.template.loader import select_template
from django.utils.autoreload import autoreload_started, file_changed
import yaml
from slippers.templatetags.slippers import autodiscover_components, register_components
def get_components_yaml():
return select_template(["components.yaml", "components.yml"])
async def autodiscover():
"""Auto-discover components and add to components.yaml"""
try:
await asyncio.wait(autodiscover_components())
except TemplateDoesNotExist:
pass
async def register_tags():
"""Register tags from components.yaml"""
try:
template = get_components_yaml()
components = yaml.safe_load(template.template.source)
register_components(components.get("components", {}))
except TemplateDoesNotExist:
pass
def watch(sender, **kwargs):
"""Watch when component.yaml changes"""
try:
template = get_components_yaml()
sender.extra_files.add(Path(template.origin.name))
except TemplateDoesNotExist:
pass
def changed(sender, file_path: PosixPath, **kwargs):
"""Refresh tag registry when component.yaml changes"""
if file_path.name == "components.yaml":
print("components.yaml changed. Updating component tags...")
register_tags()
def checks(app_configs, **kwargs):
"""Warn if unable to find components.yaml"""
try:
get_components_yaml()
except TemplateDoesNotExist:
return [
Warning(
"Slippers was unable to find a components.yaml file.",
hint="Make sure it's in a root template directory.",
id="slippers.E001",
)
]
return []
class SlippersConfig(AppConfig):
name = "slippers"
def ready(self):
loop = asyncio.get_event_loop()
loop.run_until_complete(autodiscover())
loop.close()
register(checks)
autoreload_started.connect(watch)
file_changed.connect(changed) When I'm running Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...EEEEEEE........
======================================================================
ERROR: test_kwargs_with_filters (tests.test_templatetags.ComponentTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 470, in parse
compile_func = self.tags[command]
KeyError: '#card'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/maxshapira/Development/public/slippers/tests/test_templatetags.py", line 75, in test_kwargs_with_filters
self.assertHTMLEqual(expected, Template(template).render(Context()))
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 155, in __init__
self.nodelist = self.compile_nodelist()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 193, in compile_nodelist
return parser.parse()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 472, in parse
self.invalid_block_tag(token, command, parse_until)
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 531, in invalid_block_tag
raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 2: '#card'. Did you forget to register or load this tag?
======================================================================
ERROR: test_pass_boolean_flags (tests.test_templatetags.ComponentTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 470, in parse
compile_func = self.tags[command]
KeyError: '#button'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/maxshapira/Development/public/slippers/tests/test_templatetags.py", line 107, in test_pass_boolean_flags
self.assertHTMLEqual(expected, Template(template).render(Context()))
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 155, in __init__
self.nodelist = self.compile_nodelist()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 193, in compile_nodelist
return parser.parse()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 472, in parse
self.invalid_block_tag(token, command, parse_until)
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 531, in invalid_block_tag
raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 2: '#button'. Did you forget to register or load this tag?
======================================================================
ERROR: test_render_as_variable (tests.test_templatetags.ComponentTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 470, in parse
compile_func = self.tags[command]
KeyError: 'avatar'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/maxshapira/Development/public/slippers/tests/test_templatetags.py", line 96, in test_render_as_variable
self.assertHTMLEqual(expected, Template(template).render(Context()))
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 155, in __init__
self.nodelist = self.compile_nodelist()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 193, in compile_nodelist
return parser.parse()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 472, in parse
self.invalid_block_tag(token, command, parse_until)
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 531, in invalid_block_tag
raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 2: 'avatar'. Did you forget to register or load this tag?
======================================================================
ERROR: test_render_block_component (tests.test_templatetags.ComponentTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 470, in parse
compile_func = self.tags[command]
KeyError: '#button'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/maxshapira/Development/public/slippers/tests/test_templatetags.py", line 26, in test_render_block_component
self.assertHTMLEqual(expected, Template(template).render(Context()))
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 155, in __init__
self.nodelist = self.compile_nodelist()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 193, in compile_nodelist
return parser.parse()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 472, in parse
self.invalid_block_tag(token, command, parse_until)
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 531, in invalid_block_tag
raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 2: '#button'. Did you forget to register or load this tag?
======================================================================
ERROR: test_render_inline_component (tests.test_templatetags.ComponentTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 470, in parse
compile_func = self.tags[command]
KeyError: 'avatar'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/maxshapira/Development/public/slippers/tests/test_templatetags.py", line 15, in test_render_inline_component
self.assertHTMLEqual(expected, Template(template).render(Context()))
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 155, in __init__
self.nodelist = self.compile_nodelist()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 193, in compile_nodelist
return parser.parse()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 472, in parse
self.invalid_block_tag(token, command, parse_until)
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 531, in invalid_block_tag
raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 2: 'avatar'. Did you forget to register or load this tag?
======================================================================
ERROR: test_render_nested (tests.test_templatetags.ComponentTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 470, in parse
compile_func = self.tags[command]
KeyError: '#card'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/maxshapira/Development/public/slippers/tests/test_templatetags.py", line 57, in test_render_nested
self.assertHTMLEqual(expected, Template(template).render(Context()))
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 155, in __init__
self.nodelist = self.compile_nodelist()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 193, in compile_nodelist
return parser.parse()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 472, in parse
self.invalid_block_tag(token, command, parse_until)
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 531, in invalid_block_tag
raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 2: '#card'. Did you forget to register or load this tag?
======================================================================
ERROR: test_render_without_children (tests.test_templatetags.ComponentTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 470, in parse
compile_func = self.tags[command]
KeyError: 'icon_button'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/maxshapira/Development/public/slippers/tests/test_templatetags.py", line 39, in test_render_without_children
self.assertHTMLEqual(expected, Template(template).render(Context()))
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 155, in __init__
self.nodelist = self.compile_nodelist()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 193, in compile_nodelist
return parser.parse()
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 472, in parse
self.invalid_block_tag(token, command, parse_until)
File "/Users/maxshapira/Library/Caches/pypoetry/virtualenvs/slippers-0WtscrBH-py3.10/lib/python3.10/site-packages/django/template/base.py", line 531, in invalid_block_tag
raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 2: 'icon_button'. Did you forget to register or load this tag?
----------------------------------------------------------------------
Ran 18 tests in 0.024s
FAILED (errors=7)
Destroying test database for alias 'default'... This is because the |
In line with how Django discovers templates by default in |
Here is the code we are using to register components automatically from any directory called from pathlib import Path
from django.conf import settings
from slippers.templatetags.slippers import register_components
SLIPPERS_SUBDIR = "components"
def register():
"""
Register discovered slippers components.
"""
from django.template import engines
slippers_dirs = []
for backend in engines.all():
for loader in backend.engine.template_loaders:
if not hasattr(loader, "get_dirs"):
continue
for templates_dir in loader.get_dirs():
templates_path = Path(templates_dir)
slippers_dir = templates_path / SLIPPERS_SUBDIR
if slippers_dir.exists():
register_components(
{
template.stem: str(template.relative_to(templates_path))
for template in slippers_dir.glob("*.html")
}
)
slippers_dirs.append(slippers_dir)
if settings.DEBUG:
# To support autoreload for `manage.py runserver`, also add a watch so that
# we re-run this code if new slippers templates are added
from django.dispatch import receiver
from django.utils.autoreload import autoreload_started, file_changed
@receiver(autoreload_started, dispatch_uid="watch_slippers_dirs")
def watch_slippers_dirs(sender, **kwargs):
for path in slippers_dirs:
sender.watch_dir(path, "*.html")
@receiver(file_changed, dispatch_uid="slippers_template_changed")
def template_changed(sender, file_path, **kwargs):
path = Path(file_path)
if path.exists() and path.is_dir():
# This happens when new html files are created, re-run registration
register() We then call this In addition you need an empty components: {} |
This is excellent! I like that component names are derived from the template filenames. Are there use-cases in which one would want to use a both Maybe we should emit warnings when auto-discovery is activated and
|
Maintaining a
yaml
file of components is a little annoying. There should be a built-in way to auto-discover components.I have a few ideas but if you have suggestions, please share. 🙂
The text was updated successfully, but these errors were encountered: