diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76ec4614..10c3e6d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -34,6 +34,10 @@
Now, each subclass has it's own `Template` instance, and changes to the template of the subclass do not affect the template of the parent class.
+- Fix Django failing to restart due to "TypeError: 'Dynamic' object is not iterable" ([#1232](https://github.com/django-components/django-components/issues/1232))
+
+- Fix bug when error formatting failed when error value was not a string.
+
## v0.140.1
#### Fix
diff --git a/docs/reference/.nav.yml b/docs/reference/.nav.yml
index dbea3cb1..5f107924 100644
--- a/docs/reference/.nav.yml
+++ b/docs/reference/.nav.yml
@@ -1,16 +1,16 @@
# `.nav.yml` is provided by https://lukasgeiter.github.io/mkdocs-awesome-nav
nav:
- API: api.md
- - Commands: commands.md
+ - CLI commands: commands.md
- Components: components.md
- Exceptions: exceptions.md
- - Extension commands: extension_commands.md
- Extension hooks: extension_hooks.md
- - Extension URLs: extension_urls.md
+ - Extension commands API: extension_commands.md
+ - Extension URLs API: extension_urls.md
- Settings: settings.md
- Signals: signals.md
- Tag formatters: tag_formatters.md
- Template tags: template_tags.md
- - Template vars: template_vars.md
+ - Template variables: template_vars.md
- URLs: urls.md
- Testing API: testing_api.md
diff --git a/docs/reference/extension_commands.md b/docs/reference/extension_commands.md
index ba29d401..40a18b83 100644
--- a/docs/reference/extension_commands.md
+++ b/docs/reference/extension_commands.md
@@ -1,6 +1,6 @@
-# Extension commands
+# Extension commands API
Overview of all classes, functions, and other objects related to defining extension commands.
diff --git a/docs/reference/extension_urls.md b/docs/reference/extension_urls.md
index f651ceb7..10570d2e 100644
--- a/docs/reference/extension_urls.md
+++ b/docs/reference/extension_urls.md
@@ -1,6 +1,6 @@
-# Extension URLs
+# Extension URLs API
Overview of all classes, functions, and other objects related to defining extension URLs.
diff --git a/docs/reference/template_tags.md b/docs/reference/template_tags.md
index b0fd167b..d5c9145f 100644
--- a/docs/reference/template_tags.md
+++ b/docs/reference/template_tags.md
@@ -67,7 +67,7 @@ If you insert this tag multiple times, ALL JS scripts will be duplicately insert
-See source code
+See source code
diff --git a/docs/reference/template_vars.md b/docs/reference/template_variables.md
similarity index 100%
rename from docs/reference/template_vars.md
rename to docs/reference/template_variables.md
diff --git a/docs/scripts/reference.py b/docs/scripts/reference.py
index 6f1ee7da..28963a22 100644
--- a/docs/scripts/reference.py
+++ b/docs/scripts/reference.py
@@ -44,6 +44,7 @@ from pathlib import Path
from textwrap import dedent
from typing import Any, Dict, List, NamedTuple, Optional, Sequence, Tuple, Type, Union
+from django.conf import settings
from django.core.management.base import BaseCommand
from django.urls import URLPattern, URLResolver
@@ -465,7 +466,13 @@ def gen_reference_commands():
# Add link to source code
module_abs_path = import_module(cmd_def_cls.__module__).__file__
module_rel_path = Path(module_abs_path).relative_to(Path.cwd()).as_posix() # type: ignore[arg-type]
- obj_lineno = inspect.findsource(cmd_def_cls)[1]
+
+ # NOTE: Raises `OSError` if the file is not found.
+ try:
+ obj_lineno = inspect.findsource(cmd_def_cls)[1]
+ except Exception:
+ obj_lineno = None
+
source_code_link = _format_source_code_html(module_rel_path, obj_lineno)
# NOTE: For the commands we have to generate the markdown entries ourselves,
@@ -524,7 +531,7 @@ def gen_reference_commands():
)
-def gen_reference_templatetags():
+def gen_reference_template_tags():
"""
Generate documentation for all Django template tags defined by django-components,
like `{% slot %}`, `{% component %}`, etc.
@@ -537,7 +544,7 @@ def gen_reference_templatetags():
]
preface = "\n\n"
- preface += (root / "docs/templates/reference_templatetags.md").read_text()
+ preface += (root / "docs/templates/reference_template_tags.md").read_text()
out_file = root / "docs/reference/template_tags.md"
out_file.parent.mkdir(parents=True, exist_ok=True)
@@ -585,14 +592,14 @@ def gen_reference_templatetags():
)
-def gen_reference_templatevars():
+def gen_reference_template_variables():
"""
Generate documentation for all variables that are available inside the component templates
under the `{{ component_vars }}` variable, as defined by `ComponentVars`.
"""
preface = "\n\n"
- preface += (root / "docs/templates/reference_templatevars.md").read_text()
- out_file = root / "docs/reference/template_vars.md"
+ preface += (root / "docs/templates/reference_template_variables.md").read_text()
+ out_file = root / "docs/reference/template_variables.md"
out_file.parent.mkdir(parents=True, exist_ok=True)
with out_file.open("w", encoding="utf-8") as f:
@@ -1099,6 +1106,13 @@ def _is_extension_url_api(obj: Any) -> bool:
def gen_reference():
"""The entrypoint to generate all the reference documentation."""
+
+ # Set up Django settings so we can import `extensions`
+ if not settings.configured:
+ settings.configure(
+ BASE_DIR=Path(__file__).parent.parent.parent,
+ )
+
gen_reference_api()
gen_reference_exceptions()
gen_reference_components()
@@ -1106,8 +1120,8 @@ def gen_reference():
gen_reference_tagformatters()
gen_reference_urls()
gen_reference_commands()
- gen_reference_templatetags()
- gen_reference_templatevars()
+ gen_reference_template_tags()
+ gen_reference_template_variables()
gen_reference_signals()
gen_reference_testing_api()
gen_reference_extension_hooks()
diff --git a/docs/templates/reference_extension_commands.md b/docs/templates/reference_extension_commands.md
index 0df90645..9d9c51ea 100644
--- a/docs/templates/reference_extension_commands.md
+++ b/docs/templates/reference_extension_commands.md
@@ -1,4 +1,4 @@
-# Extension commands
+# Extension commands API
Overview of all classes, functions, and other objects related to defining extension commands.
diff --git a/docs/templates/reference_extension_urls.md b/docs/templates/reference_extension_urls.md
index 10692b23..eaa55135 100644
--- a/docs/templates/reference_extension_urls.md
+++ b/docs/templates/reference_extension_urls.md
@@ -1,4 +1,4 @@
-# Extension URLs
+# Extension URLs API
Overview of all classes, functions, and other objects related to defining extension URLs.
diff --git a/docs/templates/reference_templatetags.md b/docs/templates/reference_template_tags.md
similarity index 100%
rename from docs/templates/reference_templatetags.md
rename to docs/templates/reference_template_tags.md
diff --git a/docs/templates/reference_templatevars.md b/docs/templates/reference_template_variables.md
similarity index 100%
rename from docs/templates/reference_templatevars.md
rename to docs/templates/reference_template_variables.md
diff --git a/src/django_components/app_settings.py b/src/django_components/app_settings.py
index 53bfacc8..ab26519a 100644
--- a/src/django_components/app_settings.py
+++ b/src/django_components/app_settings.py
@@ -746,8 +746,8 @@ defaults = ComponentsSettings(
#
# Settings are loaded from Django settings only once, at `apps.py` in `ready()`.
class InternalSettings:
- def __init__(self, settings: Optional[Dict[str, Any]] = None):
- self._settings = ComponentsSettings(**settings) if settings else defaults
+ def __init__(self) -> None:
+ self._settings: Optional[ComponentsSettings] = None
def _load_settings(self) -> None:
data = getattr(settings, "COMPONENTS", {})
@@ -786,6 +786,11 @@ class InternalSettings:
tag_formatter=default(components_settings.tag_formatter, defaults.tag_formatter), # type: ignore[arg-type]
)
+ def _get_settings(self) -> ComponentsSettings:
+ if self._settings is None:
+ self._load_settings()
+ return cast(ComponentsSettings, self._settings)
+
def _prepare_extensions(self, new_settings: ComponentsSettings) -> List["ComponentExtension"]:
extensions: Sequence[Union[Type["ComponentExtension"], str]] = default(
new_settings.extensions, cast(List[str], defaults.extensions)
@@ -853,70 +858,73 @@ class InternalSettings:
return raw_value
- # TODO REMOVE THE PROPERTIES BELOW? THEY NO LONGER SERVE ANY PURPOSE
@property
def AUTODISCOVER(self) -> bool:
- return self._settings.autodiscover # type: ignore[return-value]
+ return self._get_settings().autodiscover # type: ignore[return-value]
@property
def CACHE(self) -> Optional[str]:
- return self._settings.cache
+ return self._get_settings().cache
@property
def DIRS(self) -> Sequence[Union[str, PathLike, Tuple[str, str], Tuple[str, PathLike]]]:
- return self._settings.dirs # type: ignore[return-value]
+ return self._get_settings().dirs # type: ignore[return-value]
@property
def APP_DIRS(self) -> Sequence[str]:
- return self._settings.app_dirs # type: ignore[return-value]
+ return self._get_settings().app_dirs # type: ignore[return-value]
@property
def DEBUG_HIGHLIGHT_COMPONENTS(self) -> bool:
- return self._settings.debug_highlight_components # type: ignore[return-value]
+ return self._get_settings().debug_highlight_components # type: ignore[return-value]
@property
def DEBUG_HIGHLIGHT_SLOTS(self) -> bool:
- return self._settings.debug_highlight_slots # type: ignore[return-value]
+ return self._get_settings().debug_highlight_slots # type: ignore[return-value]
@property
def DYNAMIC_COMPONENT_NAME(self) -> str:
- return self._settings.dynamic_component_name # type: ignore[return-value]
+ return self._get_settings().dynamic_component_name # type: ignore[return-value]
@property
def LIBRARIES(self) -> List[str]:
- return self._settings.libraries # type: ignore[return-value]
+ return self._get_settings().libraries # type: ignore[return-value]
@property
def EXTENSIONS(self) -> List["ComponentExtension"]:
- return self._settings.extensions # type: ignore[return-value]
+ return self._get_settings().extensions # type: ignore[return-value]
+
+ @property
+ def EXTENSIONS_DEFAULTS(self) -> Dict[str, Any]:
+ return self._get_settings().extensions_defaults # type: ignore[return-value]
@property
def MULTILINE_TAGS(self) -> bool:
- return self._settings.multiline_tags # type: ignore[return-value]
+ return self._get_settings().multiline_tags # type: ignore[return-value]
@property
def RELOAD_ON_FILE_CHANGE(self) -> bool:
- return self._settings.reload_on_file_change # type: ignore[return-value]
+ return self._get_settings().reload_on_file_change # type: ignore[return-value]
@property
def TEMPLATE_CACHE_SIZE(self) -> int:
- return self._settings.template_cache_size # type: ignore[return-value]
+ return self._get_settings().template_cache_size # type: ignore[return-value]
@property
def STATIC_FILES_ALLOWED(self) -> Sequence[Union[str, re.Pattern]]:
- return self._settings.static_files_allowed # type: ignore[return-value]
+ return self._get_settings().static_files_allowed # type: ignore[return-value]
@property
def STATIC_FILES_FORBIDDEN(self) -> Sequence[Union[str, re.Pattern]]:
- return self._settings.static_files_forbidden # type: ignore[return-value]
+ return self._get_settings().static_files_forbidden # type: ignore[return-value]
@property
def CONTEXT_BEHAVIOR(self) -> ContextBehavior:
- return ContextBehavior(self._settings.context_behavior)
+ return ContextBehavior(self._get_settings().context_behavior)
@property
def TAG_FORMATTER(self) -> Union["TagFormatterABC", str]:
- return self._settings.tag_formatter # type: ignore[return-value]
+ return self._get_settings().tag_formatter # type: ignore[return-value]
app_settings = InternalSettings()
diff --git a/src/django_components/apps.py b/src/django_components/apps.py
index 3a5e4a91..9361f558 100644
--- a/src/django_components/apps.py
+++ b/src/django_components/apps.py
@@ -20,8 +20,6 @@ class ComponentsConfig(AppConfig):
from django_components.extension import extensions
from django_components.util.django_monkeypatch import monkeypatch_template_cls
- app_settings._load_settings()
-
# NOTE: This monkeypatch is applied here, before Django processes any requests.
# To make django-components work with django-debug-toolbar-template-profiler
# See https://github.com/django-components/django-components/discussions/819
diff --git a/src/django_components/commands/ext_run.py b/src/django_components/commands/ext_run.py
index af2825cf..17bda9fd 100644
--- a/src/django_components/commands/ext_run.py
+++ b/src/django_components/commands/ext_run.py
@@ -20,7 +20,7 @@ def _gen_subcommands() -> List[Type[ComponentCommand]]:
commands: List[Type[ComponentCommand]] = []
for extension in extensions.extensions:
ExtCommand = type(
- "ExtCommand",
+ "ExtRunSubcommand_" + extension.name,
(ComponentCommand,),
{
"name": extension.name,
diff --git a/src/django_components/extension.py b/src/django_components/extension.py
index 75ee2b5b..a95d9755 100644
--- a/src/django_components/extension.py
+++ b/src/django_components/extension.py
@@ -1047,7 +1047,7 @@ class ExtensionManager:
# - `MyExtensionBase` is the base class that the extension class inherits from.
bases_list = [ext_base_class]
- all_extensions_defaults = app_settings._settings.extensions_defaults or {}
+ all_extensions_defaults = app_settings.EXTENSIONS_DEFAULTS or {}
extension_defaults = all_extensions_defaults.get(extension.name, None)
if extension_defaults:
# Create dummy class that holds the extension defaults
diff --git a/src/django_components/util/exception.py b/src/django_components/util/exception.py
index b7dbb97b..dc57b7c3 100644
--- a/src/django_components/util/exception.py
+++ b/src/django_components/util/exception.py
@@ -25,7 +25,7 @@ def component_error_message(component_path: List[str]) -> Generator[None, None,
if not components:
orig_msg = str(err.args[0])
else:
- orig_msg = err.args[0].split("\n", 1)[-1]
+ orig_msg = str(err.args[0]).split("\n", 1)[-1]
else:
orig_msg = str(err)