From 9b0f382a9766799a6e0bc467a30402868e800b9d Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Thu, 28 Oct 2021 22:36:45 -0700 Subject: [PATCH 01/22] Auto populate IDOM registered components --- src/django_idom/apps.py | 11 ++++++ src/django_idom/utils.py | 76 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/django_idom/apps.py create mode 100644 src/django_idom/utils.py diff --git a/src/django_idom/apps.py b/src/django_idom/apps.py new file mode 100644 index 00000000..48ff4c85 --- /dev/null +++ b/src/django_idom/apps.py @@ -0,0 +1,11 @@ +from django.apps import AppConfig +from .utils import TemplateLoader + + +class HomeConfig(AppConfig): + name = "django_idom" + + def ready(self): + # Render all templates once when Django is ready + # in order to populate the IDOM component registry + TemplateLoader().render_all() diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py new file mode 100644 index 00000000..8b53d9c4 --- /dev/null +++ b/src/django_idom/utils.py @@ -0,0 +1,76 @@ +import os +from fnmatch import fnmatch +from importlib import import_module + +from django.template import engines +from django.template.loader import ( # noqa Leave this in to preload template locations + get_template, +) +from django.utils.encoding import smart_str + + +class TemplateLoader: + def render_all(self): + """Renders all available HTML templates known to Django.""" + # Get all template folder paths + paths = self._get_paths() + + # Get all HTML template files + templates = self._get_templates(paths) + + # Render all templates + self._render_templates(templates) + + def _get_loaders(self): + """Obtains currently configured template loaders.""" + template_source_loaders = [] + for e in engines.all(): + if hasattr(e, "engine"): + template_source_loaders.extend( + e.engine.get_template_loaders(e.engine.loaders) + ) + loaders = [] + for loader in template_source_loaders: + if hasattr(loader, "loaders"): + loaders.extend(loader.loaders) + else: + loaders.append(loader) + return loaders + + def _get_paths(self): + """Obtains a set of all template directories.""" + paths = set() + for loader in self._get_loaders(): + try: + module = import_module(loader.__module__) + get_template_sources = getattr(module, "get_template_sources", None) + if get_template_sources is None: + get_template_sources = loader.get_template_sources + paths.update(smart_str(origin) for origin in get_template_sources("")) + except (ImportError, AttributeError, TypeError): + pass + + return paths + + def _get_templates(self, paths): + """Obtains a set of all HTML template paths.""" + extensions = [".html"] + templates = set() + for path in paths: + for root, dirs, files in os.walk(path, followlinks=False): + templates.update( + os.path.relpath(os.path.join(root, name), path) + for name in files + if not name.startswith(".") + and any(fnmatch(name, "*%s" % glob) for glob in extensions) + ) + + return templates + + def _render_templates(self, templates): + """Renders all templates. Templates with invalid syntax are ignored.""" + for template in templates: + try: + get_template(template).render({"csrf_token": "123"}) + except Exception: + pass From 7bf6842d9e524a1a36e7f89c2eeedab046670b07 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Thu, 28 Oct 2021 22:40:52 -0700 Subject: [PATCH 02/22] fix styling --- src/django_idom/apps.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/django_idom/apps.py b/src/django_idom/apps.py index 48ff4c85..ed2dbdef 100644 --- a/src/django_idom/apps.py +++ b/src/django_idom/apps.py @@ -1,4 +1,5 @@ from django.apps import AppConfig + from .utils import TemplateLoader From e6e28ea63ff03e0824b92b00d3d106093c6d700b Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Fri, 29 Oct 2021 10:01:59 -0700 Subject: [PATCH 03/22] remove noqa --- src/django_idom/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index 8b53d9c4..848f5792 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -3,9 +3,7 @@ from importlib import import_module from django.template import engines -from django.template.loader import ( # noqa Leave this in to preload template locations - get_template, -) +from django.template.loader import get_template from django.utils.encoding import smart_str From 80e6bd81af63afee86a9f75b027334e93e67c2d2 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Fri, 29 Oct 2021 22:28:59 -0700 Subject: [PATCH 04/22] use regex instead of render --- src/django_idom/apps.py | 7 +++---- src/django_idom/utils.py | 41 ++++++++++++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/django_idom/apps.py b/src/django_idom/apps.py index ed2dbdef..d0bd9b07 100644 --- a/src/django_idom/apps.py +++ b/src/django_idom/apps.py @@ -1,12 +1,11 @@ from django.apps import AppConfig -from .utils import TemplateLoader +from .utils import ComponentPreloader class HomeConfig(AppConfig): name = "django_idom" def ready(self): - # Render all templates once when Django is ready - # in order to populate the IDOM component registry - TemplateLoader().render_all() + # Populate the IDOM component registry when Django is ready + ComponentPreloader().register_all() diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index 848f5792..37aee3e4 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -1,23 +1,25 @@ import os +import re from fnmatch import fnmatch from importlib import import_module from django.template import engines -from django.template.loader import get_template from django.utils.encoding import smart_str +from django_idom.templatetags.idom import _register_component -class TemplateLoader: - def render_all(self): - """Renders all available HTML templates known to Django.""" + +class ComponentPreloader: + def register_all(self): + """Registers all IDOM Components within Django templates.""" # Get all template folder paths paths = self._get_paths() # Get all HTML template files templates = self._get_templates(paths) - # Render all templates - self._render_templates(templates) + # Register all components + self._register_components(templates) def _get_loaders(self): """Obtains currently configured template loaders.""" @@ -57,7 +59,7 @@ def _get_templates(self, paths): for path in paths: for root, dirs, files in os.walk(path, followlinks=False): templates.update( - os.path.relpath(os.path.join(root, name), path) + os.path.join(root, name) for name in files if not name.startswith(".") and any(fnmatch(name, "*%s" % glob) for glob in extensions) @@ -65,10 +67,29 @@ def _get_templates(self, paths): return templates - def _render_templates(self, templates): - """Renders all templates. Templates with invalid syntax are ignored.""" + def _register_components(self, templates): + """Parses templates for IDOM components and then registers them.""" + component_regex = re.compile( + r"\{% idom_component ((\"[^\"']*\")|('[^\"']*')) ((\S)*( )*)%\}" + ) + components = set() + + # Find IDOM components in the template for template in templates: try: - get_template(template).render({"csrf_token": "123"}) + with open(template, "r", encoding="utf-8") as template_file: + match = component_regex.findall(template_file.read()) + if not match: + continue + components.update( + [group[0].replace('"', "").replace("'", "") for group in match] + ) + except Exception: + pass + + # Register IDOM all found IDOM components + for component in components: + try: + _register_component(component) except Exception: pass From 419f5c16aaf794d340b3e5644b9ec663cd6e5710 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Fri, 29 Oct 2021 22:34:45 -0700 Subject: [PATCH 05/22] string cleanup --- src/django_idom/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index 37aee3e4..ef6542d3 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -11,7 +11,7 @@ class ComponentPreloader: def register_all(self): - """Registers all IDOM Components within Django templates.""" + """Registers all IDOM components found within Django templates.""" # Get all template folder paths paths = self._get_paths() From f3dbd6d50844542b4638164484c4e7d0411ea824 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sat, 30 Oct 2021 01:33:38 -0700 Subject: [PATCH 06/22] more robust matching --- src/django_idom/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index ef6542d3..4bb5abf4 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -70,7 +70,7 @@ def _get_templates(self, paths): def _register_components(self, templates): """Parses templates for IDOM components and then registers them.""" component_regex = re.compile( - r"\{% idom_component ((\"[^\"']*\")|('[^\"']*')) ((\S)*( )*)%\}" + r"\{% *idom_component ((\"[^\"']*\")|('[^\"']*')).*%\}" ) components = set() From 00b7323fc7be653cdc2c55ece83f6a744ca14458 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sat, 30 Oct 2021 01:44:53 -0700 Subject: [PATCH 07/22] remove unneeded escape slashes --- src/django_idom/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index 4bb5abf4..eb6ab18d 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -70,7 +70,7 @@ def _get_templates(self, paths): def _register_components(self, templates): """Parses templates for IDOM components and then registers them.""" component_regex = re.compile( - r"\{% *idom_component ((\"[^\"']*\")|('[^\"']*')).*%\}" + r"{% *idom_component ((\"[^\"']*\")|('[^\"']*')).*%}" ) components = set() From 7cf9aabfa8598d0250641d6f4d8548b440262ca3 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sat, 30 Oct 2021 01:51:41 -0700 Subject: [PATCH 08/22] move _register_component to utils --- src/django_idom/templatetags/idom.py | 31 ++-------------------------- src/django_idom/utils.py | 26 ++++++++++++++++++++++- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/django_idom/templatetags/idom.py b/src/django_idom/templatetags/idom.py index ba44878c..2a92dd2a 100644 --- a/src/django_idom/templatetags/idom.py +++ b/src/django_idom/templatetags/idom.py @@ -1,16 +1,12 @@ import json -import sys from importlib import import_module from urllib.parse import urlencode from uuid import uuid4 from django import template -from django_idom.config import ( - IDOM_REGISTERED_COMPONENTS, - IDOM_WEB_MODULES_URL, - IDOM_WEBSOCKET_URL, -) +from django_idom.config import IDOM_WEB_MODULES_URL, IDOM_WEBSOCKET_URL +from django_idom.utils import _register_component register = template.Library() @@ -31,26 +27,3 @@ def idom_component(_component_id_, **kwargs): "idom_component_id": _component_id_, "idom_component_params": urlencode({"kwargs": json_kwargs}), } - - -def _register_component(full_component_name: str) -> None: - module_name, component_name = full_component_name.rsplit(".", 1) - - if module_name in sys.modules: - module = sys.modules[module_name] - else: - try: - module = import_module(module_name) - except ImportError as error: - raise RuntimeError( - f"Failed to import {module_name!r} while loading {component_name!r}" - ) from error - - try: - component = getattr(module, component_name) - except AttributeError as error: - raise RuntimeError( - f"Module {module_name!r} has no component named {component_name!r}" - ) from error - - IDOM_REGISTERED_COMPONENTS[full_component_name] = component diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index eb6ab18d..aaa571e9 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -1,12 +1,36 @@ import os import re +import sys from fnmatch import fnmatch from importlib import import_module from django.template import engines from django.utils.encoding import smart_str -from django_idom.templatetags.idom import _register_component +from django_idom.config import IDOM_REGISTERED_COMPONENTS + + +def _register_component(full_component_name: str) -> None: + module_name, component_name = full_component_name.rsplit(".", 1) + + if module_name in sys.modules: + module = sys.modules[module_name] + else: + try: + module = import_module(module_name) + except ImportError as error: + raise RuntimeError( + f"Failed to import {module_name!r} while loading {component_name!r}" + ) from error + + try: + component = getattr(module, component_name) + except AttributeError as error: + raise RuntimeError( + f"Module {module_name!r} has no component named {component_name!r}" + ) from error + + IDOM_REGISTERED_COMPONENTS[full_component_name] = component class ComponentPreloader: From 3368eaab3b6e157f6cf8d9351cb47d59755e94bf Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sat, 30 Oct 2021 01:52:54 -0700 Subject: [PATCH 09/22] use absolute django idom import path --- src/django_idom/apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/django_idom/apps.py b/src/django_idom/apps.py index d0bd9b07..79184552 100644 --- a/src/django_idom/apps.py +++ b/src/django_idom/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig -from .utils import ComponentPreloader +from django_idom.utils import ComponentPreloader class HomeConfig(AppConfig): From 30ad8136f57ab815586c444d02beab4f7cdb2351 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sat, 30 Oct 2021 01:54:24 -0700 Subject: [PATCH 10/22] remove unused import --- src/django_idom/templatetags/idom.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/django_idom/templatetags/idom.py b/src/django_idom/templatetags/idom.py index 2a92dd2a..67ac90a1 100644 --- a/src/django_idom/templatetags/idom.py +++ b/src/django_idom/templatetags/idom.py @@ -1,5 +1,4 @@ import json -from importlib import import_module from urllib.parse import urlencode from uuid import uuid4 From 8b500cbea2c9501da18d04b970ffa91f353e5fb5 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sat, 30 Oct 2021 14:58:57 -0700 Subject: [PATCH 11/22] add logging to auto registration --- src/django_idom/utils.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index aaa571e9..1d0f1260 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -1,3 +1,4 @@ +import logging import os import re import sys @@ -10,6 +11,9 @@ from django_idom.config import IDOM_REGISTERED_COMPONENTS +_logger = logging.getLogger(__name__) + + def _register_component(full_component_name: str) -> None: module_name, component_name = full_component_name.rsplit(".", 1) @@ -38,12 +42,12 @@ def register_all(self): """Registers all IDOM components found within Django templates.""" # Get all template folder paths paths = self._get_paths() - # Get all HTML template files templates = self._get_templates(paths) - + # Get all components + components = self._get_components(templates) # Register all components - self._register_components(templates) + self._register_components(components) def _get_loaders(self): """Obtains currently configured template loaders.""" @@ -91,7 +95,7 @@ def _get_templates(self, paths): return templates - def _register_components(self, templates): + def _get_components(self, templates): """Parses templates for IDOM components and then registers them.""" component_regex = re.compile( r"{% *idom_component ((\"[^\"']*\")|('[^\"']*')).*%}" @@ -111,9 +115,13 @@ def _register_components(self, templates): except Exception: pass + return components + + def _register_components(self, components): # Register IDOM all found IDOM components for component in components: try: _register_component(component) + _logger.info("IDOM has registered component %s", component) except Exception: - pass + _logger.warning("IDOM failed to register component %s", component) From f2c60966acd118c53d1485d15011f316935286f2 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sat, 30 Oct 2021 15:21:08 -0700 Subject: [PATCH 12/22] clean up docstring and comments --- src/django_idom/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index 1d0f1260..a0c5d952 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -101,8 +101,6 @@ def _get_components(self, templates): r"{% *idom_component ((\"[^\"']*\")|('[^\"']*')).*%}" ) components = set() - - # Find IDOM components in the template for template in templates: try: with open(template, "r", encoding="utf-8") as template_file: @@ -118,7 +116,7 @@ def _get_components(self, templates): return components def _register_components(self, components): - # Register IDOM all found IDOM components + """Registers all IDOM components in an iterable.""" for component in components: try: _register_component(component) From 7c84ef3feebc2a826e5d2398a93c7f1eeee4dacf Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sat, 30 Oct 2021 15:26:14 -0700 Subject: [PATCH 13/22] remove unneeded sys.modules check --- src/django_idom/utils.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index a0c5d952..974b8cb0 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -1,7 +1,6 @@ import logging import os import re -import sys from fnmatch import fnmatch from importlib import import_module @@ -17,15 +16,12 @@ def _register_component(full_component_name: str) -> None: module_name, component_name = full_component_name.rsplit(".", 1) - if module_name in sys.modules: - module = sys.modules[module_name] - else: - try: - module = import_module(module_name) - except ImportError as error: - raise RuntimeError( - f"Failed to import {module_name!r} while loading {component_name!r}" - ) from error + try: + module = import_module(module_name) + except ImportError as error: + raise RuntimeError( + f"Failed to import {module_name!r} while loading {component_name!r}" + ) from error try: component = getattr(module, component_name) From daabf623b607e775254d26c140f8d9da29881de7 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sat, 30 Oct 2021 15:29:15 -0700 Subject: [PATCH 14/22] avoid multiple component registrations --- src/django_idom/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index 974b8cb0..3230c7e0 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -14,6 +14,9 @@ def _register_component(full_component_name: str) -> None: + if IDOM_REGISTERED_COMPONENTS.get(full_component_name): + return + module_name, component_name = full_component_name.rsplit(".", 1) try: From afe643ed4d515b797ebb62800172a5fcf1ee49ba Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sat, 30 Oct 2021 15:32:07 -0700 Subject: [PATCH 15/22] use in instead of get --- src/django_idom/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index 3230c7e0..d0073b84 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -14,7 +14,7 @@ def _register_component(full_component_name: str) -> None: - if IDOM_REGISTERED_COMPONENTS.get(full_component_name): + if full_component_name in IDOM_REGISTERED_COMPONENTS: return module_name, component_name = full_component_name.rsplit(".", 1) From 5b2e6194a8114993e9e85202cb639d0b13bf3d05 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sun, 31 Oct 2021 16:12:50 -0700 Subject: [PATCH 16/22] fix _get_components docstring --- src/django_idom/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index d0073b84..8037f1c0 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -95,7 +95,7 @@ def _get_templates(self, paths): return templates def _get_components(self, templates): - """Parses templates for IDOM components and then registers them.""" + """Obtains a set of all IDOM components by parsing HTML templates.""" component_regex = re.compile( r"{% *idom_component ((\"[^\"']*\")|('[^\"']*')).*%}" ) From bc1666e4ed12d845136282f57b6c628a4bda89a6 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sun, 31 Oct 2021 16:14:43 -0700 Subject: [PATCH 17/22] add type hints --- src/django_idom/utils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index 8037f1c0..7f0a11e5 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -3,6 +3,7 @@ import re from fnmatch import fnmatch from importlib import import_module +from typing import Set from django.template import engines from django.utils.encoding import smart_str @@ -64,7 +65,7 @@ def _get_loaders(self): loaders.append(loader) return loaders - def _get_paths(self): + def _get_paths(self) -> Set: """Obtains a set of all template directories.""" paths = set() for loader in self._get_loaders(): @@ -79,7 +80,7 @@ def _get_paths(self): return paths - def _get_templates(self, paths): + def _get_templates(self, paths: Set) -> Set: """Obtains a set of all HTML template paths.""" extensions = [".html"] templates = set() @@ -94,7 +95,7 @@ def _get_templates(self, paths): return templates - def _get_components(self, templates): + def _get_components(self, templates: Set) -> Set: """Obtains a set of all IDOM components by parsing HTML templates.""" component_regex = re.compile( r"{% *idom_component ((\"[^\"']*\")|('[^\"']*')).*%}" @@ -114,7 +115,7 @@ def _get_components(self, templates): return components - def _register_components(self, components): + def _register_components(self, components: Set) -> None: """Registers all IDOM components in an iterable.""" for component in components: try: From fa46d509700e08ffc8fe6d5e4c8bf77bd93d4642 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Sun, 31 Oct 2021 20:15:11 -0700 Subject: [PATCH 18/22] fix appconfig name --- src/django_idom/apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/django_idom/apps.py b/src/django_idom/apps.py index 79184552..6a963664 100644 --- a/src/django_idom/apps.py +++ b/src/django_idom/apps.py @@ -3,7 +3,7 @@ from django_idom.utils import ComponentPreloader -class HomeConfig(AppConfig): +class DjangoIdomConfig(AppConfig): name = "django_idom" def ready(self): From d030b869a1b210d3a290fb5958fcaa128f3f081a Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Mon, 1 Nov 2021 01:19:09 -0700 Subject: [PATCH 19/22] add component regex tests --- src/django_idom/utils.py | 5 ++-- tests/test_app/tests/__init__.py | 1 + tests/test_app/tests/test_regex.py | 41 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 tests/test_app/tests/__init__.py create mode 100644 tests/test_app/tests/test_regex.py diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index 7f0a11e5..036bffb7 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -11,6 +11,7 @@ from django_idom.config import IDOM_REGISTERED_COMPONENTS +COMPONENT_REGEX_PATTERN = r"{% *idom_component ((\"[^\"']*\")|('[^\"']*')).*?%}" _logger = logging.getLogger(__name__) @@ -97,9 +98,7 @@ def _get_templates(self, paths: Set) -> Set: def _get_components(self, templates: Set) -> Set: """Obtains a set of all IDOM components by parsing HTML templates.""" - component_regex = re.compile( - r"{% *idom_component ((\"[^\"']*\")|('[^\"']*')).*%}" - ) + component_regex = re.compile(COMPONENT_REGEX_PATTERN) components = set() for template in templates: try: diff --git a/tests/test_app/tests/__init__.py b/tests/test_app/tests/__init__.py new file mode 100644 index 00000000..fff5a11e --- /dev/null +++ b/tests/test_app/tests/__init__.py @@ -0,0 +1 @@ +from . import * # noqa: F401, F403 diff --git a/tests/test_app/tests/test_regex.py b/tests/test_app/tests/test_regex.py new file mode 100644 index 00000000..e3eac988 --- /dev/null +++ b/tests/test_app/tests/test_regex.py @@ -0,0 +1,41 @@ +from django.test import TestCase + +from django_idom.utils import COMPONENT_REGEX_PATTERN + + +class RegexTests(TestCase): + def test_component_regex(self): + self.assertRegex(r'{%idom_component "my.component"%}', COMPONENT_REGEX_PATTERN) + self.assertRegex(r"{%idom_component 'my.component'%}", COMPONENT_REGEX_PATTERN) + self.assertRegex( + r'{% idom_component "my.component" %}', COMPONENT_REGEX_PATTERN + ) + self.assertRegex( + r"{% idom_component 'my.component' %}", COMPONENT_REGEX_PATTERN + ) + self.assertRegex( + r'{% idom_component "my.component" class="my_thing" %}', + COMPONENT_REGEX_PATTERN, + ) + self.assertRegex( + r'{% idom_component "my.component" class="my_thing" attr="attribute" %}', + COMPONENT_REGEX_PATTERN, + ) + self.assertNotRegex( + r'{% not_a_real_thing "my.component" %}', COMPONENT_REGEX_PATTERN + ) + self.assertNotRegex( + r"{% idom_component my.component %}", COMPONENT_REGEX_PATTERN + ) + self.assertNotRegex( + r"""{% idom_component 'my.component" %}""", COMPONENT_REGEX_PATTERN + ) + self.assertNotRegex( + r'{ idom_component "my.component" }', COMPONENT_REGEX_PATTERN + ) + self.assertNotRegex( + r'{{ idom_component "my.component" }}', COMPONENT_REGEX_PATTERN + ) + self.assertNotRegex(r"idom_component", COMPONENT_REGEX_PATTERN) + self.assertNotRegex(r"{%%}", COMPONENT_REGEX_PATTERN) + self.assertNotRegex(r"", COMPONENT_REGEX_PATTERN) From b7ea800b6fca7fe12ed36cc23d704717471f5827 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Mon, 1 Nov 2021 01:19:31 -0700 Subject: [PATCH 20/22] move selenium tests to folder --- tests/test_app/tests.py | 53 ----------------------- tests/test_app/tests/test_components.py | 57 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 53 deletions(-) delete mode 100644 tests/test_app/tests.py create mode 100644 tests/test_app/tests/test_components.py diff --git a/tests/test_app/tests.py b/tests/test_app/tests.py deleted file mode 100644 index 1b94fe47..00000000 --- a/tests/test_app/tests.py +++ /dev/null @@ -1,53 +0,0 @@ -import os - -from channels.testing import ChannelsLiveServerTestCase -from selenium import webdriver -from selenium.webdriver.common.by import By -from selenium.webdriver.support import expected_conditions -from selenium.webdriver.support.ui import WebDriverWait - - -class TestIdomCapabilities(ChannelsLiveServerTestCase): - def setUp(self): - self.driver = make_driver(5, 5) - self.driver.get(self.live_server_url) - - def tearDown(self) -> None: - self.driver.quit() - - def wait(self, timeout=10): - return WebDriverWait(self.driver, timeout) - - def wait_until(self, condition, timeout=10): - return self.wait(timeout).until(lambda driver: condition()) - - def test_hello_world(self): - self.driver.find_element_by_id("hello-world") - - def test_counter(self): - button = self.driver.find_element_by_id("counter-inc") - count = self.driver.find_element_by_id("counter-num") - - for i in range(5): - self.wait_until(lambda: count.get_attribute("data-count") == str(i)) - button.click() - - def test_parametrized_component(self): - element = self.driver.find_element_by_id("parametrized-component") - self.assertEqual(element.get_attribute("data-value"), "579") - - def test_component_from_web_module(self): - self.wait(20).until( - expected_conditions.visibility_of_element_located( - (By.CLASS_NAME, "VictoryContainer") - ) - ) - - -def make_driver(page_load_timeout, implicit_wait_timeout): - options = webdriver.ChromeOptions() - options.headless = bool(int(os.environ.get("SELENIUM_HEADLESS", 0))) - driver = webdriver.Chrome(options=options) - driver.set_page_load_timeout(page_load_timeout) - driver.implicitly_wait(implicit_wait_timeout) - return driver diff --git a/tests/test_app/tests/test_components.py b/tests/test_app/tests/test_components.py new file mode 100644 index 00000000..cb10cca9 --- /dev/null +++ b/tests/test_app/tests/test_components.py @@ -0,0 +1,57 @@ +import os +import sys + +from channels.testing import ChannelsLiveServerTestCase +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions +from selenium.webdriver.support.ui import WebDriverWait + + +# These tests are broken on Windows due to Selenium +if sys.platform != "win32": + + class TestIdomCapabilities(ChannelsLiveServerTestCase): + def setUp(self): + self.driver = make_driver(5, 5) + self.driver.get(self.live_server_url) + + def tearDown(self) -> None: + self.driver.quit() + + def wait(self, timeout=10): + return WebDriverWait(self.driver, timeout) + + def wait_until(self, condition, timeout=10): + return self.wait(timeout).until(lambda driver: condition()) + + def test_hello_world(self): + self.driver.find_element_by_id("hello-world") + + def test_counter(self): + button = self.driver.find_element_by_id("counter-inc") + count = self.driver.find_element_by_id("counter-num") + + for i in range(5): + self.wait_until(lambda: count.get_attribute("data-count") == str(i)) + button.click() + + def test_parametrized_component(self): + element = self.driver.find_element_by_id("parametrized-component") + self.assertEqual(element.get_attribute("data-value"), "579") + + def test_component_from_web_module(self): + self.wait(20).until( + expected_conditions.visibility_of_element_located( + (By.CLASS_NAME, "VictoryContainer") + ) + ) + + +def make_driver(page_load_timeout, implicit_wait_timeout): + options = webdriver.ChromeOptions() + options.headless = bool(int(os.environ.get("SELENIUM_HEADLESS", 0))) + driver = webdriver.Chrome(options=options) + driver.set_page_load_timeout(page_load_timeout) + driver.implicitly_wait(implicit_wait_timeout) + return driver From 7a9ba2d52fb1229c4def2e72b9ba6203f46bfc26 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Mon, 1 Nov 2021 15:33:52 -0700 Subject: [PATCH 21/22] precompile component regex --- src/django_idom/utils.py | 5 ++-- tests/test_app/tests/test_regex.py | 46 +++++++++++------------------- 2 files changed, 18 insertions(+), 33 deletions(-) diff --git a/src/django_idom/utils.py b/src/django_idom/utils.py index 036bffb7..178995c9 100644 --- a/src/django_idom/utils.py +++ b/src/django_idom/utils.py @@ -11,7 +11,7 @@ from django_idom.config import IDOM_REGISTERED_COMPONENTS -COMPONENT_REGEX_PATTERN = r"{% *idom_component ((\"[^\"']*\")|('[^\"']*')).*?%}" +COMPONENT_REGEX = re.compile(r"{% *idom_component ((\"[^\"']*\")|('[^\"']*')).*?%}") _logger = logging.getLogger(__name__) @@ -98,12 +98,11 @@ def _get_templates(self, paths: Set) -> Set: def _get_components(self, templates: Set) -> Set: """Obtains a set of all IDOM components by parsing HTML templates.""" - component_regex = re.compile(COMPONENT_REGEX_PATTERN) components = set() for template in templates: try: with open(template, "r", encoding="utf-8") as template_file: - match = component_regex.findall(template_file.read()) + match = COMPONENT_REGEX.findall(template_file.read()) if not match: continue components.update( diff --git a/tests/test_app/tests/test_regex.py b/tests/test_app/tests/test_regex.py index e3eac988..a8f50063 100644 --- a/tests/test_app/tests/test_regex.py +++ b/tests/test_app/tests/test_regex.py @@ -1,41 +1,27 @@ from django.test import TestCase -from django_idom.utils import COMPONENT_REGEX_PATTERN +from django_idom.utils import COMPONENT_REGEX class RegexTests(TestCase): def test_component_regex(self): - self.assertRegex(r'{%idom_component "my.component"%}', COMPONENT_REGEX_PATTERN) - self.assertRegex(r"{%idom_component 'my.component'%}", COMPONENT_REGEX_PATTERN) - self.assertRegex( - r'{% idom_component "my.component" %}', COMPONENT_REGEX_PATTERN - ) - self.assertRegex( - r"{% idom_component 'my.component' %}", COMPONENT_REGEX_PATTERN - ) + self.assertRegex(r'{%idom_component "my.component"%}', COMPONENT_REGEX) + self.assertRegex(r"{%idom_component 'my.component'%}", COMPONENT_REGEX) + self.assertRegex(r'{% idom_component "my.component" %}', COMPONENT_REGEX) + self.assertRegex(r"{% idom_component 'my.component' %}", COMPONENT_REGEX) self.assertRegex( r'{% idom_component "my.component" class="my_thing" %}', - COMPONENT_REGEX_PATTERN, + COMPONENT_REGEX, ) self.assertRegex( r'{% idom_component "my.component" class="my_thing" attr="attribute" %}', - COMPONENT_REGEX_PATTERN, - ) - self.assertNotRegex( - r'{% not_a_real_thing "my.component" %}', COMPONENT_REGEX_PATTERN - ) - self.assertNotRegex( - r"{% idom_component my.component %}", COMPONENT_REGEX_PATTERN - ) - self.assertNotRegex( - r"""{% idom_component 'my.component" %}""", COMPONENT_REGEX_PATTERN - ) - self.assertNotRegex( - r'{ idom_component "my.component" }', COMPONENT_REGEX_PATTERN - ) - self.assertNotRegex( - r'{{ idom_component "my.component" }}', COMPONENT_REGEX_PATTERN - ) - self.assertNotRegex(r"idom_component", COMPONENT_REGEX_PATTERN) - self.assertNotRegex(r"{%%}", COMPONENT_REGEX_PATTERN) - self.assertNotRegex(r"", COMPONENT_REGEX_PATTERN) + COMPONENT_REGEX, + ) + self.assertNotRegex(r'{% not_a_real_thing "my.component" %}', COMPONENT_REGEX) + self.assertNotRegex(r"{% idom_component my.component %}", COMPONENT_REGEX) + self.assertNotRegex(r"""{% idom_component 'my.component" %}""", COMPONENT_REGEX) + self.assertNotRegex(r'{ idom_component "my.component" }', COMPONENT_REGEX) + self.assertNotRegex(r'{{ idom_component "my.component" }}', COMPONENT_REGEX) + self.assertNotRegex(r"idom_component", COMPONENT_REGEX) + self.assertNotRegex(r"{%%}", COMPONENT_REGEX) + self.assertNotRegex(r"", COMPONENT_REGEX) From 6142cce890c2ac74bcd1772160c9b9e5a2911779 Mon Sep 17 00:00:00 2001 From: Archmonger <16909269+Archmonger@users.noreply.github.com> Date: Mon, 1 Nov 2021 15:40:23 -0700 Subject: [PATCH 22/22] use for loop for testing regex --- tests/test_app/tests/test_regex.py | 38 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/tests/test_app/tests/test_regex.py b/tests/test_app/tests/test_regex.py index a8f50063..3e418f1d 100644 --- a/tests/test_app/tests/test_regex.py +++ b/tests/test_app/tests/test_regex.py @@ -5,23 +5,25 @@ class RegexTests(TestCase): def test_component_regex(self): - self.assertRegex(r'{%idom_component "my.component"%}', COMPONENT_REGEX) - self.assertRegex(r"{%idom_component 'my.component'%}", COMPONENT_REGEX) - self.assertRegex(r'{% idom_component "my.component" %}', COMPONENT_REGEX) - self.assertRegex(r"{% idom_component 'my.component' %}", COMPONENT_REGEX) - self.assertRegex( + for component in { + r'{%idom_component "my.component"%}', + r"{%idom_component 'my.component'%}", + r'{% idom_component "my.component" %}', + r"{% idom_component 'my.component' %}", r'{% idom_component "my.component" class="my_thing" %}', - COMPONENT_REGEX, - ) - self.assertRegex( r'{% idom_component "my.component" class="my_thing" attr="attribute" %}', - COMPONENT_REGEX, - ) - self.assertNotRegex(r'{% not_a_real_thing "my.component" %}', COMPONENT_REGEX) - self.assertNotRegex(r"{% idom_component my.component %}", COMPONENT_REGEX) - self.assertNotRegex(r"""{% idom_component 'my.component" %}""", COMPONENT_REGEX) - self.assertNotRegex(r'{ idom_component "my.component" }', COMPONENT_REGEX) - self.assertNotRegex(r'{{ idom_component "my.component" }}', COMPONENT_REGEX) - self.assertNotRegex(r"idom_component", COMPONENT_REGEX) - self.assertNotRegex(r"{%%}", COMPONENT_REGEX) - self.assertNotRegex(r"", COMPONENT_REGEX) + }: + self.assertRegex(component, COMPONENT_REGEX) + + for fake_component in { + r'{% not_a_real_thing "my.component" %}', + r"{% idom_component my.component %}", + r"""{% idom_component 'my.component" %}""", + r'{ idom_component "my.component" }', + r'{{ idom_component "my.component" }}', + r"idom_component", + r"{%%}", + r" ", + r"", + }: + self.assertNotRegex(fake_component, COMPONENT_REGEX)