Merge pull request #432 from JuroOravec/fix-relative-templates

refactor: use sys.modules to get component file path
This commit is contained in:
Emil Stenström 2024-04-11 23:53:21 +02:00 committed by GitHub
commit 3facfb14a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 37 additions and 13 deletions

View file

@ -1,5 +1,6 @@
import inspect
import os
import sys
from pathlib import Path
from typing import Any, ClassVar, Dict, Iterable, List, Mapping, MutableMapping, Optional, Tuple, Type, Union
@ -73,7 +74,18 @@ def _resolve_component_relative_files(attrs: MutableMapping) -> None:
as the component class. If so, modify the attributes so the class Django's rendering
will pick up these files correctly.
"""
component_name = attrs["__qualname__"]
# Derive the full path of the file where the component was defined
module_name = attrs["__module__"]
module_obj = sys.modules[module_name]
file_path = module_obj.__file__
if not file_path:
logger.debug(
f"Could not resolve the path to the file for component '{component_name}'."
" Paths for HTML, JS or CSS templates will NOT be resolved relative to the component file."
)
return
# Prepare all possible directories we need to check when searching for
# component's template and media files
@ -81,11 +93,11 @@ def _resolve_component_relative_files(attrs: MutableMapping) -> None:
# Get the directory where the component class is defined
try:
comp_dir_abs, comp_dir_rel = _get_dir_path_from_component_module_path(module_name, components_dirs)
comp_dir_abs, comp_dir_rel = _get_dir_path_from_component_path(file_path, components_dirs)
except RuntimeError:
# If no dir was found, we assume that the path is NOT relative to the component dir
logger.debug(
f"No component directory found for component '{module_name}'."
f"No component directory found for component '{component_name}' in {file_path}"
" If this component defines HTML, JS or CSS templates relatively to the component file,"
" then check that the component's directory is accessible from one of the paths"
" specified in the Django's 'STATICFILES_DIRS' settings."
@ -127,17 +139,11 @@ def _resolve_component_relative_files(attrs: MutableMapping) -> None:
media.js = [resolve_file(filepath) for filepath in media.js]
def _get_dir_path_from_component_module_path(
component_module_path: str, candidate_dirs: Union[List[str], List[Path]]
def _get_dir_path_from_component_path(
abs_component_file_path: str,
candidate_dirs: Union[List[str], List[Path]],
) -> Tuple[str, str]:
# Transform python module notation "pkg.module.name" to file path "pkg/module/name"
# Thus, we should get file path relative to Django project root
comp_path = os.sep.join(component_module_path.split("."))
comp_dir_path = os.path.dirname(comp_path)
# NOTE: We assume that Django project root == current working directory!
cwd = os.getcwd()
comp_dir_path_abs = os.path.join(cwd, comp_dir_path)
comp_dir_path_abs = os.path.dirname(abs_component_file_path)
# From all dirs defined in settings.STATICFILES_DIRS, find one that's the parent
# to the component file.
@ -150,7 +156,7 @@ def _get_dir_path_from_component_module_path(
if root_dir_abs is None:
raise RuntimeError(
f"Failed to resolve template directory for component '{component_module_path}'",
f"Failed to resolve template directory for component file '{abs_component_file_path}'",
)
# Derive the path from matched STATICFILES_DIRS to the dir where the current component file is.

View file

@ -186,6 +186,24 @@ class ComponentTest(SimpleTestCase):
),
)
def test_component_with_relative_paths_as_subcomponent(
self,
):
template = Template(
"""
{% load component_tags %}{% component_dependencies %}
{% component 'parent_component' %}
{% fill 'content' %}
{% component name='relative_file_component' variable='hello' %}
{% endcomponent %}
{% endfill %}
{% endcomponent %}
""" # NOQA
)
rendered = template.render(Context({}))
self.assertIn('<input type="text" name="variable" value="hello">', rendered, rendered)
class InlineComponentTest(SimpleTestCase):
def test_inline_html_component(self):